Compare commits
16 Commits
695aa4b3c2
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8502ee3c5c | ||
|
|
678f16111b | ||
|
|
3ea6ff0e14 | ||
|
|
d59dec50a8 | ||
|
|
402e1f4e43 | ||
|
|
14592c6f55 | ||
|
|
55d0548ea8 | ||
|
|
3538225a91 | ||
|
|
7bd0b44871 | ||
|
|
53e3d47a04 | ||
|
|
90ea67cf08 | ||
|
|
ebd47ee2c5 | ||
|
|
c2e823dfb5 | ||
|
|
2546447652 | ||
|
|
eaf4e9d814 | ||
|
|
9c114b136a |
3
barista/.gitignore
vendored
3
barista/.gitignore
vendored
@@ -1 +1,4 @@
|
||||
build/
|
||||
misc/
|
||||
*.test
|
||||
*.o
|
||||
|
||||
@@ -1,17 +1,49 @@
|
||||
# Copyright (c) 2025 Rodrigo Arias Mallo <rodarima@gmail.com>
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
PORT=/dev/ttyUSB0
|
||||
FQBN=arduino:avr:atmega328bb
|
||||
OPTS=--no-color --log-level=info -v
|
||||
OPTS=--no-color --log-level=info
|
||||
#OPTS+=-v
|
||||
PROJ=barista
|
||||
BUILD=build/$(subst :,.,$(FQBN))
|
||||
HEX=$(BUILD)/$(PROJ).ino.hex
|
||||
|
||||
SRC=barista.ino \
|
||||
ntc.c \
|
||||
ntc.h \
|
||||
pinout.h \
|
||||
overheat.c \
|
||||
overheat.h
|
||||
|
||||
TESTS=ntc.test \
|
||||
overheat.test \
|
||||
led.test
|
||||
|
||||
# For host test programs
|
||||
CPPFLAGS=-I.
|
||||
LIBS=-lm
|
||||
|
||||
all: $(HEX)
|
||||
|
||||
$(HEX): barista.ino
|
||||
$(HEX): $(SRC)
|
||||
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
|
||||
picocom -b 9600 --lower-rts --lower-dtr /dev/ttyUSB0 --imap lfcrlf
|
||||
|
||||
test: ntc.test overheat.test led.test
|
||||
|
||||
%.test: test/test_%.o test/compat.o %.o
|
||||
gcc $^ -o $@ $(LIBS)
|
||||
|
||||
thermostat.test: test/test_thermostat.o test/compat.o thermostat.o overheat.o heater.o
|
||||
gcc $^ -o $@ $(LIBS)
|
||||
|
||||
clean:
|
||||
rm -f $(TESTS) test/*.o *.o
|
||||
|
||||
.PHONY: test all clean
|
||||
|
||||
@@ -1,64 +1,13 @@
|
||||
/* Copyright (c) 2025 Rodrigo Arias Mallo <rodarima@gmail.com>
|
||||
* 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,
|
||||
};
|
||||
#include "ntc.h"
|
||||
#include "overheat.h"
|
||||
#include "led.h"
|
||||
#include "heater.h"
|
||||
#include "thermostat.h"
|
||||
#include "pinout.h"
|
||||
#include <avr/wdt.h>
|
||||
|
||||
enum logic {
|
||||
ON = 1,
|
||||
@@ -66,30 +15,30 @@ enum logic {
|
||||
};
|
||||
|
||||
#define DEBOUNCE_TIME 20L /* ms */
|
||||
#define TEMP_MIN 600
|
||||
#define TEMP_MAX 700
|
||||
#define TEMP_HOT 75.0f /* C */
|
||||
|
||||
#define LED_MIN_VALUE 0
|
||||
|
||||
enum state {
|
||||
SLEEPING,
|
||||
enum machine_state {
|
||||
SLEEPING = 0,
|
||||
DEBOUNCE,
|
||||
HEATING,
|
||||
HOT,
|
||||
BREWING,
|
||||
BREWING_HOT,
|
||||
BREWING_COLD,
|
||||
COOLING,
|
||||
PANIC_OVERHEAT,
|
||||
};
|
||||
|
||||
long debounce_t0 = 0;
|
||||
int state = SLEEPING;
|
||||
//int state = SLEEPING;
|
||||
|
||||
enum button {
|
||||
BUTTON_ON = 0,
|
||||
BUTTON_HOT,
|
||||
MAX_BUTTON,
|
||||
enum btn_index {
|
||||
BTN_ON = 0,
|
||||
BTN_HOT,
|
||||
MAX_BTN,
|
||||
};
|
||||
|
||||
enum button_state {
|
||||
enum btn_state {
|
||||
RESTING = 0,
|
||||
PRESSING,
|
||||
PRESSED,
|
||||
@@ -98,21 +47,57 @@ enum button_state {
|
||||
};
|
||||
|
||||
enum buzz_state {
|
||||
BUZZ_OFF = 0,
|
||||
BUZZ_HEY,
|
||||
BUZZ_STOP,
|
||||
BUZZ_OFF = 0,
|
||||
BUZZ_HEY,
|
||||
BUZZ_ACTIVE,
|
||||
};
|
||||
|
||||
int buzz_state;
|
||||
|
||||
int button_pin[MAX_BUTTON] = {
|
||||
[BUTTON_ON] = PIN_POWER_ON,
|
||||
[BUTTON_HOT] = PIN_HOT,
|
||||
int button_pin[MAX_BTN] = {
|
||||
[BTN_ON] = PIN_POWER_ON,
|
||||
[BTN_HOT] = PIN_HOT,
|
||||
};
|
||||
|
||||
int button_state[MAX_BUTTON];
|
||||
unsigned long button_press_t0[MAX_BUTTON];
|
||||
unsigned long button_release_t0[MAX_BUTTON];
|
||||
struct btn {
|
||||
enum btn_state state;
|
||||
unsigned long press_t0;
|
||||
unsigned long release_t0;
|
||||
};
|
||||
|
||||
struct input {
|
||||
unsigned long t_ms;
|
||||
int ntc_V;
|
||||
enum logic btn[MAX_BTN];
|
||||
} g_in;
|
||||
|
||||
#define MAX_SAMPLES 16
|
||||
|
||||
struct state {
|
||||
enum machine_state mstate;
|
||||
unsigned long brewing_t0;
|
||||
unsigned long cooling_t0;
|
||||
unsigned long heating_t0;
|
||||
unsigned long hot_t0;
|
||||
|
||||
int ntc_i; /* Next available place */
|
||||
int ntc_n; /* Samples in array */
|
||||
float ntc_R;
|
||||
float ntc_last_T;
|
||||
float ntc_array_T[MAX_SAMPLES];
|
||||
float ntc_T; /* average */
|
||||
|
||||
struct btn btn[MAX_BTN];
|
||||
enum buzz_state buzz_state;
|
||||
unsigned long buzz_t0;
|
||||
|
||||
struct overheat overheat;
|
||||
unsigned long overheat_t0;
|
||||
|
||||
struct led red_led;
|
||||
struct led green_led;
|
||||
|
||||
struct heater heater;
|
||||
struct thermostat thermostat;
|
||||
} g_st;
|
||||
|
||||
int read_input(int pin)
|
||||
{
|
||||
@@ -121,205 +106,297 @@ int read_input(int pin)
|
||||
|
||||
void relay(int pin, enum logic st)
|
||||
{
|
||||
/* Relays are active low */
|
||||
if (st == ON)
|
||||
digitalWrite(pin, 0);
|
||||
else
|
||||
digitalWrite(pin, 1);
|
||||
/* 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);
|
||||
/* LEDs are active high */
|
||||
if (st == ON)
|
||||
digitalWrite(pin, 1);
|
||||
else
|
||||
digitalWrite(pin, 0);
|
||||
}
|
||||
|
||||
void update_buttons()
|
||||
void do_input(struct input *input)
|
||||
{
|
||||
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();
|
||||
input->t_ms = millis();
|
||||
|
||||
/* Read buttons */
|
||||
for (int i = 0; i < MAX_BTN; i++)
|
||||
input->btn[i] = read_input(button_pin[i]);
|
||||
|
||||
/* Read temperature sensor */
|
||||
input->ntc_V = analogRead(PIN_NTC);
|
||||
}
|
||||
|
||||
void proc_ntc(struct state *state, const struct input *input)
|
||||
{
|
||||
state->ntc_R = ntc_resistance(input->ntc_V);
|
||||
state->ntc_last_T = ntc_temp(state->ntc_R);
|
||||
state->ntc_array_T[state->ntc_i++] = state->ntc_last_T;
|
||||
if (state->ntc_i >= MAX_SAMPLES)
|
||||
state->ntc_i = 0;
|
||||
if (state->ntc_n < MAX_SAMPLES)
|
||||
state->ntc_n++;
|
||||
|
||||
float avg = 0;
|
||||
for (int i = 0; i < state->ntc_n; i++)
|
||||
avg += state->ntc_array_T[i];
|
||||
|
||||
state->ntc_T = avg / state->ntc_n;
|
||||
|
||||
overheat_input(&state->overheat, millis(), state->ntc_T);
|
||||
}
|
||||
|
||||
void proc_buttons(struct state *state, const struct input *input)
|
||||
{
|
||||
for (int i = 0; i < MAX_BTN; i++) {
|
||||
struct btn *btn = &state->btn[i];
|
||||
int v = input->btn[i];
|
||||
|
||||
if (btn->state == RESTING) {
|
||||
if (v == ON) {
|
||||
btn->state = PRESSING;
|
||||
btn->press_t0 = 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 (btn->state == PRESSING) {
|
||||
if (v != ON) {
|
||||
btn->state = RESTING;
|
||||
} else if (millis() - btn->press_t0 > DEBOUNCE_TIME) {
|
||||
btn->state = PRESSED;
|
||||
}
|
||||
} else if (st == PRESSED) {
|
||||
if (read_input(pin) != ON) {
|
||||
button_state[i] = RELEASING;
|
||||
button_release_t0[i] = millis();
|
||||
} else if (btn->state == PRESSED) {
|
||||
if (v != ON) {
|
||||
btn->state = RELEASING;
|
||||
btn->release_t0 = 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 (btn->state == RELEASING) {
|
||||
if (v == ON) {
|
||||
btn->state = PRESSED;
|
||||
} else if (millis() - btn->release_t0 > DEBOUNCE_TIME) {
|
||||
btn->state = RELEASED;
|
||||
}
|
||||
} else if (st == RELEASED) {
|
||||
button_state[i] = RESTING;
|
||||
} else if (btn->state == RELEASED) {
|
||||
btn->state = RESTING;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 max_heating_time = 10000UL; /* 10 seconds */
|
||||
unsigned long max_idle_time = 10000UL; /* 10 seconds */
|
||||
unsigned long brewing_max_time = 30000UL; /* 30 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 = 300000UL; /* 300 seconds */
|
||||
|
||||
void progress()
|
||||
void proc_machine(struct state *st)
|
||||
{
|
||||
int temp = analogRead(PIN_NTC);
|
||||
int on = (button_state[BUTTON_ON] == RELEASED);
|
||||
int hot = (button_state[BUTTON_HOT] == PRESSED);
|
||||
static unsigned long last_print_t = 0;
|
||||
|
||||
static unsigned long brewing_t0 = 0;
|
||||
static unsigned long cooling_t0 = 0;
|
||||
static unsigned long heating_t0 = 0;
|
||||
static unsigned long hot_t0 = 0;
|
||||
float temp = st->ntc_T;
|
||||
int on = (st->btn[BTN_ON].state == RELEASED);
|
||||
int brew_hot = (st->btn[BTN_HOT].state == PRESSED);
|
||||
unsigned long t = millis();
|
||||
|
||||
Serial.print("state=");
|
||||
Serial.print(state);
|
||||
Serial.print(" on=");
|
||||
Serial.print(on);
|
||||
Serial.print(" temp=");
|
||||
Serial.println(temp);
|
||||
/* 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 (state != SLEEPING && on) {
|
||||
state = SLEEPING;
|
||||
return;
|
||||
}
|
||||
/* Pressing ON cancels any operation */
|
||||
if (st->mstate != SLEEPING && on) {
|
||||
st->mstate = SLEEPING;
|
||||
st->buzz_state = BUZZ_OFF;
|
||||
return;
|
||||
}
|
||||
|
||||
if (state == SLEEPING) {
|
||||
if (on) {
|
||||
state = HEATING;
|
||||
heating_t0 = millis();
|
||||
if (st->mstate == SLEEPING) {
|
||||
/* Allow brewing cold at without turning the heater */
|
||||
/* FIXME: Use the cold button instead */
|
||||
if (brew_hot) {
|
||||
st->mstate = BREWING_COLD;
|
||||
Serial.println("brewing cold");
|
||||
} else if (on) {
|
||||
st->mstate = HEATING;
|
||||
st->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");
|
||||
}
|
||||
}
|
||||
} else if (st->mstate == HEATING) {
|
||||
if (temp > TEMP_HOT) {
|
||||
st->mstate = HOT;
|
||||
st->hot_t0 = millis();
|
||||
st->buzz_state = BUZZ_HEY;
|
||||
Serial.println("hot");
|
||||
} else if (millis() - st->heating_t0 > max_heating_time) {
|
||||
/* TODO: Add alarm state */
|
||||
st->mstate = SLEEPING;
|
||||
Serial.println("cannot heat, going to sleep");
|
||||
}
|
||||
} else if (st->mstate == HOT) {
|
||||
if (brew_hot) {
|
||||
st->mstate = BREWING_HOT;
|
||||
st->brewing_t0 = millis();
|
||||
Serial.println("brewing");
|
||||
} else if (millis() - st->hot_t0 > max_idle_time) {
|
||||
st->mstate = SLEEPING;
|
||||
Serial.println("idle timeout, going to sleep");
|
||||
}
|
||||
} else if (st->mstate == BREWING_HOT) {
|
||||
/* Stop brewing if no longer pressing the brew button or we
|
||||
* exceed the brew max time */
|
||||
if (!brew_hot || millis() - st->brewing_t0 > brewing_max_time) {
|
||||
st->mstate = COOLING;
|
||||
st->cooling_t0 = millis();
|
||||
Serial.println("cooling");
|
||||
}
|
||||
} else if (st->mstate == BREWING_COLD) {
|
||||
/* FIXME: Use cold button instead */
|
||||
if (!brew_hot) {
|
||||
st->mstate = SLEEPING;
|
||||
Serial.println("going back to sleeping after cold brewing");
|
||||
}
|
||||
} else if (st->mstate == COOLING) {
|
||||
/* TODO: Wait a bit and go back to heating */
|
||||
if (millis() - st->cooling_t0 > cooling_time) {
|
||||
st->mstate = HEATING;
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
update_leds()
|
||||
proc_buzz(struct state *st)
|
||||
{
|
||||
static int r = 0;
|
||||
static int g = 0;
|
||||
if (st->buzz_state == BUZZ_HEY) {
|
||||
tone(PIN_BUZZ, 1500);
|
||||
st->buzz_state = BUZZ_ACTIVE;
|
||||
st->buzz_t0 = millis();
|
||||
} else if (st->buzz_state == BUZZ_ACTIVE) {
|
||||
if (millis() - st->buzz_t0 > 20) {
|
||||
st->buzz_state = BUZZ_OFF;
|
||||
noTone(PIN_BUZZ);
|
||||
}
|
||||
} else {
|
||||
noTone(PIN_BUZZ);
|
||||
}
|
||||
}
|
||||
|
||||
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 do_proc(struct state *st, const struct input *input)
|
||||
{
|
||||
proc_ntc(st, input);
|
||||
proc_buttons(st, input);
|
||||
proc_machine(st);
|
||||
proc_buzz(st);
|
||||
}
|
||||
|
||||
void
|
||||
update_heater()
|
||||
output_leds(const struct state *st)
|
||||
{
|
||||
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);
|
||||
}
|
||||
unsigned long t = millis();
|
||||
|
||||
if (st->mstate == SLEEPING) {
|
||||
led_off(&st->red_led);
|
||||
led_off(&st->green_led);
|
||||
} else if (st->mstate == HEATING) {
|
||||
led_off(&st->red_led);
|
||||
led_pattern(&st->green_led, t, 1000UL, "000123456789abcdefff");
|
||||
} else if (st->mstate == COOLING) {
|
||||
led_off(&st->red_led);
|
||||
led_pattern(&st->green_led, t, 1000UL, "fffedcba987654321000");
|
||||
} else if (st->mstate == HOT) {
|
||||
led_off(&st->red_led);
|
||||
led_on(&st->green_led);
|
||||
} else if (st->mstate == PANIC_OVERHEAT) {
|
||||
led_pattern(&st->red_led, t, 3000UL, "f0f0f0000000");
|
||||
led_pattern(&st->green_led, t, 3000UL, "000000f0f0f0");
|
||||
} else if (st->mstate == BREWING_HOT || st->mstate == BREWING_COLD) {
|
||||
led_off(&st->red_led);
|
||||
led_pattern(&st->green_led, millis(), 2000UL, "0123456789abcdefedcba9876543210");
|
||||
} else {
|
||||
led_off(&st->red_led);
|
||||
led_off(&st->green_led);
|
||||
}
|
||||
|
||||
analogWrite(PIN_LED_RED, led_level(&st->red_led, t));
|
||||
analogWrite(PIN_LED_GREEN, led_level(&st->green_led, t));
|
||||
}
|
||||
|
||||
void
|
||||
update_pump()
|
||||
output_relays(const struct state *st)
|
||||
{
|
||||
if (state == BREWING) {
|
||||
unsigned long t = millis();
|
||||
|
||||
/* First configure thermostate */
|
||||
if (st->mstate == HEATING || st->mstate == HOT || st->mstate == BREWING_HOT)
|
||||
thermostat_set(&st->thermostat, TEMP_HOT+5.0);
|
||||
else
|
||||
thermostat_off(&st->thermostat);
|
||||
|
||||
/* Then update heater state from thermostate */
|
||||
float T = st->ntc_T;
|
||||
float dT_dt = overheat_speed(&st->overheat);
|
||||
float u = thermostat_state(&st->thermostat, T, dT_dt);
|
||||
heater_on(&st->heater, t, u);
|
||||
|
||||
/* Then switch relays accordingly */
|
||||
int heater_relay_st = heater_state(&st->heater, t);
|
||||
if (heater_relay_st)
|
||||
relay(PIN_HEAT, ON);
|
||||
else
|
||||
relay(PIN_HEAT, OFF);
|
||||
|
||||
int pump_relay_st = 0;
|
||||
if (st->mstate == BREWING_HOT || st->mstate == BREWING_COLD)
|
||||
pump_relay_st = 1;
|
||||
|
||||
if (pump_relay_st)
|
||||
relay(PIN_PUMP, ON);
|
||||
} else {
|
||||
else
|
||||
relay(PIN_PUMP, OFF);
|
||||
}
|
||||
|
||||
Serial.print(t);
|
||||
Serial.print(" ");
|
||||
Serial.print(st->mstate);
|
||||
Serial.print(" ");
|
||||
Serial.print(T);
|
||||
Serial.print(" ");
|
||||
Serial.print(u);
|
||||
Serial.print(" ");
|
||||
Serial.print(heater_relay_st);
|
||||
Serial.print(" ");
|
||||
Serial.println(pump_relay_st);
|
||||
}
|
||||
|
||||
void
|
||||
update_buzz()
|
||||
void do_output(const struct state *st)
|
||||
{
|
||||
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;
|
||||
}
|
||||
output_leds(st);
|
||||
output_relays(st);
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
wdt_disable();
|
||||
Serial.begin(9600);
|
||||
Serial.println("Booting");
|
||||
|
||||
@@ -331,21 +408,22 @@ void setup()
|
||||
pinMode(PIN_HEAT, OUTPUT);
|
||||
pinMode(PIN_PUMP, OUTPUT);
|
||||
|
||||
/* Turn all relays off */
|
||||
relay(PIN_HEAT, OFF);
|
||||
relay(PIN_PUMP, OFF);
|
||||
/* Turn all relays off */
|
||||
relay(PIN_HEAT, OFF);
|
||||
relay(PIN_PUMP, OFF);
|
||||
|
||||
overheat_init(&g_st.overheat);
|
||||
|
||||
Serial.println("Ready");
|
||||
wdt_enable(WDTO_250MS);
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
//Serial.println("Looping...");
|
||||
update_buttons();
|
||||
progress();
|
||||
update_leds();
|
||||
update_heater();
|
||||
update_pump();
|
||||
update_buzz();
|
||||
delay(5);
|
||||
do_input(&g_in);
|
||||
do_proc(&g_st, &g_in);
|
||||
do_output(&g_st);
|
||||
|
||||
delay(5);
|
||||
wdt_reset();
|
||||
}
|
||||
|
||||
1770
barista/data/brew.csv
Normal file
1770
barista/data/brew.csv
Normal file
File diff suppressed because it is too large
Load Diff
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
959
barista/data/overheat-cutoff.csv
Normal file
959
barista/data/overheat-cutoff.csv
Normal file
@@ -0,0 +1,959 @@
|
||||
t_s st temp_C
|
||||
68.167 0 28.12
|
||||
68.204 0 28.15
|
||||
68.241 0 28.16
|
||||
68.278 0 28.18
|
||||
68.313 0 28.19
|
||||
68.349 0 28.22
|
||||
68.386 0 28.22
|
||||
68.423 0 28.21
|
||||
68.458 0 28.19
|
||||
68.495 0 28.19
|
||||
68.532 0 28.21
|
||||
68.569 0 28.22
|
||||
68.605 0 28.24
|
||||
68.640 0 28.22
|
||||
68.677 0 28.21
|
||||
68.714 0 28.22
|
||||
68.751 0 28.24
|
||||
68.786 0 28.26
|
||||
68.823 0 28.26
|
||||
68.859 0 28.24
|
||||
68.896 0 28.22
|
||||
68.933 0 28.21
|
||||
68.968 0 28.22
|
||||
69.005 0 28.13
|
||||
69.042 0 28.10
|
||||
69.079 0 28.08
|
||||
69.113 0 28.07
|
||||
69.150 0 28.05
|
||||
69.187 0 28.05
|
||||
69.224 0 28.07
|
||||
69.259 0 28.05
|
||||
69.296 0 28.04
|
||||
69.332 0 28.02
|
||||
69.369 0 28.01
|
||||
69.406 0 28.01
|
||||
69.441 0 28.02
|
||||
69.478 0 28.04
|
||||
69.515 0 28.04
|
||||
69.552 0 28.04
|
||||
69.586 0 28.07
|
||||
69.623 0 28.12
|
||||
69.660 0 28.13
|
||||
69.697 0 28.15
|
||||
69.734 0 28.15
|
||||
69.769 0 28.13
|
||||
69.806 0 28.12
|
||||
69.842 0 28.13
|
||||
69.879 0 28.15
|
||||
69.914 0 28.12
|
||||
69.951 0 28.12
|
||||
69.988 0 28.10
|
||||
70.025 0 28.08
|
||||
70.060 0 28.08
|
||||
70.096 0 28.10
|
||||
70.133 0 28.10
|
||||
70.170 0 28.16
|
||||
70.207 0 28.13
|
||||
70.242 0 28.12
|
||||
70.279 0 28.10
|
||||
70.316 0 28.12
|
||||
70.352 0 28.13
|
||||
70.387 0 28.15
|
||||
70.424 0 28.12
|
||||
70.461 0 28.10
|
||||
70.498 0 28.13
|
||||
70.535 0 28.15
|
||||
70.569 0 28.18
|
||||
70.606 0 28.12
|
||||
70.643 0 28.08
|
||||
70.680 0 28.07
|
||||
70.715 0 28.05
|
||||
70.752 0 28.07
|
||||
70.789 0 28.08
|
||||
70.825 0 28.10
|
||||
70.860 0 28.10
|
||||
70.897 0 28.08
|
||||
70.934 0 28.07
|
||||
70.971 0 28.07
|
||||
71.008 0 28.12
|
||||
71.043 0 28.08
|
||||
71.079 0 28.08
|
||||
71.116 0 28.07
|
||||
71.153 0 28.05
|
||||
71.188 0 28.12
|
||||
71.225 0 28.13
|
||||
71.262 0 28.15
|
||||
71.299 0 28.16
|
||||
71.335 0 28.15
|
||||
71.370 0 28.15
|
||||
71.407 0 28.15
|
||||
71.444 0 28.16
|
||||
71.481 0 28.18
|
||||
71.516 0 28.18
|
||||
71.553 0 28.15
|
||||
71.589 0 28.13
|
||||
71.626 0 28.18
|
||||
71.663 0 28.18
|
||||
71.698 0 28.19
|
||||
71.735 0 28.24
|
||||
71.772 0 28.24
|
||||
71.809 0 28.24
|
||||
71.843 0 28.22
|
||||
71.880 0 28.21
|
||||
71.917 0 28.21
|
||||
71.954 0 28.21
|
||||
71.989 0 28.21
|
||||
72.026 0 28.21
|
||||
72.062 0 28.19
|
||||
72.099 0 28.15
|
||||
72.136 0 28.07
|
||||
72.171 0 28.00
|
||||
72.208 0 28.00
|
||||
72.245 0 28.02
|
||||
72.282 0 28.00
|
||||
72.316 0 27.94
|
||||
72.353 0 27.90
|
||||
72.390 0 27.90
|
||||
72.427 0 27.91
|
||||
72.464 0 27.93
|
||||
72.499 0 28.03
|
||||
72.536 0 28.03
|
||||
72.572 0 27.91
|
||||
72.609 0 27.86
|
||||
72.644 0 27.88
|
||||
72.681 0 27.95
|
||||
72.718 0 28.02
|
||||
72.755 0 28.06
|
||||
72.790 0 28.03
|
||||
72.826 0 28.00
|
||||
72.863 0 28.02
|
||||
72.900 0 28.05
|
||||
72.937 0 28.13
|
||||
72.972 0 28.13
|
||||
73.009 0 28.09
|
||||
73.046 0 28.02
|
||||
73.082 0 27.70
|
||||
73.117 0 27.72
|
||||
73.154 0 27.84
|
||||
73.191 0 27.89
|
||||
73.228 0 27.83
|
||||
73.265 0 27.76
|
||||
73.299 0 27.81
|
||||
73.336 0 27.90
|
||||
73.373 0 28.01
|
||||
73.410 0 27.98
|
||||
73.445 0 27.95
|
||||
73.482 0 27.90
|
||||
73.519 0 27.87
|
||||
73.555 0 27.88
|
||||
73.592 0 28.09
|
||||
73.627 0 28.15
|
||||
73.664 0 28.32
|
||||
73.701 0 28.29
|
||||
73.738 0 28.27
|
||||
73.773 0 28.27
|
||||
73.809 0 28.33
|
||||
73.846 0 28.38
|
||||
73.883 0 28.41
|
||||
73.918 0 28.37
|
||||
73.955 0 28.26
|
||||
73.992 0 28.32
|
||||
74.029 0 28.35
|
||||
74.065 0 28.40
|
||||
74.100 0 28.46
|
||||
74.137 0 28.43
|
||||
74.174 0 28.24
|
||||
74.211 0 28.25
|
||||
74.246 0 28.33
|
||||
74.283 0 28.36
|
||||
74.319 0 28.35
|
||||
74.356 0 28.35
|
||||
74.393 0 28.24
|
||||
74.428 0 28.24
|
||||
74.465 0 28.22
|
||||
74.502 0 28.22
|
||||
74.539 0 28.28
|
||||
74.573 0 28.22
|
||||
74.610 0 28.19
|
||||
74.647 0 28.19
|
||||
74.684 0 28.14
|
||||
74.719 0 28.19
|
||||
74.756 0 28.19
|
||||
74.792 0 28.13
|
||||
74.829 0 28.08
|
||||
74.866 0 28.26
|
||||
74.901 0 28.29
|
||||
74.938 0 28.29
|
||||
74.975 0 28.46
|
||||
75.012 0 28.40
|
||||
75.046 0 28.35
|
||||
75.083 0 28.26
|
||||
75.120 0 28.24
|
||||
75.157 0 28.30
|
||||
75.194 0 28.34
|
||||
75.229 0 28.27
|
||||
75.266 0 28.21
|
||||
75.302 0 28.16
|
||||
75.339 0 28.18
|
||||
75.374 0 28.26
|
||||
75.411 0 28.30
|
||||
75.448 0 28.03
|
||||
75.485 0 28.00
|
||||
75.520 0 27.96
|
||||
75.556 0 27.90
|
||||
75.593 0 28.00
|
||||
75.630 0 28.08
|
||||
75.667 0 28.16
|
||||
75.702 0 28.00
|
||||
75.739 0 28.00
|
||||
75.776 0 28.00
|
||||
75.812 0 28.06
|
||||
75.847 0 28.12
|
||||
75.884 0 28.19
|
||||
75.921 0 28.15
|
||||
75.958 0 28.12
|
||||
75.995 0 28.11
|
||||
76.029 0 28.22
|
||||
76.066 0 28.25
|
||||
76.103 0 28.28
|
||||
76.140 0 28.22
|
||||
76.175 0 28.14
|
||||
76.212 0 28.10
|
||||
76.249 0 28.10
|
||||
76.285 0 28.24
|
||||
76.322 0 28.22
|
||||
76.357 0 28.19
|
||||
76.394 0 28.15
|
||||
76.431 0 28.13
|
||||
76.468 0 28.12
|
||||
76.503 0 28.16
|
||||
76.539 0 28.11
|
||||
76.576 0 28.05
|
||||
76.613 0 27.99
|
||||
76.648 0 28.11
|
||||
76.685 0 28.13
|
||||
76.722 0 28.19
|
||||
76.759 0 28.22
|
||||
76.795 0 28.19
|
||||
76.830 0 28.13
|
||||
76.867 0 28.09
|
||||
76.904 0 28.18
|
||||
76.941 0 28.22
|
||||
76.976 0 28.26
|
||||
77.012 0 28.26
|
||||
77.049 0 28.22
|
||||
77.086 0 28.17
|
||||
77.123 0 28.23
|
||||
77.158 0 28.29
|
||||
77.195 0 28.34
|
||||
77.232 0 28.19
|
||||
77.268 0 28.13
|
||||
77.303 0 28.20
|
||||
77.340 0 28.19
|
||||
77.377 0 28.26
|
||||
77.414 0 28.31
|
||||
77.449 0 28.33
|
||||
77.486 0 28.24
|
||||
77.522 0 28.17
|
||||
77.559 0 28.16
|
||||
77.596 0 28.16
|
||||
77.631 0 28.19
|
||||
77.668 0 28.22
|
||||
77.705 0 28.17
|
||||
77.742 0 28.16
|
||||
77.776 0 28.20
|
||||
77.813 0 28.24
|
||||
77.850 0 28.15
|
||||
77.887 0 28.11
|
||||
77.924 0 28.08
|
||||
77.959 0 27.99
|
||||
77.996 0 27.99
|
||||
78.032 0 28.00
|
||||
78.069 0 28.02
|
||||
78.104 0 28.03
|
||||
78.141 0 28.02
|
||||
78.178 0 27.97
|
||||
78.215 0 27.96
|
||||
78.252 0 27.94
|
||||
78.286 0 27.99
|
||||
78.323 0 28.00
|
||||
78.360 0 27.94
|
||||
78.397 0 27.88
|
||||
78.432 0 27.98
|
||||
78.469 0 27.94
|
||||
78.505 0 28.07
|
||||
78.542 0 28.10
|
||||
78.577 0 28.11
|
||||
78.614 0 28.05
|
||||
78.651 0 28.14
|
||||
78.688 0 28.17
|
||||
78.725 0 28.20
|
||||
78.759 0 28.28
|
||||
78.796 0 28.30
|
||||
78.833 0 28.20
|
||||
78.870 0 28.19
|
||||
78.905 0 28.17
|
||||
78.942 0 28.19
|
||||
78.979 0 28.23
|
||||
79.015 0 28.28
|
||||
79.052 0 28.22
|
||||
79.087 0 28.16
|
||||
79.124 0 28.17
|
||||
79.161 0 28.17
|
||||
79.198 0 28.22
|
||||
79.233 0 28.20
|
||||
79.269 0 28.17
|
||||
79.306 0 28.14
|
||||
79.343 0 28.05
|
||||
79.378 0 28.20
|
||||
79.415 0 28.31
|
||||
79.452 0 28.31
|
||||
79.489 0 28.28
|
||||
79.525 0 28.25
|
||||
79.560 0 28.23
|
||||
79.597 0 28.21
|
||||
79.634 0 28.28
|
||||
79.671 0 28.28
|
||||
79.706 0 28.29
|
||||
79.742 0 28.23
|
||||
79.779 0 28.20
|
||||
79.816 0 28.11
|
||||
79.853 0 28.15
|
||||
79.888 0 28.20
|
||||
79.925 0 28.17
|
||||
79.962 0 27.99
|
||||
79.998 0 27.96
|
||||
80.033 0 27.96
|
||||
80.070 0 28.00
|
||||
80.107 0 28.04
|
||||
80.144 0 28.10
|
||||
80.179 0 27.85
|
||||
80.216 0 27.82
|
||||
80.252 0 27.77
|
||||
80.289 0 27.76
|
||||
80.326 0 27.84
|
||||
80.361 0 27.84
|
||||
80.398 0 27.81
|
||||
80.435 0 27.77
|
||||
80.472 0 27.71
|
||||
80.506 0 27.82
|
||||
80.543 0 27.84
|
||||
80.580 0 27.87
|
||||
80.617 0 27.85
|
||||
80.654 0 27.82
|
||||
80.689 0 27.78
|
||||
80.736 2 27.70
|
||||
80.771 2 27.95
|
||||
80.807 2 28.04
|
||||
80.844 2 28.02
|
||||
80.881 2 28.11
|
||||
80.918 2 27.99
|
||||
80.953 2 28.02
|
||||
80.990 2 28.03
|
||||
81.027 2 27.95
|
||||
81.063 2 27.95
|
||||
81.098 2 27.91
|
||||
81.135 2 27.83
|
||||
81.172 2 27.81
|
||||
81.209 2 27.81
|
||||
81.246 2 27.80
|
||||
81.281 2 27.84
|
||||
81.317 2 27.86
|
||||
81.354 2 27.76
|
||||
81.391 2 27.67
|
||||
81.426 2 27.72
|
||||
81.463 2 27.55
|
||||
81.500 2 27.66
|
||||
81.537 2 27.63
|
||||
81.571 2 27.65
|
||||
81.608 2 27.77
|
||||
81.645 2 27.82
|
||||
81.682 2 27.85
|
||||
81.719 2 27.94
|
||||
81.754 2 27.94
|
||||
81.790 2 27.94
|
||||
81.827 2 27.93
|
||||
81.864 2 27.91
|
||||
81.899 2 27.94
|
||||
81.936 2 28.05
|
||||
81.973 2 28.05
|
||||
82.010 2 28.05
|
||||
82.046 2 28.07
|
||||
82.081 2 28.16
|
||||
82.118 2 28.17
|
||||
82.155 2 28.17
|
||||
82.192 2 28.16
|
||||
82.227 2 28.17
|
||||
82.264 2 28.08
|
||||
82.300 2 28.06
|
||||
82.337 2 28.08
|
||||
82.372 2 28.11
|
||||
82.409 2 28.19
|
||||
82.446 2 28.20
|
||||
82.483 2 28.20
|
||||
82.520 2 28.20
|
||||
82.554 2 28.26
|
||||
82.591 2 28.34
|
||||
82.628 2 28.43
|
||||
82.665 2 28.34
|
||||
82.700 2 28.36
|
||||
82.737 2 28.39
|
||||
82.774 2 28.37
|
||||
82.810 2 28.34
|
||||
82.847 2 28.44
|
||||
82.882 2 28.45
|
||||
82.919 2 28.44
|
||||
82.956 2 28.45
|
||||
82.993 2 28.44
|
||||
83.027 2 28.51
|
||||
83.064 2 28.43
|
||||
83.101 2 28.43
|
||||
83.138 2 28.40
|
||||
83.175 2 28.33
|
||||
83.210 2 28.31
|
||||
83.247 2 28.31
|
||||
83.283 2 28.33
|
||||
83.320 2 28.31
|
||||
83.355 2 28.33
|
||||
83.392 2 28.36
|
||||
83.429 2 28.39
|
||||
83.466 2 28.41
|
||||
83.501 2 28.45
|
||||
83.537 2 28.48
|
||||
83.574 2 28.54
|
||||
83.611 2 28.40
|
||||
83.648 2 28.54
|
||||
83.683 2 28.56
|
||||
83.720 2 28.57
|
||||
83.757 2 28.57
|
||||
83.793 2 28.60
|
||||
83.828 2 28.65
|
||||
83.865 2 28.69
|
||||
83.902 2 28.73
|
||||
83.939 2 28.76
|
||||
83.976 2 28.82
|
||||
84.011 2 28.86
|
||||
84.047 2 28.92
|
||||
84.084 2 28.86
|
||||
84.121 2 28.89
|
||||
84.156 2 28.90
|
||||
84.193 2 29.00
|
||||
84.230 2 29.06
|
||||
84.267 2 29.13
|
||||
84.301 2 29.21
|
||||
84.338 2 29.30
|
||||
84.375 2 29.35
|
||||
84.412 2 29.40
|
||||
84.449 2 29.50
|
||||
84.484 2 29.58
|
||||
84.520 2 29.62
|
||||
84.557 2 29.64
|
||||
84.594 2 29.67
|
||||
84.629 2 29.74
|
||||
84.666 2 29.90
|
||||
84.703 2 29.93
|
||||
84.740 2 30.05
|
||||
84.776 2 30.12
|
||||
84.811 2 30.14
|
||||
84.848 2 30.18
|
||||
84.885 2 30.22
|
||||
84.922 2 30.27
|
||||
84.957 2 30.35
|
||||
84.994 2 30.32
|
||||
85.030 2 30.32
|
||||
85.067 2 30.35
|
||||
85.104 2 30.51
|
||||
85.139 2 30.60
|
||||
85.176 2 30.67
|
||||
85.213 2 30.70
|
||||
85.250 2 30.81
|
||||
85.284 2 30.90
|
||||
85.321 2 30.87
|
||||
85.358 2 30.94
|
||||
85.395 2 31.00
|
||||
85.430 2 31.08
|
||||
85.467 2 31.19
|
||||
85.504 2 31.25
|
||||
85.540 2 31.27
|
||||
85.577 2 31.45
|
||||
85.612 2 31.54
|
||||
85.649 2 31.63
|
||||
85.686 2 31.63
|
||||
85.723 2 31.69
|
||||
85.757 2 31.79
|
||||
85.794 2 31.79
|
||||
85.831 2 31.76
|
||||
85.868 2 31.84
|
||||
85.905 2 31.63
|
||||
85.940 2 31.75
|
||||
85.977 2 31.83
|
||||
86.013 2 31.91
|
||||
86.050 2 31.99
|
||||
86.085 2 32.10
|
||||
86.122 2 32.18
|
||||
86.159 2 32.21
|
||||
86.196 2 32.38
|
||||
86.231 2 32.48
|
||||
86.267 2 32.66
|
||||
86.304 2 32.84
|
||||
86.341 2 32.92
|
||||
86.378 2 33.08
|
||||
86.413 2 33.16
|
||||
86.450 2 33.22
|
||||
86.487 2 33.60
|
||||
86.523 2 33.67
|
||||
86.579 6 33.79
|
||||
86.636 6 33.88
|
||||
86.691 6 33.97
|
||||
86.747 6 34.10
|
||||
86.804 6 34.26
|
||||
86.859 6 34.43
|
||||
86.917 6 34.47
|
||||
86.972 6 34.60
|
||||
87.027 6 34.64
|
||||
87.085 6 34.73
|
||||
87.140 6 34.79
|
||||
87.197 6 34.94
|
||||
87.252 6 35.15
|
||||
87.310 6 35.30
|
||||
87.365 6 35.48
|
||||
87.420 6 35.67
|
||||
87.478 6 35.76
|
||||
87.533 6 35.94
|
||||
87.590 6 36.07
|
||||
87.646 6 36.15
|
||||
87.703 6 36.27
|
||||
87.758 6 36.36
|
||||
87.814 6 36.55
|
||||
87.871 6 36.72
|
||||
87.926 6 36.91
|
||||
87.984 6 37.10
|
||||
88.039 6 37.33
|
||||
88.094 6 37.50
|
||||
88.152 6 37.68
|
||||
88.207 6 37.81
|
||||
88.264 6 37.99
|
||||
88.320 6 38.15
|
||||
88.377 6 38.40
|
||||
88.432 6 38.52
|
||||
88.487 6 38.71
|
||||
88.545 6 38.99
|
||||
88.600 6 39.20
|
||||
88.657 6 39.43
|
||||
88.713 6 39.57
|
||||
88.770 6 39.73
|
||||
88.825 6 39.90
|
||||
88.881 6 40.03
|
||||
88.938 6 40.21
|
||||
88.993 6 40.38
|
||||
89.051 6 40.64
|
||||
89.106 6 40.89
|
||||
89.161 6 41.06
|
||||
89.219 6 41.32
|
||||
89.274 6 41.44
|
||||
89.331 6 41.65
|
||||
89.387 6 41.81
|
||||
89.444 6 41.95
|
||||
89.499 6 42.11
|
||||
89.554 6 42.30
|
||||
89.612 6 42.44
|
||||
89.667 6 42.62
|
||||
89.724 6 42.77
|
||||
89.780 6 42.91
|
||||
89.837 6 43.07
|
||||
89.892 6 43.23
|
||||
89.948 6 43.29
|
||||
90.005 6 43.47
|
||||
90.060 6 43.58
|
||||
90.118 6 43.66
|
||||
90.173 6 43.85
|
||||
90.228 6 43.99
|
||||
90.286 6 44.13
|
||||
90.341 6 44.26
|
||||
90.398 6 44.42
|
||||
90.454 6 44.53
|
||||
90.511 6 44.70
|
||||
90.566 6 44.84
|
||||
90.621 6 44.98
|
||||
90.679 6 45.14
|
||||
90.734 6 45.27
|
||||
90.791 6 45.41
|
||||
90.847 6 45.55
|
||||
90.904 6 45.63
|
||||
90.959 6 45.78
|
||||
91.015 6 45.91
|
||||
91.072 6 46.02
|
||||
91.127 6 46.14
|
||||
91.185 6 46.28
|
||||
91.240 6 46.38
|
||||
91.297 6 46.48
|
||||
91.353 6 46.60
|
||||
91.408 6 46.71
|
||||
91.465 6 46.82
|
||||
91.521 6 46.95
|
||||
91.578 6 47.04
|
||||
91.633 6 47.17
|
||||
91.688 6 47.27
|
||||
91.746 6 47.36
|
||||
91.801 6 47.47
|
||||
91.858 6 47.58
|
||||
91.914 6 47.67
|
||||
91.971 6 47.78
|
||||
92.026 6 47.89
|
||||
92.082 6 47.99
|
||||
92.139 6 48.12
|
||||
92.194 6 48.21
|
||||
92.252 6 48.28
|
||||
92.307 6 48.37
|
||||
92.344 6 48.45
|
||||
92.381 6 48.53
|
||||
92.416 6 48.63
|
||||
92.452 6 48.67
|
||||
92.489 6 48.76
|
||||
92.526 6 48.84
|
||||
92.563 6 48.92
|
||||
92.598 6 48.95
|
||||
92.635 6 49.03
|
||||
92.672 6 49.08
|
||||
92.708 6 49.15
|
||||
92.743 6 49.20
|
||||
92.780 6 49.28
|
||||
92.817 6 49.34
|
||||
92.854 6 49.42
|
||||
92.889 6 49.49
|
||||
92.925 6 49.54
|
||||
92.962 6 49.57
|
||||
92.999 6 49.61
|
||||
93.036 6 49.67
|
||||
93.071 6 49.71
|
||||
93.108 6 49.78
|
||||
93.145 6 49.82
|
||||
93.181 6 49.90
|
||||
93.216 6 49.95
|
||||
93.253 6 50.00
|
||||
93.290 6 50.04
|
||||
93.327 6 50.14
|
||||
93.364 6 50.16
|
||||
93.399 6 50.20
|
||||
93.435 6 50.24
|
||||
93.472 6 50.36
|
||||
93.509 6 50.40
|
||||
93.544 6 50.47
|
||||
93.581 6 50.52
|
||||
93.618 6 50.56
|
||||
93.655 6 50.59
|
||||
93.691 6 50.62
|
||||
93.726 6 50.67
|
||||
93.763 6 50.76
|
||||
93.800 6 50.81
|
||||
93.837 6 50.85
|
||||
93.872 6 50.88
|
||||
93.908 6 50.86
|
||||
93.945 6 50.87
|
||||
93.982 6 50.87
|
||||
94.017 6 50.93
|
||||
94.054 6 50.82
|
||||
94.091 6 50.86
|
||||
94.128 6 50.87
|
||||
94.164 6 50.94
|
||||
94.199 6 50.98
|
||||
94.236 6 51.04
|
||||
94.273 6 51.08
|
||||
94.310 6 51.11
|
||||
94.345 6 51.09
|
||||
94.382 6 51.13
|
||||
94.418 6 51.18
|
||||
94.455 6 51.22
|
||||
94.492 6 51.27
|
||||
94.527 6 51.32
|
||||
94.564 6 51.44
|
||||
94.601 6 51.40
|
||||
94.638 6 51.51
|
||||
94.672 6 51.50
|
||||
94.709 6 51.54
|
||||
94.746 6 51.54
|
||||
94.783 6 51.37
|
||||
94.818 6 51.40
|
||||
94.855 6 51.45
|
||||
94.892 6 51.57
|
||||
94.928 6 51.60
|
||||
94.965 6 51.62
|
||||
95.000 6 51.63
|
||||
95.037 6 51.65
|
||||
95.074 6 51.69
|
||||
95.111 6 51.72
|
||||
95.145 6 51.71
|
||||
95.182 6 51.79
|
||||
95.219 6 51.81
|
||||
95.256 6 51.91
|
||||
95.293 6 51.95
|
||||
95.328 6 52.01
|
||||
95.365 6 52.14
|
||||
95.401 6 52.16
|
||||
95.438 6 52.17
|
||||
95.473 6 52.09
|
||||
95.510 6 52.17
|
||||
95.547 6 52.18
|
||||
95.584 6 52.24
|
||||
95.619 6 52.35
|
||||
95.655 6 52.36
|
||||
95.692 6 52.37
|
||||
95.729 6 52.38
|
||||
95.766 6 52.42
|
||||
95.801 6 52.50
|
||||
95.838 6 52.54
|
||||
95.875 6 52.55
|
||||
95.911 6 52.56
|
||||
95.946 6 52.68
|
||||
95.983 6 52.71
|
||||
96.020 6 52.76
|
||||
96.057 6 52.69
|
||||
96.094 6 52.67
|
||||
96.129 6 52.71
|
||||
96.165 6 52.71
|
||||
96.202 6 52.68
|
||||
96.239 6 52.72
|
||||
96.274 6 52.77
|
||||
96.311 6 52.82
|
||||
96.348 6 52.83
|
||||
96.385 6 52.80
|
||||
96.421 6 52.77
|
||||
96.456 6 52.81
|
||||
96.493 6 52.85
|
||||
96.530 6 52.85
|
||||
96.567 6 52.84
|
||||
96.602 6 52.84
|
||||
96.638 6 52.96
|
||||
96.675 6 53.00
|
||||
96.712 6 53.04
|
||||
96.747 6 53.08
|
||||
96.784 6 53.07
|
||||
96.821 6 53.10
|
||||
96.858 6 53.12
|
||||
96.894 6 53.13
|
||||
96.929 6 53.18
|
||||
96.966 6 53.19
|
||||
97.003 6 53.22
|
||||
97.040 6 53.22
|
||||
97.075 6 53.24
|
||||
97.112 6 53.27
|
||||
97.148 6 53.35
|
||||
97.185 6 53.33
|
||||
97.222 6 53.34
|
||||
97.257 6 53.34
|
||||
97.294 6 53.38
|
||||
97.331 6 53.48
|
||||
97.368 6 53.50
|
||||
97.402 6 53.49
|
||||
97.439 6 53.49
|
||||
97.476 6 53.47
|
||||
97.513 6 53.48
|
||||
97.548 6 53.50
|
||||
97.585 6 53.54
|
||||
97.622 6 53.57
|
||||
97.658 6 53.55
|
||||
97.695 6 53.55
|
||||
97.730 6 53.56
|
||||
97.767 6 53.66
|
||||
97.804 6 53.70
|
||||
97.841 6 53.72
|
||||
97.875 6 53.70
|
||||
97.912 6 53.63
|
||||
97.949 6 53.51
|
||||
97.986 6 53.54
|
||||
98.023 6 53.56
|
||||
98.058 6 53.62
|
||||
98.095 6 53.61
|
||||
98.131 6 53.62
|
||||
98.168 6 53.63
|
||||
98.203 6 53.66
|
||||
98.240 6 53.70
|
||||
98.277 6 53.75
|
||||
98.314 6 53.76
|
||||
98.351 6 53.73
|
||||
98.385 6 53.73
|
||||
98.422 6 53.75
|
||||
98.459 6 53.77
|
||||
98.496 6 53.80
|
||||
98.531 6 53.94
|
||||
98.568 6 53.93
|
||||
98.605 6 53.93
|
||||
98.641 6 53.94
|
||||
98.676 6 53.99
|
||||
98.713 6 54.00
|
||||
98.750 6 54.02
|
||||
98.787 6 54.04
|
||||
98.824 6 54.05
|
||||
98.859 6 54.05
|
||||
98.895 6 54.07
|
||||
98.932 6 54.14
|
||||
98.969 6 54.16
|
||||
99.004 6 54.19
|
||||
99.041 6 54.18
|
||||
99.078 6 54.19
|
||||
99.115 6 54.21
|
||||
99.151 6 54.26
|
||||
99.186 6 54.28
|
||||
99.223 6 54.29
|
||||
99.260 6 54.28
|
||||
99.297 6 54.29
|
||||
99.332 6 54.31
|
||||
99.368 6 54.33
|
||||
99.405 6 54.41
|
||||
99.442 6 54.41
|
||||
99.477 6 54.41
|
||||
99.514 6 54.37
|
||||
99.551 6 54.37
|
||||
99.588 6 54.37
|
||||
99.624 6 54.41
|
||||
99.659 6 54.41
|
||||
99.696 6 54.42
|
||||
99.733 6 54.41
|
||||
99.770 6 54.43
|
||||
99.805 6 54.45
|
||||
99.842 6 54.48
|
||||
99.878 6 54.49
|
||||
99.915 6 54.45
|
||||
99.952 6 54.42
|
||||
99.987 6 54.36
|
||||
100.024 6 54.32
|
||||
100.061 6 54.35
|
||||
100.100 6 54.38
|
||||
100.136 6 54.40
|
||||
100.173 6 54.40
|
||||
100.210 6 54.37
|
||||
100.249 6 54.38
|
||||
100.286 6 54.39
|
||||
100.323 6 54.41
|
||||
100.362 6 54.41
|
||||
100.399 6 54.42
|
||||
100.435 6 54.43
|
||||
100.472 6 54.44
|
||||
100.511 6 54.54
|
||||
100.548 6 54.54
|
||||
100.585 6 54.54
|
||||
100.624 6 54.61
|
||||
100.661 6 54.60
|
||||
100.698 6 54.60
|
||||
100.734 6 54.61
|
||||
100.773 6 54.63
|
||||
100.810 6 54.68
|
||||
100.847 6 54.69
|
||||
100.886 6 54.69
|
||||
100.923 6 54.69
|
||||
100.960 6 54.69
|
||||
100.997 6 54.74
|
||||
101.036 6 54.74
|
||||
101.072 6 54.76
|
||||
101.109 6 54.75
|
||||
101.148 6 54.77
|
||||
101.185 6 54.78
|
||||
101.222 6 54.78
|
||||
101.259 6 54.78
|
||||
101.298 6 54.79
|
||||
101.335 6 54.79
|
||||
101.371 6 54.82
|
||||
101.408 6 54.83
|
||||
101.447 6 54.87
|
||||
101.484 6 54.89
|
||||
101.521 6 54.91
|
||||
101.560 6 54.91
|
||||
101.597 6 54.81
|
||||
101.634 6 54.74
|
||||
101.670 6 54.74
|
||||
101.709 6 54.71
|
||||
101.746 6 54.74
|
||||
101.783 6 54.74
|
||||
101.822 6 54.77
|
||||
101.859 6 54.78
|
||||
101.896 6 54.77
|
||||
101.933 6 54.72
|
||||
101.971 6 54.68
|
||||
102.008 6 54.67
|
||||
102.045 6 54.67
|
||||
102.084 6 54.72
|
||||
102.121 6 54.74
|
||||
102.158 6 54.76
|
||||
102.195 6 54.83
|
||||
102.234 6 54.89
|
||||
102.270 6 54.89
|
||||
102.307 6 54.88
|
||||
102.346 6 54.87
|
||||
102.383 6 54.90
|
||||
102.420 6 54.92
|
||||
102.457 6 54.95
|
||||
102.496 6 54.97
|
||||
102.533 6 55.04
|
||||
102.569 6 55.05
|
||||
102.606 6 55.05
|
||||
102.645 6 55.04
|
||||
102.682 6 55.00
|
||||
102.719 6 55.00
|
||||
102.758 6 55.01
|
||||
102.795 6 55.04
|
||||
102.832 6 55.07
|
||||
102.868 6 55.08
|
||||
102.907 6 55.10
|
||||
102.944 6 55.11
|
||||
102.981 6 55.05
|
||||
103.020 6 55.06
|
||||
103.057 6 55.05
|
||||
103.094 6 55.06
|
||||
103.131 6 55.07
|
||||
103.170 6 55.11
|
||||
103.206 6 55.12
|
||||
103.243 6 55.12
|
||||
103.282 6 55.11
|
||||
103.319 6 55.09
|
||||
103.356 6 55.09
|
||||
103.393 6 55.14
|
||||
103.432 6 55.07
|
||||
103.469 6 55.09
|
||||
103.505 6 55.12
|
||||
103.544 6 55.14
|
||||
103.581 6 55.19
|
||||
103.618 6 55.17
|
||||
103.655 6 54.96
|
||||
103.694 6 54.92
|
||||
103.731 6 54.94
|
||||
103.768 6 54.93
|
||||
103.804 6 54.96
|
||||
103.843 6 54.99
|
||||
103.880 6 55.02
|
||||
103.917 6 55.02
|
||||
103.956 6 54.98
|
||||
103.993 6 54.92
|
||||
104.030 6 54.97
|
||||
104.067 6 55.02
|
||||
104.105 6 55.02
|
||||
104.142 6 55.02
|
||||
104.179 6 55.05
|
||||
104.218 6 55.07
|
||||
104.255 6 55.19
|
||||
104.292 6 55.22
|
||||
104.329 6 55.12
|
||||
104.368 6 55.11
|
||||
104.404 6 55.06
|
||||
104.441 6 55.06
|
||||
104.480 6 55.04
|
||||
104.517 6 55.06
|
||||
104.554 6 55.12
|
||||
104.591 6 55.17
|
||||
104.630 6 55.18
|
||||
104.667 6 55.14
|
||||
104.704 6 55.12
|
||||
104.742 6 55.10
|
||||
104.779 6 55.14
|
||||
104.816 6 55.17
|
||||
104.853 6 55.28
|
||||
104.892 6 55.31
|
||||
104.929 6 55.40
|
||||
104.966 6 55.40
|
||||
105.003 6 55.42
|
||||
105.041 6 55.40
|
||||
105.078 6 55.44
|
||||
105.115 6 55.45
|
||||
105.154 6 55.45
|
||||
105.191 6 55.41
|
||||
|
70
barista/heater.c
Normal file
70
barista/heater.c
Normal file
@@ -0,0 +1,70 @@
|
||||
/* Copyright (c) 2025 Rodrigo Arias Mallo <rodarima@gmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#include "heater.h"
|
||||
|
||||
#define HEATER_MIN 500UL /* ms */
|
||||
#define HEATER_MAX 2000UL /* ms */
|
||||
#define HEATER_PERIOD 5000UL /* ms */
|
||||
|
||||
void
|
||||
heater_on(struct heater *h, unsigned long t_ms, float duty)
|
||||
{
|
||||
unsigned long dt_on = duty * HEATER_MAX;
|
||||
|
||||
if (dt_on < HEATER_MIN)
|
||||
dt_on = 0;
|
||||
else if (dt_on > HEATER_MAX)
|
||||
dt_on = HEATER_MAX;
|
||||
|
||||
h->next_on_dt = dt_on;
|
||||
|
||||
if (h->st == HEATER_OFF) {
|
||||
h->st = HEATER_ON;
|
||||
h->t0_on = t_ms;
|
||||
h->t0_off = t_ms + h->next_on_dt;
|
||||
h->cycle = CYCLE_ON;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
heater_off(struct heater *h)
|
||||
{
|
||||
h->st = HEATER_OFF;
|
||||
}
|
||||
|
||||
int
|
||||
heater_state(struct heater *h, unsigned long t_ms)
|
||||
{
|
||||
if (h->st == HEATER_OFF)
|
||||
return 0;
|
||||
|
||||
/* Switch state if current time exceeds time limit
|
||||
* in the current state */
|
||||
/* FIXME: Integer overflow can cause the heater to turn on forever */
|
||||
while (1) {
|
||||
int changed = 0;
|
||||
if (h->cycle == CYCLE_ON) {
|
||||
if (t_ms >= h->t0_off) {
|
||||
h->cycle = CYCLE_OFF;
|
||||
h->t0_on += HEATER_PERIOD;
|
||||
changed = 1;
|
||||
}
|
||||
} else if (h->cycle == CYCLE_OFF) {
|
||||
if (t_ms >= h->t0_on) {
|
||||
/* Compute current cycle t0_off */
|
||||
h->cycle = CYCLE_ON;
|
||||
h->t0_off = h->t0_on + h->next_on_dt;
|
||||
changed = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!changed)
|
||||
break;
|
||||
}
|
||||
|
||||
if (h->cycle == CYCLE_ON)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
39
barista/heater.h
Normal file
39
barista/heater.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/* Copyright (c) 2025 Rodrigo Arias Mallo <rodarima@gmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#ifndef BARISTA_HEATER_H
|
||||
#define BARISTA_HEATER_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum heater_state {
|
||||
HEATER_OFF = 0,
|
||||
HEATER_ON,
|
||||
};
|
||||
|
||||
enum heater_cycle {
|
||||
CYCLE_ON = 0,
|
||||
CYCLE_OFF,
|
||||
};
|
||||
|
||||
struct heater {
|
||||
enum heater_state st;
|
||||
enum heater_cycle cycle;
|
||||
unsigned long t0_on; /* current cycle time on */
|
||||
unsigned long t0_off; /* current cycle time off */
|
||||
|
||||
/* Next cycle */
|
||||
unsigned long next_on_dt; /* in ms */
|
||||
};
|
||||
|
||||
void heater_on(struct heater *h, unsigned long t_ms, float duty);
|
||||
void heater_off(struct heater *h);
|
||||
int heater_state(struct heater *h, unsigned long t_ms);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* BARISTA_HEATER_H */
|
||||
71
barista/led.c
Normal file
71
barista/led.c
Normal file
@@ -0,0 +1,71 @@
|
||||
/* Copyright (c) 2025 Rodrigo Arias Mallo <rodarima@gmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#include "led.h"
|
||||
#include <string.h>
|
||||
|
||||
void led_on(struct led *led)
|
||||
{
|
||||
led->mode = LED_ON;
|
||||
}
|
||||
|
||||
void led_off(struct led *led)
|
||||
{
|
||||
led->mode = LED_OFF;
|
||||
}
|
||||
|
||||
void led_pattern(struct led *led, unsigned long t_ms, unsigned long period_ms, const char *pattern)
|
||||
{
|
||||
int n = strlen(pattern);
|
||||
unsigned long step_ms = period_ms / n;
|
||||
|
||||
/* Don't change the current state */
|
||||
if (led->mode == LED_PATTERN &&
|
||||
led->step_ms == step_ms &&
|
||||
led->pattern == pattern)
|
||||
return;
|
||||
|
||||
led->mode = LED_PATTERN;
|
||||
led->pat_i = 0;
|
||||
led->pat_n = n;
|
||||
led->pattern = pattern;
|
||||
led->step_ms = step_ms;
|
||||
led->t_ms = t_ms;
|
||||
}
|
||||
|
||||
/* Return led level brightness in [0, 255] at current time */
|
||||
int led_level(struct led *led, unsigned long t_ms)
|
||||
{
|
||||
if (led->mode == LED_OFF)
|
||||
return 0;
|
||||
|
||||
if (led->mode == LED_ON)
|
||||
return 255;
|
||||
|
||||
if (led->mode == LED_PATTERN) {
|
||||
while (led->t_ms + led->step_ms < t_ms) {
|
||||
led->t_ms += led->step_ms;
|
||||
led->pat_i++;
|
||||
if (led->pat_i >= led->pat_n)
|
||||
led->pat_i = 0;
|
||||
}
|
||||
|
||||
int c = led->pattern[led->pat_i];
|
||||
int level;
|
||||
|
||||
if (c >= '0' && c <= '9')
|
||||
level = 17 * (c - '0');
|
||||
else
|
||||
level = 17 * (10 + (c - 'a'));
|
||||
|
||||
if (level < 0)
|
||||
level = 0;
|
||||
else if (level > 255)
|
||||
level = 255;
|
||||
|
||||
return level;
|
||||
}
|
||||
|
||||
/* Unknown mode, turn off */
|
||||
return 0;
|
||||
}
|
||||
35
barista/led.h
Normal file
35
barista/led.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/* Copyright (c) 2025 Rodrigo Arias Mallo <rodarima@gmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#ifndef BARISTA_LED_H
|
||||
#define BARISTA_LED_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum led_mode {
|
||||
LED_OFF = 0,
|
||||
LED_ON,
|
||||
LED_PATTERN,
|
||||
};
|
||||
|
||||
struct led {
|
||||
enum led_mode mode;
|
||||
const char *pattern;
|
||||
int pat_i;
|
||||
int pat_n;
|
||||
unsigned long step_ms;
|
||||
unsigned long t_ms;
|
||||
};
|
||||
|
||||
void led_on(struct led *led);
|
||||
void led_off(struct led *led);
|
||||
void led_pattern(struct led *led, unsigned long t_ms, unsigned long period_ms, const char *pattern);
|
||||
int led_level(struct led *led, unsigned long t_ms);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* BARISTA_LED_H */
|
||||
44
barista/ntc.c
Normal file
44
barista/ntc.c
Normal file
@@ -0,0 +1,44 @@
|
||||
/* Copyright (c) 2025 Rodrigo Arias Mallo <rodarima@gmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#include <math.h>
|
||||
|
||||
/* Steinhart-Hart Thermistor Coefficients, used to convert resistance into
|
||||
* temperature.
|
||||
*
|
||||
* The current NTC sensor has 102kOhm at 24C but I don't know the specific
|
||||
* model, so the coefficients are computed for this NTC sensor instead:
|
||||
* https://www.tme.eu/Document/f9d2f5e38227fc1c7d979e546ff51768/NTCM-100K-B3950.pdf
|
||||
*
|
||||
* The table seems to match what I would expect. Their R2 resistor is 6.8 kOhm,
|
||||
* which yields a cuttof temperature of around 98.5 C at exactly half voltage
|
||||
* (where the ADC would have more precision).
|
||||
*
|
||||
* In any case, we can calibrate the original NTC sensor by taking three
|
||||
* temperature points. See:
|
||||
* https://www.thinksrs.com/downloads/programs/therm%20calc/ntccalibrator/ntccalculator.html
|
||||
*/
|
||||
#define C1 0.7740577674e-3
|
||||
#define C2 2.073449619e-4
|
||||
#define C3 1.263502259e-7
|
||||
|
||||
/* Return the temperature in celsisus */
|
||||
float
|
||||
ntc_temp(float R)
|
||||
{
|
||||
/* Computing the log is slow, we may want to build a table */
|
||||
float logR = log(R);
|
||||
float T = (1.0 / (C1 + C2*logR + C3*logR*logR*logR));
|
||||
float Tc = T - 273.15;
|
||||
return Tc;
|
||||
}
|
||||
|
||||
/* Return resistance in Ohms */
|
||||
float
|
||||
ntc_resistance(int Vo)
|
||||
{
|
||||
float R1 = 10.0e3; /* Resistor for voltage divider */
|
||||
float R2 = R1 * (1023.0 / (float)Vo - 1.0);
|
||||
|
||||
return R2;
|
||||
}
|
||||
18
barista/ntc.h
Normal file
18
barista/ntc.h
Normal file
@@ -0,0 +1,18 @@
|
||||
/* Copyright (c) 2025 Rodrigo Arias Mallo <rodarima@gmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#ifndef BARISTA_NTC_H
|
||||
#define BARISTA_NTC_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
float ntc_resistance(int Vo);
|
||||
float ntc_temp(float omhs);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* BARISTA_NTC_H */
|
||||
61
barista/overheat.c
Normal file
61
barista/overheat.c
Normal file
@@ -0,0 +1,61 @@
|
||||
/* 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;
|
||||
|
||||
/* If already go n samples, recompute delta and speed */
|
||||
if (o->n == OVH_NSAMPLES) {
|
||||
float last_T = o->temp[o->next];
|
||||
float last_t = o->t[o->next];
|
||||
float dt = (float) (t_ms - last_t) * 1e-3;
|
||||
o->delta = temp - last_T;
|
||||
o->speed = o->delta / dt;
|
||||
}
|
||||
|
||||
/* Add the new sample */
|
||||
o->temp[o->next] = temp;
|
||||
o->t[o->next] = t_ms;
|
||||
o->next++;
|
||||
if (o->next >= OVH_NSAMPLES)
|
||||
o->next = 0;
|
||||
if (o->n < OVH_NSAMPLES)
|
||||
o->n++;
|
||||
|
||||
o->last_time = t_ms;
|
||||
}
|
||||
|
||||
float
|
||||
overheat_delta(struct overheat *o)
|
||||
{
|
||||
return o->delta;
|
||||
}
|
||||
|
||||
float
|
||||
overheat_speed(struct overheat *o)
|
||||
{
|
||||
return o->speed;
|
||||
}
|
||||
|
||||
int
|
||||
overheat_panic(struct overheat *o)
|
||||
{
|
||||
if (o->speed > OVH_THRESHOLD)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
35
barista/overheat.h
Normal file
35
barista/overheat.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/* 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 / s */
|
||||
|
||||
struct overheat {
|
||||
unsigned long last_time;
|
||||
float temp[OVH_NSAMPLES];
|
||||
long unsigned t[OVH_NSAMPLES];
|
||||
int next;
|
||||
int n;
|
||||
float delta;
|
||||
float speed;
|
||||
};
|
||||
|
||||
void overheat_init(struct overheat *o);
|
||||
void overheat_input(struct overheat *o, unsigned long t_ms, float temp);
|
||||
float overheat_delta(struct overheat *o);
|
||||
float overheat_speed(struct overheat *o);
|
||||
int overheat_panic(struct overheat *o);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* BARISTA_OVERHEAT_H */
|
||||
78
barista/pinout.h
Normal file
78
barista/pinout.h
Normal file
@@ -0,0 +1,78 @@
|
||||
/* Copyright (c) 2025 Rodrigo Arias Mallo <rodarima@gmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#ifndef BARISTA_PINOUT_H
|
||||
#define BARISTA_PINOUT_H
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* 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,
|
||||
};
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* BARISTA_PINOUT_H */
|
||||
1
barista/test/Makefile
Normal file
1
barista/test/Makefile
Normal file
@@ -0,0 +1 @@
|
||||
test_ntc: test_ntc.o ../ntc.c
|
||||
13
barista/test/compat.c
Normal file
13
barista/test/compat.c
Normal file
@@ -0,0 +1,13 @@
|
||||
/* Copyright (c) 2025 Rodrigo Arias Mallo <rodarima@gmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#include "compat.h"
|
||||
#include <time.h>
|
||||
|
||||
unsigned long millis(void)
|
||||
{
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
|
||||
return ts.tv_sec * 1000UL + ts.tv_nsec / 1000000UL;
|
||||
}
|
||||
17
barista/test/compat.h
Normal file
17
barista/test/compat.h
Normal file
@@ -0,0 +1,17 @@
|
||||
/* Copyright (c) 2025 Rodrigo Arias Mallo <rodarima@gmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#ifndef BARISTA_COMPAT_H
|
||||
#define BARISTA_COMPAT_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
unsigned long millis(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* BARISTA_COMPAT_H */
|
||||
42
barista/test/test_led.c
Normal file
42
barista/test/test_led.c
Normal file
@@ -0,0 +1,42 @@
|
||||
/* Copyright (c) 2025 Rodrigo Arias Mallo <rodarima@gmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#include <stdio.h>
|
||||
#include "led.h"
|
||||
#include "compat.h"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
struct led led;
|
||||
unsigned long t0 = millis();
|
||||
unsigned long step = 1000UL;
|
||||
|
||||
led_pattern(&led, t0, step, "000123456789abcdefff");
|
||||
|
||||
int last_level = -1;
|
||||
|
||||
unsigned long last_t = millis();
|
||||
while (1) {
|
||||
unsigned long t = millis();
|
||||
|
||||
if (t - t0 >= 10000UL)
|
||||
break;
|
||||
|
||||
if (t - last_t < 50UL)
|
||||
continue;
|
||||
|
||||
int level = led_level(&led, millis());
|
||||
printf("|");
|
||||
for (int i = 0; i < 256; i+=4) {
|
||||
if (i < level)
|
||||
printf("=");
|
||||
else
|
||||
printf(" ");
|
||||
}
|
||||
printf("|\n");
|
||||
last_level = level;
|
||||
last_t = t;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
18
barista/test/test_ntc.c
Normal file
18
barista/test/test_ntc.c
Normal file
@@ -0,0 +1,18 @@
|
||||
/* Copyright (c) 2025 Rodrigo Arias Mallo <rodarima@gmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#include <stdio.h>
|
||||
#include "ntc.h"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
for (int i = 0; i <= 1023; i++) {
|
||||
float R = ntc_resistance(i);
|
||||
float T = ntc_temp(R);
|
||||
if (T < 96.0 || T > 100.2)
|
||||
continue;
|
||||
|
||||
printf("%6d %12.1f Ohm %12.1f C\n", i, R, T);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
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;
|
||||
}
|
||||
49
barista/test/test_thermostat.c
Normal file
49
barista/test/test_thermostat.c
Normal file
@@ -0,0 +1,49 @@
|
||||
/* Copyright (c) 2025 Rodrigo Arias Mallo <rodarima@gmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#include <stdio.h>
|
||||
#include "overheat.h"
|
||||
#include "thermostat.h"
|
||||
#include "heater.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 = { 0 };
|
||||
struct thermostat th = { 0 };
|
||||
struct heater heater = { 0 };
|
||||
|
||||
overheat_init(&ovh);
|
||||
|
||||
for (int i = 0; scanf("%f %d %f", &t, &st, &temp) == 3; i++) {
|
||||
if (i == 0)
|
||||
thermostat_set(&th, 60.0);
|
||||
|
||||
unsigned long t_ms = t * 1000;
|
||||
|
||||
overheat_input(&ovh, t_ms, temp);
|
||||
float delta = overheat_delta(&ovh);
|
||||
float speed = overheat_speed(&ovh);
|
||||
float u = thermostat_state(&th, temp, speed);
|
||||
heater_on(&heater, t_ms, u);
|
||||
int h = heater_state(&heater, t_ms);
|
||||
int panic = overheat_panic(&ovh);
|
||||
|
||||
printf("%8.3f %2d %6.2f %6.1f %8.3f %8.3f %d %s\n", t, st, temp, delta, speed, u, h, panic ? "PANIC" : "OK");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
64
barista/thermostat.c
Normal file
64
barista/thermostat.c
Normal file
@@ -0,0 +1,64 @@
|
||||
/* Copyright (c) 2025 Rodrigo Arias Mallo <rodarima@gmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#include "thermostat.h"
|
||||
|
||||
#define TEMP_MIN 35.0 /* °C */
|
||||
#define DELTA_LOW 1.0
|
||||
#define DELTA_HIGH 1.0
|
||||
|
||||
#define T_ERR_MIN 35.0 /* °C */
|
||||
|
||||
void
|
||||
thermostat_set(struct thermostat *th, float temp_target)
|
||||
{
|
||||
if (th->st == THERMOSTAT_ON && th->temp_target == temp_target)
|
||||
return;
|
||||
|
||||
th->st = THERMOSTAT_ON;
|
||||
th->temp_target = temp_target;
|
||||
th->temp_min = temp_target - DELTA_LOW;
|
||||
th->temp_max = temp_target + DELTA_HIGH;
|
||||
th->on = 1;
|
||||
}
|
||||
|
||||
void
|
||||
thermostat_off(struct thermostat *th)
|
||||
{
|
||||
th->st = THERMOSTAT_OFF;
|
||||
th->on = 0;
|
||||
}
|
||||
|
||||
static float
|
||||
pid(float T0, float T, float dT_dt)
|
||||
{
|
||||
float err_min = 2.0;
|
||||
|
||||
/* The rate of change of error is the same as the temperature, as they
|
||||
* are only offset by a mostly constant value */
|
||||
float derr_dt = dT_dt;
|
||||
|
||||
if ((T0 - T) < err_min)
|
||||
return 0.0;
|
||||
|
||||
float Kp = 1.0 / 20.0;
|
||||
float Kd = - 1.0 / 3.0;
|
||||
float u = Kp * (T0 - T) + Kd * dT_dt;
|
||||
|
||||
if (u < 0.0)
|
||||
u = 0.0;
|
||||
else if (u > 1.0)
|
||||
u = 1.0;
|
||||
|
||||
return u;
|
||||
}
|
||||
|
||||
/* Return a value in [0, 1] to set the heater duty cycle */
|
||||
float
|
||||
thermostat_state(struct thermostat *th, float T, float dT_dt)
|
||||
{
|
||||
if (th->st == THERMOSTAT_OFF)
|
||||
return 0.0;
|
||||
|
||||
return pid(th->temp_target, T, dT_dt);
|
||||
}
|
||||
32
barista/thermostat.h
Normal file
32
barista/thermostat.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_THERMOSTAT_H
|
||||
#define BARISTA_THERMOSTAT_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum thermostat_state {
|
||||
THERMOSTAT_OFF = 0,
|
||||
THERMOSTAT_ON,
|
||||
};
|
||||
|
||||
struct thermostat {
|
||||
enum thermostat_state st;
|
||||
float temp_target;
|
||||
float temp_min;
|
||||
float temp_max;
|
||||
int on;
|
||||
};
|
||||
|
||||
void thermostat_set(struct thermostat *th, float temp_target);
|
||||
void thermostat_off(struct thermostat *th);
|
||||
float thermostat_state(struct thermostat *th, float T, float dT_dt);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* BARISTA_THERMOSTAT_H */
|
||||
Reference in New Issue
Block a user