diff --git a/Core/Src/app_gui.c b/Core/Src/app_gui.c index 6cab52a..21404eb 100644 --- a/Core/Src/app_gui.c +++ b/Core/Src/app_gui.c @@ -15,11 +15,11 @@ #define MAX_TEMP 400 -typedef enum GuiScreen { - SCREEN_HOME, - SCREEN_MANUAL, - SCREEN_MANUAL_MENU, -} GuiScreen; +typedef void (*menu_callback_t)(int choice); + +void screen_menu(GuiEvent event, const char **options, menu_callback_t cb); + +typedef void (*screen_t)(GuiEvent event); static struct State { float oven_temp; @@ -32,7 +32,7 @@ static struct State { // true if the button is still held since init (i.e. the push action should not work as "enter") bool initial_pushed; - GuiScreen screen; + screen_t screen; int menu_pos; int menu_len; } s_app = {}; @@ -42,25 +42,17 @@ 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 switch_screen(screen_t pScreen, bool init); static void calc_set_temp() { @@ -76,14 +68,17 @@ static void calc_set_temp() app_heater_set_target((float) s_app.set_temp); } -char tmp[100]; +static char tmp[100]; +/** Main loop */ void app_task_gui(void *argument) { // Wait until inited ulTaskNotifyTake(pdTRUE, portMAX_DELAY); PUTS("GUI task starts\r\n"); + switch_screen(screen_home, true); + while (1) { s_app.oven_temp = app_temp_read_oven(); s_app.soc_temp = app_temp_read_soc(); @@ -109,26 +104,27 @@ void app_task_gui(void *argument) 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(); - screens[s_app.screen](message); + s_app.screen(message); fb_blit(); } } } -static void switch_screen(GuiScreen screen, bool init) { +/** Switch to a different screen handler. + * If "init" is true, immediately call it with the init event. */ +static void switch_screen(screen_t pScreen, bool init) { s_app.initial_pushed = s_app.pushed; - s_app.screen = screen; + s_app.screen = pScreen; if (init) { - screens[screen](GUI_EVENT_SCREEN_INIT); + pScreen(GUI_EVENT_SCREEN_INIT); } } +/** Draw GUI common to all screens */ static void draw_common_overlay() { SPRINTF(tmp, "%3.1f°C →%3d°C", s_app.oven_temp, s_app.set_temp); @@ -142,7 +138,7 @@ static void draw_common_overlay() } } - +/** Play input sound effect if this is an input event */ static void input_sound_effect(GuiEvent event) { switch (event) { @@ -151,8 +147,24 @@ static void input_sound_effect(GuiEvent event) case GUI_EVENT_KNOB_RELEASE: app_buzzer_beep(); break; + } +} + +// ------------- home screen ---------------- + +static const char* main_menu_opts[] = { + "Manual mode", + "Calibration", + NULL +}; - default: +static void main_menu_cb(int opt) { + switch (opt) { + case 0: + switch_screen(screen_manual, true); + break; + case 1: + // TODO break; } } @@ -160,49 +172,14 @@ static void input_sound_effect(GuiEvent event) 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; - } + screen_menu(event, main_menu_opts, main_menu_cb); } +// --------- manual mode screen --------------- + static void screen_manual(GuiEvent event) { if (event == GUI_EVENT_SCREEN_INIT) { @@ -210,9 +187,9 @@ static void screen_manual(GuiEvent event) return; } - // menu activated by long push + // menu is activated by long push if (push_time() >= pdMS_TO_TICKS(500)) { - switch_screen(SCREEN_MANUAL_MENU, true); + switch_screen(screen_manual_menu, true); return; } @@ -237,54 +214,72 @@ static void screen_manual(GuiEvent event) s_app.set_temp_wheel--; calc_set_temp(); break; + } +} + +// --------------------------- + +static const char* manual_menu_opts[] = { + "Close menu", + "Exit manual", + NULL +}; + +static void manual_menu_cb(int opt) { + switch (opt) { + case 0: + // Close menu + switch_screen(screen_manual, false); + break; - default: + case 1: + // Close menu + switch_screen(screen_home, true); break; } } static void screen_manual_menu(GuiEvent event) { + screen_menu(event, manual_menu_opts, manual_menu_cb); +} + +// ------------------------ + +/** + * Generic screen menu handler + * + * @param event - the currently handled event + * @param options - text labels for the menu buttons. Array of C strings, terminated by NULL + * @param cb - callback func, called when an option is selected + */ +void screen_menu(GuiEvent event, const char **options, menu_callback_t cb) { if (event == GUI_EVENT_SCREEN_INIT) { s_app.menu_pos = 0; - s_app.menu_len = 2; + s_app.menu_len = 0; + const char **opt = options; + while (*opt) { + s_app.menu_len++; + opt++; + } 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); + for (int i = 0; i < s_app.menu_len; i++) { + fbcolor_t color = s_app.menu_pos != i; + fbpos_t y = 27 + i * 10; + fb_rect(0, y, 64, 10, !color); + fb_text(1, y + 1, options[i], FONT_5X7, color); + // ensure the text doesnt escape the screen + fb_vline(63, y, 10, !color); } - 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; - } + cb(s_app.menu_pos); break; case GUI_EVENT_KNOB_PLUS: @@ -298,8 +293,5 @@ static void screen_manual_menu(GuiEvent event) s_app.menu_pos--; } break; - - default: - break; } }