From ba8789797c38ced17d1b1cd8bcb63caf3639a804 Mon Sep 17 00:00:00 2001 From: David Gwilliam Date: Fri, 20 Feb 2026 02:11:21 -0800 Subject: [PATCH] feat: add status screen with system info display --- boards/esp32-32e-4/DisplayDriverTFT.cpp | 86 ++++++++++++++++++++ boards/esp32-32e-4/DisplayDriverTFT.h | 6 ++ justfile | 2 + libraries/KlubhausCore/src/DoorbellLogic.cpp | 14 +++- libraries/KlubhausCore/src/ScreenState.h | 4 +- vendor/.gitignore | 2 - vendor/esp32-s3-lcd-43/LovyanGFX | 1 - 7 files changed, 110 insertions(+), 5 deletions(-) delete mode 100644 vendor/.gitignore delete mode 160000 vendor/esp32-s3-lcd-43/LovyanGFX diff --git a/boards/esp32-32e-4/DisplayDriverTFT.cpp b/boards/esp32-32e-4/DisplayDriverTFT.cpp index e5e1b0a..cc6f521 100644 --- a/boards/esp32-32e-4/DisplayDriverTFT.cpp +++ b/boards/esp32-32e-4/DisplayDriverTFT.cpp @@ -138,6 +138,12 @@ void DisplayDriverTFT::render(const ScreenState& st) { _needsRedraw = false; } break; + case ScreenID::STATUS: + if(_needsRedraw) { + drawStatus(st); + _needsRedraw = false; + } + break; case ScreenID::OFF: if(_needsRedraw) { _tft.fillScreen(TFT_BLACK); @@ -194,6 +200,22 @@ void DisplayDriverTFT::drawAlert(const ScreenState& st) { _tft.fillScreen(bg); _tft.setTextColor(TFT_WHITE, bg); + // Progressive fill hint - draws dark overlay from bottom rising up + if(_alertTouchDown) { + uint32_t touchElapsed = millis() - _alertTouchStartMs; + float progress = (float)touchElapsed / (float)ALERT_FILL_DURATION_MS; + if(progress > 1.0f) + progress = 1.0f; + + int dispH = _tft.height(); + int fillHeight = (int)(dispH * progress); + if(fillHeight > 0) { + // Draw dark overlay from bottom + uint16_t overlay = _tft.color565(80, 0, 0); // Dark red + _tft.fillRect(0, dispH - fillHeight, _tft.width(), fillHeight, overlay); + } + } + setTitleFont(); _tft.setCursor(10, 28); // y=28 baseline for ~18px font _tft.print(st.alertTitle.length() > 0 ? st.alertTitle : "ALERT"); @@ -257,6 +279,61 @@ void DisplayDriverTFT::drawDashboard(const ScreenState& st) { } } +void DisplayDriverTFT::drawStatus(const ScreenState& st) { + int dispW = _tft.width(); + int dispH = _tft.height(); + + _tft.fillScreen(TFT_BLACK); + + // Header + _tft.fillRect(0, 0, dispW, STYLE_HEADER_HEIGHT, 0x1A1A); + _tft.setTextSize(1); + _tft.setTextColor(TFT_WHITE); + _tft.setCursor(5, 20); + _tft.print("STATUS"); + + // Back button in lower right + _tft.setCursor(dispW - 60, dispH - 20); + _tft.print("[BACK]"); + + // Status info + setBodyFont(); + int y = STYLE_HEADER_HEIGHT + 20; + + // WiFi + _tft.setCursor(10, y); + _tft.printf("WiFi: %s", st.wifiSsid.length() > 0 ? st.wifiSsid.c_str() : "N/A"); + y += 20; + + _tft.setCursor(10, y); + _tft.printf("RSSI: %d dBm", st.wifiRssi); + y += 20; + + _tft.setCursor(10, y); + _tft.printf("IP: %s", st.ipAddr.length() > 0 ? st.ipAddr.c_str() : "N/A"); + y += 30; + + // Uptime + uint32_t upSec = st.uptimeMs / 1000; + uint32_t upMin = upSec / 60; + uint32_t upHr = upMin / 60; + upSec = upSec % 60; + upMin = upMin % 60; + _tft.setCursor(10, y); + _tft.printf("Uptime: %02lu:%02lu:%02lu", upHr, upMin, upSec); + y += 20; + + // Heap + _tft.setCursor(10, y); + _tft.printf("Heap: %d bytes", ESP.getFreeHeap()); + y += 20; + + // Last poll + uint32_t pollAgo = (millis() - st.lastPollMs) / 1000; + _tft.setCursor(10, y); + _tft.printf("Last poll: %lu sec ago", pollAgo); +} + // ── Touch ─────────────────────────────────────────────────── TouchEvent DisplayDriverTFT::readTouch() { @@ -317,6 +394,15 @@ TouchEvent DisplayDriverTFT::readTouch() { // Track previous state for next call _touchWasPressed = touched; + + // Track alert touch for progressive hint + if(evt.pressed) { + _alertTouchDown = true; + _alertTouchStartMs = millis(); + } else if(evt.released) { + _alertTouchDown = false; + } + return evt; } diff --git a/boards/esp32-32e-4/DisplayDriverTFT.h b/boards/esp32-32e-4/DisplayDriverTFT.h index e0c3d6e..e4f9e2f 100644 --- a/boards/esp32-32e-4/DisplayDriverTFT.h +++ b/boards/esp32-32e-4/DisplayDriverTFT.h @@ -31,6 +31,7 @@ private: void drawBoot(const ScreenState& st); void drawAlert(const ScreenState& st); void drawDashboard(const ScreenState& st); + void drawStatus(const ScreenState& st); TFT_eSPI _tft; @@ -40,6 +41,11 @@ private: BootStage _lastBootStage = BootStage::SPLASH; bool _needsRedraw = true; + // Touch hint for alert - progressive fill from bottom + bool _alertTouchDown = false; + uint32_t _alertTouchStartMs = 0; + static constexpr uint32_t ALERT_FILL_DURATION_MS = 3000; + // Touch tracking for press/release detection bool _touchWasPressed = false; int _touchDownX = -1; diff --git a/justfile b/justfile index 19bf84f..a06e807 100644 --- a/justfile +++ b/justfile @@ -125,11 +125,13 @@ install-libs-shared: install: install-libs-shared #!/usr/bin/env bash ./boards/{{BOARD}}/install.sh + arduino-cli core install esp32:esp32 # Clean build artifacts clean: #!/usr/bin/env bash rm -rf vendor/ + rm -rf .cache/ rm -rf boards/esp32-32e/build rm -rf boards/esp32-32e-4/build rm -rf boards/esp32-s3-lcd-43/build diff --git a/libraries/KlubhausCore/src/DoorbellLogic.cpp b/libraries/KlubhausCore/src/DoorbellLogic.cpp index 3e1cb5e..677d508 100644 --- a/libraries/KlubhausCore/src/DoorbellLogic.cpp +++ b/libraries/KlubhausCore/src/DoorbellLogic.cpp @@ -354,6 +354,18 @@ int DoorbellLogic::handleTouch(const TouchEvent& evt) { return -1; } + // Handle STATUS screen back button (lower right corner) + if(_state.screen == ScreenID::STATUS) { + int dispW = _display->width(); + int dispH = _display->height(); + // Back button area: lower right (dispW-60 to dispW, dispH-30 to dispH) + if(evt.x >= dispW - 60 && evt.y >= dispH - 30) { + Serial.printf("[%lu] [TOUCH] STATUS → DASHBOARD (back)\n", millis()); + setScreen(ScreenID::DASHBOARD); + return -1; + } + } + // Draw debug crosshair at touch point #ifdef DEBUG_MODE if(_state.screen == ScreenID::DASHBOARD) { @@ -380,7 +392,7 @@ int DoorbellLogic::handleTouch(const TouchEvent& evt) { silenceAlert(); break; case TileAction::STATUS: - heartbeat(); + setScreen(ScreenID::STATUS); break; case TileAction::REBOOT: flushStatus("REBOOT (tile)"); diff --git a/libraries/KlubhausCore/src/ScreenState.h b/libraries/KlubhausCore/src/ScreenState.h index 4d0c10a..0e0b6a5 100644 --- a/libraries/KlubhausCore/src/ScreenState.h +++ b/libraries/KlubhausCore/src/ScreenState.h @@ -3,7 +3,7 @@ enum class DeviceState { BOOTED, SILENT, ALERTING, SILENCED }; -enum class ScreenID { BOOT, OFF, ALERT, DASHBOARD }; +enum class ScreenID { BOOT, OFF, ALERT, DASHBOARD, STATUS }; enum class BootStage { SPLASH, INIT_DISPLAY, INIT_NETWORK, CONNECTING_WIFI, READY, DONE }; @@ -94,6 +94,8 @@ inline const char* screenIdStr(ScreenID s) { return "ALERT"; case ScreenID::DASHBOARD: return "DASHBOARD"; + case ScreenID::STATUS: + return "STATUS"; } return "?"; } diff --git a/vendor/.gitignore b/vendor/.gitignore deleted file mode 100644 index d6b7ef3..0000000 --- a/vendor/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore diff --git a/vendor/esp32-s3-lcd-43/LovyanGFX b/vendor/esp32-s3-lcd-43/LovyanGFX deleted file mode 160000 index 4299835..0000000 --- a/vendor/esp32-s3-lcd-43/LovyanGFX +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 42998359d8a2eaf188643da7813122c6c3efd2fd