Add overheat protection
This commit is contained in:
		
							parent
							
								
									53e3d47a04
								
							
						
					
					
						commit
						7bd0b44871
					
				| @ -9,13 +9,20 @@ PROJ=barista | ||||
| BUILD=build/$(subst :,.,$(FQBN)) | ||||
| HEX=$(BUILD)/$(PROJ).ino.hex | ||||
| 
 | ||||
| SRC=barista.ino \
 | ||||
|     ntc.c \
 | ||||
|     ntc.h \
 | ||||
|     pinout.h \
 | ||||
|     overheat.c \
 | ||||
|     overheat.h | ||||
| 
 | ||||
| # For host test programs
 | ||||
| CPPFLAGS=-I. | ||||
| LIBS=-lm | ||||
| 
 | ||||
| all: $(HEX) | ||||
| 
 | ||||
| $(HEX): barista.ino ntc.c ntc.h pinout.h | ||||
| $(HEX): $(SRC) | ||||
| 	arduino-cli compile $(OPTS) -e --fqbn $(FQBN) | ||||
| 
 | ||||
| upload: $(HEX) | ||||
| @ -24,11 +31,14 @@ upload: $(HEX) | ||||
| serial: | ||||
| 	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 | ||||
| 	gcc $^ -o $@ $(LIBS) | ||||
| 
 | ||||
| test_overheat: test/test_overheat.o overheat.o | ||||
| 	gcc $^ -o $@ $(LIBS) | ||||
| 
 | ||||
| clean: | ||||
| 	rm -f test/test_ntc.o ntc.o | ||||
| 
 | ||||
|  | ||||
| @ -2,6 +2,7 @@ | ||||
|  * SPDX-License-Identifier: GPL-3.0-or-later */ | ||||
| 
 | ||||
| #include "ntc.h" | ||||
| #include "overheat.h" | ||||
| #include "pinout.h" | ||||
| #include <avr/wdt.h> | ||||
| 
 | ||||
| @ -23,6 +24,7 @@ enum machine_state { | ||||
| 	HOT, | ||||
| 	BREWING, | ||||
| 	COOLING, | ||||
| 	PANIC_OVERHEAT, | ||||
| }; | ||||
| 
 | ||||
| //int state = SLEEPING;
 | ||||
| @ -83,6 +85,9 @@ struct state { | ||||
| 	struct btn btn[MAX_BTN]; | ||||
| 	enum buzz_state buzz_state; | ||||
| 	unsigned long buzz_t0; | ||||
| 
 | ||||
| 	struct overheat overheat; | ||||
| 	unsigned long overheat_t0; | ||||
| } g_st; | ||||
| 
 | ||||
| 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]; | ||||
| 
 | ||||
| 	state->ntc_T = avg / MAX_SAMPLES; | ||||
| 
 | ||||
| 	overheat_input(&state->overheat, millis(), state->ntc_T); | ||||
| } | ||||
| 
 | ||||
| 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_state = red_min; | ||||
| unsigned long brewing_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_idle_time    = 10000UL; /* 10 seconds */ | ||||
| 
 | ||||
| @ -192,6 +205,13 @@ void proc_machine(struct state *st) | ||||
| 	Serial.print(temp); | ||||
| 	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 */ | ||||
| 	if (st->mstate != SLEEPING && on) { | ||||
| 		st->mstate = SLEEPING; | ||||
| @ -238,6 +258,13 @@ void proc_machine(struct state *st) | ||||
| 			st->heating_t0 = millis(); | ||||
| 			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) | ||||
| 			r = 0; | ||||
| 		else | ||||
| 			r += 3; | ||||
| 			r += 5; | ||||
| 	} else if (st->mstate == HOT) { | ||||
| 		setled(PIN_LED_RED, 0); | ||||
| 		setled(PIN_LED_GREEN, 1); | ||||
| 		r = 0; | ||||
| 	} else if (st->mstate == PANIC_OVERHEAT) { | ||||
| 		setled(PIN_LED_RED, 1); | ||||
| 		setled(PIN_LED_GREEN, 0); | ||||
| 	} else if (st->mstate == BREWING) { | ||||
| 		setled(PIN_LED_RED, 0); | ||||
| 		analogWrite(PIN_LED_GREEN, g); | ||||
| 		if (g >= 255) | ||||
| 			g = 0; | ||||
| 		else | ||||
| 			g += 3; | ||||
| 			g += 5; | ||||
| 	} else { | ||||
| 		setled(PIN_LED_RED, 0); | ||||
| 		setled(PIN_LED_GREEN, 0); | ||||
| @ -343,6 +373,8 @@ void setup() | ||||
| 	relay(PIN_HEAT, OFF); | ||||
| 	relay(PIN_PUMP, OFF); | ||||
| 
 | ||||
| 	overheat_init(&g_st.overheat); | ||||
| 
 | ||||
| 	Serial.println("Ready"); | ||||
| 	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 "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