Compare commits

...

10 Commits

Author SHA1 Message Date
Rodrigo Arias Mallo
8502ee3c5c Implement PID thremostat controller
For now only the proportional (Kp) and derivative (Kd) components are
used, the integral term is 0.
2025-11-02 19:24:02 +01:00
Rodrigo Arias Mallo
678f16111b Add thermostat and heater control 2025-11-02 19:24:02 +01:00
Rodrigo Arias Mallo
3ea6ff0e14 Merge makefile rules for tests 2025-11-02 19:24:02 +01:00
Rodrigo Arias Mallo
d59dec50a8 Implement led control in another module 2025-11-02 19:24:02 +01:00
Rodrigo Arias Mallo
402e1f4e43 Add first brew data 2025-10-16 21:13:40 +02:00
Rodrigo Arias Mallo
14592c6f55 Allow cold brewing with heater off 2025-10-16 21:12:58 +02:00
Rodrigo Arias Mallo
55d0548ea8 Add temperature data with overheat protection 2025-10-15 21:28:16 +02:00
Rodrigo Arias Mallo
3538225a91 Only average available temperature samples 2025-10-15 21:27:18 +02:00
Rodrigo Arias Mallo
7bd0b44871 Add overheat protection 2025-10-15 21:14:19 +02:00
Rodrigo Arias Mallo
53e3d47a04 Add watchdog to turn off relays in a hang
Prevents a hang from keeping the heater or pump relays always on, which
could cause the thermal fuses to burn or the pump to empty the deposit
and then burn as well.
2025-10-13 21:22:26 +02:00
20 changed files with 5339 additions and 67 deletions

3
barista/.gitignore vendored
View File

@@ -1,3 +1,4 @@
build/ build/
test_ntc misc/
*.test
*.o *.o

View File

