Compare commits

...

5 Commits

  1. 1
      Core/Src/Gui/app_gui.c
  2. 14
      Core/Src/Gui/app_gui.h
  3. 218
      Core/Src/Gui/screen_calibration.c
  4. 6
      Core/Src/Gui/screen_home.c
  5. 2
      Core/Src/Gui/screen_manual.c
  6. 77
      Core/Src/Gui/screen_menu.c
  7. 3
      Core/Src/Gui/screen_menu.h
  8. 34
      Core/Src/app_heater.c
  9. 7
      Core/Src/app_heater.h
  10. 113
      Core/Src/app_temp.c
  11. 8
      Core/Src/app_temp.h
  12. 22
      Lib/snprintf/snprintf.c
  13. 13
      Lib/ufb/Src/font_57.inc.c
  14. 225
      Lib/ufb/Src/fontedit_57.c
  15. 1
      Makefile

@ -124,7 +124,6 @@ void switch_screen(screen_t pScreen, bool init) {
s_app.initial_pushed = s_app.pushed; s_app.initial_pushed = s_app.pushed;
s_app.screen = pScreen; s_app.screen = pScreen;
// clear the union field // clear the union field
memset(&s_app.page, 0, sizeof(s_app.page));
request_paint(); request_paint();
if (init) { if (init) {

@ -41,6 +41,7 @@ uint32_t push_time();
void screen_home(GuiEvent event); void screen_home(GuiEvent event);
void screen_manual(GuiEvent event); void screen_manual(GuiEvent event);
void screen_manual_menu(GuiEvent event); void screen_manual_menu(GuiEvent event);
void screen_calibration(GuiEvent event);
struct State { struct State {
/// Latest oven temp readout /// Latest oven temp readout
@ -78,19 +79,6 @@ struct State {
/// Pointer to the currently active screen func /// Pointer to the currently active screen func
screen_t screen; screen_t screen;
/// data specific for each of the screens (not persistent across screen switching)
union {
/// Generic menu
struct menu_state {
int pos;
int len;
uint32_t change_time;
uint32_t slide_end_time;
uint16_t text_slide;
uint8_t tick_counter;
} menu;
} page;
}; };
extern struct State s_app; extern struct State s_app;

@ -0,0 +1,218 @@
//
// Created by MightyPork on 2023/04/09.
//
#include <stddef.h>
#include <string.h>
#include "app_gui.h"
#include "app_heater.h"
#include "screen_menu.h"
#include "ufb/fb_text.h"
#include "app_temp.h"
#include "snprintf.h"
#include "FreeRTOS.h"
struct calib_state {
int phase;
float sample1;
float sample2;
int temp1;
int temp2;
} s_calib;
enum Phase {
PH_HELLO = 0,
PH_SAMPLE1,
PH_TEMP1,
PH_SAMPLE2,
PH_TEMP2,
PH_DONE,
};
static void next_phase() {
PUTS("Phase++\r\n");
s_calib.phase++;
}
static const char* hello_menu_opts[] = {
"Pokračovat",
"Zrušit",
NULL
};
static void hello_menu_cb(int opt) {
switch (opt) {
case 0:
// Continue
next_phase();
request_paint();
app_heater_manual_override(100);
break;
case 1:
switch_screen(screen_home, true);
break;
}
}
static const char* sample1_menu_opts[] = {
"Vzorek 1",
"Zrušit",
NULL
};
static void sample1_menu_cb(int opt) {
switch (opt) {
case 0:
// Continue
next_phase();
request_paint();
s_calib.sample1 = app_temp_read_oven_raw();
app_heater_manual_override(0);
break;
case 1:
app_heater_manual_override(-1);
switch_screen(screen_home, true);
break;
}
}
static const char* sample2_menu_opts[] = {
"Vzorek 2",
"Zrušit",
NULL
};
static void sample2_menu_cb(int opt) {
switch (opt) {
case 0:
// Continue
next_phase();
request_paint();
s_calib.sample2 = app_temp_read_oven_raw();
app_heater_manual_override(0);
break;
case 1:
app_heater_manual_override(-1);
switch_screen(screen_home, true);
break;
}
}
void screen_calibration(GuiEvent event)
{
if (event == GUI_EVENT_SCREEN_INIT) {
memset(&s_calib, 0, sizeof(s_calib));
// continue to the rest - so the menu can be inited
}
int *pT;
switch (s_calib.phase) {
case PH_HELLO:
if (event == GUI_EVENT_PAINT) {
fb_text(FBW/2, 16, "Vychlaď", TEXT_CENTER, 1);
fb_text(FBW/2, 26, "troubu", TEXT_CENTER, 1);
}
screen_menu_offset(event, hello_menu_opts, hello_menu_cb, 30);
break;
case PH_SAMPLE1: // Heater is active, waiting for mark
case PH_SAMPLE2:
if (event == GUI_EVENT_PAINT) {
fb_text(FBW/2, 16, "Zapiš", TEXT_CENTER, 1);
fb_text(FBW/2, 26, "teplotu", TEXT_CENTER, 1);
}
if (s_calib.phase == PH_SAMPLE1) {
screen_menu_offset(event, sample1_menu_opts, sample1_menu_cb, 30);
} else {
screen_menu_offset(event, sample2_menu_opts, sample2_menu_cb, 30);
}
break;
case PH_TEMP1:
case PH_TEMP2:
if (s_calib.phase == PH_TEMP1) {
pT = &s_calib.temp1;
} else {
pT = &s_calib.temp2;
}
if (push_time() > pdMS_TO_TICKS(500)) {
input_sound_effect();
app_heater_manual_override_clear();
switch_screen(screen_home, true);
return;
}
switch (event) {
case GUI_EVENT_PAINT: {
fb_text(FBW/2, 14, s_calib.phase == PH_TEMP1 ? "Teplota 1" : "Teplota 2", TEXT_CENTER, 1);
SPRINTF(stmp, "%d°C", *pT);
fb_text(FBW/2, 30, stmp, TEXT_CENTER | FONT_DOUBLE, 1);
fb_text(2, FBH - 8 * 3, "←→Nastav", 0, 1);
fb_text(2, FBH - 8 * 2, "> Potvrdit", 0, 1);
fb_text(2, FBH - 8 * 1, "» Zrušit", 0, 1);
return;
}
case GUI_EVENT_KNOB_PLUS: {
if (*pT < 500) {
*pT += 1;
input_sound_effect();
request_paint();
}
break;
}
case GUI_EVENT_KNOB_MINUS: {
if (*pT > 0) {
input_sound_effect();
*pT -= 1;
request_paint();
}
break;
}
case GUI_EVENT_KNOB_RELEASE: {
next_phase();
request_paint();
if (s_calib.phase == PH_DONE) {
app_heater_manual_override(-1);
// TODO do the math
PRINTF("Sample 1 %f, T1 %d\r\n", s_calib.sample1, s_calib.temp1);
PRINTF("Sample 2 %f, T2 %d\r\n", s_calib.sample2, s_calib.temp2);
float corrected1 = c_to_val((float) s_calib.temp1);
float corrected2 = c_to_val((float) s_calib.temp2);
float a = (corrected1 - corrected2) / (s_calib.sample1 - s_calib.sample2);
float b = corrected1 - a * s_calib.sample1;
app_temp_set_calib(a, b);
} else {
// for the next phase
app_heater_manual_override(100);
}
break;
}
}
break;
case PH_DONE:
if (event == GUI_EVENT_PAINT) {
fb_text(FBW/2, 14, "Hotovo", TEXT_CENTER, 1);
fb_text(FBW/2, 36, "→Hlavní menu", TEXT_CENTER, 1);
}
if (event == GUI_EVENT_KNOB_RELEASE) {
switch_screen(screen_home, 1);
}
break;
}
}

@ -12,8 +12,8 @@
static const char* main_menu_opts[] = { static const char* main_menu_opts[] = {
"Ruční režim", "Ruční režim",
"Kalibrace", "Kalibrace",
"Programy", // "Programy",
"Diagnostika", // "Diagnostika",
NULL NULL
}; };
@ -23,7 +23,7 @@ static void main_menu_cb(int opt) {
switch_screen(screen_manual, true); switch_screen(screen_manual, true);
break; break;
case 1: case 1:
// TODO switch_screen(screen_calibration, true);
break; break;
} }
} }

@ -17,6 +17,8 @@ void screen_manual(GuiEvent event)
{ {
bool temp_changed = false; bool temp_changed = false;
if (event == GUI_EVENT_SCREEN_INIT) { if (event == GUI_EVENT_SCREEN_INIT) {
app_heater_manual_override_clear();
app_heater_enable(false);
return; return;
} }

@ -3,6 +3,7 @@
// //
#include <stdbool.h> #include <stdbool.h>
#include <string.h>
#include "screen_menu.h" #include "screen_menu.h"
#include "app_gui.h" #include "app_gui.h"
#include "FreeRTOS.h" #include "FreeRTOS.h"
@ -12,40 +13,58 @@
#include "ufb/framebuffer.h" #include "ufb/framebuffer.h"
#include "ufb/fb_text.h" #include "ufb/fb_text.h"
struct menu_state {
int pos;
int len;
uint32_t change_time;
uint32_t slide_end_time;
uint16_t text_slide;
void * last_opts;
} s_menu;
void screen_menu(GuiEvent event, const char **options, menu_callback_t cb) { void screen_menu(GuiEvent event, const char **options, menu_callback_t cb) {
screen_menu_offset(event, options, cb, 15);
}
void screen_menu_offset(GuiEvent event, const char **options, menu_callback_t cb, fbpos_t offset) {
if (event != GUI_EVENT_SCREEN_INIT && s_menu.last_opts != (void*) options) {
// ensure the menu is properly inited when the options list changes.
screen_menu(GUI_EVENT_SCREEN_INIT, options, cb);
}
bool menu_changed = false; bool menu_changed = false;
const uint32_t tickNow = xTaskGetTickCount(); const uint32_t tickNow = xTaskGetTickCount();
struct menu_state *menu = &s_app.page.menu;
switch (event) { switch (event) {
case GUI_EVENT_SCREEN_INIT: case GUI_EVENT_SCREEN_INIT:
menu->pos = 0; memset(&s_menu, 0, sizeof(s_menu));
menu->len = 0; s_menu.last_opts = (void*) options;
menu->change_time = tickNow;
menu->text_slide = 0; s_menu.change_time = tickNow;
// count options
const char **opt = options; const char **opt = options;
while (*opt) { while (*opt) {
menu->len++; s_menu.len++;
opt++; opt++;
} }
break; break;
case GUI_EVENT_SCREEN_TICK: case GUI_EVENT_SCREEN_TICK:
// long text sliding animation // long text sliding animation
if (tickNow - menu->change_time >= pdMS_TO_TICKS(500)) { if (tickNow - s_menu.change_time >= pdMS_TO_TICKS(500)) {
const uint32_t textlen = utf8_strlen(options[menu->pos]) * 6; const uint32_t textlen = utf8_strlen(options[s_menu.pos]) * 6;
if (textlen >= FBW - 2) { if (textlen >= FBW - 2) {
if (textlen - menu->text_slide > FBW - 1) { if (textlen - s_menu.text_slide > FBW - 1) {
menu->text_slide += 1; s_menu.text_slide += 1;
if (textlen - menu->text_slide >= FBW - 1) { if (textlen - s_menu.text_slide >= FBW - 1) {
menu->slide_end_time = tickNow; s_menu.slide_end_time = tickNow;
} }
} else if (tickNow - menu->slide_end_time >= pdMS_TO_TICKS(500)) { } else if (tickNow - s_menu.slide_end_time >= pdMS_TO_TICKS(500)) {
menu->change_time = tickNow; s_menu.change_time = tickNow;
menu->slide_end_time = 0; s_menu.slide_end_time = 0;
menu->text_slide = 0; s_menu.text_slide = 0;
} }
request_paint(); request_paint();
} }
@ -53,13 +72,13 @@ void screen_menu(GuiEvent event, const char **options, menu_callback_t cb) {
break; break;
case GUI_EVENT_PAINT: case GUI_EVENT_PAINT:
for (int i = 0; i < menu->len; i++) { for (int i = 0; i < s_menu.len; i++) {
// is the row currently rendered the selected row? // is the row currently rendered the selected row?
const bool is_selected = menu->pos == i; const bool is_selected = s_menu.pos == i;
const fbcolor_t color = !is_selected; // text color - black if selected, because it's inverted const fbcolor_t color = !is_selected; // text color - black if selected, because it's inverted
const fbpos_t y = 27 + i * 10; const fbpos_t y = 12 + offset + i * 10 ;
fb_rect(0, y, FBW, 10, !color); fb_rect(0, y, FBW, 10, !color);
fb_text(1 - (is_selected ? menu->text_slide : 0), y + 1, options[i], FONT_5X7, color); fb_text(1 - (is_selected ? s_menu.text_slide : 0), y + 1, options[i], FONT_5X7, color);
// ensure the text doesn't touch the edge (looks ugly) // ensure the text doesn't touch the edge (looks ugly)
fb_vline(FBW - 1, y, 10, !color); fb_vline(FBW - 1, y, 10, !color);
fb_vline(0, y, 10, !color); fb_vline(0, y, 10, !color);
@ -69,28 +88,28 @@ void screen_menu(GuiEvent event, const char **options, menu_callback_t cb) {
// the button is held! release is what activates the button // the button is held! release is what activates the button
case GUI_EVENT_KNOB_RELEASE: case GUI_EVENT_KNOB_RELEASE:
input_sound_effect(); input_sound_effect();
cb(menu->pos); cb(s_menu.pos);
break; break;
case GUI_EVENT_KNOB_PLUS: case GUI_EVENT_KNOB_PLUS:
if (menu->pos < menu->len - 1) { if (s_menu.pos < s_menu.len - 1) {
menu->pos++; s_menu.pos++;
menu_changed = true; menu_changed = true;
} }
break; break;
case GUI_EVENT_KNOB_MINUS: case GUI_EVENT_KNOB_MINUS:
if (menu->pos > 0) { if (s_menu.pos > 0) {
menu->pos--; s_menu.pos--;
menu_changed = true; menu_changed = true;
} }
break; break;
} }
if (menu_changed) { if (menu_changed) {
menu->change_time = tickNow; s_menu.change_time = tickNow;
menu->text_slide = 0; s_menu.text_slide = 0;
menu->slide_end_time = 0; s_menu.slide_end_time = 0;
input_sound_effect(); input_sound_effect();
request_paint(); request_paint();
} }

@ -8,6 +8,7 @@
#define TOASTER_OVEN_BLUEPILL_SCREEN_MENU_H #define TOASTER_OVEN_BLUEPILL_SCREEN_MENU_H
#include "gui_event.h" #include "gui_event.h"
#include "ufb/framebuffer.h"
/** /**
* Choice callback for the generic menu screen. Options are indexed from zero * Choice callback for the generic menu screen. Options are indexed from zero
@ -23,5 +24,7 @@ typedef void (*menu_callback_t)(int choice);
*/ */
void screen_menu(GuiEvent event, const char **options, menu_callback_t cb); void screen_menu(GuiEvent event, const char **options, menu_callback_t cb);
/** Menu with custom offset (default is 15) */
void screen_menu_offset(GuiEvent event, const char **options, menu_callback_t cb, fbpos_t offset);
#endif //TOASTER_OVEN_BLUEPILL_SCREEN_MENU_H #endif //TOASTER_OVEN_BLUEPILL_SCREEN_MENU_H

@ -39,12 +39,15 @@ static struct {
float tuning_p; float tuning_p;
float tuning_i; float tuning_i;
float tuning_d; float tuning_d;
/// Manual PWM override (percent, -1 = override disable)
int manual_override;
// PID state // PID state
struct PID pid; struct PID pid;
} state = { } state = {
.tuning_p = 10.0f, .tuning_p = 10.0f,
.tuning_i = 0.052f, .tuning_i = 0.052f,
.tuning_d = 100.0f, .tuning_d = 100.0f,
.manual_override = -1,
.pid = { .pid = {
.SampleTimeTicks = pdMS_TO_TICKS(1000), .SampleTimeTicks = pdMS_TO_TICKS(1000),
.outMax = 100.0f, .outMax = 100.0f,
@ -62,6 +65,19 @@ static inline void heaterExitCritical() {
osMutexRelease(heaterMutexHandle); osMutexRelease(heaterMutexHandle);
} }
void app_heater_manual_override(int percent) {
PRINTF("Set manual override: %d\r\n", percent);
heaterEnterCritical();
PID_SetCtlMode(&state.pid, PID_MANUAL);
state.manual_override = percent;
heaterExitCritical();
}
void app_heater_manual_override_clear() {
app_heater_manual_override(-1);
}
void app_heater_set_tuning(float p, float i, float d) { void app_heater_set_tuning(float p, float i, float d) {
heaterEnterCritical(); heaterEnterCritical();
PID_SetTunings(&state.pid, p, i, d); PID_SetTunings(&state.pid, p, i, d);
@ -127,13 +143,19 @@ void app_task_heater(void *argument)
heaterEnterCritical(); heaterEnterCritical();
app_safety_pass_reg_loop_running(); app_safety_pass_reg_loop_running();
PID_Compute(&state.pid, state.oven_temp);
if (state.pid.ctlMode == PID_AUTOMATIC) { if (state.manual_override >= 0 && state.manual_override <= 100) {
PRINTF("temp %d, output %d\r\n", (int) state.oven_temp, (int) state.pid.Output); PRINTF("manual override %d%%\r\n", state.manual_override);
heater_pwm_set_perc(state.pid.Output); heater_pwm_set_perc(state.manual_override);
} else { } else {
// turn it off PID_Compute(&state.pid, state.oven_temp);
heater_pwm_set_perc(0); if (state.pid.ctlMode == PID_AUTOMATIC) {
PRINTF("temp %d, output %d\r\n", (int) state.oven_temp, (int) state.pid.Output);
heater_pwm_set_perc(state.pid.Output);
} else {
// turn it off
heater_pwm_set_perc(0);
}
} }
heaterExitCritical(); heaterExitCritical();

@ -14,6 +14,13 @@ extern osThreadId_t heaterTskHandle;
void app_task_heater(void *argument); void app_task_heater(void *argument);
/// Clear manual override, disable heater for normal mode.
void app_heater_manual_override_clear();
/// Set manual override PWM 0-100%.
/// Also disables heater in the normal mode.
void app_heater_manual_override(int percent);
/// Set heater regulator tuning. /// Set heater regulator tuning.
/// Mutex is locked internally. /// Mutex is locked internally.
void app_heater_set_tuning(float p, float i, float d); void app_heater_set_tuning(float p, float i, float d);

@ -26,17 +26,17 @@ const float V_REFINT = 1.23f;
static struct App { static struct App {
float oven_temp; float oven_temp;
float oven_temp_raw;
float soc_temp; float soc_temp;
float v_sensor;
float cal_a; float cal_a;
float cal_b; float cal_b;
float oventemp_history[OVENTEMP_HISTORY_DEPTH]; // raw temp
uint16_t adc_averagebuf[AVERAGEBUF_DEPTH * 4]; uint16_t adc_averagebuf[AVERAGEBUF_DEPTH * 4];
uint8_t averagebuf_ptr; uint8_t averagebuf_ptr;
float adc_averages[4];
float oventemp_history[OVENTEMP_HISTORY_DEPTH];
uint8_t oventemp_history_ptr; uint8_t oventemp_history_ptr;
} s_analog = { } s_analog = {
.cal_a = 1.0f, // Ax + B = y ... X = raw sample (ratio 0-1 of 3.3), Y = corrected sample
.cal_a = 1.0f, // safe default calibration constants
.cal_b = 0.0f, .cal_b = 0.0f,
}; };
@ -148,6 +148,35 @@ static const float TSENSE_LOOKUP[TSENSE_LOOKUP_LEN] = {
0.219346163379138f, 0.219346163379138f,
}; };
/// if the calibration constants are zero, reset to defaults
static void correct_invalid_calib() {
if (s_analog.cal_a == 0.0f || s_analog.cal_b == 0.0f) {
PRINTF("ADC invalid calib, reset\r\n");
s_analog.cal_a = 1.0f;
s_analog.cal_b = 0.0f;
}
}
/// Set and persist calibration constants
void app_temp_set_calib(float a, float b) {
s_analog.cal_a = a;
s_analog.cal_b = b;
correct_invalid_calib();
EE_Status st = EE_WriteVariable32bits(EE_ADDR_CAL_A, ((x32_t) { .f = a }).u);
if (st == EE_CLEANUP_REQUIRED) {
EE_CleanUp();
} else if (st != EE_OK) {
PRINTF("EE write err %d!\r\n", st);
}
st = EE_WriteVariable32bits(EE_ADDR_CAL_B, ((x32_t) { .f = b }).u);
if (st == EE_CLEANUP_REQUIRED) {
EE_CleanUp();
} else if (st != EE_OK) {
PRINTF("EE write err %d!\r\n", st);
}
}
void app_analog_init() void app_analog_init()
{ {
// read calibration constants // read calibration constants
@ -161,11 +190,7 @@ void app_analog_init()
PRINTF("ADC calib b read from EE: %f\r\n", s_analog.cal_b); PRINTF("ADC calib b read from EE: %f\r\n", s_analog.cal_b);
} }
if (s_analog.cal_a == 0.0f || s_analog.cal_b == 0.0f) { correct_invalid_calib();
PRINTF("ADC invalid calib, reset\r\n");
s_analog.cal_a = 1.0f;
s_analog.cal_b = 0.0f;
}
LL_ADC_Enable(ADC_TEMP); LL_ADC_Enable(ADC_TEMP);
@ -192,7 +217,7 @@ void app_analog_init()
LL_TIM_EnableCounter(TIM1); LL_TIM_EnableCounter(TIM1);
} }
static float val_to_c(float val) float val_to_c(float val)
{ {
// TODO use binary search.. lol // TODO use binary search.. lol
for (int i = 1; i < TSENSE_LOOKUP_LEN; i++) { for (int i = 1; i < TSENSE_LOOKUP_LEN; i++) {
@ -207,6 +232,22 @@ static float val_to_c(float val)
return TSENSE_T_MAX; return TSENSE_T_MAX;
} }
float c_to_val(float cf)
{
int lower = (int) (cf / TSENSE_T_STEP);
if (lower < 0) {
lower = 0;
}
if (lower >= TSENSE_LOOKUP_LEN - 1) {
lower = TSENSE_LOOKUP_LEN - 2;
}
int upper = lower + 1;
float ratio = (cf - ((float)lower * TSENSE_T_STEP)) / 5.0f;
return TSENSE_LOOKUP[lower] + (TSENSE_LOOKUP[upper] - TSENSE_LOOKUP[lower]) * ratio;
}
void app_temp_sample() void app_temp_sample()
{ {
uint32_t sums[4] = {}; uint32_t sums[4] = {};
@ -224,39 +265,38 @@ void app_temp_sample()
return; return;
} }
s_analog.adc_averages[0] = (float) sums[0] / count; float adc_averages[4];
s_analog.adc_averages[1] = (float) sums[1] / count;
s_analog.adc_averages[2] = (float) sums[2] / count; adc_averages[0] = (float) sums[0] / count;
s_analog.adc_averages[3] = (float) sums[3] / count; adc_averages[1] = (float) sums[1] / count;
adc_averages[2] = (float) sums[2] / count;
adc_averages[3] = (float) sums[3] / count;
PRINTF("%f\t%f\t%f\t%f\r\n", // PRINTF("%f\t%f\t%f\t%f\r\n",
s_analog.adc_averages[0], // adc_averages[0],
s_analog.adc_averages[1], // adc_averages[1],
s_analog.adc_averages[2], // adc_averages[2],
s_analog.adc_averages[3] // adc_averages[3]
); // );
/* r_pt100, r_ref, internal_temp, v_ref_int */ /* r_pt100, r_ref, internal_temp, v_ref_int */
float refint = s_analog.adc_averages[3]; float refint = adc_averages[3];
float scale = V_REFINT / refint; float scale = V_REFINT / refint;
const float avg_slope = 4.3f * scale; const float avg_slope = 4.3f * scale;
const float v25 = 1.43f; const float v25 = 1.43f;
const float v_tsen = s_analog.adc_averages[2] * scale; const float v_tsen = adc_averages[2] * scale;
s_analog.soc_temp = (v25 - v_tsen) / avg_slope + 25.f; s_analog.soc_temp = (v25 - v_tsen) / avg_slope + 25.f;
s_analog.v_sensor = s_analog.adc_averages[0] * scale; // good for debug/tuning //s_analog.v_sensor = adc_averages[0] * scale; // good for debug/tuning
// using a voltage divider, so assuming the reference resistor is measured well, // using a voltage divider, so assuming the reference resistor is measured well,
// we can just use the ratio and the exact voltage value is not important. // we can just use the ratio and the exact voltage value is not important.
float x = s_analog.adc_averages[0] / s_analog.adc_averages[1]; float oventemp_sample = adc_averages[0] / adc_averages[1];
float y = s_analog.cal_a * x + s_analog.cal_b;
float actual_temp = val_to_c(y);
PRINTF("Compensated x %f -> y %f, temp %f C\r\n", x, y, actual_temp);
s_analog.oventemp_history[s_analog.oventemp_history_ptr] = actual_temp; s_analog.oventemp_history[s_analog.oventemp_history_ptr] = oventemp_sample;
s_analog.oventemp_history_ptr = (s_analog.oventemp_history_ptr + 1) % OVENTEMP_HISTORY_DEPTH; s_analog.oventemp_history_ptr = (s_analog.oventemp_history_ptr + 1) % OVENTEMP_HISTORY_DEPTH;
float sum = 0; float sum = 0;
@ -270,15 +310,21 @@ void app_temp_sample()
if (depth > 0) { if (depth > 0) {
sum /= depth; sum /= depth;
} }
s_analog.oven_temp = sum;
float y = s_analog.cal_a * sum + s_analog.cal_b;
float actual_temp = val_to_c(y);
PRINTF("T raw %f %f -> comp %f, temp %f°C\r\n", sum, y, actual_temp);
s_analog.oven_temp = actual_temp;
s_analog.oven_temp_raw = sum;
app_safety_pass_temp_calculation(); app_safety_pass_temp_calculation();
if (s_analog.oven_temp >= 5.0 && s_analog.oven_temp <= 455.0) { if (s_analog.oven_temp_raw >= 0.05 && s_analog.oven_temp_raw <= 0.22) {
app_safety_pass_temp_normal(); app_safety_pass_temp_normal();
} }
if (s_analog.soc_temp >= 5.0 && s_analog.soc_temp <= 80.0) { if (s_analog.soc_temp >= 2.0 && s_analog.soc_temp <= 80.0) {
app_safety_pass_soc_temp_ok(); app_safety_pass_soc_temp_ok();
} }
} }
@ -288,6 +334,11 @@ float app_temp_read_oven()
return s_analog.oven_temp; return s_analog.oven_temp;
} }
float app_temp_read_oven_raw()
{
return s_analog.oven_temp_raw;
}
float app_temp_read_soc() float app_temp_read_soc()
{ {
return s_analog.soc_temp; return s_analog.soc_temp;

@ -7,6 +7,11 @@
void app_analog_init(); void app_analog_init();
float val_to_c(float val);
float c_to_val(float c);
void app_temp_set_calib(float a, float b);
/** /**
* Update temperature measurement. * Update temperature measurement.
* *
@ -20,6 +25,9 @@ void app_temp_sample();
/// The value is valid after calling app_temp_sample() /// The value is valid after calling app_temp_sample()
float app_temp_read_oven(); float app_temp_read_oven();
/// Get the raw ADC value (divider voltage)
float app_temp_read_oven_raw();
/// Read current SOC temperature (celsius) /// Read current SOC temperature (celsius)
/// The value is valid after calling app_temp_sample() /// The value is valid after calling app_temp_sample()
float app_temp_read_soc(); float app_temp_read_soc();

@ -655,6 +655,8 @@ static void fmtfp(char *buffer, size_t *currlen, size_t maxlen,
int zpadlen = 0; int zpadlen = 0;
int caps = 0; int caps = 0;
int index; int index;
int fzeropad = 0;
int fzerocnt = 0;
double intpart; double intpart;
double fracpart; double fracpart;
double temp; double temp;
@ -728,6 +730,13 @@ static void fmtfp(char *buffer, size_t *currlen, size_t maxlen,
/* Convert fractional part */ /* Convert fractional part */
if (fracpart) { if (fracpart) {
// leading zeros in the fractional part
fzeropad = 0;
fzerocnt = max - 1;
while (fracpart < POW10(fzerocnt)) {
fzeropad++;
fzerocnt--;
}
do { do {
temp = fracpart; temp = fracpart;
my_modf(fracpart * 0.1, &fracpart); my_modf(fracpart * 0.1, &fracpart);
@ -785,8 +794,19 @@ static void fmtfp(char *buffer, size_t *currlen, size_t maxlen,
if (max > 0) { if (max > 0) {
dopr_outch(buffer, currlen, maxlen, '.'); dopr_outch(buffer, currlen, maxlen, '.');
while (fplace > 0) if (zpadlen > fzeropad) {
zpadlen -= fzeropad;
} else {
zpadlen = 0;
}
while (fzeropad-- > 0) {
dopr_outch(buffer, currlen, maxlen, '0');
}
while (fplace > 0) {
dopr_outch(buffer, currlen, maxlen, fconvert[--fplace]); dopr_outch(buffer, currlen, maxlen, fconvert[--fplace]);
}
} }
while (zpadlen > 0) { while (zpadlen > 0) {

@ -98,24 +98,13 @@ static const font5x_bitmap_t PROGMEM font57_ascii[] = {
static const struct utf_glyph5x PROGMEM font57_extra[] = { static const struct utf_glyph5x PROGMEM font57_extra[] = {
{.utf={.symbol="<EFBFBD>"}, {{0x7f, 0x41, 0x41, 0x41, 0x7f}}}, {.utf={.symbol="<EFBFBD>"}, {{0x7f, 0x41, 0x41, 0x41, 0x7f}}},
{.utf={.symbol="×"}, {{0x22, 0x14, 0x08, 0x14, 0x22}}},
{.utf={.symbol=""}, {{0x08, 0x04, 0x3e, 0x04, 0x08}}},
{.utf={.symbol=""}, {{0x08, 0x10, 0x3e, 0x10, 0x08}}},
{.utf={.symbol=""}, {{0x08, 0x1c, 0x2a, 0x08, 0x08}}}, {.utf={.symbol=""}, {{0x08, 0x1c, 0x2a, 0x08, 0x08}}},
{.utf={.symbol=""}, {{0x08, 0x08, 0x2a, 0x1c, 0x08}}}, {.utf={.symbol=""}, {{0x08, 0x08, 0x2a, 0x1c, 0x08}}},
{.utf={.symbol=""}, {{0x1c, 0x22, 0x2e, 0x2a, 0x1c}}},
{.utf={.symbol=""}, {{0x63, 0x55, 0x4d, 0x55, 0x63}}},
{.utf={.symbol=""}, {{0x1c, 0x22, 0x2a, 0x22, 0x1c}}},
{.utf={.symbol=""}, {{0x10, 0x38, 0x54, 0x10, 0x1e}}},
{.utf={.symbol="🌡"}, {{0x20, 0x7e, 0x79, 0x7e, 0x2a}}},
{.utf={.symbol="°"}, {{0x00, 0x07, 0x05, 0x07, 0x00}}}, {.utf={.symbol="°"}, {{0x00, 0x07, 0x05, 0x07, 0x00}}},
{.utf={.symbol="μ"}, {{0x7c, 0x20, 0x20, 0x10, 0x3c}}},
{.utf={.symbol=""}, {{0x04, 0x4e, 0x55, 0x44, 0x38}}},
{.utf={.symbol=""}, {{0x7f, 0x3e, 0x1c, 0x08, 0x00}}},
{.utf={.symbol=""}, {{0x00, 0x08, 0x1c, 0x3e, 0x7f}}},
{.utf={.symbol="č"}, {{0x38, 0x45, 0x46, 0x45, 0x20}}}, {.utf={.symbol="č"}, {{0x38, 0x45, 0x46, 0x45, 0x20}}},
{.utf={.symbol="š"}, {{0x48, 0x55, 0x56, 0x55, 0x20}}}, {.utf={.symbol="š"}, {{0x48, 0x55, 0x56, 0x55, 0x20}}},
{.utf={.symbol="í"}, {{0x00, 0x44, 0x7d, 0x41, 0x00}}}, {.utf={.symbol="í"}, {{0x00, 0x44, 0x7d, 0x41, 0x00}}},
{.utf={.symbol="ž"}, {{0x44, 0x65, 0x56, 0x4d, 0x44}}}, {.utf={.symbol="ž"}, {{0x44, 0x65, 0x56, 0x4d, 0x44}}},
{.utf={.symbol="ď"}, {{0x38, 0x44, 0x45, 0x48, 0x7f}}},
{.utf={.symbol="»"}, {{0x22, 0x14, 0x2a, 0x14, 0x08}}}, {.utf={.symbol="»"}, {{0x22, 0x14, 0x2a, 0x14, 0x08}}},
}; };

@ -775,30 +775,30 @@ const char *font_extras[] = {
"# #", "# #",
"# #", "# #",
"#####", "#####",
// Extra 1 // // Extra 1
" ", // " ",
"# #", // "# #",
" # # ", // " # # ",
" # ", // " # ",
" # # ", // " # # ",
"# #", // "# #",
" ", // " ",
// Extra 2 // // Extra 2
" ", // " ",
" # ", // " # ",
" ### ", // " ### ",
"# # #", // "# # #",
" # ", // " # ",
" # ", // " # ",
" ", // " ",
// Extra 3 // // Extra 3
" ", // " ",
" # ", // " # ",
" # ", // " # ",
"# # #", // "# # #",
" ### ", // " ### ",
" # ", // " # ",
" ", // " ",
// Extra 4 // Extra 4
" ", " ",
" # ", " # ",
@ -815,46 +815,46 @@ const char *font_extras[] = {
" # ", " # ",
" # ", " # ",
" ", " ",
// Extra 6 // // Extra 6
" ", // " ",
" ### ", // " ### ",
"# # #", // "# # #",
"# ###", // "# ###",
"# #", // "# #",
" ### ", // " ### ",
" ", // " ",
// Extra 7 // // Extra 7
"#####", // "#####",
"# #", // "# #",
" ### ", // " ### ",
" # ", // " # ",
" # # ", // " # # ",
"# #", // "# #",
"#####", // "#####",
// Extra 8 // // Extra 8
" ", // " ",
" ### ", // " ### ",
"# #", // "# #",
"# # #", // "# # #",
"# #", // "# #",
" ### ", // " ### ",
" ", // " ",
// Extra 9 // // Extra 9
" ", // " ",
" #", // " #",
" # #", // " # #",
" # #", // " # #",
"#####", // "#####",
" # ", // " # ",
" # ", // " # ",
// Extra 10 // // Extra 10
" # ", // " # ",
" # ##", // " # ##",
" # # ", // " # # ",
" ####", // " ####",
" ### ", // " ### ",
"#####", // "#####",
" ### ", // " ### ",
// Extra 11 // Extra 11
" ### ", " ### ",
" # # ", " # # ",
@ -863,38 +863,38 @@ const char *font_extras[] = {
" ", " ",
" ", " ",
" ", " ",
// Extra 12 // // Extra 12
" ", // " ",
" ", // " ",
"# #", // "# #",
"# #", // "# #",
"# ##", // "# ##",
"### #", // "### #",
"# ", // "# ",
// Extra 13 // // Extra 13
" # ", // " # ",
" # ", // " # ",
"#### ", // "#### ",
" # #", // " # #",
" # #", // " # #",
" #", // " #",
" ### ", // " ### ",
// Extra 14 // // Extra 14
"# ", // "# ",
"## ", // "## ",
"### ", // "### ",
"#### ", // "#### ",
"### ", // "### ",
"## ", // "## ",
"# ", // "# ",
// Extra 15 // // Extra 15
" #", // " #",
" ##", // " ##",
" ###", // " ###",
" ####", // " ####",
" ###", // " ###",
" ##", // " ##",
" #", // " #",
// 99 "č" // 99 "č"
" x x ", " x x ",
" x ", " x ",
@ -927,6 +927,14 @@ const char *font_extras[] = {
" # ", " # ",
" # ", " # ",
"#####", "#####",
// 100 "d"
" x #",
" #",
" ## #",
"# ##",
"# #",
"# #",
" ####",
// » // »
" ", " ",
"x x ", "x x ",
@ -939,25 +947,26 @@ const char *font_extras[] = {
const char *font_extras_utf[] = { const char *font_extras_utf[] = {
"<EFBFBD>", "<EFBFBD>",
"×", // "×",
"", // "↑",
"", // "↓",
"", "",
"", "",
"", // clock // "⏱", // clock
"", // "⌛",
"", // "☸",
"", // "⏎",
"🌡", // "🌡",
"°", "°",
"μ", // "μ",
"", // back // "⎌", // back
"", // "▶",
"", // "◀",
"č", "č",
"š", "š",
"í", "í",
"ž", "ž",
"ď",
"»", "»",
}; };

@ -51,6 +51,7 @@ Core/Src/Gui/app_gui.c \
Core/Src/Gui/screen_menu.c \ Core/Src/Gui/screen_menu.c \
Core/Src/Gui/screen_home.c \ Core/Src/Gui/screen_home.c \
Core/Src/Gui/screen_manual.c \ Core/Src/Gui/screen_manual.c \
Core/Src/Gui/screen_calibration.c \
Core/Src/Gui/screen_manual_menu.c \ Core/Src/Gui/screen_manual_menu.c \
Core/Src/app_temp.c \ Core/Src/app_temp.c \
Core/Src/app_knob.c \ Core/Src/app_knob.c \

Loading…
Cancel
Save