From e4609c69786c5c9b23aa7b31ada0fb2986979b02 Mon Sep 17 00:00:00 2001 From: David Gwilliam Date: Wed, 18 Feb 2026 17:57:54 -0800 Subject: [PATCH] refactor(Format code): Improve readability and structure --- boards/esp32-32e-4/DisplayDriverTFT.cpp | 10 +- boards/esp32-32e/DisplayDriverTFT.cpp | 3 +- boards/esp32-s3-lcd-43/DisplayDriverGFX.cpp | 5 +- boards/esp32-s3-lcd-43/DisplayDriverGFX.h | 3 + boards/esp32-s3-lcd-43/board-config.sh | 2 +- libraries/KlubhausCore/src/DisplayManager.h | 152 +++++++++---------- libraries/KlubhausCore/src/DoorbellLogic.cpp | 8 +- libraries/KlubhausCore/src/IDisplayDriver.h | 12 +- libraries/KlubhausCore/src/ScreenState.h | 28 ++-- mise.toml | 32 +--- vendor/esp32-s3-lcd-43/LovyanGFX | 1 - 11 files changed, 120 insertions(+), 136 deletions(-) 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 77eb1e3..585370a 100644 --- a/boards/esp32-32e-4/DisplayDriverTFT.cpp +++ b/boards/esp32-32e-4/DisplayDriverTFT.cpp @@ -1,4 +1,5 @@ #include "DisplayDriverTFT.h" + #include extern DisplayManager display; @@ -35,7 +36,8 @@ void DisplayDriverTFT::setBacklight(bool on) { digitalWrite(PIN_LCD_BL, on ? HIG // ── Rendering ─────────────────────────────────────────────── void DisplayDriverTFT::render(const ScreenState& st) { - if(st.screen != _lastScreen || (st.screen == ScreenID::BOOT && st.bootStage != _lastBootStage)) { + if(st.screen != _lastScreen + || (st.screen == ScreenID::BOOT && st.bootStage != _lastBootStage)) { _needsRedraw = true; _lastScreen = st.screen; _lastBootStage = st.bootStage; @@ -162,7 +164,7 @@ void DisplayDriverTFT::drawDashboard(const ScreenState& st) { _tft.setTextSize(2); int textLen = strlen(tileLabels[i]); int textW = textLen * 12; - _tft.setCursor(x + w/2 - textW/2, y + h/2 - 10); + _tft.setCursor(x + w / 2 - textW / 2, y + h / 2 - 10); _tft.print(tileLabels[i]); } } @@ -202,9 +204,7 @@ TouchEvent DisplayDriverTFT::readTouch() { return evt; } -uint16_t DisplayDriverTFT::getRawTouchZ() { - return _tft.getTouchRawZ(); -} +uint16_t DisplayDriverTFT::getRawTouchZ() { return _tft.getTouchRawZ(); } void DisplayDriverTFT::transformTouch(int* x, int* y) { // Resistive touch panel is rotated 90° vs display - swap coordinates diff --git a/boards/esp32-32e/DisplayDriverTFT.cpp b/boards/esp32-32e/DisplayDriverTFT.cpp index 07a8d13..951eb01 100644 --- a/boards/esp32-32e/DisplayDriverTFT.cpp +++ b/boards/esp32-32e/DisplayDriverTFT.cpp @@ -25,7 +25,8 @@ void DisplayDriverTFT::setBacklight(bool on) { digitalWrite(PIN_LCD_BL, on ? HIG // ── Rendering ─────────────────────────────────────────────── void DisplayDriverTFT::render(const ScreenState& st) { - if(st.screen != _lastScreen || (st.screen == ScreenID::BOOT && st.bootStage != _lastBootStage)) { + if(st.screen != _lastScreen + || (st.screen == ScreenID::BOOT && st.bootStage != _lastBootStage)) { _needsRedraw = true; _lastScreen = st.screen; _lastBootStage = st.bootStage; diff --git a/boards/esp32-s3-lcd-43/DisplayDriverGFX.cpp b/boards/esp32-s3-lcd-43/DisplayDriverGFX.cpp index 7d05f85..aa6b8ab 100644 --- a/boards/esp32-s3-lcd-43/DisplayDriverGFX.cpp +++ b/boards/esp32-s3-lcd-43/DisplayDriverGFX.cpp @@ -151,7 +151,8 @@ void DisplayDriverGFX::render(const ScreenState& state) { return; // Check if we need full redraw - if(state.screen != _lastScreen || (state.screen == ScreenID::BOOT && state.bootStage != _lastBootStage)) { + if(state.screen != _lastScreen + || (state.screen == ScreenID::BOOT && state.bootStage != _lastBootStage)) { _needsRedraw = true; _lastScreen = state.screen; _lastBootStage = state.bootStage; @@ -284,7 +285,7 @@ void DisplayDriverGFX::drawDashboard(const ScreenState& state) { // Tile label _gfx->setTextColor(0xFFFF); _gfx->setTextSize(2); - _gfx->setCursor(x + w/2 - 10, y + h/2 - 10); + _gfx->setCursor(x + w / 2 - 10, y + h / 2 - 10); _gfx->print(tileLabels[i]); } } diff --git a/boards/esp32-s3-lcd-43/DisplayDriverGFX.h b/boards/esp32-s3-lcd-43/DisplayDriverGFX.h index d0c1cbb..a683ba2 100644 --- a/boards/esp32-s3-lcd-43/DisplayDriverGFX.h +++ b/boards/esp32-s3-lcd-43/DisplayDriverGFX.h @@ -18,6 +18,9 @@ public: int width() override; int height() override; + // Dashboard tile mapping + int dashboardTouch(int x, int y); + // ── Internal ── static DisplayDriverGFX& instance(); diff --git a/boards/esp32-s3-lcd-43/board-config.sh b/boards/esp32-s3-lcd-43/board-config.sh index 77e0f15..58eb01c 100644 --- a/boards/esp32-s3-lcd-43/board-config.sh +++ b/boards/esp32-s3-lcd-43/board-config.sh @@ -1,4 +1,4 @@ FQBN="esp32:esp32:waveshare_esp32_s3_touch_lcd_43:PSRAM=enabled,FlashSize=16M,USBMode=hwcdc,PartitionScheme=app3M_fat9M_16MB" PORT="/dev/ttyACM0" -LIBS="--libraries ./vendor/esp32-s3-lcd-43/LovyanGFX" +LIBS="--libraries ~/Arduino/libraries/LovyanGFX" OPTS="-DDEBUG_MODE -DBOARD_HAS_PSRAM -DLGFX_USE_V1" diff --git a/libraries/KlubhausCore/src/DisplayManager.h b/libraries/KlubhausCore/src/DisplayManager.h index 240533b..159f377 100644 --- a/libraries/KlubhausCore/src/DisplayManager.h +++ b/libraries/KlubhausCore/src/DisplayManager.h @@ -2,6 +2,7 @@ #include "IDisplayDriver.h" #include "ScreenState.h" + #include /// Layout helper for dashboard tiles - computes positions based on constraints @@ -10,50 +11,45 @@ public: /// Calculate tile layouts for the given display dimensions /// Returns array of TileLayout (must have capacity for DASHBOARD_TILE_COUNT) /// Returns the number of columns and rows used - static int calculateLayouts( - int displayW, - int displayH, - int headerH, - int margin, - TileLayout* outLayouts, - int* outCols, - int* outRows - ) { + static int calculateLayouts(int displayW, int displayH, int headerH, int margin, + TileLayout* outLayouts, int* outCols, int* outRows) { int contentH = displayH - headerH; int tileCount = DASHBOARD_TILE_COUNT; - + // Determine base grid based on display aspect ratio int cols, rows; calculateGrid(tileCount, displayW, contentH, &cols, &rows); - + // Calculate base cell sizes int cellW = displayW / cols; int cellH = contentH / rows; - + // Simple first-fit: place tiles in order, respecting min sizes // For more complex layouts, tiles could specify preferred positions for(int i = 0; i < tileCount; i++) { const DashboardTile& tile = DASHBOARD_TILES[i]; int tileCols = tile.constraint.minCols; int tileRows = tile.constraint.minRows; - + // Find next available position int col = 0, row = 0; findNextPosition(outLayouts, i, cols, rows, &col, &row); - + // Ensure tile fits within grid - if(col + tileCols > cols) tileCols = cols - col; - if(row + tileRows > rows) tileRows = rows - row; - + if(col + tileCols > cols) + tileCols = cols - col; + if(row + tileRows > rows) + tileRows = rows - row; + // Calculate pixel position int x = col * cellW + margin; int y = headerH + row * cellH + margin; int w = tileCols * cellW - 2 * margin; int h = tileRows * cellH - 2 * margin; - - outLayouts[i] = {x, y, w, h, col, row, tileCols, tileRows}; + + outLayouts[i] = { x, y, w, h, col, row, tileCols, tileRows }; } - + *outCols = cols; *outRows = rows; return tileCount; @@ -61,17 +57,20 @@ public: private: /// Calculate optimal grid dimensions based on display and tile constraints - static void calculateGrid(int tileCount, int displayW, int contentH, int* outCols, int* outRows) { + static void calculateGrid( + int tileCount, int displayW, int contentH, int* outCols, int* outRows) { // Calculate aspect ratio to determine preferred layout float aspectRatio = (float)displayW / contentH; - + // Start with simple square-ish grid int cols = (int)std::sqrt(tileCount * aspectRatio); - if(cols < 1) cols = 1; - if(cols > tileCount) cols = tileCount; - + if(cols < 1) + cols = 1; + if(cols > tileCount) + cols = tileCount; + int rows = (tileCount + cols - 1) / cols; - + // For wide displays (landscape), prefer more columns if(aspectRatio > 1.5f && tileCount <= 6) { cols = tileCount; @@ -82,21 +81,22 @@ private: rows = tileCount; cols = 1; } - + *outCols = cols; *outRows = rows; } - + /// Find next available grid position - static void findNextPosition(const TileLayout* layouts, int count, int gridCols, int gridRows, int* outCol, int* outRow) { + static void findNextPosition(const TileLayout* layouts, int count, int gridCols, int gridRows, + int* outCol, int* outRow) { // Simple: find first empty cell // Could be enhanced to pack tightly based on tile sizes for(int r = 0; r < gridRows; r++) { for(int c = 0; c < gridCols; c++) { bool occupied = false; for(int i = 0; i < count; i++) { - if(layouts[i].col <= c && c < layouts[i].col + layouts[i].cols && - layouts[i].row <= r && r < layouts[i].row + layouts[i].rows) { + if(layouts[i].col <= c && c < layouts[i].col + layouts[i].cols + && layouts[i].row <= r && r < layouts[i].row + layouts[i].rows) { occupied = true; break; } @@ -130,7 +130,8 @@ public: } void render(const ScreenState& st) { - if(!_drv) return; + if(!_drv) + return; _drv->render(st); } @@ -146,62 +147,65 @@ public: /// Show touch feedback - highlights the tile at given coordinates /// Returns true if a valid tile is being touched bool showTouchFeedback(int x, int y) { - if(!_drv || _gridCols <= 0) return false; - + if(!_drv || _gridCols <= 0) + return false; + // Transform touch coordinates _drv->transformTouch(&x, &y); - + int headerH = 30; - if(y < headerH) return false; - + if(y < headerH) + return false; + // Calculate which cell int cellW = _drv->width() / _gridCols; int cellH = (_drv->height() - headerH) / _gridRows; - + int col = x / cellW; int row = (y - headerH) / cellH; - + if(col < 0 || col >= _gridCols || row < 0 || row >= _gridRows) return false; - + // Find which tile is at this position for(int i = 0; i < _tileCount; i++) { const TileLayout& lay = _layouts[i]; - if(lay.col <= col && col < lay.col + lay.cols && - lay.row <= row && lay.row + lay.rows > row) { + if(lay.col <= col && col < lay.col + lay.cols && lay.row <= row + && lay.row + lay.rows > row) { // Found the tile - draw highlight via driver - _drv->updateHint(lay.x, lay.y, true); // active=true means show feedback + _drv->updateHint(lay.x, lay.y, true); // active=true means show feedback return true; } } return false; } - + /// Clear touch feedback void clearTouchFeedback() { if(_drv) - _drv->updateHint(0, 0, false); // active=false means clear + _drv->updateHint(0, 0, false); // active=false means clear } - + /// Check if current position is still in same tile as touch-down bool isSameTile(int downX, int downY, int currentX, int currentY) const { - if(!_drv || _gridCols <= 0 || downX < 0) return false; - + if(!_drv || _gridCols <= 0 || downX < 0) + return false; + int dx = downX, dy = downY; int cx = currentX, cy = currentY; - + _drv->transformTouch(&dx, &dy); _drv->transformTouch(&cx, &cy); - + int headerH = 30; int cellW = _drv->width() / _gridCols; int cellH = (_drv->height() - headerH) / _gridRows; - + int downCol = dx / cellW; int downRow = (dy - headerH) / cellH; int curCol = cx / cellW; int curRow = (cy - headerH) / cellH; - + return downCol == curCol && downRow == curRow; } @@ -210,59 +214,55 @@ public: /// Handle dashboard touch - returns action for tapped tile, or NONE TileAction handleDashboardTouch(int x, int y) const { - if(!_drv || _gridCols <= 0) return TileAction::NONE; - + if(!_drv || _gridCols <= 0) + return TileAction::NONE; + // Transform touch coordinates (handles rotated touch panels) _drv->transformTouch(&x, &y); - + int dispW = _drv->width(); int dispH = _drv->height(); int headerH = 30; - + // Check if in header area - if(y < headerH) return TileAction::NONE; - + if(y < headerH) + return TileAction::NONE; + // Calculate which tile was touched using grid int cellW = dispW / _gridCols; int cellH = (dispH - headerH) / _gridRows; - + int col = x / cellW; int row = (y - headerH) / cellH; - + // Bounds check if(col < 0 || col >= _gridCols || row < 0 || row >= _gridRows) { return TileAction::NONE; } - + // Find which tile occupies this cell for(int i = 0; i < _tileCount; i++) { const TileLayout& layout = _layouts[i]; - if(layout.col <= col && col < layout.col + layout.cols && - layout.row <= row && row < layout.row + layout.rows) { + if(layout.col <= col && col < layout.col + layout.cols && layout.row <= row + && row < layout.row + layout.rows) { return DASHBOARD_TILES[i].action; } } - + return TileAction::NONE; } - + /// Calculate and store layouts for dashboard tiles /// Called by drivers who want to use the layout helper int calculateDashboardLayouts(int headerH = 30, int margin = 8) { - if(!_drv) return 0; - + if(!_drv) + return 0; + _tileCount = TileLayoutHelper::calculateLayouts( - _drv->width(), - _drv->height(), - headerH, - margin, - _layouts, - &_gridCols, - &_gridRows - ); + _drv->width(), _drv->height(), headerH, margin, _layouts, &_gridCols, &_gridRows); return _tileCount; } - + /// Get calculated layout for a specific tile const TileLayout* getTileLayouts() const { return _layouts; } int getGridCols() const { return _gridCols; } diff --git a/libraries/KlubhausCore/src/DoorbellLogic.cpp b/libraries/KlubhausCore/src/DoorbellLogic.cpp index c8644ff..4577eb3 100644 --- a/libraries/KlubhausCore/src/DoorbellLogic.cpp +++ b/libraries/KlubhausCore/src/DoorbellLogic.cpp @@ -133,8 +133,8 @@ void DoorbellLogic::update() { default: // Inactivity timeout: turn off screen after no activity if(_state.screen != ScreenID::OFF && now - _lastActivityMs > INACTIVITY_TIMEOUT_MS) { - Serial.printf("[%lu] [STATE] Inactivity timeout (%lu ms) → OFF\n", - millis(), now - _lastActivityMs); + Serial.printf("[%lu] [STATE] Inactivity timeout (%lu ms) → OFF\n", millis(), + now - _lastActivityMs); _state.screen = ScreenID::OFF; _display->setBacklight(false); _state.backlightOn = false; @@ -231,13 +231,13 @@ void DoorbellLogic::onAdmin(const String& cmd) { ESP.restart(); } else if(cmd == "dashboard") { Serial.printf("[%lu] [ADMIN] dashboard\n", millis()); - _lastActivityMs = millis(); // Reset inactivity timer + _lastActivityMs = millis(); // Reset inactivity timer _state.screen = ScreenID::DASHBOARD; _display->setBacklight(true); _state.backlightOn = true; } else if(cmd == "off") { Serial.printf("[%lu] [ADMIN] off\n", millis()); - _lastActivityMs = millis(); // Reset inactivity timer + _lastActivityMs = millis(); // Reset inactivity timer _state.screen = ScreenID::OFF; _display->setBacklight(false); _state.backlightOn = false; diff --git a/libraries/KlubhausCore/src/IDisplayDriver.h b/libraries/KlubhausCore/src/IDisplayDriver.h index fe56b27..ede9ab6 100644 --- a/libraries/KlubhausCore/src/IDisplayDriver.h +++ b/libraries/KlubhausCore/src/IDisplayDriver.h @@ -2,12 +2,12 @@ #include "ScreenState.h" struct TouchEvent { - bool pressed = false; // finger just touched down - bool released = false; // finger just lifted (after being pressed) - int x = 0; // current x position - int y = 0; // current y position - int downX = -1; // x position where touch started - int downY = -1; // y position where touch started + bool pressed = false; // finger just touched down + bool released = false; // finger just lifted (after being pressed) + int x = 0; // current x position + int y = 0; // current y position + int downX = -1; // x position where touch started + int downY = -1; // y position where touch started }; struct HoldState { diff --git a/libraries/KlubhausCore/src/ScreenState.h b/libraries/KlubhausCore/src/ScreenState.h index b114404..c72d337 100644 --- a/libraries/KlubhausCore/src/ScreenState.h +++ b/libraries/KlubhausCore/src/ScreenState.h @@ -10,24 +10,24 @@ enum class BootStage { SPLASH, INIT_DISPLAY, INIT_NETWORK, CONNECTING_WIFI, READ /// Dashboard tile action handlers enum class TileAction { NONE, - ALERT, // Trigger alert - SILENCE, // Silence alert - STATUS, // Send heartbeat/status - REBOOT, // Reboot device + ALERT, // Trigger alert + SILENCE, // Silence alert + STATUS, // Send heartbeat/status + REBOOT, // Reboot device }; /// Dashboard tile layout constraints for flexible grid struct TileConstraint { - uint8_t minCols = 1; // minimum columns this tile needs - uint8_t minRows = 1; // minimum rows this tile needs - uint8_t weight = 1; // priority for growing/shrinking (higher = more flexible) + uint8_t minCols = 1; // minimum columns this tile needs + uint8_t minRows = 1; // minimum rows this tile needs + uint8_t weight = 1; // priority for growing/shrinking (higher = more flexible) }; /// Computed tile position in the grid struct TileLayout { - int x, y; // pixel position - int w, h; // pixel size - int col, row; // grid position + int x, y; // pixel position + int w, h; // pixel size + int col, row; // grid position int cols, rows; // grid span }; @@ -41,10 +41,10 @@ struct DashboardTile { /// Standard dashboard tiles (auto-gridded based on count and constraints) static constexpr DashboardTile DASHBOARD_TILES[] = { - { "Alert", 0x0280, TileAction::ALERT, {1, 1, 1} }, - { "Silent", 0x0400, TileAction::SILENCE, {1, 1, 1} }, - { "Status", 0x0440, TileAction::STATUS, {1, 1, 1} }, - { "Reboot", 0x0100, TileAction::REBOOT, {1, 1, 1} }, + { "Alert", 0x0280, TileAction::ALERT, { 1, 1, 1 } }, + { "Silent", 0x0400, TileAction::SILENCE, { 1, 1, 1 } }, + { "Status", 0x0440, TileAction::STATUS, { 1, 1, 1 } }, + { "Reboot", 0x0100, TileAction::REBOOT, { 1, 1, 1 } }, }; static constexpr int DASHBOARD_TILE_COUNT = sizeof(DASHBOARD_TILES) / sizeof(DASHBOARD_TILES[0]); diff --git a/mise.toml b/mise.toml index f7200d4..f935f8b 100644 --- a/mise.toml +++ b/mise.toml @@ -28,27 +28,7 @@ arduino-cli compile --fqbn "$FQBN" --libraries ./libraries $LIBS --build-propert [tasks.upload] description = "Upload (uses BOARD env var)" depends = ["compile"] -run = """ -# Kill any processes using the serial port first -source ./boards/$BOARD/board-config.sh -PORT="${PORT:-$PORT}" -fuser -k "$PORT" 2>/dev/null || true -for pid in $(pgrep -f "monitor-agent.py" 2>/dev/null || true); do - kill "$pid" 2>/dev/null || true -done -rm -f "/tmp/doorbell-${BOARD}.lock" 2>/dev/null || true -sleep 1 - -source ./scripts/lockfile.sh - -FORCE=1 TASK_NAME=upload acquire_lock || exit 1 - -arduino-cli upload --fqbn "$FQBN" --port "$PORT" ./boards/$BOARD - -# Restart monitor in background -python3 ./scripts/monitor-agent.py "$BOARD" & -echo "[OK] Monitor restarted in background" -""" +run = "source ./boards/$BOARD/board-config.sh && arduino-cli upload --fqbn $FQBN --port $PORT ./boards/$BOARD" [tasks.monitor-raw] run = """ @@ -80,15 +60,15 @@ set +e # Kill any processes using the serial port source ./boards/$BOARD/board-config.sh PORT="${PORT:-$PORT}" -fuser -k "$PORT" 2>/dev/null +fuser -k "$PORT" 2>/dev/null || true # Kill monitor-agent processes for this board -for pid in $(pgrep -f "monitor-agent.py"); do - kill "$pid" 2>/dev/null +for pid in $(pgrep -f "monitor-agent.py" 2>/dev/null || true); do + kill "$pid" 2>/dev/null || true done # Also clean up lockfile -rm -f "/tmp/doorbell-${BOARD}.lock" 2>/dev/null +rm -f "/tmp/doorbell-${BOARD}.lock" 2>/dev/null || true sleep 1 echo "[OK] Killed processes for $BOARD" @@ -224,4 +204,4 @@ echo "[OK] Generated .crush.json with FQBN: $FQBN" """ [env] -BOARD = "esp32-32e-4" +BOARD = "esp32-s3-lcd-43" 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