332 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			332 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* Copyright (c) 2025 Rodrigo Arias Mallo <rodarima@gmail.com>
 | |
|  * SPDX-License-Identifier: GPL-3.0-or-later */
 | |
| 
 | |
| #include "ntc.h"
 | |
| #include "pinout.h"
 | |
| 
 | |
| enum logic {
 | |
| 	ON = 1,
 | |
| 	OFF = 0,
 | |
| };
 | |
| 
 | |
| #define DEBOUNCE_TIME 20L /* ms */
 | |
| #define TEMP_MIN 80.0f
 | |
| #define TEMP_MAX 90.0f
 | |
| 
 | |
| #define LED_MIN_VALUE 0
 | |
| 
 | |
| enum state {
 | |
| 	SLEEPING,
 | |
| 	DEBOUNCE,
 | |
| 	HEATING,
 | |
| 	HOT,
 | |
| 	BREWING,
 | |
| 	COOLING,
 | |
| };
 | |
| 
 | |
| long debounce_t0 = 0;
 | |
| int state = SLEEPING;
 | |
| 
 | |
| enum button {
 | |
| 	BUTTON_ON = 0,
 | |
| 	BUTTON_HOT,
 | |
| 	MAX_BUTTON,
 | |
| };
 | |
| 
 | |
| enum button_state {
 | |
| 	RESTING = 0,
 | |
| 	PRESSING,
 | |
| 	PRESSED,
 | |
| 	RELEASING,
 | |
| 	RELEASED,
 | |
| };
 | |
| 
 | |
| enum buzz_state {
 | |
| 	BUZZ_OFF = 0,
 | |
| 	BUZZ_HEY,
 | |
| 	BUZZ_STOP,
 | |
| };
 | |
| 
 | |
| int buzz_state;
 | |
| 
 | |
| int button_pin[MAX_BUTTON] = {
 | |
| 	[BUTTON_ON] = PIN_POWER_ON,
 | |
| 	[BUTTON_HOT] = PIN_HOT,
 | |
| };
 | |
| 
 | |
| int button_state[MAX_BUTTON];
 | |
| unsigned long button_press_t0[MAX_BUTTON];
 | |
| unsigned long button_release_t0[MAX_BUTTON];
 | |
| 
 | |
| float ntc_R;
 | |
| float ntc_T;
 | |
| 
 | |
| 
 | |
| int read_input(int pin)
 | |
| {
 | |
| 	return !digitalRead(pin);
 | |
| }
 | |
| 
 | |
| void relay(int pin, enum logic st)
 | |
| {
 | |
| 	/* Relays are active low */
 | |
| 	if (st == ON)
 | |
| 		digitalWrite(pin, 0);
 | |
| 	else
 | |
| 		digitalWrite(pin, 1);
 | |
| }
 | |
| 
 | |
| void setled(int pin, enum logic st)
 | |
| {
 | |
| 	/* LEDs are active high */
 | |
| 	if (st == ON)
 | |
| 		digitalWrite(pin, 1);
 | |
| 	else
 | |
| 		digitalWrite(pin, 0);
 | |
| }
 | |
| 
 | |
| void update_buttons()
 | |
