/* Copyright (c) 2025 Rodrigo Arias Mallo * 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); }