@@ -9,13 +9,24 @@ PROJ=barista
BUILD=build/$(subst :,.,$(FQBN)) BUILD=build/$(subst :,.,$(FQBN))
HEX=$(BUILD)/$(PROJ).ino.hex 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 # For host test programs
CPPFLAGS=-I. CPPFLAGS=-I.
LIBS=-lm LIBS=-lm
all: $(HEX) all: $(HEX)
$(HEX): barista.ino ntc.c ntc.h pinout.h $(HEX): $(SRC)
arduino-cli compile $(OPTS) -e --fqbn $(FQBN) arduino-cli compile $(OPTS) -e --fqbn $(FQBN)
upload: $(HEX) upload: $(HEX)
@@ -24,12 +35,15 @@ upload: $(HEX)
serial: serial:
picocom -b 9600 --lower-rts --lower-dtr /dev/ttyUSB0 --imap lfcrlf picocom -b 9600 --lower-rts --lower-dtr /dev/ttyUSB0 --imap lfcrlf
test: test_ntc test: ntc.test overheat.test led.test
test_ntc: test/test_ntc.o ntc.o %.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) gcc $^ -o $@ $(LIBS)
clean: clean:
rm -f test/test_ntc.o ntc.o rm -f $(TESTS) test/*.o *.o
.PHONY: test all clean .PHONY: test all clean

View File

@@ -2,7 +2,12 @@
* SPDX-License-Identifier: GPL-3.0-or-later */ * SPDX-License-Identifier: GPL-3.0-or-later */
#include "ntc.h" #include "ntc.h"
#include "overheat.h"
#include "led.h"
#include "heater.h"
#include "thermostat.h"
#include "pinout.h" #include "pinout.h"
#include <avr/wdt.h>
enum logic { enum logic {
ON = 1, ON = 1,
@@ -10,8 +15,7 @@ enum logic {
}; };
#define DEBOUNCE_TIME 20L /* ms */ #define DEBOUNCE_TIME 20L /* ms */
#define TEMP_MIN 40.0f /* C */ #define TEMP_HOT 75.0f /* C */
#define TEMP_MAX 50.0f /* C */
#define LED_MIN_VALUE 0 #define LED_MIN_VALUE 0
@@ -20,8 +24,10 @@ enum machine_state {
DEBOUNCE, DEBOUNCE,
HEATING, HEATING,
HOT, HOT,
BREWING, BREWING_HOT,
BREWING_COLD,
COOLING, COOLING,
PANIC_OVERHEAT,
}; };
//int state = SLEEPING; //int state = SLEEPING;
@@ -46,7 +52,6 @@ enum buzz_state {
BUZZ_ACTIVE, BUZZ_ACTIVE,
}; };
int button_pin[MAX_BTN] = { int button_pin[MAX_BTN] = {
[BTN_ON] = PIN_POWER_ON, [BTN_ON] = PIN_POWER_ON,
[BTN_HOT] = PIN_HOT, [BTN_HOT] = PIN_HOT,
@@ -74,6 +79,7 @@ struct state {
unsigned long hot_t0; unsigned long hot_t0;
int ntc_i; /* Next available place */ int ntc_i; /* Next available place */
int ntc_n; /* Samples in array */
float ntc_R; float ntc_R;
float ntc_last_T; float ntc_last_T;
float ntc_array_T[MAX_SAMPLES]; float ntc_array_T[MAX_SAMPLES];
@@ -82,6 +88,15 @@ struct state {
struct btn btn[MAX_BTN]; struct btn btn[MAX_BTN];
enum buzz_state buzz_state; enum buzz_state buzz_state;
unsigned long buzz_t0; 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; } g_st;
int read_input(int pin) int read_input(int pin)
@@ -126,12 +141,16 @@ void proc_ntc(struct state *state, const struct input *input)
state->ntc_array_T[state->ntc_i++] = state->ntc_last_T; state->ntc_array_T[state->ntc_i++] = state->ntc_last_T;
if (state->ntc_i >= MAX_SAMPLES) if (state->ntc_i >= MAX_SAMPLES)
state->ntc_i = 0; state->ntc_i = 0;
if (state->ntc_n < MAX_SAMPLES)
state->ntc_n++;
float avg = 0; float avg = 0;
for (int i = 0; i < MAX_SAMPLES; i++) for (int i = 0; i < state->ntc_n; i++)
avg += state->ntc_array_T[i]; avg += state->ntc_array_T[i];
state->ntc_T = avg / MAX_SAMPLES; 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) void proc_buttons(struct state *state, const struct input *input)
@@ -168,28 +187,34 @@ void proc_buttons(struct state *state, const struct input *input)
} }
} }
/* 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_min = 50;
int red_state = red_min; int red_state = red_min;
unsigned long brewing_time = 3000UL; /* 3 seconds */ unsigned long brewing_max_time = 30000UL; /* 30 seconds */
unsigned long cooling_time = 3000UL; /* 3 seconds */ unsigned long cooling_time = 3000UL; /* 3 seconds */
unsigned long max_heating_time = 60000UL; /* 60 seconds */ unsigned long overheat_time = 10000UL; /* 10 seconds */
unsigned long max_idle_time = 10000UL; /* 10 seconds */ unsigned long max_heating_time = 60000UL; /* 60 seconds */
unsigned long max_idle_time = 300000UL; /* 300 seconds */
void proc_machine(struct state *st) void proc_machine(struct state *st)
{ {
static unsigned long last_print_t = 0;
float temp = st->ntc_T; float temp = st->ntc_T;
int on = (st->btn[BTN_ON].state == RELEASED); int on = (st->btn[BTN_ON].state == RELEASED);
int hot = (st->btn[BTN_HOT].state == PRESSED); int brew_hot = (st->btn[BTN_HOT].state == PRESSED);
unsigned long t = millis();
Serial.print("t="); /* If the machine is overheating */
Serial.print(millis()); if (overheat_panic(&st->overheat)) {
Serial.print(" state="); st->mstate = PANIC_OVERHEAT;
Serial.print(st->mstate); st->overheat_t0 = millis();
Serial.print(" on="); Serial.println("PANIC OVERHEATING");
Serial.print(on); }
Serial.print(" temp=");
Serial.print(temp);
Serial.println(" C");
/* Pressing ON cancels any operation */ /* Pressing ON cancels any operation */
if (st->mstate != SLEEPING && on) { if (st->mstate != SLEEPING && on) {
@@ -199,13 +224,18 @@ void proc_machine(struct state *st)
} }
if (st->mstate == SLEEPING) { if (st->mstate == SLEEPING) {
if (on) { /* 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->mstate = HEATING;
st->heating_t0 = millis(); st->heating_t0 = millis();
Serial.println("heating"); Serial.println("heating");
} }
} else if (st->mstate == HEATING) { } else if (st->mstate == HEATING) {
if (temp > TEMP_MAX) { if (temp > TEMP_HOT) {
st->mstate = HOT; st->mstate = HOT;
st->hot_t0 = millis(); st->hot_t0 = millis();
st->buzz_state = BUZZ_HEY; st->buzz_state = BUZZ_HEY;
@@ -216,20 +246,28 @@ void proc_machine(struct state *st)
Serial.println("cannot heat, going to sleep"); Serial.println("cannot heat, going to sleep");
} }
} else if (st->mstate == HOT) { } else if (st->mstate == HOT) {
if (hot) { if (brew_hot) {
st->mstate = BREWING; st->mstate = BREWING_HOT;
st->brewing_t0 = millis(); st->brewing_t0 = millis();
Serial.println("brewing"); Serial.println("brewing");
} else if (millis() - st->hot_t0 > max_idle_time) { } else if (millis() - st->hot_t0 > max_idle_time) {
st->mstate = SLEEPING; st->mstate = SLEEPING;
Serial.println("idle timeout, going to sleep"); Serial.println("idle timeout, going to sleep");
} }
} else if (st->mstate == BREWING) { } else if (st->mstate == BREWING_HOT) {
if (millis() - st->brewing_t0 > brewing_time) { /* 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->mstate = COOLING;
st->cooling_t0 = millis(); st->cooling_t0 = millis();
Serial.println("cooling"); 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) { } else if (st->mstate == COOLING) {
/* TODO: Wait a bit and go back to heating */ /* TODO: Wait a bit and go back to heating */
if (millis() - st->cooling_t0 > cooling_time) { if (millis() - st->cooling_t0 > cooling_time) {
@@ -237,6 +275,13 @@ void proc_machine(struct state *st)
st->heating_t0 = millis(); st->heating_t0 = millis();
Serial.println("heating"); 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");
}
} }
} }
@@ -268,64 +313,90 @@ void do_proc(struct state *st, const struct input *input)
void void
output_leds(const struct state *st) output_leds(const struct state *st)
{ {
static int r = 0; unsigned long t = millis();
static int g = 0;
if (st->mstate == HEATING || st->mstate == COOLING) { if (st->mstate == SLEEPING) {
analogWrite(PIN_LED_RED, r); led_off(&st->red_led);
setled(PIN_LED_GREEN, 0); led_off(&st->green_led);
if (r >= 255) } else if (st->mstate == HEATING) {
r = 0; led_off(&st->red_led);
else led_pattern(&st->green_led, t, 1000UL, "000123456789abcdefff");
r += 3; } else if (st->mstate == COOLING) {
led_off(&st->red_led);
led_pattern(&st->green_led, t, 1000UL, "fffedcba987654321000");
} else if (st->mstate == HOT) { } else if (st->mstate == HOT) {
setled(PIN_LED_RED, 0); led_off(&st->red_led);
setled(PIN_LED_GREEN, 1); led_on(&st->green_led);
r = 0; } else if (st->mstate == PANIC_OVERHEAT) {
} else if (st->mstate == BREWING) { led_pattern(&st->red_led, t, 3000UL, "f0f0f0000000");
setled(PIN_LED_RED, 0); led_pattern(&st->green_led, t, 3000UL, "000000f0f0f0");
analogWrite(PIN_LED_GREEN, g); } else if (st->mstate == BREWING_HOT || st->mstate == BREWING_COLD) {
if (g >= 255) led_off(&st->red_led);
g = 0; led_pattern(&st->green_led, millis(), 2000UL, "0123456789abcdefedcba9876543210");
else
g += 3;
} else { } else {
setled(PIN_LED_RED, 0); led_off(&st->red_led);
setled(PIN_LED_GREEN, 0); led_off(&st->green_led);
r = 0;
} }
analogWrite(PIN_LED_RED, led_level(&st->red_led, t));
analogWrite(PIN_LED_GREEN, led_level(&st->green_led, t));
} }
void void
output_heater(const struct state *st) output_relays(const struct state *st)
{ {
if (st->mstate == HEATING || st->mstate == HOT || st->mstate == BREWING) { unsigned long t = millis();
if (st->ntc_T < TEMP_MIN)
relay(PIN_HEAT, ON); /* First configure thermostate */
else if (st->ntc_T > TEMP_MAX) if (st->mstate == HEATING || st->mstate == HOT || st->mstate == BREWING_HOT)
relay(PIN_HEAT, OFF); thermostat_set(&st->thermostat, TEMP_HOT+5.0);
} else { 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); relay(PIN_HEAT, OFF);
}
}
void int pump_relay_st = 0;
output_pump(const struct state *st) if (st->mstate == BREWING_HOT || st->mstate == BREWING_COLD)
{ pump_relay_st = 1;
if (st->mstate == BREWING)
if (pump_relay_st)
relay(PIN_PUMP, ON); relay(PIN_PUMP, ON);
else else
relay(PIN_PUMP, OFF); 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 do_output(const struct state *st) void do_output(const struct state *st)
{ {
output_leds(st); output_leds(st);
output_heater(st); output_relays(st);
} }
void setup() void setup()
{ {
wdt_disable();
Serial.begin(9600); Serial.begin(9600);
Serial.println("Booting"); Serial.println("Booting");
@@ -341,7 +412,10 @@ void setup()
relay(PIN_HEAT, OFF); relay(PIN_HEAT, OFF);
relay(PIN_PUMP, OFF); relay(PIN_PUMP, OFF);
overheat_init(&g_st.overheat);
Serial.println("Ready"); Serial.println("Ready");
wdt_enable(WDTO_250MS);
} }
void loop() void loop()
@@ -351,4 +425,5 @@ void loop()
do_output(&g_st); do_output(&g_st);
delay(5); delay(5);
wdt_reset();
} }

1770
barista/data/brew.csv Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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
1 t_s st temp_C
2 68.167 0 28.12
3 68.204 0 28.15
4 68.241 0 28.16
5 68.278 0 28.18
6 68.313 0 28.19
7 68.349 0 28.22
8 68.386 0 28.22
9 68.423 0 28.21
10 68.458 0 28.19
11 68.495 0 28.19
12 68.532 0 28.21
13 68.569 0 28.22
14 68.605 0 28.24
15 68.640 0 28.22
16 68.677 0 28.21
17 68.714 0 28.22
18 68.751 0 28.24
19 68.786 0 28.26
20 68.823 0 28.26
21 68.859 0 28.24
22 68.896 0 28.22
23 68.933 0 28.21
24 68.968 0 28.22
25 69.005 0 28.13
26 69.042 0 28.10
27 69.079 0 28.08
28 69.113 0 28.07
29 69.150 0 28.05
30 69.187 0 28.05
31 69.224 0 28.07
32 69.259 0 28.05
33 69.296 0 28.04
34 69.332 0 28.02
35 69.369 0 28.01
36 69.406 0 28.01
37 69.441 0 28.02
38 69.478 0 28.04
39 69.515 0 28.04
40 69.552 0 28.04
41 69.586 0 28.07
42 69.623 0 28.12
43 69.660 0 28.13
44 69.697 0 28.15
45 69.734 0 28.15
46 69.769 0 28.13
47 69.806 0 28.12
48 69.842 0 28.13
49 69.879 0 28.15
50 69.914 0 28.12
51 69.951 0 28.12
52 69.988 0 28.10
53 70.025 0 28.08
54 70.060 0 28.08
55 70.096 0 28.10
56 70.133 0 28.10
57 70.170 0 28.16
58 70.207 0 28.13
59 70.242 0 28.12
60 70.279 0 28.10
61 70.316 0 28.12
62 70.352 0 28.13
63 70.387 0 28.15
64 70.424 0 28.12
65 70.461 0 28.10
66 70.498 0 28.13
67 70.535 0 28.15
68 70.569 0 28.18
69 70.606 0 28.12
70 70.643 0 28.08
71 70.680 0 28.07
72 70.715 0 28.05
73 70.752 0 28.07
74 70.789 0 28.08
75 70.825 0 28.10
76 70.860 0 28.10
77 70.897 0 28.08
78 70.934 0 28.07
79 70.971 0 28.07
80 71.008 0 28.12
81 71.043 0 28.08
82 71.079 0 28.08
83 71.116 0 28.07
84 71.153 0 28.05
85 71.188 0 28.12
86 71.225 0 28.13
87 71.262 0 28.15
88 71.299 0 28.16
89 71.335 0 28.15
90 71.370 0 28.15
91 71.407 0 28.15
92 71.444 0 28.16
93 71.481 0 28.18
94 71.516 0 28.18
95 71.553 0 28.15
96 71.589 0 28.13
97 71.626 0 28.18
98 71.663 0 28.18
99 71.698 0 28.19
100 71.735 0 28.24
101 71.772 0 28.24
102 71.809 0 28.24
103 71.843 0 28.22
104 71.880 0 28.21
105 71.917 0 28.21
106 71.954 0 28.21
107 71.989 0 28.21
108 72.026 0 28.21
109 72.062 0 28.19
110 72.099 0 28.15
111 72.136 0 28.07
112 72.171 0 28.00
113 72.208 0 28.00
114 72.245 0 28.02
115 72.282 0 28.00
116 72.316 0 27.94
117 72.353 0 27.90
118 72.390 0 27.90
119 72.427 0 27.91
120 72.464 0 27.93
121 72.499 0 28.03
122 72.536 0 28.03
123 72.572 0 27.91
124 72.609 0 27.86
125 72.644 0 27.88
126 72.681 0 27.95
127 72.718 0 28.02
128 72.755 0 28.06
129 72.790 0 28.03
130 72.826 0 28.00
131 72.863 0 28.02
132 72.900 0 28.05
133 72.937 0 28.13
134 72.972 0 28.13
135 73.009 0 28.09
136 73.046 0 28.02
137 73.082 0 27.70
138 73.117 0 27.72
139 73.154 0 27.84
140 73.191 0 27.89
141 73.228 0 27.83
142 73.265 0 27.76
143 73.299 0 27.81
144 73.336 0 27.90
145 73.373 0 28.01
146 73.410 0 27.98
147 73.445 0 27.95
148 73.482 0 27.90
149 73.519 0 27.87
150 73.555 0 27.88
151 73.592 0 28.09
152 73.627 0 28.15
153 73.664 0 28.32
154 73.701 0 28.29
155 73.738 0 28.27
156 73.773 0 28.27
157 73.809 0 28.33
158 73.846 0 28.38
159 73.883 0 28.41
160 73.918 0 28.37
161 73.955 0 28.26
162 73.992 0 28.32
163 74.029 0 28.35
164 74.065 0 28.40
165 74.100 0 28.46
166 74.137 0 28.43
167 74.174 0 28.24
168 74.211 0 28.25
169 74.246 0 28.33
170 74.283 0 28.36
171 74.319 0 28.35
172 74.356 0 28.35
173 74.393 0 28.24
174 74.428 0 28.24
175 74.465 0 28.22
176 74.502 0 28.22
177 74.539 0 28.28
178 74.573 0 28.22
179 74.610 0 28.19
180 74.647 0 28.19
181 74.684 0 28.14
182 74.719 0 28.19
183 74.756 0 28.19
184 74.792 0 28.13
185 74.829 0 28.08
186 74.866 0 28.26
187 74.901 0 28.29
188 74.938 0 28.29
189 74.975 0 28.46
190 75.012 0 28.40
191 75.046 0 28.35
192 75.083 0 28.26
193 75.120 0 28.24
194 75.157 0 28.30
195 75.194 0 28.34
196 75.229 0 28.27
197 75.266 0 28.21
198 75.302 0 28.16
199 75.339 0 28.18
200 75.374 0 28.26
201 75.411 0 28.30
202 75.448 0 28.03
203 75.485 0 28.00
204 75.520 0 27.96
205 75.556 0 27.90
206 75.593 0 28.00
207 75.630 0 28.08
208 75.667 0 28.16
209 75.702 0 28.00
210 75.739 0 28.00
211 75.776 0 28.00
212 75.812 0 28.06
213 75.847 0 28.12
214 75.884 0 28.19
215 75.921 0 28.15
216 75.958 0 28.12
217 75.995 0 28.11
218 76.029 0 28.22
219 76.066 0 28.25
220 76.103 0 28.28
221 76.140 0 28.22
222 76.175 0 28.14
223 76.212 0 28.10
224 76.249 0 28.10
225 76.285 0 28.24
226 76.322 0 28.22
227 76.357 0 28.19
228 76.394 0 28.15
229 76.431 0 28.13
230 76.468 0 28.12
231 76.503 0 28.16
232 76.539 0 28.11
233 76.576 0 28.05
234 76.613 0 27.99
235 76.648 0 28.11
236 76.685 0 28.13
237 76.722 0 28.19
238 76.759 0 28.22
239 76.795 0 28.19
240 76.830 0 28.13
241 76.867 0 28.09
242 76.904 0 28.18
243 76.941 0 28.22
244 76.976 0 28.26
245 77.012 0 28.26
246 77.049 0 28.22
247 77.086 0 28.17
248 77.123 0 28.23
249 77.158 0 28.29
250 77.195 0 28.34
251 77.232 0 28.19
252 77.268 0 28.13
253 77.303 0 28.20
254 77.340 0 28.19
255 77.377 0 28.26
256 77.414 0 28.31
257 77.449 0 28.33
258 77.486 0 28.24
259 77.522 0 28.17
260 77.559 0 28.16
261 77.596 0 28.16
262 77.631 0 28.19
263 77.668 0 28.22
264 77.705 0 28.17
265 77.742 0 28.16
266 77.776 0 28.20
267 77.813 0 28.24
268 77.850 0 28.15
269 77.887 0 28.11
270 77.924 0 28.08
271 77.959 0 27.99
272 77.996 0 27.99
273 78.032 0 28.00
274 78.069 0 28.02
275 78.104 0 28.03
276 78.141 0 28.02
277 78.178 0 27.97
278 78.215 0 27.96
279 78.252 0 27.94
280 78.286 0 27.99
281 78.323 0 28.00
282 78.360 0 27.94
283 78.397 0 27.88
284 78.432 0 27.98
285 78.469 0 27.94
286 78.505 0 28.07
287 78.542 0 28.10
288 78.577 0 28.11
289 78.614 0 28.05
290 78.651 0 28.14
291 78.688 0 28.17
292 78.725 0 28.20
293 78.759 0 28.28
294 78.796 0 28.30
295 78.833 0 28.20
296 78.870 0 28.19
297 78.905 0 28.17
298 78.942 0 28.19
299 78.979 0 28.23
300 79.015 0 28.28
301 79.052 0 28.22
302 79.087 0 28.16
303 79.124 0 28.17
304 79.161 0 28.17
305 79.198 0 28.22
306 79.233 0 28.20
307 79.269 0 28.17
308 79.306 0 28.14
309 79.343 0 28.05
310 79.378 0 28.20
311 79.415 0 28.31
312 79.452 0 28.31
313 79.489 0 28.28
314 79.525 0 28.25
315 79.560 0 28.23
316 79.597 0 28.21
317 79.634 0 28.28
318 79.671 0 28.28
319 79.706 0 28.29
320 79.742 0 28.23
321 79.779 0 28.20
322 79.816 0 28.11
323 79.853 0 28.15
324 79.888 0 28.20
325 79.925 0 28.17
326 79.962 0 27.99
327 79.998 0 27.96
328 80.033 0 27.96
329 80.070 0 28.00
330 80.107 0 28.04
331 80.144 0 28.10
332 80.179 0 27.85
333 80.216 0 27.82
334 80.252 0 27.77
335 80.289 0 27.76
336 80.326 0 27.84
337 80.361 0 27.84
338 80.398 0 27.81
339 80.435 0 27.77
340 80.472 0 27.71
341 80.506 0 27.82
342 80.543 0 27.84
343 80.580 0 27.87
344 80.617 0 27.85
345 80.654 0 27.82
346 80.689 0 27.78
347 80.736 2 27.70
348 80.771 2 27.95
349 80.807 2 28.04
350 80.844 2 28.02
351 80.881 2 28.11
352 80.918 2 27.99
353 80.953 2 28.02
354 80.990 2 28.03
355 81.027 2 27.95
356 81.063 2 27.95
357 81.098 2 27.91
358 81.135 2 27.83
359 81.172 2 27.81
360 81.209 2 27.81
361 81.246 2 27.80
362 81.281 2 27.84
363 81.317 2 27.86
364 81.354 2 27.76
365 81.391 2 27.67
366 81.426 2 27.72
367 81.463 2 27.55
368 81.500 2 27.66
369 81.537 2 27.63
370 81.571 2 27.65
371 81.608 2 27.77
372 81.645 2 27.82
373 81.682 2 27.85
374 81.719 2 27.94
375 81.754 2 27.94
376 81.790 2 27.94
377 81.827 2 27.93
378 81.864 2 27.91
379 81.899 2 27.94
380 81.936 2 28.05
381 81.973 2 28.05
382 82.010 2 28.05
383 82.046 2 28.07
384 82.081 2 28.16
385 82.118 2 28.17
386 82.155 2 28.17
387 82.192 2 28.16
388 82.227 2 28.17
389 82.264 2 28.08
390 82.300 2 28.06
391 82.337 2 28.08
392 82.372 2 28.11
393 82.409 2 28.19
394 82.446 2 28.20
395 82.483 2 28.20
396 82.520 2 28.20
397 82.554 2 28.26
398 82.591 2 28.34
399 82.628 2 28.43
400 82.665 2 28.34
401 82.700 2 28.36
402 82.737 2 28.39
403 82.774 2 28.37
404 82.810 2 28.34
405 82.847 2 28.44
406 82.882 2 28.45
407 82.919 2 28.44
408 82.956 2 28.45
409 82.993 2 28.44
410 83.027 2 28.51
411 83.064 2 28.43
412 83.101 2 28.43
413 83.138 2 28.40
414 83.175 2 28.33
415 83.210 2 28.31
416 83.247 2 28.31
417 83.283 2 28.33
418 83.320 2 28.31
419 83.355 2 28.33
420 83.392 2 28.36
421 83.429 2 28.39
422 83.466 2 28.41
423 83.501 2 28.45
424 83.537 2 28.48
425 83.574 2 28.54
426 83.611 2 28.40
427 83.648 2 28.54
428 83.683 2 28.56
429 83.720 2 28.57
430 83.757 2 28.57
431 83.793 2 28.60
432 83.828 2 28.65
433 83.865 2 28.69
434 83.902 2 28.73
435 83.939 2 28.76
436 83.976 2 28.82
437 84.011 2 28.86
438 84.047 2 28.92
439 84.084 2 28.86
440 84.121 2 28.89
441 84.156 2 28.90
442 84.193 2 29.00
443 84.230 2 29.06
444 84.267 2 29.13
445 84.301 2 29.21
446 84.338 2 29.30
447 84.375 2 29.35
448 84.412 2 29.40
449 84.449 2 29.50
450 84.484 2 29.58
451 84.520 2 29.62
452 84.557 2 29.64
453 84.594 2 29.67
454 84.629 2 29.74
455 84.666 2 29.90
456 84.703 2 29.93
457 84.740 2 30.05
458 84.776 2 30.12
459 84.811 2 30.14
460 84.848 2 30.18
461 84.885 2 30.22
462 84.922 2 30.27
463 84.957 2 30.35
464 84.994 2 30.32
465 85.030 2 30.32
466 85.067 2 30.35
467 85.104 2 30.51
468 85.139 2 30.60
469 85.176 2 30.67
470 85.213 2 30.70
471 85.250 2 30.81
472 85.284 2 30.90
473 85.321 2 30.87
474 85.358 2 30.94
475 85.395 2 31.00
476 85.430 2 31.08
477 85.467 2 31.19
478 85.504 2 31.25
479 85.540 2 31.27
480 85.577 2 31.45
481 85.612 2 31.54
482 85.649 2 31.63
483 85.686 2 31.63
484 85.723 2 31.69
485 85.757 2 31.79
486 85.794 2 31.79
487 85.831 2 31.76
488 85.868 2 31.84
489 85.905 2 31.63
490 85.940 2 31.75
491 85.977 2 31.83
492 86.013 2 31.91
493 86.050 2 31.99
494 86.085 2 32.10
495 86.122 2 32.18
496 86.159 2 32.21
497 86.196 2 32.38
498 86.231 2 32.48
499 86.267 2 32.66
500 86.304 2 32.84
501 86.341 2 32.92
502 86.378 2 33.08
503 86.413 2 33.16
504 86.450 2 33.22
505 86.487 2 33.60
506 86.523 2 33.67
507 86.579 6 33.79
508 86.636 6 33.88
509 86.691 6 33.97
510 86.747 6 34.10
511 86.804 6 34.26
512 86.859 6 34.43
513 86.917 6 34.47
514 86.972 6 34.60
515 87.027 6 34.64
516 87.085 6 34.73
517 87.140 6 34.79
518 87.197 6 34.94
519 87.252 6 35.15
520 87.310 6 35.30
521 87.365 6 35.48
522 87.420 6 35.67
523 87.478 6 35.76
524 87.533 6 35.94
525 87.590 6 36.07
526 87.646 6 36.15
527 87.703 6 36.27
528 87.758 6 36.36
529 87.814 6 36.55
530 87.871 6 36.72
531 87.926 6 36.91
532 87.984 6 37.10
533 88.039 6 37.33
534 88.094 6 37.50
535 88.152 6 37.68
536 88.207 6 37.81
537 88.264 6 37.99
538 88.320 6 38.15
539 88.377 6 38.40
540 88.432 6 38.52
541 88.487 6 38.71
542 88.545 6 38.99
543 88.600 6 39.20
544 88.657 6 39.43
545 88.713 6 39.57
546 88.770 6 39.73
547 88.825 6 39.90
548 88.881 6 40.03
549 88.938 6 40.21
550 88.993 6 40.38
551 89.051 6 40.64
552 89.106 6 40.89
553 89.161 6 41.06
554 89.219 6 41.32
555 89.274 6 41.44
556 89.331 6 41.65
557 89.387 6 41.81
558 89.444 6 41.95
559 89.499 6 42.11
560 89.554 6 42.30
561 89.612 6 42.44
562 89.667 6 42.62
563 89.724 6 42.77
564 89.780 6 42.91
565 89.837 6 43.07
566 89.892 6 43.23
567 89.948 6 43.29
568 90.005 6 43.47
569 90.060 6 43.58
570 90.118 6 43.66
571 90.173 6 43.85
572 90.228 6 43.99
573 90.286 6 44.13
574 90.341 6 44.26
575 90.398 6 44.42
576 90.454 6 44.53
577 90.511 6 44.70
578 90.566 6 44.84
579 90.621 6 44.98
580 90.679 6 45.14
581 90.734 6 45.27
582 90.791 6 45.41
583 90.847 6 45.55
584 90.904 6 45.63
585 90.959 6 45.78
586 91.015 6 45.91
587 91.072 6 46.02
588 91.127 6 46.14
589 91.185 6 46.28
590 91.240 6 46.38
591 91.297 6 46.48
592 91.353 6 46.60
593 91.408 6 46.71
594 91.465 6 46.82
595 91.521 6 46.95
596 91.578 6 47.04
597 91.633 6 47.17
598 91.688 6 47.27
599 91.746 6 47.36
600 91.801 6 47.47
601 91.858 6 47.58
602 91.914 6 47.67
603 91.971 6 47.78
604 92.026 6 47.89
605 92.082 6 47.99
606 92.139 6 48.12
607 92.194 6 48.21
608 92.252 6 48.28
609 92.307 6 48.37
610 92.344 6 48.45
611 92.381 6 48.53
612 92.416 6 48.63
613 92.452 6 48.67
614 92.489 6 48.76
615 92.526 6 48.84
616 92.563 6 48.92
617 92.598 6 48.95
618 92.635 6 49.03
619 92.672 6 49.08
620 92.708 6 49.15
621 92.743 6 49.20
622 92.780 6 49.28
623 92.817 6 49.34
624 92.854 6 49.42
625 92.889 6 49.49
626 92.925 6 49.54
627 92.962 6 49.57
628 92.999 6 49.61
629 93.036 6 49.67
630 93.071 6 49.71
631 93.108 6 49.78
632 93.145 6 49.82
633 93.181 6 49.90
634 93.216 6 49.95
635 93.253 6 50.00
636 93.290 6 50.04
637 93.327 6 50.14
638 93.364 6 50.16
639 93.399 6 50.20
640 93.435 6 50.24
641 93.472 6 50.36
642 93.509 6 50.40
643 93.544 6 50.47
644 93.581 6 50.52
645 93.618 6 50.56
646 93.655 6 50.59
647 93.691 6 50.62
648 93.726 6 50.67
649 93.763 6 50.76
650 93.800 6 50.81
651 93.837 6 50.85
652 93.872 6 50.88
653 93.908 6 50.86
654 93.945 6 50.87
655 93.982 6 50.87
656 94.017 6 50.93
657 94.054 6 50.82
658 94.091 6 50.86
659 94.128 6 50.87
660 94.164 6 50.94
661 94.199 6 50.98
662 94.236 6 51.04
663 94.273 6 51.08
664 94.310 6 51.11
665 94.345 6 51.09
666 94.382 6 51.13
667 94.418 6 51.18
668 94.455 6 51.22
669 94.492 6 51.27
670 94.527 6 51.32
671 94.564 6 51.44
672 94.601 6 51.40
673 94.638 6 51.51
674 94.672 6 51.50
675 94.709 6 51.54
676 94.746 6 51.54
677 94.783 6 51.37
678 94.818 6 51.40
679 94.855 6 51.45
680 94.892 6 51.57
681 94.928 6 51.60
682 94.965 6 51.62
683 95.000 6 51.63
684 95.037 6 51.65
685 95.074 6 51.69
686 95.111 6 51.72
687 95.145 6 51.71
688 95.182 6 51.79
689 95.219 6 51.81
690 95.256 6 51.91
691 95.293 6 51.95
692 95.328 6 52.01
693 95.365 6 52.14
694 95.401 6 52.16
695 95.438 6 52.17
696 95.473 6 52.09
697 95.510 6 52.17
698 95.547 6 52.18
699 95.584 6 52.24
700 95.619 6 52.35
701 95.655 6 52.36
702 95.692 6 52.37
703 95.729 6 52.38
704 95.766 6 52.42
705 95.801 6 52.50
706 95.838 6 52.54
707 95.875 6 52.55
708 95.911 6 52.56
709 95.946 6 52.68
710 95.983 6 52.71
711 96.020 6 52.76
712 96.057 6 52.69
713 96.094 6 52.67
714 96.129 6 52.71
715 96.165 6 52.71
716 96.202 6 52.68
717 96.239 6 52.72
718 96.274 6 52.77
719 96.311 6 52.82
720 96.348 6 52.83
721 96.385 6 52.80
722 96.421 6 52.77
723 96.456 6 52.81
724 96.493 6 52.85
725 96.530 6 52.85
726 96.567 6 52.84
727 96.602 6 52.84
728 96.638 6 52.96
729 96.675 6 53.00
730 96.712 6 53.04
731 96.747 6 53.08
732 96.784 6 53.07
733 96.821 6 53.10
734 96.858 6 53.12
735 96.894 6 53.13
736 96.929 6 53.18
737 96.966 6 53.19
738 97.003 6 53.22
739 97.040 6 53.22
740 97.075 6 53.24
741 97.112 6 53.27
742 97.148 6 53.35
743 97.185 6 53.33
744 97.222 6 53.34
745 97.257 6 53.34
746 97.294 6 53.38
747 97.331 6 53.48
748 97.368 6 53.50
749 97.402 6 53.49
750 97.439 6 53.49
751 97.476 6 53.47
752 97.513 6 53.48
753 97.548 6 53.50
754 97.585 6 53.54
755 97.622 6 53.57
756 97.658 6 53.55
757 97.695 6 53.55
758 97.730 6 53.56
759 97.767 6 53.66
760 97.804 6 53.70
761 97.841 6 53.72
762 97.875 6 53.70
763 97.912 6 53.63
764 97.949 6 53.51
765 97.986 6 53.54
766 98.023 6 53.56
767 98.058 6 53.62
768 98.095 6 53.61
769 98.131 6 53.62
770 98.168 6 53.63
771 98.203 6 53.66
772 98.240 6 53.70
773 98.277 6 53.75
774 98.314 6 53.76
775 98.351 6 53.73
776 98.385 6 53.73
777 98.422 6 53.75
778 98.459 6 53.77
779 98.496 6 53.80
780 98.531 6 53.94
781 98.568 6 53.93
782 98.605 6 53.93
783 98.641 6 53.94
784 98.676 6 53.99
785 98.713 6 54.00
786 98.750 6 54.02
787 98.787 6 54.04
788 98.824 6 54.05
789 98.859 6 54.05
790 98.895 6 54.07
791 98.932 6 54.14
792 98.969 6 54.16
793 99.004 6 54.19
794 99.041 6 54.18
795 99.078 6 54.19
796 99.115 6 54.21
797 99.151 6 54.26
798 99.186 6 54.28
799 99.223 6 54.29
800 99.260 6 54.28
801 99.297 6 54.29
802 99.332 6 54.31
803 99.368 6 54.33
804 99.405 6 54.41
805 99.442 6 54.41
806 99.477 6 54.41
807 99.514 6 54.37
808 99.551 6 54.37
809 99.588 6 54.37
810 99.624 6 54.41
811 99.659 6 54.41
812 99.696 6 54.42
813 99.733 6 54.41
814 99.770 6 54.43
815 99.805 6 54.45
816 99.842 6 54.48
817 99.878 6 54.49
818 99.915 6 54.45
819 99.952 6 54.42
820 99.987 6 54.36
821 100.024 6 54.32
822 100.061 6 54.35
823 100.100 6 54.38
824 100.136 6 54.40
825 100.173 6 54.40
826 100.210 6 54.37
827 100.249 6 54.38
828 100.286 6 54.39
829 100.323 6 54.41
830 100.362 6 54.41
831 100.399 6 54.42
832 100.435 6 54.43
833 100.472 6 54.44
834 100.511 6 54.54
835 100.548 6 54.54
836 100.585 6 54.54
837 100.624 6 54.61
838 100.661 6 54.60
839 100.698 6 54.60
840 100.734 6 54.61
841 100.773 6 54.63
842 100.810 6 54.68
843 100.847 6 54.69
844 100.886 6 54.69
845 100.923 6 54.69
846 100.960 6 54.69
847 100.997 6 54.74
848 101.036 6 54.74
849 101.072 6 54.76
850 101.109 6 54.75
851 101.148 6 54.77
852 101.185 6 54.78
853 101.222 6 54.78
854 101.259 6 54.78
855 101.298 6 54.79
856 101.335 6 54.79
857 101.371 6 54.82
858 101.408 6 54.83
859 101.447 6 54.87
860 101.484 6 54.89
861 101.521 6 54.91
862 101.560 6 54.91
863 101.597 6 54.81
864 101.634 6 54.74
865 101.670 6 54.74
866 101.709 6 54.71
867 101.746 6 54.74
868 101.783 6 54.74
869 101.822 6 54.77
870 101.859 6 54.78
871 101.896 6 54.77
872 101.933 6 54.72
873 101.971 6 54.68
874 102.008 6 54.67
875 102.045 6 54.67
876 102.084 6 54.72
877 102.121 6 54.74
878 102.158 6 54.76
879 102.195 6 54.83
880 102.234 6 54.89
881 102.270 6 54.89
882 102.307 6 54.88
883 102.346 6 54.87
884 102.383 6 54.90
885 102.420 6 54.92
886 102.457 6 54.95
887 102.496 6 54.97
888 102.533 6 55.04
889 102.569 6 55.05
890 102.606 6 55.05
891 102.645 6 55.04
892 102.682 6 55.00
893 102.719 6 55.00
894 102.758 6 55.01
895 102.795 6 55.04
896 102.832 6 55.07
897 102.868 6 55.08
898 102.907 6 55.10
899 102.944 6 55.11
900 102.981 6 55.05
901 103.020 6 55.06
902 103.057 6 55.05
903 103.094 6 55.06
904 103.131 6 55.07
905 103.170 6 55.11
906 103.206 6 55.12
907 103.243 6 55.12
908 103.282 6 55.11
909 103.319 6 55.09
910 103.356 6 55.09
911 103.393 6 55.14
912 103.432 6 55.07
913 103.469 6 55.09
914 103.505 6 55.12
915 103.544 6 55.14
916 103.581 6 55.19
917 103.618 6 55.17
918 103.655 6 54.96
919 103.694 6 54.92
920 103.731 6 54.94
921 103.768 6 54.93
922 103.804 6 54.96
923 103.843 6 54.99
924 103.880 6 55.02
925 103.917 6 55.02
926 103.956 6 54.98
927 103.993 6 54.92
928 104.030 6 54.97
929 104.067 6 55.02
930 104.105 6 55.02
931 104.142 6 55.02
932 104.179 6 55.05
933 104.218 6 55.07
934 104.255 6 55.19
935 104.292 6 55.22
936 104.329 6 55.12
937 104.368 6 55.11
938 104.404 6 55.06
939 104.441 6 55.06
940 104.480 6 55.04
941 104.517 6 55.06
942 104.554 6 55.12
943 104.591 6 55.17
944 104.630 6 55.18
945 104.667 6 55.14
946 104.704 6 55.12
947 104.742 6 55.10
948 104.779 6 55.14
949 104.816 6 55.17
950 104.853 6 55.28
951 104.892 6 55.31
952 104.929 6 55.40
953 104.966 6 55.40
954 105.003 6 55.42
955 105.041 6 55.40
956 105.078 6 55.44
957 105.115 6 55.45
958 105.154 6 55.45
959 105.191 6 55.41

70
barista/heater.c Normal file
View 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
View 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
View 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
View 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 */

61
barista/overheat.c Normal file
View 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
View 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 */

13
barista/test/compat.c Normal file
View 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
View 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
View 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;
}

View File

@@ -1,3 +1,6 @@
/* Copyright (c) 2025 Rodrigo Arias Mallo <rodarima@gmail.com>
* SPDX-License-Identifier: GPL-3.0-or-later */
#include <stdio.h> #include <stdio.h>
#include "ntc.h" #include "ntc.h"

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

View 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
View 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
View 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 */