Add thermostat and heater control
This commit is contained in:
parent
3ea6ff0e14
commit
678f16111b
@ -4,6 +4,8 @@
|
|||||||
#include "ntc.h"
|
#include "ntc.h"
|
||||||
#include "overheat.h"
|
#include "overheat.h"
|
||||||
#include "led.h"
|
#include "led.h"
|
||||||
|
#include "heater.h"
|
||||||
|
#include "thermostat.h"
|
||||||
#include "pinout.h"
|
#include "pinout.h"
|
||||||
#include <avr/wdt.h>
|
#include <avr/wdt.h>
|
||||||
|
|
||||||
@ -13,8 +15,7 @@ enum logic {
|
|||||||
};
|
};
|
||||||
|
|
||||||
#define DEBOUNCE_TIME 20L /* ms */
|
#define DEBOUNCE_TIME 20L /* ms */
|
||||||
#define TEMP_MIN 40.0f /* C */
|
#define TEMP_HOT 70.0f /* C */
|
||||||
#define TEMP_MAX 50.0f /* C */
|
|
||||||
|
|
||||||
#define LED_MIN_VALUE 0
|
#define LED_MIN_VALUE 0
|
||||||
|
|
||||||
@ -51,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,
|
||||||
@ -94,6 +94,9 @@ struct state {
|
|||||||
|
|
||||||
struct led red_led;
|
struct led red_led;
|
||||||
struct led green_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)
|
||||||
@ -191,27 +194,29 @@ void proc_buttons(struct state *state, const struct input *input)
|
|||||||
|
|
||||||
int red_min = 50;
|
int red_min = 50;
|
||||||
int red_state = red_min;
|
int red_state = red_min;
|
||||||
unsigned long brewing_max_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 overheat_time = 10000UL; /* 10 seconds */
|
unsigned long overheat_time = 10000UL; /* 10 seconds */
|
||||||
unsigned long max_heating_time = 60000UL; /* 60 seconds */
|
unsigned long max_heating_time = 60000UL; /* 60 seconds */
|
||||||
unsigned long max_idle_time = 10000UL; /* 10 seconds */
|
unsigned long max_idle_time = 120000UL; /* 120 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 brew_hot = (st->btn[BTN_HOT].state == PRESSED);
|
int brew_hot = (st->btn[BTN_HOT].state == PRESSED);
|
||||||
|
unsigned long t = millis();
|
||||||
|
|
||||||
Serial.print("t=");
|
if (t - last_print_t > 100) {
|
||||||
Serial.print(millis());
|
Serial.print(t);
|
||||||
Serial.print(" state=");
|
Serial.print(" ");
|
||||||
Serial.print(st->mstate);
|
Serial.print(st->mstate);
|
||||||
Serial.print(" on=");
|
Serial.print(" ");
|
||||||
Serial.print(on);
|
Serial.println(temp);
|
||||||
Serial.print(" temp=");
|
last_print_t = t;
|
||||||
Serial.print(temp);
|
}
|
||||||
Serial.println(" C");
|
|
||||||
|
|
||||||
/* If the machine is overheating */
|
/* If the machine is overheating */
|
||||||
if (overheat_panic(&st->overheat)) {
|
if (overheat_panic(&st->overheat)) {
|
||||||
@ -239,7 +244,7 @@ void proc_machine(struct state *st)
|
|||||||
Serial.println("heating");
|
Serial.println("heating");
|
||||||
}
|
}
|
||||||
} else if (st->mstate == HEATING) {
|
} else if (st->mstate == HEATING) {
|
||||||
if (temp > TEMP_MIN) {
|
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;
|
||||||
@ -349,14 +354,25 @@ output_leds(const struct state *st)
|
|||||||
void
|
void
|
||||||
output_heater(const struct state *st)
|
output_heater(const struct state *st)
|
||||||
{
|
{
|
||||||
if (st->mstate == HEATING || st->mstate == HOT || st->mstate == BREWING_HOT) {
|
unsigned long t = millis();
|
||||||
if (st->ntc_T < TEMP_MIN)
|
|
||||||
|
/* First configure thermostate */
|
||||||
|
if (st->mstate == HEATING || st->mstate == HOT || st->mstate == BREWING_HOT)
|
||||||
|
thermostat_set(&st->thermostat, TEMP_HOT);
|
||||||
|
else
|
||||||
|
thermostat_off(&st->thermostat);
|
||||||
|
|
||||||
|
/* Then update heater state from thermostate */
|
||||||
|
if (thermostat_state(&st->thermostat, st->ntc_T))
|
||||||
|
heater_on(&st->heater, t);
|
||||||
|
else
|
||||||
|
heater_off(&st->heater);
|
||||||
|
|
||||||
|
/* Then switch relays accordingly */
|
||||||
|
if (heater_state(&st->heater, t))
|
||||||
relay(PIN_HEAT, ON);
|
relay(PIN_HEAT, ON);
|
||||||
else if (st->ntc_T > TEMP_MAX)
|
else
|
||||||
relay(PIN_HEAT, OFF);
|
relay(PIN_HEAT, OFF);
|
||||||
} else {
|
|
||||||
relay(PIN_HEAT, OFF);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|||||||
47
barista/heater.c
Normal file
47
barista/heater.c
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/* Copyright (c) 2025 Rodrigo Arias Mallo <rodarima@gmail.com>
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||||
|
|
||||||
|
#include "heater.h"
|
||||||
|
|
||||||
|
#define HEATER_T_ON 500UL /* ms */
|
||||||
|
#define HEATER_T_OFF 5000UL /* ms */
|
||||||
|
|
||||||
|
void
|
||||||
|
heater_on(struct heater *h, unsigned long t_ms)
|
||||||
|
{
|
||||||
|
if (h->st == HEATER_ON)
|
||||||
|
return;
|
||||||
|
|
||||||
|
h->next_t = t_ms + HEATER_T_ON;
|
||||||
|
h->st = HEATER_ON;
|
||||||
|
h->turn_on = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
heater_off(struct heater *h)
|
||||||
|
{
|
||||||
|
h->st = HEATER_OFF;
|
||||||
|
h->turn_on = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 */
|
||||||
|
if (t_ms > h->next_t) {
|
||||||
|
if (h->turn_on) {
|
||||||
|
h->turn_on = 0;
|
||||||
|
h->next_t = t_ms + HEATER_T_OFF;
|
||||||
|
} else {
|
||||||
|
h->turn_on = 1;
|
||||||
|
h->next_t = t_ms + HEATER_T_ON;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return h->turn_on;
|
||||||
|
}
|
||||||
30
barista/heater.h
Normal file
30
barista/heater.h
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/* 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,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct heater {
|
||||||
|
enum heater_state st;
|
||||||
|
int turn_on;
|
||||||
|
unsigned long next_t; /* in ms */
|
||||||
|
};
|
||||||
|
|
||||||
|
void heater_on(struct heater *h, unsigned long t_ms);
|
||||||
|
void heater_off(struct heater *h);
|
||||||
|
int heater_state(struct heater *h, unsigned long t_ms);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* BARISTA_HEATER_H */
|
||||||
41
barista/thermostat.c
Normal file
41
barista/thermostat.c
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/* Copyright (c) 2025 Rodrigo Arias Mallo <rodarima@gmail.com>
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||||
|
|
||||||
|
#include "thermostat.h"
|
||||||
|
|
||||||
|
#define DELTA_LOW 1.0
|
||||||
|
#define DELTA_HIGH 1.0
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
thermostat_state(struct thermostat *th, float temp)
|
||||||
|
{
|
||||||
|
if (th->st == THERMOSTAT_OFF)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (th->on && temp > th->temp_max)
|
||||||
|
th->on = 0;
|
||||||
|
else if (!th->on && temp < th->temp_min)
|
||||||
|
th->on = 1;
|
||||||
|
|
||||||
|
return th->on;
|
||||||
|
}
|
||||||
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);
|
||||||
|
int thermostat_state(struct thermostat *th, float temp);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* BARISTA_THERMOSTAT_H */
|
||||||
Loading…
x
Reference in New Issue
Block a user