/** * TODO file description */ #include #include #include "app_gui.h" #include "lcd/lcdbuf.h" #include "lcd.h" #include "app_config.h" #include "app_io.h" struct State s_app = {}; struct LcdBuffer lcd = {}; /** Schedule paint (the screen func will be called with the PAINT event argument */ void request_paint() { s_app.paint_needed = true; } /** Draw the common overlay / HUD (with temperatures and heater status) */ static void draw_common_overlay(); //stratch string buffer char sbuf[100]; void gui_read_moisture() { s_app.moisture_pt = moisture_convert(s_app.moisture_raw = moisture_read()); } static void read_time_and_moisture() { rtc_get_time(&s_app.rtc_time); // now is a good time to update timestamp - we could just increment it though gui_read_moisture(); } /** Main loop */ void gui_init() { switch_screen(screen_home, true); read_time_and_moisture(); LcdBuffer_Init(&lcd, CGROM_A00, CGRAM_CZ); } void gui_loop_iter(GuiEvent message) { uint32_t tickNow = timestamp_ms(); // Time program logic - since this runs very often, we are guaranteed not to miss a cycle - unless there is a power outage // exactly in the scheduled watering time if (s_app.screen != screen_cyklus && app_config.scheduler_enable) { if (s_app.cycle_time_checking) { if (s_app.rtc_time.hour != s_app.last_cycle_time.hour || s_app.rtc_time.minute != s_app.last_cycle_time.minute) { s_app.cycle_time_checking = false; } } else { for (int i = 0; i < SCHEDULE_COUNT; i++) { if (!app_config.schedules[i].enable) { continue; } if (s_app.rtc_time.hour == app_config.schedules[i].h && s_app.rtc_time.minute == app_config.schedules[i].m) { // cycle can preempt all screens // Check if moisture is OK if (app_config.moisture_enable) { if (s_app.moisture_pt > app_config.moisture_threshold_percent) { // watering not needed break; } } switch_screen(screen_cyklus, true); break; } } } } // screen auto-close if (s_app.screen != screen_home && s_app.screen != screen_cyklus) { if ((tickNow - s_app.screen_open_time) >= (MENU_AUTOEXIT_TIME_S * 1000)) { switch_screen(screen_home, true); } } // Read RTC every second if (tickNow - s_app.last_1s_time >= 1000) { s_app.screen(GUI_EVENT_SCREEN_TICK_1S); read_time_and_moisture(); s_app.last_1s_time = tickNow; } // 100ms tick event if (tickNow - s_app.last_100ms_time >= 100) { s_app.screen(GUI_EVENT_SCREEN_TICK_100MS); s_app.last_100ms_time = tickNow; } // 10ms tick event if (tickNow - s_app.last_10ms_time >= 10) { s_app.screen(GUI_EVENT_SCREEN_TICK_10MS); s_app.last_10ms_time = tickNow; } if (message != GUI_EVENT_NONE) { s_app.screen(message); } if (message >= 32) { // lazy shortcut so we dont have to list all of them // key was pressed input_sound_effect(); } if (s_app.paint_needed) { s_app.paint_needed = false; draw_common_overlay(); s_app.screen(GUI_EVENT_PAINT); // If there is anything to print, do it LcdBuffer_Flush(&lcd); } } /** Switch to a different screen handler. * If "init" is true, immediately call it with the init event. */ void switch_screen(screen_t pScreen, bool init) { if (s_app.screen == pScreen) { // already, no op return; } s_app.screen_open_time = timestamp_ms(); s_app.screen = pScreen; LcdBuffer_Clear(&lcd); LcdBuffer_SetCursor(&lcd, 0, 0, CURSOR_NONE); // always start with a hidden cursor. If the page wants a visible cursor, it should do that in PAINT request_paint(); // Reset the timers, so the screen has nicely aligned events s_app.last_10ms_time = s_app.last_100ms_time = s_app.last_1s_time = timestamp_ms(); // also read time so we have the latest greatest rtc_get_time(&s_app.rtc_time); if (init) { pScreen(GUI_EVENT_SCREEN_INIT); } } /** Draw GUI common to all screens */ static void draw_common_overlay() { // TODO } /** Play input sound effect if this is an input event */ void input_sound_effect() { // TODO }