/** * TODO file description */ #include #include "app_gui.h" #include "app_heater.h" #include "app_buzzer.h" #include "app_temp.h" #include "FreeRTOS.h" #include "task.h" #include "queue.h" #include "ufb/framebuffer.h" #include "ufb/fb_text.h" #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 = {}; /** 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; } s_app.set_temp = (clamped / 2) * 5; if (s_app.set_temp > MAX_TEMP) { s_app.set_temp = MAX_TEMP; } app_heater_set_target((float) s_app.set_temp); } char tmp[100]; void app_task_gui(void *argument) { // Wait until inited ulTaskNotifyTake(pdTRUE, portMAX_DELAY); PUTS("GUI task starts\r\n"); 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(50)); switch (message) { case GUI_EVENT_KNOB_PRESS: s_app.pushed = true; s_app.push_tick = xTaskGetTickCount(); break; case GUI_EVENT_KNOB_RELEASE: s_app.pushed = false; break; default: break; } 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(); screens[s_app.screen](message); fb_blit(); } } } static void switch_screen(GuiScreen screen, bool init) { s_app.initial_pushed = s_app.pushed; s_app.screen = screen; if (init) { screens[screen](GUI_EVENT_SCREEN_INIT); } } 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; default: break; } }