Add overheat protection
This commit is contained in:
		
							parent
							
								
									53e3d47a04
								
							
						
					
					
						commit
						7bd0b44871
					
				| @ -9,13 +9,20 @@ PROJ=barista | |||||||
| BUILD=build/$(subst :,.,$(FQBN)) | BUILD=build/$(subst :,.,$(FQBN)) | ||||||
| HEX=$(BUILD)/$(PROJ).ino.hex | HEX=$(BUILD)/$(PROJ).ino.hex | ||||||
| 
 | 
 | ||||||
|  | SRC=barista.ino \
 | ||||||
|  |     ntc.c \
 | ||||||
|  |     ntc.h \
 | ||||||
|  |     pinout.h \
 | ||||||
|  |     overheat.c \
 | ||||||
|  |     overheat.h | ||||||
|  | 
 | ||||||
| # For host test programs
 | # For host test programs
 | ||||||
| CPPFLAGS=-I. | CPPFLAGS=-I. | ||||||
| LIBS=-lm | LIBS=-lm | ||||||
| 
 | 
 | ||||||
| all: $(HEX) | all: $(HEX) | ||||||
| 
 | 
 | ||||||
| $(HEX): barista.ino ntc.c ntc.h pinout.h | $(HEX): $(SRC) | ||||||
| 	arduino-cli compile $(OPTS) -e --fqbn $(FQBN) | 	arduino-cli compile $(OPTS) -e --fqbn $(FQBN) | ||||||
| 
 | 
 | ||||||
| upload: $(HEX) | upload: $(HEX) | ||||||
| @ -24,11 +31,14 @@ upload: $(HEX) | |||||||
| serial: | serial: | ||||||
| 	picocom -b 9600 --lower-rts --lower-dtr /dev/ttyUSB0 --imap lfcrlf | 	picocom -b 9600 --lower-rts --lower-dtr /dev/ttyUSB0 --imap lfcrlf | ||||||
| 
 | 
 | ||||||
| test: test_ntc | test: test_ntc test_overheat | ||||||
| 
 | 
 | ||||||
| test_ntc: test/test_ntc.o ntc.o | test_ntc: test/test_ntc.o ntc.o | ||||||
| 	gcc $^ -o $@ $(LIBS) | 	gcc $^ -o $@ $(LIBS) | ||||||
| 
 | 
 | ||||||
|  | test_overheat: test/test_overheat.o overheat.o | ||||||
|  | 	gcc $^ -o $@ $(LIBS) | ||||||
|  | 
 | ||||||
| clean: | clean: | ||||||
| 	rm -f test/test_ntc.o ntc.o | 	rm -f test/test_ntc.o ntc.o | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -2,6 +2,7 @@ | |||||||
|  * SPDX-License-Identifier: GPL-3.0-or-later */ |  * SPDX-License-Identifier: GPL-3.0-or-later */ | ||||||
| 
 | 
 | ||||||
| #include "ntc.h" | #include "ntc.h" | ||||||
|  | #include "overheat.h" | ||||||
| #include "pinout.h" | #include "pinout.h" | ||||||
| #include <avr/wdt.h> | #include <avr/wdt.h> | ||||||
| 
 | 
 | ||||||
| @ -23,6 +24,7 @@ enum machine_state { | |||||||
| 	HOT, | 	HOT, | ||||||
| 	BREWING, | 	BREWING, | ||||||
| 	COOLING, | 	COOLING, | ||||||
|  | 	PANIC_OVERHEAT, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| //int state = SLEEPING;
 | //int state = SLEEPING;
 | ||||||
| @ -83,6 +85,9 @@ struct state { | |||||||
| 	struct btn btn[MAX_BTN]; | 	struct btn btn[MAX_BTN]; | ||||||
| 	enum buzz_state buzz_state; | 	enum buzz_state buzz_state; | ||||||
| 	unsigned long buzz_t0; | 	unsigned long buzz_t0; | ||||||
|  | 
 | ||||||
|  | 	struct overheat overheat; | ||||||
|  | 	unsigned long overheat_t0; | ||||||
| } g_st; | } g_st; | ||||||
| 
 | 
 | ||||||
