#include "stm32f0xx_hal.h"
#include "ssd1306.h"
#include "stringhelpers.h"
#include "display.h"
#include "config.h"
#include "states.h"
#include "syslib.h"
#include "flash.h"
#include "gpio.h"
#ifdef MAX31865_RTD_SENSOR
#include "max31865.h"
#endif
// Private function prototypes
static void draw_setpoint(therm_status_t* status);
// Button transition variables
static uint8_t sw_btn_last = 0;
static uint8_t sw_up_last = 0;
static uint8_t sw_down_last = 0;
static uint8_t sw_left_last = 0;
static uint8_t sw_right_last = 0;
// Buttonpress macros
#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)
// States
static uint8_t trigger_drawsetpoint = 1;
static int16_t last_temp = 21245;
static uint8_t goto_mode = MODE_HEAT;
static uint8_t reset_mode = RESET_REBOOT;
// Display state machine
void display_process(therm_settings_t* set, therm_status_t* status)
{
uint8_t last_state = status->state;
uint8_t temp_changed = status->temp != last_temp;
last_temp = status->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(status->state)
// Idle state
case STATE_IDLE:
@@ -114,331 +104,331 @@ void display_process(therm_settings_t* s
#ifdef BOOTLOADER_SHORTCUT
case MODE_BOOTLOADER:
ssd1306_clearscreen();
ssd1306_drawstring("Bootloader Entered", 0, 0);
ssd1306_drawstring("Device won't boot", 2, 0);
ssd1306_drawstring("until reflashed!", 3, 0);
bootloader_enter(); // Resets into bootloader
status->state = STATE_RESET; // Just in case
break;
default:
status->state = STATE_PREHEAT;
}
else if(SW_DOWN_PRESSED && goto_mode < (MODE_SIZE - 1)) {
goto_mode++;
else if(SW_UP_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(set->val.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) {
status->state = STATE_SETI;
else {
user_input(&set->val.k_p);
user_input((uint16_t*)&set->val.k_p);
case STATE_SETI:
// [ therm :: set i ]
// [ i = 12 ]
ssd1306_drawstring("Integral", 0, 40);
itoa(set->val.k_i, tempstr, 10);
ssd1306_drawstring("I=", 1, 45);
status->state = STATE_SETD;
user_input(&set->val.k_i);
user_input((uint16_t*)&set->val.k_i);
case STATE_SETD:
// [ therm :: set d ]
// [ d = 12 ]
ssd1306_drawstring("Derivative", 0, 40);
itoa(set->val.k_d, tempstr, 10);
ssd1306_drawstring("D=", 1, 45);
status->state = STATE_SETWINDUP;
user_input(&set->val.k_d);
user_input((uint16_t*)&set->val.k_d);
case STATE_SETWINDUP:
// [ therm :: set windup ]
// [ g = 12 ]
ssd1306_drawstring("Windup Guard", 0, 40);
itoa(set->val.windup_guard, tempstr, 10);
ssd1306_drawstring("G=", 1, 45);
status->state = STATE_SETBOOTTOBREW;
user_input(&set->val.windup_guard);
user_input((uint16_t*)&set->val.windup_guard);
case STATE_SETBOOTTOBREW:
ssd1306_drawstring("Start on Boot", 0, 40);
ssd1306_drawstring("sob=", 1, 45);
if(set->val.boottobrew)
ssd1306_drawstring("Enabled ", 1, 70);
else
ssd1306_drawstring("Disabled", 1, 70);
status->state = STATE_SETUNITS;
else if(!HAL_GPIO_ReadPin(SW_UP)) {
set->val.boottobrew = 1;
else if(!HAL_GPIO_ReadPin(SW_DOWN)) {
set->val.boottobrew = 0;
case STATE_SETUNITS:
ssd1306_drawstring("Units: ", 0, 40);
if(set->val.temp_units == TEMP_UNITS_FAHRENHEIT)
ssd1306_drawstring("Fahrenheit", 1, 60);
ssd1306_drawstring("Celsius ", 1, 60);
status->state = STATE_SETTEMPOFFSET;
set->val.temp_units = TEMP_UNITS_FAHRENHEIT;
set->val.temp_units = TEMP_UNITS_CELSIUS;
case STATE_SETTEMPOFFSET:
// [ therm :: set temp offset ]
ssd1306_drawstring("Temp Cal Offset", 0, 40);
itoa(set->val.temp_offset, tempstr, 10);
ssd1306_drawstring("O=", 1, 45);
flash_save(set);
status->state = STATE_IDLE;
user_input_signed(&set->val.temp_offset);
user_input_signed((int16_t*)(&set->val.temp_offset));
case STATE_PREHEAT:
// [ therm : preheating brew ]
// [ 30 => 120 C ]
ssd1306_drawstring("Preheating...", 0, 0);
//ssd1306_drawlogo();
draw_setpoint(status);
status->pid_enabled = 1;
status->setpoint = set->val.setpoint_brew;
user_input(&set->val.setpoint_brew);
user_input((uint16_t*)&set->val.setpoint_brew);
if(status->temp >= status->setpoint) {
status->state = STATE_MAINTAIN;
case STATE_MAINTAIN:
// [ therm : ready to brew ]
ssd1306_drawstring("Preheated!", 0, 0);
// Thermocouple error
case STATE_TC_ERROR:
// [ therm : ready to steam ]
ssd1306_drawstring("Error: ", 0, 0);
itoa(status->error_code, tempstr, 10);
ssd1306_drawstring(tempstr, 0, 57);
//TODO: add RTD error codes
if(status->error_code == 1)
ssd1306_drawstring(" TC Open Circuit", 1, 0);
else if(status->error_code == 4)
ssd1306_drawstring(" TC Short to GND", 1, 0);
else if(status->error_code == 8)
ssd1306_drawstring(" TC Short to VCC", 1, 0);
ssd1306_drawstring("#?, Unknown Error", 1, 0);
ssd1306_drawstring(" ", 2, 0);
ssd1306_drawstring("-> to ignore all or", 2, 0);
ssd1306_drawstring("press to continue", 3, 0);
max31865_clear_errors(spi_get());
else if(SW_RIGHT_PRESSED) {
set->val.ignore_error = 1;
// Maybe handle if TC is plugged in
#ifndef DISPLAY_H
#define DISPLAY_H
#include <stdlib.h>
void display_process(therm_settings_t* set, therm_status_t* status);
#include "stm32f0xx_hal_conf.h"
#include <inttypes.h>
// Increase on each press, and increase at a fast rate after duration elapsed of continuously holding down... somehow...
static uint32_t change_time_reset = 0;
// Increment/decrement unsigned variable with up/down buttons
void user_input(uint16_t* to_modify)
if(CHANGE_ELAPSED) {
if(!HAL_GPIO_ReadPin(SW_UP) ) {
CHANGE_RESET;
(*to_modify)++;
else if(!HAL_GPIO_ReadPin(SW_DOWN) && (*to_modify) > 0) {
(*to_modify)--;
// Increment/decrement signed variable with up/down buttons
void user_input_signed(int16_t* to_modify)
// TODO: Bounds check on int16_t
// Initialize GPIO
void gpio_init(void)
GPIO_InitTypeDef GPIO_InitStruct;
/* GPIO Ports Clock Enable */
__GPIOF_CLK_ENABLE();
__GPIOA_CLK_ENABLE();
__GPIOB_CLK_ENABLE();
__SPI1_CLK_ENABLE();
#ifndef GPIO_H
#define GPIO_H
#define CHANGE_PERIOD_MS 100
#define CHANGE_ELAPSED (HAL_GetTick() - change_time_reset) > CHANGE_PERIOD_MS
#define CHANGE_RESET change_time_reset = HAL_GetTick()
void user_input(uint16_t* to_modify);
void user_input_signed(int16_t* to_modify);
void gpio_init(void);
// vim:softtabstop=4 shiftwidth=4 expandtab
#include "pid.h"
#ifdef MAX31855_TC_SENSOR
#include "max31855.h"
#include "spi.h"
#include "usb_device.h"
#include "usbd_cdc_if.h"
therm_settings_t set;
therm_status_t status;
int main(void)
// Initialize HAL
hal_init();
// Configure the system clock
systemclock_init();
// Unset bootloader option bytes (if set)
// FIXME this was never getting called. Try again sometime.
//bootloader_unset();
// Init GPIO
gpio_init();
// Init USB (TODO: Handle plugged/unplugged with external power)
MX_USB_DEVICE_Init();
// set.val.usb_plugged =
// USB startup delay
HAL_Delay(500);
HAL_GPIO_WritePin(LED_POWER, 1);
// Enter into bootloader if up button pressed on boot
if(!HAL_GPIO_ReadPin(SW_UP))
bootloader_enter();
// Init SPI busses
spi_init();
max31865_config(spi_get());
// Init OLED over SPI
ssd1306_init();
@@ -215,152 +215,150 @@ void ssd1306_clearscreen()
for(i = 0; i<128; i++)
WriteData(0x00);
void ssd1306_drawlogo()
uint8_t i = 0;
setStartPage(3);
setStartColumn(0);
for(i = 0; i<32; i++)
WriteData(row[0][i]);
setStartPage(2);
WriteData(row[1][i]);
setStartPage(1);
WriteData(row[2][i]);
setStartPage(0);
WriteData(row[3][i]);
/* Print a single character from font.cpp */
void ssd1306_drawchar(char ascii, unsigned char row, unsigned char xPos)
char *srcPointer = -1;
unsigned char i;
const char *srcPointer = (char*)-1;
srcPointer = &fontData[(ascii-32)][0];
setStartPage(row);
setStartColumn(xPos);
for(i=0;i<5;i++)
for(uint8_t i = 0; i < 5; i++)
WriteData(*srcPointer);
srcPointer++;
void ssd1306_drawcharbig(char ascii, unsigned char row, unsigned char xPos)
setStartPage(row-1);
// Write first row
uint8_t data = 0;
data |= ((*srcPointer) & 0b1000) << 4; // get top 4 bits
data |= ((*srcPointer) & 0b1000) << 3; // get top 4 bits
data |= ((*srcPointer) & 0b0100) << 3; // get top 4 bits
data |= ((*srcPointer) & 0b0100) << 2; // get top 4 bits
data |= ((*srcPointer) & 0b0010) << 2; // get top 4 bits
data |= ((*srcPointer) & 0b0010) << 1; // get top 4 bits
data |= ((*srcPointer) & 0b0001) << 1; // get top 4 bits
data |= ((*srcPointer) & 0b0001); // get top 4 bits
WriteData(data);
srcPointer -= 5;
// Write second row
data |= (*srcPointer) & 0b10000000; // get top 4 bits
data |= ((*srcPointer) & 0b10000000) >> 1; // get top 4 bits
data |= ((*srcPointer) & 0b01000000) >> 1; // get top 4 bits
data |= ((*srcPointer) & 0b01000000) >> 2; // get top 4 bits
data |= ((*srcPointer) & 0b00100000) >> 2; // get top 4 bits
data |= ((*srcPointer) & 0b00100000) >> 3; // get top 4 bits
data |= ((*srcPointer) & 0b00010000) >> 3; // get top 4 bits
data |= ((*srcPointer) & 0b00010000) >> 4; // get top 4 bits
void ssd1306_drawstring(const char *dataPtr, unsigned char row, unsigned char xPos)
char *srcPointer;
srcPointer = (char*)dataPtr;
ssd1306_drawchar(' ',row,xPos); // NBSP must be written first before the string start
while(1)
ssd1306_drawchar(*srcPointer,row,xPos);
xPos+=6;
if(*srcPointer == 0) break;
void ssd1306_drawstringbig(const char *dataPtr, unsigned char row, unsigned char xPos)
ssd1306_drawcharbig(' ',row,xPos); // NBSP must be written first before the string start
#ifndef ssd1306_H
#define ssd1306_H
#define SSD_RESET GPIOA,GPIO_PIN_3
#define SSD_A0 GPIOA,GPIO_PIN_4
#define SSD_CS GPIOA,GPIO_PIN_2
#define SSD_Reset_Low() HAL_GPIO_WritePin(SSD_RESET, 0)
#define SSD_Reset_High() HAL_GPIO_WritePin(SSD_RESET, 1)
#define SSD_A0_Low() HAL_GPIO_WritePin(SSD_A0, 0)
#define SSD_A0_High() HAL_GPIO_WritePin(SSD_A0, 1)
#define SSD_CS_Low() HAL_GPIO_WritePin(SSD_CS, 0)
#define SSD_CS_High() HAL_GPIO_WritePin(SSD_CS, 1)
// EMZ FIXME this won't really work
#define SPI_SendByte(data) HAL_SPI_Transmit(spi_get(), &data, 1, 100)
//#define SPI_SendByte(data)
#define SPI_Wait()
//#define SPI_SendByte(data) SPI_I2S_SendData(SPI1,data)
//#define SPI_Wait() while(!(SPI1->SR&SPI_FLAG_TXE));while(SPI1->SR&SPI_FLAG_BSY);
void ssd1306_init(void);
void ssd1306_drawchar(char ascii, unsigned char row, unsigned char xPos);
void ssd1306_drawcharbig(char ascii, unsigned char row, unsigned char xPos);
void ssd1306_drawstring(const char *dataPtr, unsigned char row, unsigned char xPos);
void ssd1306_drawstringbig(const char *dataPtr, unsigned char row, unsigned char xPos);
void ssd1306_drawlogo();
void ssd1306_clearscreen();
Status change: