From f4d88f1b8b835efc7c1711922e0942df4a1d5b9d Mon Sep 17 00:00:00 2001 From: Thomas Hooge Date: Thu, 31 Jul 2025 11:39:23 +0200 Subject: [PATCH 1/4] Code rework at page system. Preparation for config menu. --- lib/obp60task/OBP60Extensions.cpp | 21 ++ lib/obp60task/OBP60Extensions.h | 1 + lib/obp60task/PageSystem.cpp | 593 ++++++++++++++++++------------ lib/obp60task/obp60task.cpp | 22 +- 4 files changed, 374 insertions(+), 263 deletions(-) diff --git a/lib/obp60task/OBP60Extensions.cpp b/lib/obp60task/OBP60Extensions.cpp index 70348e7..29d76a8 100644 --- a/lib/obp60task/OBP60Extensions.cpp +++ b/lib/obp60task/OBP60Extensions.cpp @@ -107,6 +107,27 @@ void hardwareInit(GwApi *api) } } +void powerInit(String powermode) { + // Max Power | Only 5.0V | Min Power + if (powermode == "Max Power" || powermode == "Only 5.0V") { +#ifdef HARDWARE_V21 + setPortPin(OBP_POWER_50, true); // Power on 5.0V rail +#endif +#ifdef BOARD_OBP40S3 + setPortPin(OBP_POWER_EPD, true);// Power on ePaper display + setPortPin(OBP_POWER_SD, true); // Power on SD card +#endif + } else { // Min Power +#ifdef HARDWARE_V21 + setPortPin(OBP_POWER_50, false); // Power off 5.0V rail +#endif +#ifdef BOARD_OBP40S3 + setPortPin(OBP_POWER_EPD, false);// Power off ePaper display + setPortPin(OBP_POWER_SD, false); // Power off SD card +#endif + } +} + void setPortPin(uint pin, bool value){ pinMode(pin, OUTPUT); digitalWrite(pin, value); diff --git a/lib/obp60task/OBP60Extensions.h b/lib/obp60task/OBP60Extensions.h index ed67716..92e5486 100644 --- a/lib/obp60task/OBP60Extensions.h +++ b/lib/obp60task/OBP60Extensions.h @@ -75,6 +75,7 @@ void deepSleep(CommonData &common); uint8_t getLastPage(); void hardwareInit(GwApi *api); +void powerInit(String powermode); void setPortPin(uint pin, bool value); // Set port pin for extension port diff --git a/lib/obp60task/PageSystem.cpp b/lib/obp60task/PageSystem.cpp index ddff3d4..bc06b6b 100644 --- a/lib/obp60task/PageSystem.cpp +++ b/lib/obp60task/PageSystem.cpp @@ -25,29 +25,331 @@ * Consists of some sub-pages with following content: * 1. Hard and software information * 2. System settings - * 3. NMEA2000 device list + * 3. System configuration: running and NVRAM + * 4. NMEA2000 device list + * 5. SD Card information if available + * + * TODO + * - setCpuFrequencyMhz(80|160|240); + * - Accesspoint / ! Änderung im Gatewaycode erforderlich? + * if (! isApActive()) { + * wifiSSID = config->getString(config->wifiSSID); + * wifiPass = config->getString(config->wifiPass); + * wifiSoftAP(wifiSSID, wifiPass); + * } + * - Power mode + * powerInit(powermode); */ class PageSystem : public Page { -uint64_t chipid; -bool simulation; -bool sdcard; -String buzzer_mode; -uint8_t buzzer_power; -String cpuspeed; -String rtc_module; -String gps_module; -String env_module; +private: + // NVRAM config options + String flashLED; -String batt_sensor; -String solar_sensor; -String gen_sensor; -String rot_sensor; -double homelat; -double homelon; + // Generic data access -char mode = 'N'; // (N)ormal, (S)ettings, (D)evice list, (C)ard + uint64_t chipid; + bool simulation; + bool sdcard; + String buzzer_mode; + uint8_t buzzer_power; + String cpuspeed; + String rtc_module; + String gps_module; + String env_module; + + String batt_sensor; + String solar_sensor; + String gen_sensor; + String rot_sensor; + double homelat; + double homelon; + + char mode = 'N'; // (N)ormal, (S)ettings, (C)onfiguration, (D)evice list, c(A)rd + + void incMode() { + if (mode == 'N') { // Normal + mode = 'S'; + } else if (mode == 'S') { // Settings + mode = 'C'; + } else if (mode == 'C') { // Config + mode = 'D'; + } else if (mode == 'D') { // Device list + if (sdcard) { + mode = 'A'; // SD-Card + } else { + mode = 'N'; + } + } else { + mode = 'N'; + } + } + + void decMode() { + if (mode == 'N') { + if (sdcard) { + mode = 'A'; + } else { + mode = 'D'; + } + } else if (mode == 'S') { // Settings + mode = 'N'; + } else if (mode == 'C') { // Config + mode = 'S'; + } else if (mode == 'D') { // Device list + mode = 'C'; + } else { + mode = 'D'; + } + } + + void displayModeNormal() { + // Default system page view + + uint16_t y0 = 155; + + getdisplay().setFont(&Ubuntu_Bold12pt8b); + getdisplay().setCursor(8, 48); + getdisplay().print("System information"); + + getdisplay().drawXBitmap(320, 25, logo64_bits, logo64_width, logo64_height, commonData->fgcolor); + + getdisplay().setFont(&Ubuntu_Bold8pt8b); + + char ssid[13]; + snprintf(ssid, 13, "%04X%08X", (uint16_t)(chipid >> 32), (uint32_t)chipid); + displayBarcode(String(ssid), 320, 200, 2); + getdisplay().setCursor(8, 70); + getdisplay().print(String("MCUDEVICE-") + String(ssid)); + + getdisplay().setCursor(8, 95); + getdisplay().print("Firmware version: "); + getdisplay().setCursor(150, 95); + getdisplay().print(VERSINFO); + + getdisplay().setCursor(8, 113); + getdisplay().print("Board version: "); + getdisplay().setCursor(150, 113); + getdisplay().print(BOARDINFO); + getdisplay().print(String(" HW ") + String(PCBINFO)); + + getdisplay().setCursor(8, 131); + getdisplay().print("Display version: "); + getdisplay().setCursor(150, 131); + getdisplay().print(DISPLAYINFO); + getdisplay().print("; GxEPD2 v"); + getdisplay().print(GXEPD2INFO); + + getdisplay().setCursor(8, 265); +#ifdef BOARD_OBP60S3 + getdisplay().print("Press STBY to enter deep sleep mode"); +#endif +#ifdef BOARD_OBP40S3 + getdisplay().print("Press wheel to enter deep sleep mode"); +#endif + + // Flash memory size + uint32_t flash_size = ESP.getFlashChipSize(); + getdisplay().setCursor(8, y0); + getdisplay().print("FLASH:"); + getdisplay().setCursor(90, y0); + getdisplay().print(String(flash_size / 1024) + String(" kB")); + + // PSRAM memory size + uint32_t psram_size = ESP.getPsramSize(); + getdisplay().setCursor(8, y0 + 16); + getdisplay().print("PSRAM:"); + getdisplay().setCursor(90, y0 + 16); + getdisplay().print(String(psram_size / 1024) + String(" kB")); + + // FRAM available / status + getdisplay().setCursor(8, y0 + 32); + getdisplay().print("FRAM:"); + getdisplay().setCursor(90, y0 + 32); + getdisplay().print(hasFRAM ? "available" : "not found"); + +#ifdef BOARD_OBP40S3 + // SD-Card + getdisplay().setCursor(8, y0 + 48); + getdisplay().print("SD-Card:"); + getdisplay().setCursor(90, y0 + 48); + if (sdcard) { + uint64_t cardsize = SD.cardSize() / (1024 * 1024); + getdisplay().print(String(cardsize) + String(" MB")); + } else { + getdisplay().print("off"); + } +#endif + + // CPU speed config / active + getdisplay().setCursor(202, y0); + getdisplay().print("CPU speed:"); + getdisplay().setCursor(300, y0); + getdisplay().print(cpuspeed); + getdisplay().print(" / "); + int cpu_freq = esp_clk_cpu_freq() / 1000000; + getdisplay().print(String(cpu_freq)); + + // total RAM free + int Heap_free = esp_get_free_heap_size(); + getdisplay().setCursor(202, y0 + 16); + getdisplay().print("Total free:"); + getdisplay().setCursor(300, y0 + 16); + getdisplay().print(String(Heap_free)); + + // RAM free for task + int RAM_free = uxTaskGetStackHighWaterMark(NULL); + getdisplay().setCursor(202, y0 + 32); + getdisplay().print("Task free:"); + getdisplay().setCursor(300, y0 + 32); + getdisplay().print(String(RAM_free)); + } + + void displayModeConfig() { + // Configuration interface + + uint16_t x0 = 16; + uint16_t y0 = 80; + uint16_t dy = 20; + + getdisplay().setFont(&Ubuntu_Bold12pt8b); + getdisplay().setCursor(8, 48); + getdisplay().print("System configuration"); + + getdisplay().setFont(&Ubuntu_Bold8pt8b); + + getdisplay().setCursor(x0, y0); + getdisplay().print("CPU speed: 80 | 160 | 240"); + getdisplay().setCursor(x0, y0 + 1 * dy); + getdisplay().print("Power mode: Max | 5V | Min"); + getdisplay().setCursor(x0, y0 + 2 * dy); + getdisplay().print("Accesspoint: On | Off"); + + // TODO Change NVRAM-preferences settings here + getdisplay().setCursor(x0, y0 + 4 * dy); + getdisplay().print("Simulation: On | Off"); + } + + void displayModeSettings() { + // View some of the current settings + + const uint16_t x0 = 8; + const uint16_t y0 = 72; + + getdisplay().setFont(&Ubuntu_Bold12pt8b); + getdisplay().setCursor(x0, 48); + getdisplay().print("System settings"); + + getdisplay().setFont(&Ubuntu_Bold8pt8b); + + // left column + getdisplay().setCursor(x0, y0); + getdisplay().print("Simulation:"); + getdisplay().setCursor(120, y0); + getdisplay().print(simulation ? "on" : "off"); + + getdisplay().setCursor(x0, y0 + 16); + getdisplay().print("Environment:"); + getdisplay().setCursor(120, y0 + 16); + getdisplay().print(env_module); + + getdisplay().setCursor(x0, y0 + 32); + getdisplay().print("Buzzer:"); + getdisplay().setCursor(120, y0 + 32); + getdisplay().print(buzzer_mode); + + getdisplay().setCursor(x0, y0 + 64); + getdisplay().print("GPS:"); + getdisplay().setCursor(120, y0 + 64); + getdisplay().print(gps_module); + + getdisplay().setCursor(x0, y0 + 80); + getdisplay().print("RTC:"); + getdisplay().setCursor(120, y0 + 80); + getdisplay().print(rtc_module); + + getdisplay().setCursor(x0, y0 + 96); + getdisplay().print("Wifi:"); + getdisplay().setCursor(120, y0 + 96); + getdisplay().print(commonData->status.wifiApOn ? "on" : "off"); + + // Home location + getdisplay().setCursor(x0, y0 + 128); + getdisplay().print("Home Lat.:"); + drawTextRalign(230, y0 + 128, formatLatitude(homelat)); + getdisplay().setCursor(x0, y0 + 144); + getdisplay().print("Home Lon.:"); + drawTextRalign(230, y0 + 144, formatLongitude(homelon)); + + // right column + getdisplay().setCursor(202, y0); + getdisplay().print("Batt. sensor:"); + getdisplay().setCursor(320, y0); + getdisplay().print(batt_sensor); + + // Solar sensor + getdisplay().setCursor(202, y0 + 16); + getdisplay().print("Solar sensor:"); + getdisplay().setCursor(320, y0 + 16); + getdisplay().print(solar_sensor); + + // Generator sensor + getdisplay().setCursor(202, y0 + 32); + getdisplay().print("Gen. sensor:"); + getdisplay().setCursor(320, y0 + 32); + getdisplay().print(gen_sensor); + +#ifdef BOARD_OBP60S3 + // Backlight infos + getdisplay().setCursor(202, y0 + 64); + getdisplay().print("Backlight:"); + getdisplay().setCursor(320, y0 + 64); + getdisplay().printf("%d%%", commonData->backlight.brightness); + // TODO test function with OBP60 device + getdisplay().setCursor(202, y0 + 80); + getdisplay().print("Bl color:"); + getdisplay().setCursor(320, y0 + 80); + getdisplay().print(commonData->backlight.color); + getdisplay().setCursor(202, y0 + 96); + getdisplay().print("Bl mode:"); + getdisplay().setCursor(320, y0 + 96); + getdisplay().print(commonData->backlight.mode); + // TODO Buzzer mode and power +#endif + + // Gyro sensor + // WIP / FEATURE + } + + void displayModeSDCard() { + // SD card info + uint16_t x0 = 20; + uint16_t y0 = 72; + + getdisplay().setFont(&Ubuntu_Bold12pt8b); + getdisplay().setCursor(8, 48); + getdisplay().print("SD Card info"); + + getdisplay().setFont(&Ubuntu_Bold8pt8b); + getdisplay().setCursor(x0, y0); + getdisplay().print("Work in progress..."); + } + + void displayModeDevicelist() { + // NMEA2000 device list + getdisplay().setFont(&Ubuntu_Bold12pt8b); + getdisplay().setCursor(8, 48); + getdisplay().print("NMEA2000 device list"); + + getdisplay().setFont(&Ubuntu_Bold8pt8b); + getdisplay().setCursor(20, 80); + getdisplay().print("RxD: "); + getdisplay().print(String(commonData->status.n2kRx)); + getdisplay().setCursor(20, 100); + getdisplay().print("TxD: "); + getdisplay().print(String(commonData->status.n2kTx)); + } public: PageSystem(CommonData &common){ @@ -56,6 +358,8 @@ public: if (hasFRAM) { mode = fram.read(FRAM_SYSTEM_MODE); } + flashLED = common.config->getString(common.config->flashLED); + chipid = ESP.getEfuseMac(); simulation = common.config->getBool(common.config->useSimuData); #ifdef BOARD_OBP40S3 @@ -90,19 +394,7 @@ public: // Switch display mode commonData->logger->logDebug(GwLog::LOG, "System keyboard handler"); if (key == 2) { - if (mode == 'N') { - mode = 'S'; - } else if (mode == 'S') { - mode = 'D'; - } else if (mode == 'D') { - if (sdcard) { - mode = 'C'; - } else { - mode = 'N'; - } - } else { - mode = 'N'; - } + incMode(); if (hasFRAM) fram.write(FRAM_SYSTEM_MODE, mode); return 0; } @@ -126,8 +418,13 @@ public: } #endif #ifdef BOARD_OBP40S3 - // grab cursor keys to disable page navigation - if (key == 9 or key == 10) { + // use cursor keys for local mode navigation + if (key == 9) { + incMode(); + return 0; + } + if (key == 10) { + decMode(); return 0; } // standby / deep sleep @@ -168,225 +465,35 @@ public: GwConfigHandler *config = commonData->config; GwLog *logger = commonData->logger; - // Get config data - String flashLED = config->getString(config->flashLED); - // Optical warning by limit violation (unused) - if(String(flashLED) == "Limit Violation"){ + if(flashLED == "Limit Violation"){ setBlinkingLED(false); setFlashLED(false); } - // Logging boat values - LOG_DEBUG(GwLog::LOG,"Drawing at PageSystem"); - - // Draw page - //*********************************************************** - - uint16_t x0 = 8; // left column - uint16_t y0 = 48; // data table starts here + // Logging page information + LOG_DEBUG(GwLog::LOG,"Drawing at PageSystem, Mode=%c", mode); // Set display in partial refresh mode - getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update + getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); - if (mode == 'N') { - - getdisplay().setFont(&Ubuntu_Bold12pt8b); - getdisplay().setCursor(8, 48); - getdisplay().print("System Information"); - - getdisplay().drawXBitmap(320, 25, logo64_bits, logo64_width, logo64_height, commonData->fgcolor); - - getdisplay().setFont(&Ubuntu_Bold8pt8b); - y0 = 155; - - char ssid[13]; - snprintf(ssid, 13, "%04X%08X", (uint16_t)(chipid >> 32), (uint32_t)chipid); - displayBarcode(String(ssid), 320, 200, 2); - getdisplay().setCursor(8, 70); - getdisplay().print(String("MCUDEVICE-") + String(ssid)); - - getdisplay().setCursor(8, 95); - getdisplay().print("Firmware version: "); - getdisplay().setCursor(150, 95); - getdisplay().print(VERSINFO); - - getdisplay().setCursor(8, 113); - getdisplay().print("Board version: "); - getdisplay().setCursor(150, 113); - getdisplay().print(BOARDINFO); - getdisplay().print(String(" HW ") + String(PCBINFO)); - - getdisplay().setCursor(8, 131); - getdisplay().print("Display version: "); - getdisplay().setCursor(150, 131); - getdisplay().print(DISPLAYINFO); - getdisplay().print("; GxEPD2 v"); - getdisplay().print(GXEPD2INFO); - - getdisplay().setCursor(8, 265); -#ifdef BOARD_OBP60S3 - getdisplay().print("Press STBY to enter deep sleep mode"); -#endif -#ifdef BOARD_OBP40S3 - getdisplay().print("Press wheel to enter deep sleep mode"); -#endif - - // Flash memory size - uint32_t flash_size = ESP.getFlashChipSize(); - getdisplay().setCursor(8, y0); - getdisplay().print("FLASH:"); - getdisplay().setCursor(90, y0); - getdisplay().print(String(flash_size / 1024) + String(" kB")); - - // PSRAM memory size - uint32_t psram_size = ESP.getPsramSize(); - getdisplay().setCursor(8, y0 + 16); - getdisplay().print("PSRAM:"); - getdisplay().setCursor(90, y0 + 16); - getdisplay().print(String(psram_size / 1024) + String(" kB")); - - // FRAM available / status - getdisplay().setCursor(8, y0 + 32); - getdisplay().print("FRAM:"); - getdisplay().setCursor(90, y0 + 32); - getdisplay().print(hasFRAM ? "available" : "not found"); - -#ifdef BOARD_OBP40S3 - // SD-Card - getdisplay().setCursor(8, y0 + 48); - getdisplay().print("SD-Card:"); - getdisplay().setCursor(90, y0 + 48); - if (sdcard) { - uint64_t cardsize = SD.cardSize() / (1024 * 1024); - getdisplay().print(String(cardsize) + String(" MB")); - } else { - getdisplay().print("off"); - } -#endif - - // CPU speed config / active - getdisplay().setCursor(202, y0); - getdisplay().print("CPU speed:"); - getdisplay().setCursor(300, y0); - getdisplay().print(cpuspeed); - getdisplay().print(" / "); - int cpu_freq = esp_clk_cpu_freq() / 1000000; - getdisplay().print(String(cpu_freq)); - - // total RAM free - int Heap_free = esp_get_free_heap_size(); - getdisplay().setCursor(202, y0 + 16); - getdisplay().print("Total free:"); - getdisplay().setCursor(300, y0 + 16); - getdisplay().print(String(Heap_free)); - - // RAM free for task - int RAM_free = uxTaskGetStackHighWaterMark(NULL); - getdisplay().setCursor(202, y0 + 32); - getdisplay().print("Task free:"); - getdisplay().setCursor(300, y0 + 32); - getdisplay().print(String(RAM_free)); - - } else if (mode == 'S') { - // Settings - - getdisplay().setFont(&Ubuntu_Bold12pt8b); - getdisplay().setCursor(x0, 48); - getdisplay().print("System settings"); - - getdisplay().setFont(&Ubuntu_Bold8pt8b); - x0 = 8; - y0 = 72; - - // left column - getdisplay().setCursor(x0, y0); - getdisplay().print("Simulation:"); - getdisplay().setCursor(120, y0); - getdisplay().print(simulation ? "on" : "off"); - - getdisplay().setCursor(x0, y0 + 16); - getdisplay().print("Environment:"); - getdisplay().setCursor(120, y0 + 16); - getdisplay().print(env_module); - - getdisplay().setCursor(x0, y0 + 32); - getdisplay().print("Buzzer:"); - getdisplay().setCursor(120, y0 + 32); - getdisplay().print(buzzer_mode); - - getdisplay().setCursor(x0, y0 + 64); - getdisplay().print("GPS:"); - getdisplay().setCursor(120, y0 + 64); - getdisplay().print(gps_module); - - getdisplay().setCursor(x0, y0 + 80); - getdisplay().print("RTC:"); - getdisplay().setCursor(120, y0 + 80); - getdisplay().print(rtc_module); - - getdisplay().setCursor(x0, y0 + 96); - getdisplay().print("Wifi:"); - getdisplay().setCursor(120, y0 + 96); - getdisplay().print(commonData->status.wifiApOn ? "on" : "off"); - - // Home location - getdisplay().setCursor(x0, y0 + 128); - getdisplay().print("Home Lat.:"); - getdisplay().setCursor(120, y0 + 128); - getdisplay().print(formatLatitude(homelat)); - getdisplay().setCursor(x0, y0 + 144); - getdisplay().print("Home Lon.:"); - getdisplay().setCursor(120, y0 + 144); - getdisplay().print(formatLongitude(homelon)); - - // right column - getdisplay().setCursor(202, y0); - getdisplay().print("Batt. sensor:"); - getdisplay().setCursor(320, y0); - getdisplay().print(batt_sensor); - - // Solar sensor - getdisplay().setCursor(202, y0 + 16); - getdisplay().print("Solar sensor:"); - getdisplay().setCursor(320, y0 + 16); - getdisplay().print(solar_sensor); - - // Generator sensor - getdisplay().setCursor(202, y0 + 32); - getdisplay().print("Gen. sensor:"); - getdisplay().setCursor(320, y0 + 32); - getdisplay().print(gen_sensor); - - // Gyro sensor - - } else if (mode == 'C') { - // Card info - getdisplay().setFont(&Ubuntu_Bold12pt8b); - getdisplay().setCursor(8, 48); - getdisplay().print("SD Card info"); - - getdisplay().setFont(&Ubuntu_Bold8pt8b); - - x0 = 20; - y0 = 72; - getdisplay().setCursor(x0, y0); - getdisplay().print("Work in progress..."); - - - } else { - // NMEA2000 device list - getdisplay().setFont(&Ubuntu_Bold12pt8b); - getdisplay().setCursor(8, 48); - getdisplay().print("NMEA2000 device list"); - - getdisplay().setFont(&Ubuntu_Bold8pt8b); - getdisplay().setCursor(20, 80); - getdisplay().print("RxD: "); - getdisplay().print(String(commonData->status.n2kRx)); - getdisplay().setCursor(20, 100); - getdisplay().print("TxD: "); - getdisplay().print(String(commonData->status.n2kTx)); + // call current system page + switch (mode) { + case 'N': + displayModeNormal(); + break; + case 'S': + displayModeSettings(); + break; + case 'C': + displayModeConfig(); + break; + case 'A': + displayModeSDCard(); + break; + case 'D': + displayModeDevicelist(); + break; } // Update display diff --git a/lib/obp60task/obp60task.cpp b/lib/obp60task/obp60task.cpp index 11b986d..e5bb518 100644 --- a/lib/obp60task/obp60task.cpp +++ b/lib/obp60task/obp60task.cpp @@ -54,31 +54,13 @@ void OBP60Init(GwApi *api){ // Check I2C devices - // Init hardware hardwareInit(api); - // Init power rail 5.0V + // Init power String powermode = api->getConfig()->getConfigItem(api->getConfig()->powerMode,true)->asString(); api->getLogger()->logDebug(GwLog::DEBUG,"Power Mode is: %s", powermode.c_str()); - if(powermode == "Max Power" || powermode == "Only 5.0V"){ - #ifdef HARDWARE_V21 - setPortPin(OBP_POWER_50, true); // Power on 5.0V rail - #endif - #ifdef BOARD_OBP40S3 - setPortPin(OBP_POWER_EPD, true);// Power on ePaper display - setPortPin(OBP_POWER_SD, true); // Power on SD card - #endif - } - else{ - #ifdef HARDWARE_V21 - setPortPin(OBP_POWER_50, false); // Power off 5.0V rail - #endif - #ifdef BOARD_OBP40S3 - setPortPin(OBP_POWER_EPD, false);// Power off ePaper display - setPortPin(OBP_POWER_SD, false); // Power off SD card - #endif - } + powerInit(powermode); #ifdef BOARD_OBP40S3 bool sdcard = config->getBool(config->useSDCard); From 32ba4a64ed84e92fbdbeb36216b04dffeee8a647 Mon Sep 17 00:00:00 2001 From: Thomas Hooge Date: Thu, 31 Jul 2025 14:23:11 +0200 Subject: [PATCH 2/4] Add friendly color names to color class for system page display --- lib/obp60task/LedSpiTask.cpp | 24 ++++++++++++++++++++++++ lib/obp60task/LedSpiTask.h | 4 +++- lib/obp60task/PageSystem.cpp | 2 +- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/lib/obp60task/LedSpiTask.cpp b/lib/obp60task/LedSpiTask.cpp index fbbd640..219495c 100644 --- a/lib/obp60task/LedSpiTask.cpp +++ b/lib/obp60task/LedSpiTask.cpp @@ -14,6 +14,30 @@ https://controllerstech.com/ws2812-leds-using-spi/ */ +String Color::toHex() { + char hexColor[8]; + sprintf(hexColor, "#%02X%02X%02X", r, g, b); + return String(hexColor); +} + +String Color::toName() { + static std::map const names = { + {0xff0000, "Red"}, + {0x00ff00, "Green"}, + {0x0000ff, "Blue",}, + {0xff9900, "Orange"}, + {0xffff00, "Yellow"}, + {0x3366ff, "Aqua"}, + {0xff0066, "Violet"}, + {0xffffff, "White"} + }; + int color = (r << 16) + (g << 8) + b; + auto it = names.find(color); + if (it == names.end()) { + return toHex(); + } + return it->second; +} static uint8_t mulcolor(uint8_t f1, uint8_t f2){ uint16_t rt=f1; diff --git a/lib/obp60task/LedSpiTask.h b/lib/obp60task/LedSpiTask.h index c058503..ff63919 100644 --- a/lib/obp60task/LedSpiTask.h +++ b/lib/obp60task/LedSpiTask.h @@ -10,7 +10,7 @@ class Color{ uint8_t g; uint8_t b; Color():r(0),g(0),b(0){} - Color(uint8_t cr, uint8_t cg,uint8_t cb): + Color(uint8_t cr, uint8_t cg, uint8_t cb): b(cb),g(cg),r(cr){} Color(const Color &o):b(o.b),g(o.g),r(o.r){} bool equal(const Color &o) const{ @@ -22,6 +22,8 @@ class Color{ bool operator != (const Color &other) const{ return ! equal(other); } + String toHex(); + String toName(); }; static Color COLOR_GREEN=Color(0,255,0); diff --git a/lib/obp60task/PageSystem.cpp b/lib/obp60task/PageSystem.cpp index bc06b6b..700f9e9 100644 --- a/lib/obp60task/PageSystem.cpp +++ b/lib/obp60task/PageSystem.cpp @@ -310,7 +310,7 @@ private: getdisplay().setCursor(202, y0 + 80); getdisplay().print("Bl color:"); getdisplay().setCursor(320, y0 + 80); - getdisplay().print(commonData->backlight.color); + getdisplay().print(commonData->backlight.color.toName()); getdisplay().setCursor(202, y0 + 96); getdisplay().print("Bl mode:"); getdisplay().setCursor(320, y0 + 96); From aed389f3b21292bf9ecc5492252427c581372ed4 Mon Sep 17 00:00:00 2001 From: Thomas Hooge Date: Fri, 1 Aug 2025 17:08:01 +0200 Subject: [PATCH 3/4] Added code for configuration menu handling --- lib/obp60task/ConfigMenu.cpp | 194 +++++++++++++++++++++++++++++++++++ lib/obp60task/ConfigMenu.h | 63 ++++++++++++ 2 files changed, 257 insertions(+) create mode 100644 lib/obp60task/ConfigMenu.cpp create mode 100644 lib/obp60task/ConfigMenu.h diff --git a/lib/obp60task/ConfigMenu.cpp b/lib/obp60task/ConfigMenu.cpp new file mode 100644 index 0000000..1627531 --- /dev/null +++ b/lib/obp60task/ConfigMenu.cpp @@ -0,0 +1,194 @@ +/* + Menu system for online configuration +*/ +#include "ConfigMenu.h" + +ConfigMenuItem::ConfigMenuItem(String itemtype, String itemlabel, uint16_t itemval, String itemunit) { + if (! (itemtype == "int" or itemtype == "bool")) { + valtype = "int"; + } else { + valtype = itemtype; + } + label = itemlabel; + min = 0; + max = std::numeric_limits::max(); + value = itemval; + unit = itemunit; +} + +void ConfigMenuItem::setRange(uint16_t valmin, uint16_t valmax, std::vector valsteps) { + min = valmin; + max = valmax; + steps = valsteps; +}; + +bool ConfigMenuItem::checkRange(uint16_t checkval) { + return (checkval >= min) and (checkval <= max); +} + +String ConfigMenuItem::getLabel() { + return label; +}; + +uint16_t ConfigMenuItem::getValue() { + return value; +} + +bool ConfigMenuItem::setValue(uint16_t newval) { + if (valtype == "int") { + if (newval >= min and newval <= max) { + value = newval; + return true; + } + return false; // out of range + } else if (valtype == "bool") { + value = (newval != 0) ? 1 : 0; + return true; + } + return false; // invalid type +}; + +void ConfigMenuItem::incValue() { + // increase value by step + if (valtype == "int") { + if (value + step < max) { + value += step; + } else { + value = max; + } + } else if (valtype == "bool") { + value = !value; + } +}; + +void ConfigMenuItem::decValue() { + // decrease value by step + if (valtype == "int") { + if (value - step > min) { + value -= step; + } else { + value = min; + } + } else if (valtype == "bool") { + value = !value; + } +}; + +String ConfigMenuItem::getUnit() { + return unit; +} + +uint16_t ConfigMenuItem::getStep() { + return step; +} + +void ConfigMenuItem::setStep(uint16_t newstep) { + if (std::find(steps.begin(), steps.end(), newstep) == steps.end()) { + return; // invalid step: not in list of possible steps + } + step = newstep; +} + +int8_t ConfigMenuItem::getPos() { + return position; +}; + +void ConfigMenuItem::setPos(int8_t newpos) { + position = newpos; +}; + +String ConfigMenuItem::getType() { + return valtype; +} + +ConfigMenu::ConfigMenu(String menutitle, uint16_t menu_x, uint16_t menu_y) { + title = menutitle; + x = menu_x; + y = menu_y; +}; + +ConfigMenuItem* ConfigMenu::addItem(String key, String label, String valtype, uint16_t val, String valunit) { + if (items.find(key) != items.end()) { + // duplicate keys not allowed + return nullptr; + } + ConfigMenuItem *itm = new ConfigMenuItem(valtype, label, val, valunit); + items.insert(std::pair(key, itm)); + // Append key to index, index starting with 0 + int8_t ix = items.size() - 1; + index[ix] = key; + itm->setPos(ix); + return itm; +}; + +void ConfigMenu::setItemDimension(uint16_t itemwidth, uint16_t itemheight) { + w = itemwidth; + h = itemheight; +}; + +void ConfigMenu::setItemActive(String key) { + if (items.find(key) != items.end()) { + activeitem = items[key]->getPos(); + } else { + activeitem = -1; + } +}; + +int8_t ConfigMenu::getActiveIndex() { + return activeitem; +} + +ConfigMenuItem* ConfigMenu::getActiveItem() { + if (activeitem < 0) { + return nullptr; + } + return items[index[activeitem]]; +}; + +ConfigMenuItem* ConfigMenu::getItemByIndex(uint8_t ix) { + if (ix > index.size() - 1) { + return nullptr; + } + return items[index[ix]]; +}; + +ConfigMenuItem* ConfigMenu::getItemByKey(String key) { + if (items.find(key) == items.end()) { + return nullptr; + } + return items[key]; +}; + +uint8_t ConfigMenu::getItemCount() { + return items.size(); +}; + +void ConfigMenu::goPrev() { + if (activeitem == 0) { + activeitem = items.size() - 1; + } else { + activeitem--; + } +} + +void ConfigMenu::goNext() { + if (activeitem == items.size() - 1) { + activeitem = 0; + } else { + activeitem++; + } +} + +Point ConfigMenu::getXY() { + return {static_cast(x), static_cast(y)}; +} + +Rect ConfigMenu::getRect() { + return {static_cast(x), static_cast(y), + static_cast(w), static_cast(h)}; +} + +Rect ConfigMenu::getItemRect(int8_t index) { + return {static_cast(x), static_cast(y + index * h), + static_cast(w), static_cast(h)}; +} diff --git a/lib/obp60task/ConfigMenu.h b/lib/obp60task/ConfigMenu.h new file mode 100644 index 0000000..439097d --- /dev/null +++ b/lib/obp60task/ConfigMenu.h @@ -0,0 +1,63 @@ +#pragma once + +#include +#include +#include +#include "Graphics.h" // for Point and Rect + +class ConfigMenuItem { +private: + String label; + uint16_t value; + String unit; + String valtype; // "int" | "bool" + uint16_t min; + uint16_t max; + std::vector steps; + uint16_t step; + int8_t position; // counted fom 0 + +public: + ConfigMenuItem(String itemtype, String itemlabel, uint16_t itemval, String itemunit); + void setRange(uint16_t valmin, uint16_t valmax, std::vector steps); + bool checkRange(uint16_t checkval); + String getLabel(); + uint16_t getValue(); + bool setValue(uint16_t newval); + void incValue(); + void decValue(); + String getUnit(); + uint16_t getStep(); + void setStep(uint16_t newstep); + int8_t getPos(); + void setPos(int8_t newpos); + String getType(); +}; + +class ConfigMenu { +private: + String title; + std::map items; + std::map index; + int8_t activeitem = -1; // refers to position of item + uint16_t x; + uint16_t y; + uint16_t w; + uint16_t h; + +public: + ConfigMenu(String title, uint16_t menu_x, uint16_t menu_y); + ConfigMenuItem* addItem(String key, String label, String valtype, uint16_t val, String valunit); + void setItemDimension(uint16_t itemwidth, uint16_t itemheight); + int8_t getActiveIndex(); + void setItemActive(String key); + ConfigMenuItem* getActiveItem(); + ConfigMenuItem* getItemByIndex(uint8_t index); + ConfigMenuItem* getItemByKey(String key); + uint8_t getItemCount(); + void goPrev(); + void goNext(); + Point getXY(); + Rect getRect(); + Rect getItemRect(int8_t index); +}; From d5b0af3b19dadf39a74962280f268654928abf2b Mon Sep 17 00:00:00 2001 From: Thomas Hooge Date: Sun, 3 Aug 2025 21:18:08 +0200 Subject: [PATCH 4/4] Integrated config menu into system page --- lib/obp60task/ConfigMenu.cpp | 10 +++++ lib/obp60task/ConfigMenu.h | 3 ++ lib/obp60task/Graphics.cpp | 25 +++++++++++++ lib/obp60task/Graphics.h | 17 +++++++++ lib/obp60task/OBP60Extensions.cpp | 46 +++++++++++++---------- lib/obp60task/OBP60Extensions.h | 9 ++--- lib/obp60task/PageSystem.cpp | 61 +++++++++++++++++++++++++++++-- 7 files changed, 143 insertions(+), 28 deletions(-) create mode 100644 lib/obp60task/Graphics.cpp create mode 100644 lib/obp60task/Graphics.h diff --git a/lib/obp60task/ConfigMenu.cpp b/lib/obp60task/ConfigMenu.cpp index 1627531..65cff9f 100644 --- a/lib/obp60task/ConfigMenu.cpp +++ b/lib/obp60task/ConfigMenu.cpp @@ -192,3 +192,13 @@ Rect ConfigMenu::getItemRect(int8_t index) { return {static_cast(x), static_cast(y + index * h), static_cast(w), static_cast(h)}; } + +void ConfigMenu::setCallback(void (*callback)()) { + fptrCallback = callback; +} + +void ConfigMenu::storeValues() { + if (fptrCallback) { + fptrCallback(); + } +} diff --git a/lib/obp60task/ConfigMenu.h b/lib/obp60task/ConfigMenu.h index 439097d..e3e7993 100644 --- a/lib/obp60task/ConfigMenu.h +++ b/lib/obp60task/ConfigMenu.h @@ -44,6 +44,7 @@ private: uint16_t y; uint16_t w; uint16_t h; + void (*fptrCallback)(); public: ConfigMenu(String title, uint16_t menu_x, uint16_t menu_y); @@ -60,4 +61,6 @@ public: Point getXY(); Rect getRect(); Rect getItemRect(int8_t index); + void setCallback(void (*callback)()); + void storeValues(); }; diff --git a/lib/obp60task/Graphics.cpp b/lib/obp60task/Graphics.cpp new file mode 100644 index 0000000..ae0dcbd --- /dev/null +++ b/lib/obp60task/Graphics.cpp @@ -0,0 +1,25 @@ +/* +Generic graphics functions + +*/ +#include +#include "Graphics.h" + +Point rotatePoint(const Point& origin, const Point& p, double angle) { + // rotate poind around origin by degrees + Point rotated; + double phi = angle * M_PI / 180.0; + double dx = p.x - origin.x; + double dy = p.y - origin.y; + rotated.x = origin.x + cos(phi) * dx - sin(phi) * dy; + rotated.y = origin.y + sin(phi) * dx + cos(phi) * dy; + return rotated; +} + +std::vector rotatePoints(const Point& origin, const std::vector& pts, double angle) { + std::vector rotatedPoints; + for (const auto& p : pts) { + rotatedPoints.push_back(rotatePoint(origin, p, angle)); + } + return rotatedPoints; +} diff --git a/lib/obp60task/Graphics.h b/lib/obp60task/Graphics.h new file mode 100644 index 0000000..b55971b --- /dev/null +++ b/lib/obp60task/Graphics.h @@ -0,0 +1,17 @@ +#pragma once +#include + +struct Point { + double x; + double y; +}; + +struct Rect { + double x; + double y; + double w; + double h; +}; + +Point rotatePoint(const Point& origin, const Point& p, double angle); +std::vector rotatePoints(const Point& origin, const std::vector& pts, double angle); diff --git a/lib/obp60task/OBP60Extensions.cpp b/lib/obp60task/OBP60Extensions.cpp index 29d76a8..75821e9 100644 --- a/lib/obp60task/OBP60Extensions.cpp +++ b/lib/obp60task/OBP60Extensions.cpp @@ -297,30 +297,20 @@ String xdrDelete(String input){ return input; } -Point rotatePoint(const Point& origin, const Point& p, double angle) { - // rotate poind around origin by degrees - Point rotated; - double phi = angle * M_PI / 180.0; - double dx = p.x - origin.x; - double dy = p.y - origin.y; - rotated.x = origin.x + cos(phi) * dx - sin(phi) * dy; - rotated.y = origin.y + sin(phi) * dx + cos(phi) * dy; - return rotated; -} - -std::vector rotatePoints(const Point& origin, const std::vector& pts, double angle) { - std::vector rotatedPoints; - for (const auto& p : pts) { - rotatedPoints.push_back(rotatePoint(origin, p, angle)); - } - return rotatedPoints; -} - void fillPoly4(const std::vector& p4, uint16_t color) { getdisplay().fillTriangle(p4[0].x, p4[0].y, p4[1].x, p4[1].y, p4[2].x, p4[2].y, color); getdisplay().fillTriangle(p4[0].x, p4[0].y, p4[2].x, p4[2].y, p4[3].x, p4[3].y, color); } +void drawPoly(const std::vector& points, uint16_t color) { + size_t polysize = points.size(); + for (size_t i = 0; i < polysize - 1; i++) { + getdisplay().drawLine(points[i].x, points[i].y, points[i+1].x, points[i+1].y, color); + } + // close path + getdisplay().drawLine(points[polysize-1].x, points[polysize-1].y, points[0].x, points[0].y, color); +} + // Split string into words, whitespace separated std::vector split(const String &s) { std::vector words; @@ -382,6 +372,24 @@ void drawTextRalign(int16_t x, int16_t y, String text) { getdisplay().print(text); } +// Draw text inside box, normal or inverted +void drawTextBoxed(Rect box, String text, uint16_t fg, uint16_t bg, bool inverted, bool border) { + if (inverted) { + getdisplay().fillRect(box.x, box.y, box.w, box.h, fg); + getdisplay().setTextColor(bg); + } else { + if (border) { + getdisplay().fillRect(box.x + 1, box.y + 1, box.w - 2, box.h - 2, bg); + getdisplay().drawRect(box.x, box.y, box.w, box.h, fg); + } + getdisplay().setTextColor(fg); + } + uint16_t border_offset = box.h / 4; // 25% of box height + getdisplay().setCursor(box.x + border_offset, box.y + box.h - border_offset); + getdisplay().print(text); + getdisplay().setTextColor(fg); +} + // Show a triangle for trend direction high (x, y is the left edge) void displayTrendHigh(int16_t x, int16_t y, uint16_t size, uint16_t color){ getdisplay().fillTriangle(x, y, x+size*2, y, x+size, y-size*2, color); diff --git a/lib/obp60task/OBP60Extensions.h b/lib/obp60task/OBP60Extensions.h index 92e5486..bd15974 100644 --- a/lib/obp60task/OBP60Extensions.h +++ b/lib/obp60task/OBP60Extensions.h @@ -4,6 +4,7 @@ #include #include "OBP60Hardware.h" #include "LedSpiTask.h" +#include "Graphics.h" #include // E-paper lib V2 #include // I2C FRAM @@ -62,13 +63,8 @@ GxEPD2_BW & getdisplay(); #define PAGE_UPDATE 1 // page wants display to update #define PAGE_HIBERNATE 2 // page wants displey to hibernate -struct Point { - double x; - double y; -}; -Point rotatePoint(const Point& origin, const Point& p, double angle); -std::vector rotatePoints(const Point& origin, const std::vector& pts, double angle); void fillPoly4(const std::vector& p4, uint16_t color); +void drawPoly(const std::vector& points, uint16_t color); void deepSleep(CommonData &common); @@ -97,6 +93,7 @@ String xdrDelete(String input); // Delete xdr prefix from string void drawTextCenter(int16_t cx, int16_t cy, String text); void drawTextRalign(int16_t x, int16_t y, String text); +void drawTextBoxed(Rect box, String text, uint16_t fg, uint16_t bg, bool inverted, bool border); void displayTrendHigh(int16_t x, int16_t y, uint16_t size, uint16_t color); void displayTrendLow(int16_t x, int16_t y, uint16_t size, uint16_t color); diff --git a/lib/obp60task/PageSystem.cpp b/lib/obp60task/PageSystem.cpp index 700f9e9..2de30cc 100644 --- a/lib/obp60task/PageSystem.cpp +++ b/lib/obp60task/PageSystem.cpp @@ -2,6 +2,7 @@ #include "Pagedata.h" #include "OBP60Extensions.h" +#include "ConfigMenu.h" #include "images/logo64.xbm" #include #include "qrcode.h" @@ -44,6 +45,9 @@ class PageSystem : public Page { private: + GwConfigHandler *config; + GwLog *logger; + // NVRAM config options String flashLED; @@ -67,6 +71,9 @@ private: double homelon; char mode = 'N'; // (N)ormal, (S)ettings, (C)onfiguration, (D)evice list, c(A)rd + int8_t editmode = -1; // marker for menu/edit/set function + + ConfigMenu *menu; void incMode() { if (mode == 'N') { // Normal @@ -219,7 +226,7 @@ private: getdisplay().setFont(&Ubuntu_Bold8pt8b); - getdisplay().setCursor(x0, y0); + /*getdisplay().setCursor(x0, y0); getdisplay().print("CPU speed: 80 | 160 | 240"); getdisplay().setCursor(x0, y0 + 1 * dy); getdisplay().print("Power mode: Max | 5V | Min"); @@ -228,7 +235,30 @@ private: // TODO Change NVRAM-preferences settings here getdisplay().setCursor(x0, y0 + 4 * dy); - getdisplay().print("Simulation: On | Off"); + getdisplay().print("Simulation: On | Off"); */ + + getdisplay().setFont(&Ubuntu_Bold8pt8b); + for (int i = 0 ; i < menu->getItemCount(); i++) { + ConfigMenuItem *itm = menu->getItemByIndex(i); + if (!itm) { + LOG_DEBUG(GwLog::ERROR, "Menu item not found: %d", i); + } else { + Rect r = menu->getItemRect(i); + bool inverted = (i == menu->getActiveIndex()); + drawTextBoxed(r, itm->getLabel(), commonData->fgcolor, commonData->bgcolor, inverted, false); + if (inverted and editmode > 0) { + // triangle as edit marker + getdisplay().fillTriangle(r.x + r.w + 20, r.y, r.x + r.w + 30, r.y + r.h / 2, r.x + r.w + 20, r.y + r.h, commonData->fgcolor); + } + getdisplay().setCursor(r.x + r.w + 40, r.y + r.h - 4); + if (itm->getType() == "int") { + getdisplay().print(itm->getValue()); + getdisplay().print(itm->getUnit()); + } else { + getdisplay().print(itm->getValue() == 0 ? "No" : "Yes"); + } + } + } } void displayModeSettings() { @@ -351,10 +381,17 @@ private: getdisplay().print(String(commonData->status.n2kTx)); } + void storeConfig() { + menu->storeValues(); + } + public: PageSystem(CommonData &common){ commonData = &common; - common.logger->logDebug(GwLog::LOG,"Instantiate PageSystem"); + config = commonData->config; + logger = commonData->logger; + + logger->logDebug(GwLog::LOG,"Instantiate PageSystem"); if (hasFRAM) { mode = fram.read(FRAM_SYSTEM_MODE); } @@ -378,6 +415,24 @@ public: rot_sensor = common.config->getString(common.config->useRotSensor); homelat = common.config->getString(common.config->homeLAT).toDouble(); homelon = common.config->getString(common.config->homeLON).toDouble(); + + // CPU speed: 80 | 160 | 240 + // Power mode: Max | 5V | Min + // Accesspoint: On | Off + + // TODO Change NVRAM-preferences settings here + // getdisplay().setCursor(x0, y0 + 4 * dy); + // getdisplay().print("Simulation: On | Off"); + + // Initialize config menu + menu = new ConfigMenu("Options", 40, 80); + menu->setItemDimension(150, 20); + + ConfigMenuItem *newitem; + newitem = menu->addItem("accesspoint", "Accesspoint", "bool", 0, ""); + newitem = menu->addItem("simulation", "Simulation", "on/off", 0, ""); + menu->setItemActive("accesspoint"); + } virtual void setupKeys(){