| int read_input(int pin) | int read_input(int pin) | ||||||
| @ -133,6 +138,8 @@ void proc_ntc(struct state *state, const struct input *input) | |||||||
| 		avg += state->ntc_array_T[i]; | 		avg += state->ntc_array_T[i]; | ||||||
| 
 | 
 | ||||||
| 	state->ntc_T = avg / MAX_SAMPLES; | 	state->ntc_T = avg / MAX_SAMPLES; | ||||||
|  | 
 | ||||||
|  | 	overheat_input(&state->overheat, millis(), state->ntc_T); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void proc_buttons(struct state *state, const struct input *input) | void proc_buttons(struct state *state, const struct input *input) | ||||||
| @ -169,10 +176,16 @@ void proc_buttons(struct state *state, const struct input *input) | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /* In PANIC_OVERHEAT state wait at least TIME_OVERHEAT_COOL and until the
 | ||||||
|  |  * temperature goes below TIME_OVERHEAT_TEMP before doing anything. */ | ||||||
|  | #define TIME_OVERHEAT_COOL  10000 /* ms */ | ||||||
|  | #define TIME_OVERHEAT_TEMP  40.0  /* °C */ | ||||||
|  | 
 | ||||||
| int red_min = 50; | int red_min = 50; | ||||||
| int red_state = red_min; | int red_state = red_min; | ||||||
| unsigned long brewing_time     =  3000UL; /* 3 seconds */ | unsigned long brewing_time     =  3000UL; /* 3 seconds */ | ||||||
| unsigned long cooling_time     =  3000UL; /* 3 seconds */ | unsigned long cooling_time     =  3000UL; /* 3 seconds */ | ||||||
|  | unsigned long overheat_time    = 10000UL; /* 10 seconds */ | ||||||
| unsigned long max_heating_time = 60000UL; /* 60 seconds */ | unsigned long max_heating_time = 60000UL; /* 60 seconds */ | ||||||
| unsigned long max_idle_time    = 10000UL; /* 10 seconds */ | unsigned long max_idle_time    = 10000UL; /* 10 seconds */ | ||||||
| 
 | 
 | ||||||
| @ -192,6 +205,13 @@ void proc_machine(struct state *st) | |||||||
| 	Serial.print(temp); | 	Serial.print(temp); | ||||||
| 	Serial.println(" C"); | 	Serial.println(" C"); | ||||||
| 
 | 
 | ||||||
