diff --git a/main.c b/main.c --- a/main.c +++ b/main.c @@ -1,11 +1,13 @@ #include "stm32f0xx_hal.h" #include "config.h" +#include "states.h" #include "ssd1306.h" #include "eeprom_min.h" #include "gpio.h" #include "spi.h" #include "stringhelpers.h" +#include "display.h" #include "usb_device.h" #include "usbd_cdc_if.h" @@ -14,57 +16,19 @@ // Prototypes // Move to header file void process(); -void machine(); void restore_settings(); void save_settings(); void save_setpoints(); void SystemClock_Config(void); -/////////////////////// -enum tempunits { - TEMP_UNITS_CELSIUS = 0, - TEMP_UNITS_FAHRENHEIT, -}; - -// State definition -enum state { - STATE_IDLE = 0, - - STATE_SETP, - STATE_SETI, - STATE_SETD, - STATE_SETSTEPS, - STATE_SETWINDUP, - STATE_SETBOOTTOBREW, - STATE_SETUNITS, - - STATE_PREHEAT_BREW, - STATE_MAINTAIN_BREW, - STATE_PREHEAT_STEAM, - STATE_MAINTAIN_STEAM, - - STATE_TC_ERROR -}; - -uint8_t state = STATE_IDLE; +therm_settings_t set; +therm_status_t status; // Globalish setting vars -uint8_t boottobrew = 0; -uint8_t temp_units = TEMP_UNITS_CELSIUS; -uint16_t windup_guard = 1; -uint16_t k_p = 1; -uint16_t k_i = 1; -uint16_t k_d = 1; -uint8_t ignore_tc_error = 0; -int16_t setpoint_brew = 0; -int16_t setpoint_steam = 0; - SPI_HandleTypeDef hspi1; - static __IO uint32_t TimingDelay; - void deinit(void) { HAL_DeInit(); @@ -101,6 +65,31 @@ int main(void) // Init OLED over SPI ssd1306_Init(); ssd1306_clearscreen(); + + // Default settings + set.boottobrew = 0; + set.temp_units = TEMP_UNITS_CELSIUS; + set.windup_guard = 1; + set.k_p = 1; + set.k_i = 1; + set.k_d = 1; + set.ignore_tc_error = 0; + set.setpoint_brew = 0; + set.setpoint_steam = 0; + + // Default status + status.temp = 0; + status.temp_frac = 0; + status.state_resume = 0; + status.state = STATE_IDLE; + status.setpoint = 0; + status.pid_enabled = 0; + + // Load settings (if any) from EEPROM + restore_settings(); + + if(set.boottobrew) + status.state = STATE_PREHEAT_BREW; // Go to brew instead of idle if configured thusly // Startup screen ssd1306_DrawString("therm v0.1", 1, 40); @@ -108,11 +97,7 @@ int main(void) HAL_Delay(1500); ssd1306_clearscreen(); - - restore_settings(); - if(boottobrew) - state = STATE_PREHEAT_BREW; // Go to brew instead of idle if configured thusly - + // Main loop while(1) { @@ -120,7 +105,7 @@ int main(void) process(); // Run state machine - machine(); + display_process(&set, &status); } } @@ -154,11 +139,6 @@ void SystemClock_Config(void) } -// Read temperature and update global temp vars -int32_t temp = 0; -uint8_t temp_frac = 0; -uint8_t state_resume = 0; - void update_temp() { @@ -181,29 +161,29 @@ void update_temp() { ssd1306_clearscreen(); //ssd1306_DrawString("Fatal Error", 3, 35); HAL_Delay(100); - state = STATE_TC_ERROR; - temp = 0; - temp_frac = 0; + status.state = STATE_TC_ERROR; + status.temp = 0; + status.temp_frac = 0; } - else if(temp_pre & 0b0000000000000001 && !ignore_tc_error) { - state_resume = state; - state = STATE_TC_ERROR; - temp = 0; - temp_frac = 0; + else if(temp_pre & 0b0000000000000001 && !set.ignore_tc_error) { + status.state_resume = status.state; + status.state = STATE_TC_ERROR; + status.temp = 0; + status.temp_frac = 0; } else { - if(state == STATE_TC_ERROR) + if(status.state == STATE_TC_ERROR) { - state = state_resume; + status.state = status.state_resume; ssd1306_clearscreen(); } - uint8_t sign = temp >> 15;// top bit is sign + uint8_t sign = status.temp >> 15;// top bit is sign temp_pre = temp_pre >> 2; // Drop 2 lowest bits - temp_frac = temp_pre & 0b11; // get fractional part - temp_frac *= 25; // each bit is .25 a degree, up to fixed point + 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; @@ -216,19 +196,19 @@ void update_temp() { } // Convert to Fahrenheit - if(temp_units == TEMP_UNITS_FAHRENHEIT) + if(set.temp_units == TEMP_UNITS_FAHRENHEIT) { - temp = signint * ((temp_pre*100) + temp_frac); - temp = temp * 1.8; - temp += 3200; - temp_frac = temp % 100; - temp /= 100; + 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; } // Use Celsius values else { - temp = temp_pre * signint; + status.temp = temp_pre * signint; } } } @@ -257,7 +237,7 @@ int16_t update_pid(uint16_t k_p, uint16_ // it cant help be cold despite its best efforts) // not necessary, but this makes windup guard values // relative to the current iGain - int16_t windup_guard_res = windup_guard / k_i; + int16_t windup_guard_res = set.windup_guard / k_i; // Calculate integral term with windup guard if (i_state > windup_guard_res) @@ -267,11 +247,11 @@ int16_t update_pid(uint16_t k_p, uint16_ int16_t i_term = k_i * i_state; // Calculate differential term (slope since last iteration) - int16_t d_term = (k_d * (temp - last_pid_temp)); + int16_t d_term = (k_d * (status.temp - last_pid_temp)); // Save temperature for next iteration - last_pid_temp = temp; - last_pid_temp_frac = temp_frac; + last_pid_temp = status.temp; + last_pid_temp_frac = status.temp_frac; int16_t result = p_term + i_term - d_term; @@ -289,9 +269,7 @@ int16_t update_pid(uint16_t k_p, uint16_ uint32_t last_ssr_on = 0; uint32_t last_vcp_tx = 0; uint32_t last_led = 0; -int32_t setpoint = 0; int16_t ssr_output = 0; // Duty cycle of ssr, 0 to SSR_PERIOD -uint8_t pid_enabled = 0; // Process things void process() @@ -310,10 +288,10 @@ void process() // Every 200ms, set the SSR on unless output is 0 if((ticks - last_ssr_on > SSR_PERIOD)) { - if(pid_enabled) + if(status.pid_enabled) { // Get ssr output for next time - int16_t power_percent = update_pid(k_p, k_i, k_d, temp, temp_frac, setpoint); + 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; } @@ -343,32 +321,19 @@ void process() if(ticks - last_vcp_tx > VCP_TX_FREQ) { // Print temp to cdc - char tempstr[6]; - itoa_fp(temp, temp_frac, tempstr); + char tempstr[16]; + itoa_fp(status.temp, status.temp_frac, tempstr); + uint8_t numlen = strlen(tempstr); + tempstr[numlen] = '\r'; + tempstr[numlen+1] = '\n'; - while(CDC_Transmit_FS(tempstr, sizeof(tempstr)) == USBD_BUSY); - while(CDC_Transmit_FS("\r\n", 2) == USBD_BUSY); + while(CDC_Transmit_FS(tempstr, numlen+2) == USBD_BUSY); + // while(CDC_Transmit_FS("\r\n", 2) == USBD_BUSY); last_vcp_tx = ticks; } } - -uint8_t goto_mode = 2; - -// State machine -uint8_t sw_btn_last = 0; -uint8_t sw_up_last = 0; -uint8_t sw_down_last = 0; -uint8_t sw_left_last = 0; -uint8_t sw_right_last = 0; - -#define SW_BTN_PRESSED (sw_btn_last == 0 && sw_btn == 1) // rising edge on buttonpress -#define SW_UP_PRESSED (sw_up_last == 0 && sw_up == 1) -#define SW_DOWN_PRESSED (sw_down_last == 0 && sw_down == 1) -#define SW_LEFT_PRESSED (sw_left_last == 0 && sw_left == 1) -#define SW_RIGHT_PRESSED (sw_right_last == 0 && sw_right == 1) - void save_settings() { /* @@ -426,519 +391,8 @@ void restore_settings() Minimal_EEPROM_Lock(); */ } -int16_t last_temp = 21245; -/////////////////////////////////////////////////////////////////////////////////////// -/// freaking multiple setpoint support /// -uint8_t step_duration[10] = {0,0,0,0,0,0,0,0,0,0}; -int16_t step_setpoint[10] = {0,0,0,0,0,0,0,0,0,0}; -uint8_t final_setpoint = 0; - -// Multiple screens to set setpoint and duration on each screen -// press center to go to the next one, and press left or right or something to confirm - -// When executing, complete on time AND(?) temperature. Maybe allow switching to OR via settings - -//////////////////////////////////////////////////////////////////////////////////////////////// - -uint8_t trigger_drawsetpoint = 1; -int32_t temp_last = 43002; -int32_t setpoint_last = 10023; -void draw_setpoint() { - // FIXME: need to do this when switching modes too - if(temp != temp_last || trigger_drawsetpoint) { - char tempstr[3]; - itoa_fp(temp, temp_frac, tempstr); - ssd1306_DrawStringBig(" ", 3, 0); - ssd1306_DrawStringBig(tempstr, 3, 0); - } - - if(trigger_drawsetpoint) - ssd1306_DrawStringBig(">", 3, 74); - - if(setpoint != setpoint_last || trigger_drawsetpoint) { - char tempstr[3]; - itoa(setpoint, tempstr, 10); - ssd1306_DrawStringBig(" ", 3, 90); - ssd1306_DrawStringBig(tempstr, 3, 90); - } - - trigger_drawsetpoint = 0; - setpoint_last = setpoint; - temp_last = temp; -} - - - -void machine() -{ - uint8_t last_state = state; - - uint8_t temp_changed = temp != last_temp; - last_temp = temp; - - uint8_t sw_btn = !HAL_GPIO_ReadPin(SW_BTN); - uint8_t sw_up = !HAL_GPIO_ReadPin(SW_UP); - uint8_t sw_down = !HAL_GPIO_ReadPin(SW_DOWN); - uint8_t sw_left = !HAL_GPIO_ReadPin(SW_LEFT); - uint8_t sw_right = !HAL_GPIO_ReadPin(SW_RIGHT); - - switch(state) - { - // Idle state - case STATE_IDLE: - { - // Write text to OLED - // [ therm :: idle ] - ssd1306_DrawString("therm :: idle ", 0, 40); - pid_enabled = 0; - - if(temp_changed) { - char tempstr[6]; - itoa_fp(temp, temp_frac, tempstr); - ssd1306_DrawString("Temp: ", 3, 40); - ssd1306_DrawString(" ", 3, 72); - ssd1306_DrawString(tempstr, 3, 72); - } - - ssd1306_drawlogo(); - - switch(goto_mode) { - case 2: - { - ssd1306_DrawString("-> heat ", 1, 40); - } break; - - case 1: - { - ssd1306_DrawString("-> setup ", 1, 40); - } break; - - case 0: - { - ssd1306_DrawString("-> reset ", 1, 40); - } break; - } - - // Button handler - if(SW_BTN_PRESSED) { - switch(goto_mode) { - case 2: - state = STATE_PREHEAT_BREW; - break; - case 1: - state = STATE_SETP; - break; - case 0: - { - ssd1306_clearscreen(); - ssd1306_DrawString("Entering Bootloader", 1, 0); - ssd1306_DrawString("(hopefully)", 2, 0); - //HAL_Delay(1000); - HAL_RCC_DeInit(); - SysTick->CTRL = 0; - SysTick->LOAD = 0; - SysTick->VAL = 0; - __set_PRIMASK(1); - __set_MSP(0x200010000); - *((unsigned long *)0x200017F0) = 0xDEADBEEF; // 6KB STM32F042 - NVIC_SystemReset(); - - state = STATE_IDLE; - } break; - - default: - state = STATE_PREHEAT_BREW; - } - } - else if(SW_UP_PRESSED && goto_mode < 2) { - goto_mode++; - } - else if(SW_DOWN_PRESSED && goto_mode > 0) { - goto_mode--; - } - - - // Event Handler - // N/A - - } break; - - case STATE_SETP: - { - // Write text to OLED - // [ therm :: set p ] - // [ p = 12 ] - ssd1306_DrawString("Proportional", 0, 40); - ssd1306_drawlogo(); - - char tempstr[6]; - itoa(k_p, tempstr, 10); - ssd1306_DrawString("P=", 1, 45); - ssd1306_DrawString(" ", 1, 57); - ssd1306_DrawString(tempstr, 1, 57); - - ssd1306_DrawString("Press to accept", 3, 40); - - // Button handler - if(SW_BTN_PRESSED) { - state = STATE_SETI; - } - else { - user_input(&k_p); - } - - // Event Handler - // N/A - - } break; - - case STATE_SETI: - { - // Write text to OLED - // [ therm :: set i ] - // [ i = 12 ] - ssd1306_DrawString("Integral", 0, 40); - ssd1306_drawlogo(); - - char tempstr[6]; - itoa(k_i, tempstr, 10); - ssd1306_DrawString("I=", 1, 45); - ssd1306_DrawString(" ", 1, 57); - ssd1306_DrawString(tempstr, 1, 57); - - ssd1306_DrawString("Press to accept", 3, 40); - - // Button handler - if(SW_BTN_PRESSED) { - state = STATE_SETD; - } - else { - user_input(&k_i); - } - - // Event Handler - // N/A - - } break; - - case STATE_SETD: - { - // Write text to OLED - // [ therm :: set d ] - // [ d = 12 ] - ssd1306_DrawString("Derivative", 0, 40); - ssd1306_drawlogo(); - - char tempstr[6]; - itoa(k_d, tempstr, 10); - ssd1306_DrawString("D=", 1, 45); - ssd1306_DrawString(" ", 1, 57); - ssd1306_DrawString(tempstr, 1, 57); - - ssd1306_DrawString("Press to accept", 3, 40); - - // Button handler - if(SW_BTN_PRESSED) { - state = STATE_SETWINDUP; - } - else { - user_input(&k_d); - } - - // Event Handler - // N/A - - } break; - - case STATE_SETSTEPS: - { - // Write text to OLED - // [ step #1:: Duration: ### ] - // [ Setpoint: ### ] - char tempstr[6]; - - itoa(final_setpoint, tempstr, 10); - ssd1306_DrawString("Step #", 0, 0); - ssd1306_DrawString(tempstr, 0, 40); - - ssd1306_DrawString("Duration: ", 0, 5); - itoa(step_duration[final_setpoint], tempstr, 10); - ssd1306_DrawString(tempstr, 0, 70); - - ssd1306_DrawString("Setpoint: ", 0, 0); - itoa(step_setpoint[final_setpoint], tempstr, 10); - ssd1306_DrawString(tempstr, 0, 70); - - ssd1306_DrawString("Press to accept", 3, 40); - - // Button handler - TODO: increment max_step if pressed - // return and go to next state otherwise - if(SW_BTN_PRESSED) { - state = STATE_SETSTEPS; - final_setpoint++; - } - // else if(SW_LEFT_PRESSED) { - // state++; // go to next state or something - // } - else { - user_input(&k_p); - } - - // Event Handler - // N/A - - } break; - - case STATE_SETWINDUP: - { - // Write text to OLED - // [ therm :: set windup ] - // [ g = 12 ] - ssd1306_DrawString("Windup Guard", 0, 40); - ssd1306_drawlogo(); - - char tempstr[6]; - itoa(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) { - state = STATE_SETBOOTTOBREW; - } - else { - user_input(&windup_guard); - } - - // Event Handler - // N/A - - } break; - - case STATE_SETBOOTTOBREW: - { - // Write text to OLED - // [ therm :: set windup ] - // [ g = 12 ] - ssd1306_DrawString("Start on Boot", 0, 40); - ssd1306_drawlogo(); - - ssd1306_DrawString("sob=", 1, 45); - - if(boottobrew) - ssd1306_DrawString("Enabled ", 1, 70); - else - ssd1306_DrawString("Disabled", 1, 70); - - ssd1306_DrawString("Press to accept", 3, 40); - - // Button handler - if(SW_BTN_PRESSED) { - state = STATE_SETUNITS; - } - else if(!HAL_GPIO_ReadPin(SW_UP)) { - boottobrew = 1; - } - else if(!HAL_GPIO_ReadPin(SW_DOWN)) { - boottobrew = 0; - } - - // Event Handler - // N/A - - } break; - - case STATE_SETUNITS: - { - // Write text to OLED - // [ therm :: set windup ] - // [ g = 12 ] - ssd1306_DrawString("Units: ", 0, 40); - ssd1306_drawlogo(); - - if(temp_units == TEMP_UNITS_FAHRENHEIT) - ssd1306_DrawString("Fahrenheit", 1, 60); - else - ssd1306_DrawString("Celsius ", 1, 60); - - ssd1306_DrawString("Press to accept", 3, 40); - - // Button handler - if(SW_BTN_PRESSED) { - save_settings(); - state = STATE_IDLE; - } - else if(!HAL_GPIO_ReadPin(SW_UP)) { - temp_units = TEMP_UNITS_FAHRENHEIT; - } - else if(!HAL_GPIO_ReadPin(SW_DOWN)) { - temp_units = TEMP_UNITS_CELSIUS; - } - - // Event Handler - // N/A - - } break; - - - case STATE_PREHEAT_BREW: - { - // Write text to OLED - // [ therm : preheating brew ] - // [ 30 => 120 C ] - ssd1306_DrawString("Preheating...", 0, 0); - //ssd1306_drawlogo(); - draw_setpoint(); - - pid_enabled = 1; - setpoint = setpoint_brew; - - // Button handler - if(SW_BTN_PRESSED) { - save_setpoints(); // TODO: Check for mod - state = STATE_IDLE; - } - else { - user_input(&setpoint_brew); - } - - // Event Handler - if(temp >= setpoint) { - state = STATE_MAINTAIN_BREW; - } - - } break; - - case STATE_MAINTAIN_BREW: - { - // Write text to OLED - // [ therm : ready to brew ] - // [ 30 => 120 C ] - ssd1306_DrawString("Preheated!", 0, 0); - //ssd1306_drawlogo(); - draw_setpoint(); - pid_enabled = 1; - setpoint = setpoint_brew; - - // Button handler - if(SW_BTN_PRESSED) { - save_setpoints(); // TODO: Check for mod - state = STATE_IDLE; - } - else { - user_input(&setpoint_brew); - } - - // Event Handler - // N/A - - } break; - - case STATE_PREHEAT_STEAM: - { - // Write text to OLED - // [ therm : preheating steam ] - // [ 30 => 120 C ] - ssd1306_DrawString("Preheating...", 0, 0); - //ssd1306_drawlogo(); - draw_setpoint(); - pid_enabled = 1; - setpoint = setpoint_steam; - - // Button handler - if(SW_BTN_PRESSED) { - state = STATE_IDLE; - save_setpoints(); // TODO: Check for mod - } - else { - user_input(&setpoint_steam); - } - - // Event Handler - if(temp >= setpoint) { - state = STATE_MAINTAIN_STEAM; - } - - } break; - - case STATE_MAINTAIN_STEAM: - { - // Write text to OLED - // [ therm : ready to steam ] - // [ 30 => 120 C ] - ssd1306_DrawString("Ready to Steam!", 0, 0); - //ssd1306_drawlogo(); - draw_setpoint(); - pid_enabled = 1; - setpoint = setpoint_steam; - - // Button handler - if(SW_BTN_PRESSED) { - state = STATE_IDLE; - save_setpoints(); // TODO: Check for mod - } - else { - user_input(&setpoint_steam); - } - - // Event Handler - // N/A - - } break; - - case STATE_TC_ERROR: - { - // Write text to OLED - // [ therm : ready to steam ] - // [ 30 => 120 C ] - ssd1306_DrawString("Error:", 0, 0); - ssd1306_DrawString("Connect thermocouple", 1, 0); - ssd1306_DrawString("Press -> to ignore", 3, 0); - - // Button handler - if(SW_BTN_PRESSED) { - state = STATE_IDLE; - } - else if(SW_RIGHT_PRESSED) { - ignore_tc_error = 1; - state = STATE_IDLE; - } - // Event Handler - // Maybe handle if TC is plugged in - // N/A - - } break; - - // Something is terribly wrong - default: - { - state = STATE_IDLE; - pid_enabled = 0; - - } break; - - } - - if(last_state != state) { - // Clear screen on state change - goto_mode = 2; - trigger_drawsetpoint = 1; - ssd1306_clearscreen(); - } - - // Last buttonpress - sw_btn_last = sw_btn; - sw_up_last = sw_up; - sw_down_last = sw_down; - sw_left_last = sw_left; - sw_right_last = sw_right; -} - // vim:softtabstop=4 shiftwidth=4 expandtab