commit 695aa4b3c243352fe09a116f590ac09ebb5201e6 Author: Rodrigo Arias Mallo Date: Sat Oct 11 22:20:09 2025 +0200 Initial version of firmware diff --git a/README b/README new file mode 100644 index 0000000..5e0007b --- /dev/null +++ b/README @@ -0,0 +1 @@ +Custom firmware to control the coffee machine. diff --git a/barista/.gitignore b/barista/.gitignore new file mode 100644 index 0000000..567609b --- /dev/null +++ b/barista/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/barista/Makefile b/barista/Makefile new file mode 100644 index 0000000..a2dbec4 --- /dev/null +++ b/barista/Makefile @@ -0,0 +1,17 @@ +PORT=/dev/ttyUSB0 +FQBN=arduino:avr:atmega328bb +OPTS=--no-color --log-level=info -v +PROJ=barista +BUILD=build/$(subst :,.,$(FQBN)) +HEX=$(BUILD)/$(PROJ).ino.hex + +all: $(HEX) + +$(HEX): barista.ino + arduino-cli compile $(OPTS) -e --fqbn $(FQBN) + +upload: $(HEX) + avrdude -vv -p atmega328p -c arduino -P $(PORT) -b 9600 -D -U flash:w:$(HEX):i + +serial: + picocom -b 115200 --lower-rts --lower-dtr /dev/ttyUSB0 --imap lfcrlf diff --git a/barista/barista.ino b/barista/barista.ino new file mode 100644 index 0000000..4204a63 --- /dev/null +++ b/barista/barista.ino @@ -0,0 +1,351 @@ +/* Copyright (c) 2025 Rodrigo Arias Mallo + * SPDX-License-Identifier: GPL-3.0-or-later */ + +/* + * ATmega328p + * +---------+ + * (PCINT14/RESET) PC6 -|1 \_/ 28|- PC5 (ADC5/SCL/PCINT13) + * (PCINT16/RXD) PD0 -|2 27|- PC4 (ADC4/SDA/PCINT12) + * (PCINT17/TXD) PD1 -|3 26|- PC3 (ADC3/PCINT11) + * (PCINT18/INT0) PD2 -|4 25|- PC2 (ADC2/PCINT10) + * (PCINT19/OC2B/INT1) PD3 -|5 24|- PC1 (ADC1/PCINT9) + * (PCINT20/XCK/T0) PD4 -|6 23|- PC0 (ADC0/PCINT8) + * VCC -|7 22|- GND + * GND -|8 21|- AREF + * (PCINT6/XTAL1/TOSC1) PB6 -|9 20|- AVCC + * (PCINT7/XTAL2/TOSC2) PB7 -|10 19|- PB5 (SCK/PCINT5) + * (PCINT21/OC0B/T1) PD5 -|11 18|- PB4 (MISO/PCINT4) + * (PCINT22/OC0A/AIN0) PD6 -|12 17|- PB3 (MOSI/OC2A/PCINT3) + * (PCINT23/AIN1) PD7 -|13 16|- PB2 (SS/OC1B/PCINT2) + * (PCINT0/CLKO/ICP1) PB0 -|14 15|- PB1 (OC1A/PCINT1) + * +---------+ + * + * + * ATMEL ATMEGA8 & 168 / ARDUINO + * + * +-\/-+ + * PC6 1| |28 PC5 (AI 5) + * (D 0) PD0 2| |27 PC4 (AI 4) + * (D 1) PD1 3| |26 PC3 (AI 3) + * (D 2) PD2 4| |25 PC2 (AI 2) + * PWM+ (D 3) PD3 5| |24 PC1 (AI 1) + * (D 4) PD4 6| |23 PC0 (AI 0) + * VCC 7| |22 GND + * GND 8| |21 AREF + * PB6 9| |20 AVCC + * PB7 10| |19 PB5 (D 13) + * PWM+ (D 5) PD5 11| |18 PB4 (D 12) + * PWM+ (D 6) PD6 12| |17 PB3 (D 11) PWM + * (D 7) PD7 13| |16 PB2 (D 10) PWM + * (D 8) PB0 14| |15 PB1 (D 9) PWM + * +----+ + * + * + */ + +enum pinout { + /* Inputs */ + PIN_POWER_ON = 8, + PIN_HOT = 9, + //PIN_FLOW = PIN_D5, + + /* Outputs */ + PIN_LED_GREEN = 5, + PIN_LED_RED = 6, + PIN_HEAT = 7, + PIN_PUMP = 12, + PIN_BUZZ = 10, + + /* Analog */ + PIN_NTC = PIN_A0, +}; + +enum logic { + ON = 1, + OFF = 0, +}; + +#define DEBOUNCE_TIME 20L /* ms */ +#define TEMP_MIN 600 +#define TEMP_MAX 700 + +#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]; + +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; + } + } +} + +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; + } +} + +void +update_heater() +{ + if (state == HEATING || state == HOT || state == BREWING) { + int temp = analogRead(PIN_NTC); + 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 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() +{ + //Serial.println("Looping..."); + update_buttons(); + progress(); + update_leds(); + update_heater(); + update_pump(); + update_buzz(); + delay(5); +}