|  | 	/* If the machine is overheating */ | ||||||
|  | 	if (overheat_panic(&st->overheat)) { | ||||||
|  | 		st->mstate = PANIC_OVERHEAT; | ||||||
|  | 		st->overheat_t0 = millis(); | ||||||
|  | 		Serial.println("PANIC OVERHEATING"); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	/* Pressing ON cancels any operation */ | 	/* Pressing ON cancels any operation */ | ||||||
| 	if (st->mstate != SLEEPING && on) { | 	if (st->mstate != SLEEPING && on) { | ||||||
| 		st->mstate = SLEEPING; | 		st->mstate = SLEEPING; | ||||||
| @ -238,6 +258,13 @@ void proc_machine(struct state *st) | |||||||
| 			st->heating_t0 = millis(); | 			st->heating_t0 = millis(); | ||||||
| 			Serial.println("heating"); | 			Serial.println("heating"); | ||||||
| 		} | 		} | ||||||
|  | 	} else if (st->mstate == PANIC_OVERHEAT) { | ||||||
|  | 		/* Wait until it cools down and enough time has passed */ | ||||||
|  | 		if (st->ntc_T < TIME_OVERHEAT_TEMP && | ||||||
|  | 				millis() - st->overheat_t0 > TIME_OVERHEAT_COOL) { | ||||||
|  | 			st->mstate = SLEEPING; | ||||||
|  | 			Serial.println("sleeping"); | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -278,18 +305,21 @@ output_leds(const struct state *st) | |||||||
| 		if (r >= 255) | 		if (r >= 255) | ||||||
| 			r = 0; | 			r = 0; | ||||||
| 		else | 		else | ||||||
| 			r += 3; | 			r += 5; | ||||||
| 	} else if (st->mstate == HOT) { | 	} else if (st->mstate == HOT) { | ||||||
| 		setled(PIN_LED_RED, 0); | 		setled(PIN_LED_RED, 0); | ||||||
| 		setled(PIN_LED_GREEN, 1); | 		setled(PIN_LED_GREEN, 1); | ||||||
| 		r = 0; | 		r = 0; | ||||||
|  | 	} else if (st->mstate == PANIC_OVERHEAT) { | ||||||
|  | 		setled(PIN_LED_RED, 1); | ||||||
|  | 		setled(PIN_LED_GREEN, 0); | ||||||
| 	} else if (st->mstate == BREWING) { | 	} else if (st->mstate == BREWING) { | ||||||
| 		setled(PIN_LED_RED, 0); | 		setled(PIN_LED_RED, 0); | ||||||
| 		analogWrite(PIN_LED_GREEN, g); | 		analogWrite(PIN_LED_GREEN, g); | ||||||
| 		if (g >= 255) | 		if (g >= 255) | ||||||
| 			g = 0; | 			g = 0; | ||||||
| 		else | 		else | ||||||
| 			g += 3; | 			g += 5; | ||||||
| 	} else { | 	} else { | ||||||
| 		setled(PIN_LED_RED, 0); | 		setled(PIN_LED_RED, 0); | ||||||
| 		setled(PIN_LED_GREEN, 0); | 		setled(PIN_LED_GREEN, 0); | ||||||
| @ -343,6 +373,8 @@ void setup() | |||||||
| 	relay(PIN_HEAT, OFF); | 	relay(PIN_HEAT, OFF); | ||||||
| 	relay(PIN_PUMP, OFF); | 	relay(PIN_PUMP, OFF); | ||||||
| 
 | 
 | ||||||
|  | 	overheat_init(&g_st.overheat); | ||||||
|  | 
 | ||||||
| 	Serial.println("Ready"); | 	Serial.println("Ready"); | ||||||
| 	wdt_enable(WDTO_250MS); | 	wdt_enable(WDTO_250MS); | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										1886
									
								
								barista/data/dry-heat-50-C.csv
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1886
									
								
								barista/data/dry-heat-50-C.csv
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										52
									
								
								barista/overheat.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								barista/overheat.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,52 @@ | |||||||
|  | /* Copyright (c) 2025 Rodrigo Arias Mallo <rodarima@gmail.com>
 | ||||||
|  |  * SPDX-License-Identifier: GPL-3.0-or-later */ | ||||||
|  | 
 | ||||||
|  | #include "overheat.h" | ||||||
|  | #include <string.h> | ||||||
|  | 
 | ||||||
|  | void | ||||||
|  | overheat_init(struct overheat *o) | ||||||
|  | { | ||||||
|  | 	memset(o, 0, sizeof(struct overheat)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void | ||||||
|  | overheat_input(struct overheat *o, unsigned long t_ms, float temp) | ||||||
|  | { | ||||||
|  | 	unsigned long delta = t_ms - o->last_time; | ||||||
|  | 
 | ||||||
|  | 	if (delta < OVH_INTERVAL) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
|  | 	o->temp[o->next++] = temp; | ||||||
|  | 	if (o->next >= OVH_NSAMPLES) | ||||||
|  | 		o->next = 0; | ||||||
|  | 	if (o->n < OVH_NSAMPLES) | ||||||
|  | 		o->n++; | ||||||
|  | 
 | ||||||
|  | 	/* Recompute state */ | ||||||
|  | 	float tmin = 10000.0, tmax = 0.0; | ||||||
|  | 	for (int i = 0; i < o->n; i++) { | ||||||
|  | 		if (o->temp[i] < tmin) | ||||||
|  | 			tmin = o->temp[i]; | ||||||
|  | 		if (o->temp[i] > tmax) | ||||||
|  | 			tmax = o->temp[i]; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	o->delta = tmax - tmin; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | float | ||||||
|  | overheat_delta(struct overheat *o) | ||||||
|  | { | ||||||
|  | 	return o->delta; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int | ||||||
|  | overheat_panic(struct overheat *o) | ||||||
|  | { | ||||||
|  | 	if (o->delta > OVH_THRESHOLD) | ||||||
|  | 		return 1; | ||||||
|  | 	else | ||||||
|  | 		return 0; | ||||||
|  | } | ||||||
							
								
								
									
										32
									
								
								barista/overheat.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								barista/overheat.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,32 @@ | |||||||
|  | /* Copyright (c) 2025 Rodrigo Arias Mallo <rodarima@gmail.com>
 | ||||||
|  |  * SPDX-License-Identifier: GPL-3.0-or-later */ | ||||||
|  | 
 | ||||||
|  | #ifndef BARISTA_OVERHEAT_H | ||||||
|  | #define BARISTA_OVERHEAT_H | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #define OVH_NSAMPLES	20 | ||||||
|  | #define OVH_INTERVAL	200 /* ms */ | ||||||
|  | #define OVH_THRESHOLD	2.0 /* °C */ | ||||||
|  | 
 | ||||||
|  | struct overheat { | ||||||
|  | 	unsigned long last_time; | ||||||
|  | 	float temp[OVH_NSAMPLES]; | ||||||
|  | 	int next; | ||||||
|  | 	int n; | ||||||
|  | 	float delta; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | void overheat_init(struct overheat *o); | ||||||
|  | void overheat_input(struct overheat *o, unsigned long t_ms, float temp); | ||||||
|  | float overheat_delta(struct overheat *o); | ||||||
|  | int overheat_panic(struct overheat *o); | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #endif /* BARISTA_OVERHEAT_H */ | ||||||
| @ -1,3 +1,6 @@ | |||||||
|  | /* Copyright (c) 2025 Rodrigo Arias Mallo <rodarima@gmail.com>
 | ||||||
|  |  * SPDX-License-Identifier: GPL-3.0-or-later */ | ||||||
|  | 
 | ||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
| #include "ntc.h" | #include "ntc.h" | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										36
									
								
								barista/test/test_overheat.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								barista/test/test_overheat.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,36 @@ | |||||||
|  | /* Copyright (c) 2025 Rodrigo Arias Mallo <rodarima@gmail.com>
 | ||||||
|  |  * SPDX-License-Identifier: GPL-3.0-or-later */ | ||||||
|  | 
 | ||||||
|  | #include <stdio.h> | ||||||
|  | #include "overheat.h" | ||||||
|  | 
 | ||||||
|  | /* Read a CSV from the stdin in the format
 | ||||||
|  |  * <time_in_seconds> <state> <temp_in_C> | ||||||
|  |  * skipping the first row (header). | ||||||
|  |  * | ||||||
|  |  * Outputs overheat state. */ | ||||||
|  | 
 | ||||||
|  | #define MAX_LINE 1024 | ||||||
|  | 
 | ||||||
|  | int main(void) | ||||||
|  | { | ||||||
|  | 	char buf[MAX_LINE]; | ||||||
|  | 	fgets(buf, MAX_LINE, stdin); | ||||||
|  | 
 | ||||||
|  | 	float t, temp; | ||||||
|  | 	int st; | ||||||
|  | 
 | ||||||
|  | 	struct overheat ovh; | ||||||
|  | 	overheat_init(&ovh); | ||||||
|  | 
 | ||||||
|  | 	while (scanf("%f %d %f", &t, &st, &temp) == 3) { | ||||||
|  | 
 | ||||||
|  | 		overheat_input(&ovh, t * 1000.0, temp); | ||||||
|  | 		float delta = overheat_delta(&ovh); | ||||||
|  | 		int panic = overheat_panic(&ovh); | ||||||
|  | 
 | ||||||
|  | 		printf("%.3f %d %.2f %.1f %s\n", t, st, temp, delta, panic ? "PANIC" : "OK"); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Rodrigo Arias Mallo
						Rodrigo Arias Mallo