diff --git a/CMakeLists.txt b/CMakeLists.txt index 3b5a1c5..29a695a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,9 +22,10 @@ add_executable(zavlaha src/lcd/lcdbuf.c src/screens/app_gui.c src/screens/screen_home.c + src/screens/screen_settings.c + src/screens/screen_cyklus.c src/app_io.c - src/app_config.c - src/screens/screen_cyklus.c) + src/app_config.c) # Add pico_stdlib library which aggregates commonly used features target_link_libraries(zavlaha pico_stdlib hardware_i2c hardware_adc hardware_irq) diff --git a/src/app_config.c b/src/app_config.c index 11a3cee..2b280b9 100644 --- a/src/app_config.c +++ b/src/app_config.c @@ -2,20 +2,58 @@ * TODO file description */ +#include +#include #include "app_config.h" +#include "ee.h" + +#define APPCONFIG_EE_BASE_ADDR 0 // TODO sensible defaults, loading and saving to/from EE -struct AppConfig app_config = { +struct AppConfig app_config; + +static const struct AppConfig DEFAULTS = { .circuit_base_time_s = 10, // for a test .circuit_time_percent = {30, 60, 90, 100}, // for a test .schedules = { - {6, 45}, - {255, 0}, - {255, 0}, - {255, 0} + {true, 6, 45}, + {false, 255, 0}, + {false, 255, 0}, + {false, 255, 0} }, .moisture_enable = true, .moisture_dry = 2800, .moisture_wet = 1600, .moisture_threshold_percent = 70, }; + +static void load_fallback_config() { + // Read failed, load defaults + memcpy(&app_config, &DEFAULTS, sizeof(struct AppConfig)); +} + +void appconfig_load() +{ + uint8_t magic = 0; + if (0 != ee_read(APPCONFIG_EE_BASE_ADDR, &magic, 1) || magic != APPCONFIG_VERSION) { + printf("Couldn't read EE to check magic!\r\n"); + load_fallback_config(); + return; + } + + if (0 != ee_read(APPCONFIG_EE_BASE_ADDR, (uint8_t *) &app_config, APPCONFIG_SIZE)) { + printf("Couldn't read EE to load config!\r\n"); + load_fallback_config(); + } +} + +void appconfig_save() +{ + printf("PERSISTENCE DISABLED - NOT SAVING\r\n"); + return; // FIXME - when all GUIs are finished, add persistence + + // whatever, just write everything + if (0 != ee_write(APPCONFIG_EE_BASE_ADDR, (const uint8_t *) &app_config, APPCONFIG_SIZE)) { + printf("Couldn't write EE!"); + } +} diff --git a/src/app_config.h b/src/app_config.h index 7eb28fd..f0878ef 100644 --- a/src/app_config.h +++ b/src/app_config.h @@ -11,23 +11,32 @@ #define CIRCUIT_COUNT 4 #define SCHEDULE_COUNT 4 -#define UNUSED_SCHEDULE_HOUR 0xFF - -struct ScheduleTime { +struct __attribute__((packed)) ScheduleTime { + bool enable; uint8_t h; uint8_t m; }; -struct AppConfig { +struct __attribute__((packed)) AppConfig { + uint8_t magic_version; uint16_t circuit_base_time_s; uint8_t circuit_time_percent[CIRCUIT_COUNT]; // 0% can be used to disable a branch - struct ScheduleTime schedules[SCHEDULE_COUNT]; // 0xFF hour is used to disable the slot + bool scheduler_enable; + struct ScheduleTime schedules[SCHEDULE_COUNT]; bool moisture_enable; uint16_t moisture_dry; uint16_t moisture_wet; uint8_t moisture_threshold_percent; }; +#define APPCONFIG_SIZE sizeof(struct AppConfig) + +#define APPCONFIG_VERSION 0b11100001 + extern struct AppConfig app_config; +void appconfig_load(); + +void appconfig_save(); + #endif //ZAVLAHA_APP_CONFIG_H diff --git a/src/lcd/cgram.c b/src/lcd/cgram.c index ce7df59..b8e4831 100644 --- a/src/lcd/cgram.c +++ b/src/lcd/cgram.c @@ -499,11 +499,11 @@ const struct LcdBuf_CGRAM_Symbol CGRAM_CZ[] = { .fallback = '*', .data = { 0b11111, + 0b11011, 0b01010, 0b10001, - 0b00000, - 0b10001, 0b01010, + 0b11011, 0b11111, }, }, diff --git a/src/lcd/cgrom.c b/src/lcd/cgrom.c index 3874cd4..7ff0b4b 100644 --- a/src/lcd/cgrom.c +++ b/src/lcd/cgrom.c @@ -97,8 +97,8 @@ const struct LcdBuf_CGROM_Entry CGROM_A00[] = { {.address = 123, .symbol = "{"}, {.address = 124, .symbol = "|"}, {.address = 125, .symbol = "}"}, - {.address = 126, .symbol = "←"}, - {.address = 127, .symbol = "→"}, + {.address = 126, .symbol = "→"}, + {.address = 127, .symbol = "←"}, // lots of japanese symbols - add them yourself if you need them {.address = 0xA2, .symbol = "「"}, diff --git a/src/main.c b/src/main.c index 2e7fc2d..64fc2b9 100644 --- a/src/main.c +++ b/src/main.c @@ -11,6 +11,7 @@ #include "ee.h" #include "lcd/lcdbuf.h" #include "screens/app_gui.h" +#include "app_config.h" /* @@ -71,7 +72,7 @@ void irq_uart() { } } -int main() +int __attribute__((noreturn)) main() { // picotool binary info bi_decl(bi_1pin_with_name(PIN_LED, "LED")); @@ -123,11 +124,14 @@ int main() adc_init(); adc_gpio_init(PIN_MOISTURE); + appconfig_load(); + lcd_init(); gui_init(); for(;;) { + // Check if any key was pushed uint32_t interrupts = save_and_disable_interrupts(); GuiEvent event = GUI_EVENT_NONE; if (keypad_buffer[keypad_buffer_rp] != 0) { @@ -142,6 +146,8 @@ int main() restore_interrupts(interrupts); gui_loop_iter(event); + + // TODO scheduler logic } // diff --git a/src/screens/app_gui.c b/src/screens/app_gui.c index 633ecca..df06bdd 100644 --- a/src/screens/app_gui.c +++ b/src/screens/app_gui.c @@ -62,6 +62,11 @@ void gui_loop_iter(GuiEvent message) { /** 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 = pScreen; LcdBuffer_Clear(&lcd); diff --git a/src/screens/app_gui.h b/src/screens/app_gui.h index c81b0b9..52f0f5d 100644 --- a/src/screens/app_gui.h +++ b/src/screens/app_gui.h @@ -49,6 +49,7 @@ void request_paint(); void screen_home(GuiEvent event); void screen_cyklus(GuiEvent event); +void screen_settings(GuiEvent event); // XXX other prototypes struct State { diff --git a/src/screens/screen_cyklus.c b/src/screens/screen_cyklus.c index 6a4cec5..8403a50 100644 --- a/src/screens/screen_cyklus.c +++ b/src/screens/screen_cyklus.c @@ -12,6 +12,8 @@ #include "ds_rtc.h" #include "app_io.h" +// TODO + void screen_cyklus(GuiEvent event) { diff --git a/src/screens/screen_home.c b/src/screens/screen_home.c index 133f26c..6f340ef 100644 --- a/src/screens/screen_home.c +++ b/src/screens/screen_home.c @@ -16,7 +16,6 @@ static uint32_t last_time = 0; static struct rtc_time rtc_time = {}; static uint16_t moisture = 0; -static char showbuf[20]; static uint8_t showbuf_wp = 0; void screen_home(GuiEvent event) @@ -26,7 +25,6 @@ void screen_home(GuiEvent event) switch (event) { case GUI_EVENT_SCREEN_INIT: - memset(showbuf, ' ', sizeof(showbuf)); // pass case GUI_EVENT_SCREEN_TICK: if (elapsed >= 100) { @@ -37,9 +35,8 @@ void screen_home(GuiEvent event) } break; - case GUI_EVENT_PAINT: - LcdBuffer_Write(&lcd, 0, 0, showbuf); - + case GUI_EVENT_PAINT:; + // TODO uint32_t moisture_pt = ((uint32_t)(moisture > 2800 ? 0 : (2800 - moisture)) * 100) / (2800 - 1600); sprintf(stmp, " %2d:%02d:%02d %3d%% 🌢 ", rtc_time.hour, rtc_time.minute, rtc_time.second, moisture_pt); @@ -48,17 +45,11 @@ void screen_home(GuiEvent event) LcdBuffer_Write(&lcd, 1, 0, "Plán. závlaha 7:15"); LcdBuffer_Write(&lcd, 2, 0, "❶Spustit ❷Přeskočit"); LcdBuffer_Write(&lcd, 3, 0, "🅰Nastavení"); - -// LcdBuffer_Write(&lcd, 1, 0, "❶ Spustit cyklus"); -// LcdBuffer_Write(&lcd, 2, 0, "❷ Ruční řízení"); -// LcdBuffer_Write(&lcd, 3, 0, "🅰 Nastavení"); - -// LcdBuffer_Write(&lcd, 1, 0, "🅰Program 🅱Poměry"); -// LcdBuffer_Write(&lcd, 2, 0, "🅲Vlhkoměr 🅳Čas"); break; case GUI_EVENT_KEY_1: - switch_screen(screen_cyklus, true); + // TODO + switch_screen(screen_cyklus, true); // fake cycle break; case GUI_EVENT_KEY_2: @@ -66,16 +57,7 @@ void screen_home(GuiEvent event) break; case GUI_EVENT_KEY_A: - // TODO + switch_screen(screen_settings, true); break; - - default: - if (event >= 32) { - showbuf[showbuf_wp++] = event; - if (showbuf_wp == 20) { - showbuf_wp = 0; - } - request_paint(); - } } } diff --git a/src/screens/screen_settings.c b/src/screens/screen_settings.c new file mode 100644 index 0000000..82dc8e9 --- /dev/null +++ b/src/screens/screen_settings.c @@ -0,0 +1,144 @@ +/** + * TODO file description + */ +// +// Created by MightyPork on 2023/04/09. +// + +#include +#include +#include "app_gui.h" +#include "gui_event.h" +#include "ds_rtc.h" +#include "app_io.h" +#include "app_config.h" + +static int page = 1; + +#define PAGECOUNT 3 + +static struct AppConfig scratch; + +/** Clear the top 3 lines in the LCD - this fixes some problems with utf8 allocations + * (char still held by a line later overwritten) */ +static void clear_menu_lines(); + +void screen_settings(GuiEvent event) +{ + char buf[100]; + switch (event) { + case GUI_EVENT_SCREEN_INIT: + page = 1; + memcpy(&scratch, &app_config, APPCONFIG_SIZE); + break; + + case GUI_EVENT_PAINT: + switch (page) { + case 1: + snprintf(buf, 100, "1. Program=%s", scratch.scheduler_enable ? "ZAP" : "VYP"); + LcdBuffer_Write(&lcd, 0, 0, buf); + LcdBuffer_Write(&lcd, 1, 0, "2. Upravit program"); + LcdBuffer_Write(&lcd, 2, 0, "3. Nastavit čas"); + break; + + case 2: + LcdBuffer_Write(&lcd, 0, 0, "1. Délka zálivky"); + LcdBuffer_Write(&lcd, 1, 0, "2. Kompenzace tlaku"); + break; + + case 3: + snprintf(buf, 100, "1. Blok.vlhkostí=%s", scratch.moisture_enable ? "ZAP" : "VYP"); + LcdBuffer_Write(&lcd, 0, 0, buf); + LcdBuffer_Write(&lcd, 1, 0, "2. Kalibrace vlhkom."); + LcdBuffer_Write(&lcd, 2, 0, "3. Nastavit práh"); + break; + } + + snprintf(buf, 100, "⊛←%d/%d→¤ 🅰Ulož 🅳Zruš", page, PAGECOUNT); + LcdBuffer_Write(&lcd, 3, 0, buf); + break; + + case GUI_EVENT_KEY_B: + case GUI_EVENT_KEY_STAR: + if (page > 1) { + page--; + clear_menu_lines(); + request_paint(); + } + break; + + case GUI_EVENT_KEY_C: + case GUI_EVENT_KEY_HASH: + if (page < PAGECOUNT) { + page++; + clear_menu_lines(); + request_paint(); + } + break; + + case GUI_EVENT_KEY_A: // SAVE + memcpy(&app_config, &scratch, APPCONFIG_SIZE); + appconfig_save(); + switch_screen(screen_home, true); + break; + + case GUI_EVENT_KEY_D: // CANCEL + switch_screen(screen_home, true); + break; + + default: + switch (page) { + case 1: + switch (event) { + case GUI_EVENT_KEY_1: // Program Zap/VYP + scratch.scheduler_enable ^= 1; + request_paint(); + break; + + case GUI_EVENT_KEY_2: // Upravit program + // TODO + break; + + case GUI_EVENT_KEY_3: // Nastavit cas + // TODO + break; + } + break; + + case 2: + switch (event) { + case GUI_EVENT_KEY_1: // Délka zálivky + break; + + case GUI_EVENT_KEY_2: // Kompenzace tlaku + // TODO + break; + } + break; + + case 3: + switch (event) { + case GUI_EVENT_KEY_1: // Blokace vlhkosti Zap/VYP + scratch.moisture_enable ^= 1; + request_paint(); + break; + + case GUI_EVENT_KEY_2: // Kalibrace vlhkomeru + // TODO + break; + + case GUI_EVENT_KEY_3: // Nastavit prah + // TODO + break; + } + break; + } + } +} + +static void clear_menu_lines() +{ + LcdBuffer_Write(&lcd, 0, 0, " "); + LcdBuffer_Write(&lcd, 1, 0, " "); + LcdBuffer_Write(&lcd, 2, 0, " "); +}