coffee/barista/barista.ino
2025-10-13 21:09:06 +02:00

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);
}