From 1ea4cd124d1c2dca3e394e33187b3d459a81a66b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sun, 7 May 2023 00:53:22 +0200 Subject: [PATCH] add rudimentary calib param tuning screen (TODO negative offset support!) --- Core/Src/Gui/app_gui.c | 3 + Core/Src/Gui/app_gui.h | 1 + Core/Src/Gui/screen_calib_edit.c | 217 ++++++++++++++++++++++++++++++ Core/Src/Gui/screen_calibration.c | 2 +- Core/Src/Gui/screen_home.c | 7 +- Core/Src/Gui/screen_pid_tuning.c | 9 +- Core/Src/app_heater.c | 4 + Core/Src/app_heater.h | 2 + Core/Src/app_safety.c | 2 +- Core/Src/app_temp.c | 200 ++++++++++++++++++++++++--- Core/Src/app_temp.h | 5 +- Core/Src/ee_addresses.h | 3 + Core/Src/freertos.c | 4 +- Makefile | 1 + 14 files changed, 432 insertions(+), 28 deletions(-) create mode 100644 Core/Src/Gui/screen_calib_edit.c diff --git a/Core/Src/Gui/app_gui.c b/Core/Src/Gui/app_gui.c index 396679f..64fd7f9 100644 --- a/Core/Src/Gui/app_gui.c +++ b/Core/Src/Gui/app_gui.c @@ -146,6 +146,9 @@ static void draw_common_overlay() { if (app_heater_get_state()) { fb_frame(0, 0, FBW, 11, 1, 1); + + float perc = app_heater_get_percent(); + fb_hline(0, 12, (fbpos_t) ((float)FBW * (perc / 100.0f)), 1); } } diff --git a/Core/Src/Gui/app_gui.h b/Core/Src/Gui/app_gui.h index e77bb49..cb80f38 100644 --- a/Core/Src/Gui/app_gui.h +++ b/Core/Src/Gui/app_gui.h @@ -43,6 +43,7 @@ void screen_manual(GuiEvent event); void screen_manual_menu(GuiEvent event); void screen_calibration(GuiEvent event); void screen_pid_tuning(GuiEvent event); +void screen_calib_edit(GuiEvent event); struct State { /// Latest oven temp readout diff --git a/Core/Src/Gui/screen_calib_edit.c b/Core/Src/Gui/screen_calib_edit.c new file mode 100644 index 0000000..cf5953f --- /dev/null +++ b/Core/Src/Gui/screen_calib_edit.c @@ -0,0 +1,217 @@ +// +// Created by MightyPork on 2023/05/06 +// + +#include +#include "app_gui.h" +#include "app_heater.h" +#include "ufb/fb_text.h" +#include "snprintf.h" +#include "app_temp.h" + +static void apply_calib(bool temporary); + +static struct calib_tuning_state { + uint8_t digits[6 * 4]; // a b l r + + int cursor; // the digit under cursor (range 0-17) + bool digit_editing; // true if we are editing a digit +} s_tuning; + +static void draw_digit_row(int row) { + fbpos_t x = (FBW - (6 * 7 - 1)) / 2; + fbpos_t y = 30 + row * 10; + + char buf2[2] = {}; + + int numofs = row * 6; + + const char * labels[4] = {"a","b","L", "R"}; + const int decimal_pos[4] = {2, 0, 3, 3}; + fb_text(FBW - 4, y + 2, labels[row], FONT_3X5, 1); + + bool significant = row == 1; + for (int i = 0; i < 6; i++) { + if (i >= decimal_pos[row]) significant = 1; + + int val = s_tuning.digits[numofs + i]; + if (val != 0) { + significant = true; + } + + buf2[0] = '0' + val; + + if (s_tuning.cursor == numofs + i) { + if (s_tuning.digit_editing) { + fb_rect(x-1, y-1, 5+2, 7+2, 1); + fb_text(x, y, buf2, 0, 0); + } else { + fb_hline(x, y + 8, 5, 1); + if (significant) fb_text(x, y, buf2, 0, 1); + } + } else { + if (significant) fb_text(x, y, buf2, 0, 1); + } + + x += 6; + if (i == decimal_pos[row]) { + fb_text(x, y, ".", 0, 1); + x += 6; + } + } +} + +void screen_calib_edit(GuiEvent event) +{ + float A, B, L, R; + uint32_t Ai, Bi, Li, Ri; + + if (event == GUI_EVENT_SCREEN_INIT) { + memset(&s_tuning, 0, sizeof(s_tuning)); + + app_temp_get_calib(&A, &B, &L, &R); + app_temp_backup_calib(); + + Ai = (uint32_t)(A * 1000.f); + Bi = (uint32_t)(B * 100000.f); + Li = (uint32_t)(L * 100.f); + Ri = (uint32_t)(R * 100.f); + + char buf[25]; + SNPRINTF(buf, 25, "%06lu%06lu%06lu%06lu", Ai, Bi, Li, Ri); + for(int i = 0; i < 6*4; i++) { + s_tuning.digits[i] = buf[i] - '0'; + } + } + + switch (event) { + case GUI_EVENT_PAINT: { + fb_text(FBW / 2, 16, "Kalibrace", TEXT_CENTER, 1); + + draw_digit_row(0); + draw_digit_row(1); + draw_digit_row(2); + draw_digit_row(3); + + //68 +#define BTNS_TOP 72 + + if (s_tuning.cursor == 24) { + fb_rect(0, BTNS_TOP, FBW, 9, 1); + } + fb_text(FBW / 2, BTNS_TOP + 1, "Zrušit", TEXT_CENTER, s_tuning.cursor != 24); + + if (s_tuning.cursor == 25) { + fb_rect(0, BTNS_TOP + 9, FBW, 9, 1); + } + fb_text(FBW / 2, BTNS_TOP + 1 + 9, "Uložit", TEXT_CENTER, s_tuning.cursor != 25); + + fb_text(2, FBH - 8 * (1 + (s_tuning.cursor < 24)), s_tuning.digit_editing ? "←→Hodnota" : "←→Kurzor", 0, 1); + if (s_tuning.cursor < 24) { + fb_text(2, FBH - 8 * 1, s_tuning.digit_editing ? "> Potvrdit" : "> Změnit", 0, 1); + } + return; + } + + case GUI_EVENT_KNOB_PLUS: { + input_sound_effect(); + request_paint(); + + if (s_tuning.digit_editing) { + if (s_tuning.digits[s_tuning.cursor] == 9) { + s_tuning.digits[s_tuning.cursor] = 0; + } else { + s_tuning.digits[s_tuning.cursor]++; + } + apply_calib(false); + } else { + // 24 - cancel + // 25 - save + s_tuning.cursor++; + if (s_tuning.cursor >= 24) { + s_tuning.digit_editing = false; + } + if (s_tuning.cursor > 25) { + s_tuning.cursor = 0; + } + } + break; + } + + case GUI_EVENT_KNOB_MINUS: { + input_sound_effect(); + request_paint(); + + if (s_tuning.digit_editing) { + if (s_tuning.digits[s_tuning.cursor] == 0) { + s_tuning.digits[s_tuning.cursor] = 9; + } else { + s_tuning.digits[s_tuning.cursor]--; + } + apply_calib(false); + } else { + // 24 - cancel + // 25 - save + if (s_tuning.cursor == 0) { + s_tuning.cursor = 25; + s_tuning.digit_editing = false; + } else { + s_tuning.cursor--; + } + } + break; + } + + case GUI_EVENT_KNOB_RELEASE: { + if (s_tuning.cursor < 24) { + s_tuning.digit_editing = !s_tuning.digit_editing; + } else if (s_tuning.cursor == 24) { + // cancel + app_temp_restore_calib(); + switch_screen(screen_home, true); + } else if (s_tuning.cursor == 25) { + // save + apply_calib(true); + switch_screen(screen_home, true); + } + break; + } + } +} + +static void apply_calib(bool temporary) { + float A, B, L, R; + uint32_t Ai, Bi, Li, Ri; + + Ai = Bi = Li = Ri = 0; + + for(int i = 0; i < 6; i++) { + Ai *= 10; + Ai += s_tuning.digits[i]; + } + for(int i = 6; i < 12; i++) { + Bi *= 10; + Bi += s_tuning.digits[i]; + } + for(int i = 12; i < 18; i++) { + Li *= 10; + Li += s_tuning.digits[i]; + } + for(int i = 18; i < 24; i++) { + Ri *= 10; + Ri += s_tuning.digits[i]; + } + + A = ((float) Ai) / 1000.f; + B = ((float) Bi) / 100000.f; + L = ((float) Li) / 100.f; + R = ((float) Ri) / 100.f; + + if (temporary) { + app_temp_set_calib_temporary(A, B); + app_temp_set_calib_temporary_r(L, R); + } else { + app_temp_set_calib_persistent(A, B); + app_temp_set_calib_persistent_r(L, R); + } +} diff --git a/Core/Src/Gui/screen_calibration.c b/Core/Src/Gui/screen_calibration.c index 3cb45e3..f1efb8c 100644 --- a/Core/Src/Gui/screen_calibration.c +++ b/Core/Src/Gui/screen_calibration.c @@ -215,7 +215,7 @@ void screen_calibration(GuiEvent event) float a = (corrected1 - corrected2) / (s_calib.sample1 - s_calib.sample2); float b = corrected1 - a * s_calib.sample1; - app_temp_set_persistent_calib(a, b); + app_temp_set_calib_persistent(a, b); } else if (s_calib.phase == PH_SAMPLE1) { app_heater_set_target(100); app_heater_enable(true); diff --git a/Core/Src/Gui/screen_home.c b/Core/Src/Gui/screen_home.c index b13e120..c5ad32b 100644 --- a/Core/Src/Gui/screen_home.c +++ b/Core/Src/Gui/screen_home.c @@ -12,8 +12,8 @@ static const char* main_menu_opts[] = { "Ruční režim", "Ladění PID", - "Kalibrace", -// "Diagnostika", + "Auto kalibrace", + "Ruční kalibrace", NULL }; @@ -28,6 +28,9 @@ static void main_menu_cb(int opt) { case 2: switch_screen(screen_calibration, true); break; + case 3: + switch_screen(screen_calib_edit, true); + break; } } diff --git a/Core/Src/Gui/screen_pid_tuning.c b/Core/Src/Gui/screen_pid_tuning.c index 4be9354..45abc57 100644 --- a/Core/Src/Gui/screen_pid_tuning.c +++ b/Core/Src/Gui/screen_pid_tuning.c @@ -2,17 +2,13 @@ // Created by MightyPork on 2023/04/09. // - -#include #include #include "app_gui.h" #include "app_heater.h" -#include "screen_menu.h" #include "ufb/fb_text.h" #include "snprintf.h" -#include "FreeRTOS.h" -struct pid_tuning_state { +static struct calib_tuning_state { uint8_t digits[6 * 3]; // uint32_t Kp, Ki, Kd; // these are the float x 1000 @@ -29,6 +25,9 @@ static void draw_digit_row(int row) { int numofs = row * 6; + const char * labels[3] = {"P","I","D"}; + fb_text(FBW - 4, y + 2, labels[row], FONT_3X5, 1); + bool significant = false; for (int i = 0; i < 6; i++) { if (i >= 2) significant = 1; diff --git a/Core/Src/app_heater.c b/Core/Src/app_heater.c index 03e0616..09fb1ea 100644 --- a/Core/Src/app_heater.c +++ b/Core/Src/app_heater.c @@ -140,6 +140,10 @@ bool app_heater_get_state() { return state.pid.ctlMode == PID_AUTOMATIC; } +float app_heater_get_percent() { + return state.pid.Output; +} + float app_heater_get_target() { return state.pid.Setpoint; } diff --git a/Core/Src/app_heater.h b/Core/Src/app_heater.h index b0a16bf..f41e8ff 100644 --- a/Core/Src/app_heater.h +++ b/Core/Src/app_heater.h @@ -47,4 +47,6 @@ bool app_heater_get_state(); float app_heater_get_target(); +float app_heater_get_percent(); + #endif //BLUEPILLTROUBA_APP_HEATER_H diff --git a/Core/Src/app_safety.c b/Core/Src/app_safety.c index 0a79cc3..c47bc23 100644 --- a/Core/Src/app_safety.c +++ b/Core/Src/app_safety.c @@ -46,7 +46,7 @@ void app_safety_pass_soc_temp_ok() { } void app_safety_poll() { - PRINTF("\r\n*** HB FLAGS %x ***\r\n", heartbeat_flags); + //PRINTF("\r\n*** HB FLAGS %x ***\r\n", heartbeat_flags); if ((heartbeat_flags & HB_FLAG_ALL) == HB_FLAG_ALL) { LL_IWDG_ReloadCounter(IWDG); LL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin); diff --git a/Core/Src/app_temp.c b/Core/Src/app_temp.c index 049c6df..eb8a324 100644 --- a/Core/Src/app_temp.c +++ b/Core/Src/app_temp.c @@ -3,12 +3,8 @@ */ #include "main.h" -#include -#include #include -#include #include "app_temp.h" -#include "adc.h" #include "snprintf.h" #include "app_safety.h" #include "eeprom_emul_types.h" @@ -21,22 +17,32 @@ static volatile uint16_t adc_values[4]; const float V_REFINT = 1.23f; -#define AVERAGEBUF_DEPTH 16 -#define OVENTEMP_HISTORY_DEPTH 20 +#define AVERAGEBUF_DEPTH 4 +#define OVENTEMP_HISTORY_DEPTH 4 static struct App { float oven_temp; float oven_temp_raw; float soc_temp; + float cal_a; float cal_b; + float reference_resistor; + float lead_resistance; + + // original values are copied here during calibration, so they can be reverted float saved_cal_a; float saved_cal_b; + float saved_lead_resistance; + float saved_reference_resistor; + float oventemp_history[OVENTEMP_HISTORY_DEPTH]; // raw temp uint16_t adc_averagebuf[AVERAGEBUF_DEPTH * 4]; uint8_t averagebuf_ptr; uint8_t oventemp_history_ptr; } s_analog = { + .reference_resistor = 1000.0f, + .lead_resistance = 0.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, @@ -46,6 +52,8 @@ static struct App { #define TSENSE_T_STEP 5.0f #define TSENSE_T_MIN 0.0f #define TSENSE_T_MAX 500.0f + +#if 0 // Ratios static const float TSENSE_LOOKUP[TSENSE_LOOKUP_LEN] = { 0.0909090909090909f, 0.0925200328471449f, @@ -149,18 +157,136 @@ static const float TSENSE_LOOKUP[TSENSE_LOOKUP_LEN] = { 0.218329057984116f, 0.219346163379138f, }; +#else +static const float PT100_LOOKUP[TSENSE_LOOKUP_LEN] = { + 100.f, // 0 C + 101.9527f, + 103.9025f, + 105.8495f, + 107.7935f, + 109.7347f, + 111.6729f, + 113.6083f, + 115.5408f, + 117.4704f, + 119.3971f, + 121.321f, + 123.2419f, + 125.16f, + 127.0751f, + 128.9874f, + 130.8968f, + 132.8033f, + 134.7069f, + 136.6077f, + 138.5055f, + 140.4005f, + 142.2925f, + 144.1817f, + 146.068f, + 147.9514f, + 149.8319f, + 151.7096f, + 153.5843f, + 155.4562f, + 157.3251f, + 159.1912f, + 161.0544f, + 162.9147f, + 164.7721f, + 166.6267f, + 168.4783f, + 170.3271f, + 172.1729f, + 174.0159f, + 175.856f, + 177.6932f, + 179.5275f, + 181.359f, + 183.1875f, + 185.0132f, + 186.8359f, + 188.6558f, + 190.4728f, + 192.2869f, + 194.0981f, + 195.9065f, + 197.7119f, + 199.5145f, + 201.3141f, + 203.1109f, + 204.9048f, + 206.6958f, + 208.4839f, + 210.2692f, + 212.0515f, + 213.831f, + 215.6075f, + 217.3812f, + 219.152f, + 220.9199f, + 222.6849f, + 224.4471f, + 226.2063f, + 227.9627f, + 229.7161f, + 231.4667f, + 233.2144f, + 234.9592f, + 236.7011f, + 238.4402f, + 240.1763f, + 241.9096f, + 243.6399f, + 245.3674f, + 247.092f, + 248.8137f, + 250.5325f, + 252.2485f, + 253.9615f, + 255.6717f, + 257.3789f, + 259.0833f, + 260.7848f, + 262.4834f, + 264.1791f, + 265.872f, + 267.5619f, + 269.249f, + 270.9331f, + 272.6144f, + 274.2928f, + 275.9683f, + 277.6409f, + 279.3107f, + 280.9775f, // 500 C +}; +#endif + +void app_temp_get_calib(float *a, float *b, float *l, float *r) { + *a = s_analog.cal_a; + *b = s_analog.cal_b; + *l = s_analog.lead_resistance; + *r = s_analog.reference_resistor; +} /// 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) { + 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; } + if (s_analog.reference_resistor == 0.0f) { + s_analog.reference_resistor = 1000.0f; + } + // 0 lead_r is a lie, but ok +// if (s_analog.lead_resistance == 0.0f) { +// s_analog.lead_resistance = 0.0f; +// } } /// Set and persist calibration constants -void app_temp_set_persistent_calib(float a, float b) { +void app_temp_set_calib_persistent(float a, float b) { s_analog.cal_a = a; s_analog.cal_b = b; correct_invalid_calib(); @@ -179,9 +305,30 @@ void app_temp_set_persistent_calib(float a, float b) { } } +void app_temp_set_calib_persistent_r(float lead, float reference) { + s_analog.lead_resistance = lead; + s_analog.reference_resistor = reference; + correct_invalid_calib(); + + EE_Status st = EE_WriteVariable32bits(EE_ADDR_LEAD_R, ((x32_t) { .f = lead }).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_REF_R, ((x32_t) { .f = reference }).u); + if (st == EE_CLEANUP_REQUIRED) { + EE_CleanUp(); + } else if (st != EE_OK) { + PRINTF("EE write err %d!\r\n", st); + } +} + void app_temp_backup_calib() { s_analog.saved_cal_a = s_analog.cal_a; s_analog.saved_cal_b = s_analog.cal_b; + s_analog.saved_lead_resistance = s_analog.lead_resistance; + s_analog.saved_reference_resistor = s_analog.reference_resistor; } void app_temp_set_calib_temporary(float a, float b) { @@ -189,9 +336,16 @@ void app_temp_set_calib_temporary(float a, float b) { s_analog.cal_b = b; } +void app_temp_set_calib_temporary_r(float l, float r) { + s_analog.lead_resistance = l; + s_analog.reference_resistor = r; +} + void app_temp_restore_calib() { s_analog.cal_a = s_analog.saved_cal_a; s_analog.cal_b = s_analog.saved_cal_b; + s_analog.lead_resistance = s_analog.saved_lead_resistance; + s_analog.reference_resistor = s_analog.saved_reference_resistor; } void app_analog_init() @@ -206,6 +360,14 @@ void app_analog_init() s_analog.cal_b = ((x32_t) { .u = c }).f; PRINTF("ADC calib b read from EE: %f\r\n", s_analog.cal_b); } + if (EE_OK == EE_ReadVariable32bits(EE_ADDR_LEAD_R, &c)) { + s_analog.lead_resistance = ((x32_t) { .u = c }).f; + PRINTF("ADC calib R_LEAD read from EE: %f\r\n", s_analog.lead_resistance); + } + if (EE_OK == EE_ReadVariable32bits(EE_ADDR_REF_R, &c)) { + s_analog.reference_resistor = ((x32_t) { .u = c }).f; + PRINTF("ADC calib R_REF read from EE: %f\r\n", s_analog.reference_resistor); + } correct_invalid_calib(); @@ -234,15 +396,19 @@ void app_analog_init() LL_TIM_EnableCounter(TIM1); } -float val_to_c(float val) +float val_to_c(float x) { + // val is the ratio + + float pt100r = (x * s_analog.lead_resistance + x * s_analog.reference_resistor - s_analog.lead_resistance) / (1 - x); + // TODO use binary search.. lol for (int i = 1; i < TSENSE_LOOKUP_LEN; i++) { - float cur = TSENSE_LOOKUP[i]; - if (cur >= val) { - float prev = TSENSE_LOOKUP[i - 1]; + float cur = PT100_LOOKUP[i]; + if (cur >= pt100r) { + float prev = PT100_LOOKUP[i - 1]; - float ratio = (val - prev) / (cur - prev); + float ratio = (pt100r - prev) / (cur - prev); return TSENSE_T_MIN + ((float) i + ratio) * TSENSE_T_STEP; } } @@ -262,7 +428,7 @@ float c_to_val(float cf) 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; + return PT100_LOOKUP[lower] + (PT100_LOOKUP[upper] - PT100_LOOKUP[lower]) * ratio; } void app_temp_sample() @@ -329,8 +495,10 @@ void app_temp_sample() } float y = s_analog.cal_a * sum + s_analog.cal_b; + + //float y = sum; float actual_temp = val_to_c(y); - //PRINTF("T raw %f -> comp %f, temp %f°C\r\n", sum, y, actual_temp); + PRINTF("T Raw[%f] * A[%f] + B[%f]-> %f, temp %f°C\r\n", sum, s_analog.cal_a, s_analog.cal_b, y, actual_temp); s_analog.oven_temp = actual_temp; s_analog.oven_temp_raw = sum; diff --git a/Core/Src/app_temp.h b/Core/Src/app_temp.h index a11554d..17b5702 100644 --- a/Core/Src/app_temp.h +++ b/Core/Src/app_temp.h @@ -10,11 +10,14 @@ void app_analog_init(); float val_to_c(float val); float c_to_val(float c); -void app_temp_set_persistent_calib(float a, float b); +void app_temp_set_calib_persistent(float a, float b); +void app_temp_set_calib_persistent_r(float lead, float reference); void app_temp_backup_calib(); void app_temp_set_calib_temporary(float a, float b); +void app_temp_set_calib_temporary_r(float lead, float reference); +void app_temp_get_calib(float *a, float *b, float *l, float *r); void app_temp_restore_calib(); diff --git a/Core/Src/ee_addresses.h b/Core/Src/ee_addresses.h index b7175c4..b3033f5 100644 --- a/Core/Src/ee_addresses.h +++ b/Core/Src/ee_addresses.h @@ -17,6 +17,9 @@ enum EEAddresses { EE_ADDR_PID_I = 4, // 100.0 EE_ADDR_PID_D = 5, + + EE_ADDR_LEAD_R = 6, + EE_ADDR_REF_R = 7, }; #endif //TOASTER_OVEN_BLUEPILL_EE_ADDRESSES_H diff --git a/Core/Src/freertos.c b/Core/Src/freertos.c index f0608ed..1d7dd7d 100644 --- a/Core/Src/freertos.c +++ b/Core/Src/freertos.c @@ -66,7 +66,7 @@ const osThreadAttr_t mainTsk_attributes = { }; /* Definitions for heaterTsk */ osThreadId_t heaterTskHandle; -uint32_t heaterTskBuffer[ 256 ]; +uint32_t heaterTskBuffer[ 300 ]; osStaticThreadDef_t heaterTskControlBlock; const osThreadAttr_t heaterTsk_attributes = { .name = "heaterTsk", @@ -78,7 +78,7 @@ const osThreadAttr_t heaterTsk_attributes = { }; /* Definitions for guiTsk */ osThreadId_t guiTskHandle; -uint32_t guiTskBuffer[ 256 ]; +uint32_t guiTskBuffer[ 300 ]; osStaticThreadDef_t guiTskControlBlock; const osThreadAttr_t guiTsk_attributes = { .name = "guiTsk", diff --git a/Makefile b/Makefile index 99ff54a..e913731 100644 --- a/Makefile +++ b/Makefile @@ -54,6 +54,7 @@ Core/Src/Gui/screen_manual.c \ Core/Src/Gui/screen_calibration.c \ Core/Src/Gui/screen_manual_menu.c \ Core/Src/Gui/screen_pid_tuning.c \ +Core/Src/Gui/screen_calib_edit.c \ Core/Src/app_temp.c \ Core/Src/app_knob.c \ Core/Src/app_buzzer.c \