| {
 | |
| 	for (int i = 0; i < MAX_BUTTON; i++) {
 | |
| 		int st = button_state[i];
 | |
| 		int pin = button_pin[i];
 | |
| 		if (st == RESTING) {
 | |
| 			if (read_input(pin) == ON) {
 | |
| 				button_state[i] = PRESSING;
 | |
| 				button_press_t0[i] = millis();
 | |
| 			}
 | |
| 		} else if (st == PRESSING) {
 | |
| 			if (read_input(pin) != ON) {
 | |
| 				button_state[i] = RESTING;
 | |
| 			} else if (millis() - button_press_t0[i] > DEBOUNCE_TIME) {
 | |
| 				button_state[i] = PRESSED;
 | |
| 			}
 | |
| 		} else if (st == PRESSED) {
 | |
| 			if (read_input(pin) != ON) {
 | |
| 				button_state[i] = RELEASING;
 | |
| 				button_release_t0[i] = millis();
 | |
| 			}
 | |
| 		} else if (st == RELEASING) {
 | |
| 			if (read_input(pin) == ON) {
 | |
| 				button_state[i] = PRESSED;
 | |
| 			} else if (millis() - button_release_t0[i] > DEBOUNCE_TIME) {
 | |
| 				button_state[i] = RELEASED;
 | |
| 			}
 | |
| 		} else if (st == RELEASED) {
 | |
| 			button_state[i] = RESTING;
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void update_ntc()
 | |
| {
 | |
| 	int Vo = analogRead(PIN_NTC);
 | |
| 	ntc_R = ntc_resistance(Vo);
 | |
| 	ntc_T = ntc_temp(ntc_R);
 | |
| }
 | |
| 
 | |
| 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 max_heating_time = 10000UL; /* 10 seconds */
 | |
| unsigned long max_idle_time    = 10000UL; /* 10 seconds */
 | |
| 
 | |
| void progress()
 | |
| {
 | |
| 	int temp = analogRead(PIN_NTC);
 | |
| 	int on = (button_state[BUTTON_ON] == RELEASED);
 | |
| 	int hot = (button_state[BUTTON_HOT] == PRESSED);
 | |
| 
 | |
| 	static unsigned long brewing_t0 = 0;
 | |
| 	static unsigned long cooling_t0 = 0;
 | |
| 	static unsigned long heating_t0 = 0;
 | |
| 	static unsigned long hot_t0 = 0;
 | |
| 
 | |
| 	Serial.print("state=");
 | |
| 	Serial.print(state);
 | |
| 	Serial.print(" on=");
 | |
| 	Serial.print(on);
 | |
| 	Serial.print(" temp=");
 | |
| 	Serial.println(temp);
 | |
| 
 | |
| 	/* Pressing ON cancels any operation */
 | |
| 	if (state != SLEEPING && on) {
 | |
| 		state = SLEEPING;
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	if (state == SLEEPING) {
 | |
| 		if (on) {
 | |
| 			state = HEATING;
 | |
| 			heating_t0 = millis();
 | |
| 			Serial.println("heating");
 | |
| 		}
 | |
| 	} else if (state == HEATING) {
 | |
| 		if (temp > TEMP_MAX) {
 | |
| 			state = HOT;
 | |
| 			hot_t0 = millis();
 | |
| 			buzz_state = BUZZ_HEY;
 | |
| 			Serial.println("hot");
 | |
| 		} else if (millis() - heating_t0 > max_heating_time) {
 | |
| 			/* TODO: Add alarm state */
 | |
| 			state = SLEEPING;
 | |
| 			Serial.println("cannot heat, going to sleep");
 | |
| 		}
 | |
| 	} else if (state == HOT) {
 | |
| 		if (hot) {
 | |
| 			state = BREWING;
 | |
| 			brewing_t0 = millis();
 | |
| 			Serial.println("brewing");
 | |
| 		} else if (millis() - hot_t0 > max_idle_time) {
 | |
| 			state = SLEEPING;
 | |
| 			Serial.println("idle timeout, going to sleep");
 | |
| 		}
 | |
| 	} else if (state == BREWING) {
 | |
| 		if (millis() - brewing_t0 > brewing_time) {
 | |
| 			state = COOLING;
 | |
| 			cooling_t0 = millis();
 | |
| 			Serial.println("cooling");
 | |
| 		}
 | |
| 	} else if (state == COOLING) {
 | |
| 		/* TODO: Wait a bit and go back to heating */
 | |
| 		if (millis() - cooling_t0 > cooling_time) {
 | |
| 			state = HEATING;
 | |
| 			heating_t0 = millis();
 | |
| 			Serial.println("heating");
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void
 | |
| update_leds()
 | |
| {
 | |
| 	static int r = 0;
 | |
| 	static int g = 0;
 | |
| 
 | |
| 	if (state == HEATING || state == COOLING) {
 | |
| 		analogWrite(PIN_LED_RED, r);
 | |
| 		setled(PIN_LED_GREEN, 0);
 | |
| 		if (r >= 255)
 | |
| 			r = 0;
 | |
| 		else
 | |
| 			r += 3;
 | |
| 	} else if (state == HOT) {
 | |
| 		setled(PIN_LED_RED, 0);
 | |
| 		setled(PIN_LED_GREEN, 1);
 | |
| 		r = 0;
 | |
| 	} else if (state == BREWING) {
 | |
| 		setled(PIN_LED_RED, 0);
 | |
| 		analogWrite(PIN_LED_GREEN, g);
 | |
| 		if (g >= 255)
 | |
| 			g = 0;
 | |
| 		else
 | |
| 			g += 3;
 | |
| 	} else {
 | |
| 		setled(PIN_LED_RED, 0);
 | |
| 		setled(PIN_LED_GREEN, 0);
 | |
| 		r = 0;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /* Return the temperature in celsisus */
 | |
| float
 | |
| measure_temp()
 | |
| {
 | |
| 	int Vo = analogRead(PIN_NTC);
 | |
| 	float c1 = 1.009249522e-03, c2 = 2.378405444e-04, c3 = 2.019202697e-07;
 | |
| 	float R1 = 10e3; /* 10 kOhm resistor */
 | |
| 	float R2 = R1 * (1023.0 / (float)Vo - 1.0);
 | |
| 	float logR2 = log(R2);
 | |
| 	float T = (1.0 / (c1 + c2*logR2 + c3*logR2*logR2*logR2));
 | |
| 	float Tc = T - 273.15;
 | |
| 	return Tc;
 | |
| }
 | |
| 
 | |
| void
 | |
| update_heater()
 | |
| {
 | |
| 	if (state == HEATING || state == HOT || state == BREWING) {
 | |
| 		int temp = measure_temp();
 | |
| 		if (temp < TEMP_MIN)
 | |
| 			relay(PIN_HEAT, ON);
 | |
| 		else if (temp > TEMP_MAX)
 | |
| 			relay(PIN_HEAT, OFF);
 | |
| 	} else {
 | |
| 		relay(PIN_HEAT, OFF);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void
 | |
| update_pump()
 | |
| {
 | |
| 	if (state == BREWING) {
 | |
| 		relay(PIN_PUMP, ON);
 | |
| 	} else {
 | |
| 		relay(PIN_PUMP, OFF);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void
 | |
| update_buzz()
 | |
| {
 | |
| 	static unsigned long started = 0;
 | |
| 
 | |
| 	if (buzz_state == BUZZ_HEY) {
 | |
| 		tone(PIN_BUZZ, 1500);
 | |
| 		if (started == 0)
 | |
| 			started = millis();
 | |
| 		else if (millis() - started > 20) {
 | |
| 			buzz_state = BUZZ_OFF;
 | |
| 		}
 | |
| 	} else if (buzz_state == BUZZ_STOP) {
 | |
| 		tone(PIN_BUZZ, 220);
 | |
| 	} else if (buzz_state == BUZZ_OFF) {
 | |
| 		noTone(PIN_BUZZ);
 | |
| 		started = 0;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void inputs()
 | |
| {
 | |
| 	update_buttons();
 | |
| 	update_ntc();
 | |
| }
 | |
| 
 | |
| void outputs()
 | |
| {
 | |
| 	update_leds();
 | |
| 	update_heater();
 | |
| 	update_pump();
 | |
| 	update_buzz();
 | |
| }
 | |
| 
 | |
| void setup()
 | |
| {
 | |
| 	Serial.begin(9600);
 | |
| 	Serial.println("Booting");
 | |
| 
 | |
| 	pinMode(PIN_POWER_ON, INPUT);
 | |
| 	pinMode(PIN_HOT, INPUT);
 | |
| 
 | |
| 	pinMode(PIN_LED_RED, OUTPUT);
 | |
| 	pinMode(PIN_LED_GREEN, OUTPUT);
 | |
| 	pinMode(PIN_HEAT, OUTPUT);
 | |
| 	pinMode(PIN_PUMP, OUTPUT);
 | |
| 
 | |
| 	/* Turn all relays off */
 | |
| 	relay(PIN_HEAT, OFF);
 | |
| 	relay(PIN_PUMP, OFF);
 | |
| 
 | |
| 	Serial.println("Ready");
 | |
| }
 | |
| 
 | |
| void loop()
 | |
| {
 | |
| 	inputs();
 | |
| 	progress();
 | |
| 	outputs();
 | |
| 
 | |
| 	delay(5);
 | |
| }
 | 
