@@ -268,193 +268,193 @@ void display_process(therm_settings_t* s
// [ therm :: set windup ]
// [ g = 12 ]
ssd1306_DrawString("Windup Guard", 0, 40);
ssd1306_drawlogo();
char tempstr[6];
itoa(set->windup_guard, tempstr, 10);
ssd1306_DrawString("G=", 1, 45);
ssd1306_DrawString(" ", 1, 57);
ssd1306_DrawString(tempstr, 1, 57);
ssd1306_DrawString("Press to accept", 3, 40);
// Button handler
if(SW_BTN_PRESSED) {
status->state = STATE_SETBOOTTOBREW;
}
else {
user_input(&set->windup_guard);
// Event Handler
// N/A
} break;
case STATE_SETBOOTTOBREW:
{
// Write text to OLED
ssd1306_DrawString("Start on Boot", 0, 40);
ssd1306_DrawString("sob=", 1, 45);
if(set->boottobrew)
ssd1306_DrawString("Enabled ", 1, 70);
else
ssd1306_DrawString("Disabled", 1, 70);
status->state = STATE_SETUNITS;
else if(!HAL_GPIO_ReadPin(SW_UP)) {
set->boottobrew = 1;
else if(!HAL_GPIO_ReadPin(SW_DOWN)) {
set->boottobrew = 0;
case STATE_SETUNITS:
ssd1306_DrawString("Units: ", 0, 40);
if(set->temp_units == TEMP_UNITS_FAHRENHEIT)
ssd1306_DrawString("Fahrenheit", 1, 60);
ssd1306_DrawString("Celsius ", 1, 60);
status->state = STATE_SETTEMPOFFSET;
set->temp_units = TEMP_UNITS_FAHRENHEIT;
set->temp_units = TEMP_UNITS_CELSIUS;
case STATE_SETTEMPOFFSET:
// [ therm :: set temp offset ]
ssd1306_DrawString("Thermocouple Offset", 0, 40);
ssd1306_DrawString("Temp Cal Offset", 0, 40);
itoa(set->temp_offset, tempstr, 10);
ssd1306_DrawString("O=", 1, 45);
save_settings(&set);
status->state = STATE_IDLE;
user_input_signed(&set->temp_offset);
case STATE_PREHEAT_BREW:
// [ therm : preheating brew ]
// [ 30 => 120 C ]
ssd1306_DrawString("Preheating...", 0, 0);
//ssd1306_drawlogo();
draw_setpoint(status);
status->pid_enabled = 1;
status->setpoint = set->setpoint_brew;
save_setpoints(&set); // TODO: Check for mod
user_input(&set->setpoint_brew);
if(status->temp >= status->setpoint) {
status->state = STATE_MAINTAIN_BREW;
case STATE_MAINTAIN_BREW:
// [ therm : ready to brew ]
ssd1306_DrawString("Preheated!", 0, 0);
case STATE_PREHEAT_STEAM:
// [ therm : preheating steam ]
status->setpoint = set->setpoint_steam;
user_input(&set->setpoint_steam);
@@ -142,193 +142,193 @@ void SystemClock_Config(void)
// Grab temperature reading from MAX31855
void update_temp() {
// Assert CS
HAL_GPIO_WritePin(MAX_CS, 0);
uint8_t rxdatah[1] = {0x00};
uint8_t rxdatal[1] = {0x00};
HAL_SPI_Receive(&hspi1, rxdatah, 1, 100);
HAL_SPI_Receive(&hspi1, rxdatal, 1, 100);
// Release CS
HAL_GPIO_WritePin(MAX_CS, 1);
// Assemble data array into one var
uint16_t temp_pre = rxdatal[0] | (rxdatah[0]<<8);
if(temp_pre & 0b0000000000000010) {
ssd1306_clearscreen();
HAL_Delay(100); // FIXME: remove?
status.tc_errno = 4;
status.state = STATE_TC_ERROR;
status.temp = 0;
status.temp_frac = 0;
else if(temp_pre & 0b0000000000000001 && !set.ignore_tc_error) {
status.tc_errno = 1;
status.state_resume = status.state;
if(status.state == STATE_TC_ERROR)
status.state = status.state_resume;
uint8_t sign = status.temp >> 15;// top bit is sign
temp_pre = temp_pre >> 2; // Drop 2 lowest bits
status.temp_frac = temp_pre & 0b11; // get fractional part
status.temp_frac *= 25; // each bit is .25 a degree, up to fixed point
temp_pre = temp_pre >> 2; // Drop 2 fractional bits
int8_t signint;
if(sign) {
signint = -1;
signint = 1;
// Convert to Fahrenheit
if(set.temp_units == TEMP_UNITS_FAHRENHEIT)
status.temp = signint * ((temp_pre*100) + status.temp_frac);
status.temp = status.temp * 1.8;
status.temp += 3200;
status.temp_frac = status.temp % 100;
status.temp /= 100;
status.temp += set.temp_offset;
// Use Celsius values
status.temp = temp_pre * signint;
// PID implementation
// TODO: Make struct that has the last_temp and i_state in it, pass by ref. Make struct that has other input values maybe.
int16_t last_pid_temp = 0;
uint8_t last_pid_temp_frac = 0;
int32_t i_state = 0;
int16_t update_pid(uint16_t k_p, uint16_t k_i, uint16_t k_d, int16_t temp, uint8_t temp_frac, int16_t setpoint)
// Calculate instantaneous error
int16_t error = setpoint - temp; // TODO: Use fixed point fraction
// Proportional component
int32_t p_term = k_p * error;
// Error accumulator (integrator)
i_state += error;
// to prevent the iTerm getting huge despite lots of
// to prevent the iTerm getting huge from lots of
// error, we use a "windup guard"
// (this happens when the machine is first turned on and
// it cant help be cold despite its best efforts)
// not necessary, but this makes windup guard values
// relative to the current iGain
int32_t windup_guard_res = set.windup_guard / k_i;
// Calculate integral term with windup guard
if (i_state > windup_guard_res)
i_state = windup_guard_res;
else if (i_state < -windup_guard_res)
i_state = -windup_guard_res;
int32_t i_term = k_i * i_state;
// Calculate differential term (slope since last iteration)
int32_t d_term = (k_d * (status.temp - last_pid_temp));
// Save temperature for next iteration
last_pid_temp = status.temp;
last_pid_temp_frac = status.temp_frac;
int16_t result = p_term + i_term - d_term;
// Put out tenths of percent, 0-1000.
if(result > 1000)
result = 1000;
else if(result < -1000)
result = -1000;
// Return feedback
return result;
uint32_t last_ssr_on = 0;
uint32_t last_vcp_tx = 0;
uint32_t last_led = 0;
int16_t ssr_output = 0; // Duty cycle of ssr, 0 to SSR_PERIOD
// Turn SSR output on/off according to set duty cycle.
// TODO: Eventually maybe replace with a very slow timer or something. Double-check this code...
void process()
update_temp(); // Read MAX31855
uint32_t ticks = HAL_GetTick();
if(ticks - last_led > 400)
HAL_GPIO_TogglePin(LED_POWER);
last_led = ticks;
// Every 200ms, set the SSR on unless output is 0
if((ticks - last_ssr_on > SSR_PERIOD))
if(status.pid_enabled)
// Get ssr output for next time
int16_t power_percent = update_pid(set.k_p, set.k_i, set.k_d, status.temp, status.temp_frac, status.setpoint);
//power-percent is 0-1000
ssr_output = power_percent; //(((uint32_t)SSR_PERIOD * (uint32_t)10 * (uint32_t)100) * power_percent) / (uint32_t)1000000;
ssr_output = 0;
// Only support heating (ssr_output > 0) right now
if(ssr_output > 0) {
itoa(ssr_output, tempstr, 10);
ssd1306_DrawString(tempstr, 0, 90);
HAL_GPIO_WritePin(SSR_PIN, 1);
last_ssr_on = ticks;
// Kill SSR after elapsed period less than SSR_PERIOD
if(ticks - last_ssr_on > ssr_output || ssr_output == 0)
HAL_GPIO_WritePin(SSR_PIN, 0);
if(ticks - last_vcp_tx > VCP_TX_FREQ)
// Print temp to cdc
char tempstr[16];
itoa_fp(status.temp, status.temp_frac, tempstr);
uint8_t numlen = strlen(tempstr);
tempstr[numlen] = '\r';
tempstr[numlen+1] = '\n';
#include "stm32f0xx_hal.h"
#include "states.h"
void save_settings(therm_settings_t *tosave)
// TODO: Rework with FLASH read/write
/*
Minimal_EEPROM_Unlock();
// Try programming a word at an address divisible by 4
Minimal_EEPROM_ProgramWord(EEPROM_BASE_ADDR + EEPROM_ADDR_BOOTTOBREW, boottobrew);
Minimal_EEPROM_ProgramWord(EEPROM_BASE_ADDR + EEPROM_ADDR_WINDUP_GUARD, windup_guard);
Minimal_EEPROM_ProgramWord(EEPROM_BASE_ADDR + EEPROM_ADDR_K_P, k_p);
Minimal_EEPROM_ProgramWord(EEPROM_BASE_ADDR + EEPROM_ADDR_K_I, k_i);
Minimal_EEPROM_ProgramWord(EEPROM_BASE_ADDR + EEPROM_ADDR_K_D, k_d);
Minimal_EEPROM_ProgramWord(EEPROM_BASE_ADDR + EEPROM_ADDR_UNITS, temp_units);
Minimal_EEPROM_Lock();
// TODO: Check for missing settings
*/
void save_setpoints(therm_settings_t *tosave)
Minimal_EEPROM_ProgramWord(EEPROM_BASE_ADDR + EEPROM_ADDR_BREWTEMP, setpoint_brew);
Minimal_EEPROM_ProgramWord(EEPROM_BASE_ADDR + EEPROM_ADDR_STEAMTEMP, setpoint_steam);
// TODO: Make a struct that has all settings in it. Pass by ref to this func in a library.
void restore_settings(therm_settings_t *tosave)
/* Minimal_EEPROM_Unlock();
while(Minimal_FLASH_GetStatus()==FLASH_BUSY);
boottobrew = (*(__IO uint32_t*)(EEPROM_BASE_ADDR + EEPROM_ADDR_BOOTTOBREW));
windup_guard = (*(__IO uint32_t*)(EEPROM_BASE_ADDR + EEPROM_ADDR_WINDUP_GUARD));
k_p = (*(__IO uint32_t*)(EEPROM_BASE_ADDR + EEPROM_ADDR_K_P));
k_i = (*(__IO uint32_t*)(EEPROM_BASE_ADDR + EEPROM_ADDR_K_I));
k_d = (*(__IO uint32_t*)(EEPROM_BASE_ADDR + EEPROM_ADDR_K_D));
setpoint_brew = (*(__IO uint32_t*)(EEPROM_BASE_ADDR + EEPROM_ADDR_BREWTEMP));
setpoint_steam = (*(__IO uint32_t*)(EEPROM_BASE_ADDR + EEPROM_ADDR_STEAMTEMP));
temp_units = (*(__IO uint32_t*)(EEPROM_BASE_ADDR + EEPROM_ADDR_UNITS));
Minimal_EEPROM_Lock(); */
Status change: