diff --git a/Core/Src/app_gui.c b/Core/Src/app_gui.c index b4d9229..6cab52a 100644 --- a/Core/Src/app_gui.c +++ b/Core/Src/app_gui.c @@ -15,15 +15,55 @@ #define MAX_TEMP 400 +typedef enum GuiScreen { + SCREEN_HOME, + SCREEN_MANUAL, + SCREEN_MANUAL_MENU, +} GuiScreen; + static struct State { float oven_temp; float soc_temp; int set_temp; int set_temp_wheel; bool heater_enabled; + bool pushed; + uint32_t push_tick; + // true if the button is still held since init (i.e. the push action should not work as "enter") + bool initial_pushed; + + GuiScreen screen; + int menu_pos; + int menu_len; } s_app = {}; -static void calc_set_temp() { +/** Get push time (while held) */ +static uint32_t push_time() { + return s_app.pushed ? (xTaskGetTickCount() - s_app.push_tick) : 0; +} + +typedef void (*screen_t)(GuiEvent event); + +static void screen_home(GuiEvent event); + +static void screen_manual(GuiEvent event); + +static void screen_manual_menu(GuiEvent event); + +static screen_t screens[] = { + [SCREEN_HOME] = screen_home, + [SCREEN_MANUAL] = screen_manual, + [SCREEN_MANUAL_MENU] = screen_manual_menu, +}; + +static void draw_common_overlay(); + +static void input_sound_effect(GuiEvent event); + +static void switch_screen(GuiScreen screen, bool init); + +static void calc_set_temp() +{ int clamped = s_app.set_temp_wheel; if (clamped < 0) { clamped = 0; @@ -36,58 +76,230 @@ static void calc_set_temp() { app_heater_set_target((float) s_app.set_temp); } -void app_task_gui(void *argument) { +char tmp[100]; + +void app_task_gui(void *argument) +{ // Wait until inited ulTaskNotifyTake(pdTRUE, portMAX_DELAY); PUTS("GUI task starts\r\n"); - char tmp[100]; - while (1) { s_app.oven_temp = app_temp_read_oven(); s_app.soc_temp = app_temp_read_soc(); uint32_t message = GUI_EVENT_NONE; - osMessageQueueGet(guiEventQueHandle, &message, NULL, pdMS_TO_TICKS(1000)); + osMessageQueueGet(guiEventQueHandle, &message, NULL, pdMS_TO_TICKS(50)); switch (message) { - case GUI_NOTIFY_KNOB_PLUS: - s_app.set_temp_wheel++; - calc_set_temp(); - break; - case GUI_NOTIFY_KNOB_MINUS: - s_app.set_temp_wheel--; - calc_set_temp(); + case GUI_EVENT_KNOB_PRESS: + s_app.pushed = true; + s_app.push_tick = xTaskGetTickCount(); break; - case GUI_NOTIFY_KNOB_PRESS: - break; - case GUI_NOTIFY_KNOB_RELEASE: - s_app.heater_enabled ^= 1; - app_heater_enable(s_app.heater_enabled); - app_buzzer_beep(); + + case GUI_EVENT_KNOB_RELEASE: + s_app.pushed = false; break; + default: break; } - fb_clear(); + if (s_app.initial_pushed && message == GUI_EVENT_KNOB_RELEASE) { + s_app.initial_pushed = false; + } else { + fb_clear(); + SPRINTF(tmp, "%d", s_app.screen); + fb_text(3, 80, tmp, FONT_5X7, 1); + draw_common_overlay(); - SPRINTF(tmp, "T=%.1f°C", s_app.oven_temp); - fb_text(3, 3, tmp, FONT_5X7, 1); + screens[s_app.screen](message); - SPRINTF(tmp, "Cil=%d°C", s_app.set_temp); - fb_text(3, 11, tmp, FONT_5X7, 1); + fb_blit(); + } + } +} - SPRINTF(tmp, "Stav=%s", s_app.heater_enabled ? "ZAP" : "VYP"); - fb_text(3, 19, tmp, FONT_5X7, 1); +static void switch_screen(GuiScreen screen, bool init) { + s_app.initial_pushed = s_app.pushed; + s_app.screen = screen; - SPRINTF(tmp, "Tsoc=%.1f°C", s_app.soc_temp); - fb_text(3, 27, tmp, FONT_5X7, 1); + if (init) { + screens[screen](GUI_EVENT_SCREEN_INIT); + } +} - if (s_app.heater_enabled) { - fb_frame(0, 0, FBW, FBH, 2, 1); - } +static void draw_common_overlay() +{ + SPRINTF(tmp, "%3.1f°C →%3d°C", s_app.oven_temp, s_app.set_temp); + fb_text(3, 3, tmp, FONT_3X5, 1); +// +// SPRINTF(tmp, "Tsoc=%.1f°C", s_app.soc_temp); +// fb_text(3, 19, tmp, FONT_5X7, 1); + + if (s_app.heater_enabled) { + fb_frame(0, 0, FBW, FBH, 2, 1); + } +} + + +static void input_sound_effect(GuiEvent event) +{ + switch (event) { + case GUI_EVENT_KNOB_PLUS: + case GUI_EVENT_KNOB_MINUS: + case GUI_EVENT_KNOB_RELEASE: + app_buzzer_beep(); + break; + + default: + break; + } +} + +static void screen_home(GuiEvent event) +{ + if (event == GUI_EVENT_SCREEN_INIT) { + s_app.menu_pos = 0; + s_app.menu_len = 1; + // Disable heater + app_heater_enable(false); + return; + } + + input_sound_effect(event); + + // Menu with one item, lol + + SPRINTF(tmp, "Manual mode", s_app.soc_temp); + if (s_app.menu_pos == 0) { + fb_rect(3, 27, 64, 8, 1); + } + fb_text(3, 27, tmp, FONT_5X7, s_app.menu_pos != 0); + + switch (event) { + case GUI_EVENT_KNOB_RELEASE: + switch (s_app.menu_pos) { + case 0: + switch_screen(SCREEN_MANUAL, true); + break; + } + break; + + case GUI_EVENT_KNOB_PLUS: + if (s_app.menu_pos < s_app.menu_len - 1) { + s_app.menu_pos++; + } + break; + + case GUI_EVENT_KNOB_MINUS: + if (s_app.menu_pos > 0) { + s_app.menu_pos--; + } + break; + + default: + break; + } +} + +static void screen_manual(GuiEvent event) +{ + if (event == GUI_EVENT_SCREEN_INIT) { + s_app.set_temp_wheel = 0; + return; + } + + // menu activated by long push + if (push_time() >= pdMS_TO_TICKS(500)) { + switch_screen(SCREEN_MANUAL_MENU, true); + return; + } + + input_sound_effect(event); + + switch (event) { + case GUI_EVENT_KNOB_RELEASE: + if (s_app.initial_pushed) { + s_app.initial_pushed = false; + break; + } + s_app.heater_enabled ^= 1; + app_heater_enable(s_app.heater_enabled); + break; + + case GUI_EVENT_KNOB_PLUS: + s_app.set_temp_wheel++; + calc_set_temp(); + break; + + case GUI_EVENT_KNOB_MINUS: + s_app.set_temp_wheel--; + calc_set_temp(); + break; + + default: + break; + } +} + +static void screen_manual_menu(GuiEvent event) +{ + if (event == GUI_EVENT_SCREEN_INIT) { + s_app.menu_pos = 0; + s_app.menu_len = 2; + return; + } + + input_sound_effect(event); + + SPRINTF(tmp, "Close menu", s_app.soc_temp); + if (s_app.menu_pos == 0) { + fb_rect(3, 27, 64, 8, 1); + } + fb_text(3, 27, tmp, FONT_5X7, s_app.menu_pos != 0); + + SPRINTF(tmp, "Exit manual", s_app.soc_temp); + if (s_app.menu_pos == 1) { + fb_rect(3, 27 + 8, 64, 8, 1); + } + fb_text(3, 27 + 8, tmp, FONT_5X7, s_app.menu_pos != 1); + + switch (event) { + // the button is held! release is what activates the button + case GUI_EVENT_KNOB_RELEASE: + if (s_app.initial_pushed) { + s_app.initial_pushed = false; + } + break; + + case GUI_EVENT_KNOB_PRESS: + switch (s_app.menu_pos) { + case 0: + // Close menu + switch_screen(SCREEN_MANUAL, false); + break; + + case 1: + // Close menu + switch_screen(SCREEN_HOME, true); + break; + } + break; + + case GUI_EVENT_KNOB_PLUS: + if (s_app.menu_pos < s_app.menu_len - 1) { + s_app.menu_pos++; + } + break; + + case GUI_EVENT_KNOB_MINUS: + if (s_app.menu_pos > 0) { + s_app.menu_pos--; + } + break; - fb_blit(); + default: + break; } } diff --git a/Core/Src/app_gui.h b/Core/Src/app_gui.h index 287a88c..ca07142 100644 --- a/Core/Src/app_gui.h +++ b/Core/Src/app_gui.h @@ -13,13 +13,22 @@ extern osMessageQueueId_t guiEventQueHandle; void app_task_gui(void *argument); // sent through the notify queue -enum GuiNotify { +typedef enum GuiEvent { + /// No event, zero; This is a default value. GUI_EVENT_NONE = 0, - GUI_NOTIFY_KNOB_PLUS, - GUI_NOTIFY_KNOB_MINUS, - GUI_NOTIFY_KNOB_PRESS, - GUI_NOTIFY_KNOB_RELEASE, - GUI_NOTIFY_TEMP_CHANGE, -}; + /// Used as the argument when initing a screen + GUI_EVENT_SCREEN_INIT = 1, + /// Knob rotate CW + GUI_EVENT_KNOB_PLUS, + /// Knob rotate CCW + GUI_EVENT_KNOB_MINUS, + /// Knob pressed + GUI_EVENT_KNOB_PRESS, + /// Knob released + GUI_EVENT_KNOB_RELEASE, + /// Temperature readings changed. + /// Not really an input event, but it should trigger a redraw + GUI_EVENT_TEMP_CHANGE, +} GuiEvent; #endif //BLUEPILLTROUBA_APP_GUI_H diff --git a/Core/Src/app_heater.c b/Core/Src/app_heater.c index 2e3b7c3..49625cc 100644 --- a/Core/Src/app_heater.c +++ b/Core/Src/app_heater.c @@ -122,7 +122,7 @@ void app_task_heater(void *argument) state.oven_temp = app_temp_read_oven(); state.soc_temp = app_temp_read_soc(); - enum GuiNotify ev = GUI_NOTIFY_TEMP_CHANGE; + enum GuiEvent ev = GUI_EVENT_TEMP_CHANGE; xQueueSend(guiEventQueHandle, &ev, pdMS_TO_TICKS(100)); heaterEnterCritical(); diff --git a/Core/Src/app_knob.c b/Core/Src/app_knob.c index 8834631..5311145 100644 --- a/Core/Src/app_knob.c +++ b/Core/Src/app_knob.c @@ -48,13 +48,13 @@ void app_knob_turn_isr() while (wheel_change > 0) { wheel_change--; - enum GuiNotify ev = GUI_NOTIFY_KNOB_PLUS; + enum GuiEvent ev = GUI_EVENT_KNOB_PLUS; xQueueSendFromISR(guiEventQueHandle, &ev, &yield); } while (wheel_change < 0) { wheel_change++; - enum GuiNotify ev = GUI_NOTIFY_KNOB_MINUS; + enum GuiEvent ev = GUI_EVENT_KNOB_MINUS; xQueueSendFromISR(guiEventQueHandle, &ev, &yield); } @@ -87,6 +87,6 @@ void app_knob_push_isr(bool push) void app_push_debounce(void *argument) { bool push = (bool) argument; - enum GuiNotify ev = push ? GUI_NOTIFY_KNOB_PRESS : GUI_NOTIFY_KNOB_RELEASE; + enum GuiEvent ev = push ? GUI_EVENT_KNOB_PRESS : GUI_EVENT_KNOB_RELEASE; xQueueSend(guiEventQueHandle, &ev, pdMS_TO_TICKS(100)); } diff --git a/Lib/ufb/Src/font_35.inc.c b/Lib/ufb/Src/font_35.inc.c index 6e48af8..94b1dd3 100644 --- a/Lib/ufb/Src/font_35.inc.c +++ b/Lib/ufb/Src/font_35.inc.c @@ -99,4 +99,5 @@ static const font3x_bitmap_t PROGMEM font35_ascii[] = { static const struct utf_glyph3x PROGMEM font35_extra[] = { {.utf={.symbol="�"}, {{0x1f, 0x1f, 0x1f}}}, {.utf={.symbol="°"}, {{0x07, 0x05, 0x07}}}, + {.utf={.symbol="→"}, {{0x15, 0x0e, 0x04}}}, }; diff --git a/Lib/ufb/Src/fontedit_35.c b/Lib/ufb/Src/fontedit_35.c index df3f297..6f186d6 100644 --- a/Lib/ufb/Src/fontedit_35.c +++ b/Lib/ufb/Src/fontedit_35.c @@ -588,11 +588,18 @@ const char *font_extras[] = { "xxx", " ", " ", + // Extra 2 "→" + "x ", + " x ", + "xxx", + " x ", + "x ", }; const char *font_extras_utf[] = { "�", "°", + "→", }; #include "fontedit_render.inc.c" diff --git a/Lib/ufb/Src/main.c b/Lib/ufb/Src/main.c index 44c037e..a4ccc7e 100644 --- a/Lib/ufb/Src/main.c +++ b/Lib/ufb/Src/main.c @@ -99,10 +99,10 @@ void main() { fb_text(15, 15, "MEOW", FONT_DOUBLEH, 1); fb_text(40, 15, "MEOW", FONT_DOUBLEW, 1); fb_text(40, 23, "MEOW", FONT_BOLD, 1); - - fb_text(5, 5, "Tiny 3x5", FONT_3X5, 1); + + fb_text(5, 5, "Tiny 3x5→", FONT_3X5, 1); fb_text(40, 5, "Tiny 4x5 MEOW", FONT_4X5, 1); - + fb_frame(12,12,80,20,1,1); // fb_text(36, 5, "-123456789.0 CPM°", FONT_TINY, 1);