From 6dbe1be1cd500fce9998bd6b17a180471a104a4f Mon Sep 17 00:00:00 2001 From: David Gwilliam Date: Mon, 16 Feb 2026 02:33:38 -0800 Subject: [PATCH] snapshot --- .../doorbell-touch-esp32-32e/Dashboard.cpp | 169 ++++++++++++++++++ sketches/doorbell-touch-esp32-32e/Dashboard.h | 53 ++++++ 2 files changed, 222 insertions(+) create mode 100644 sketches/doorbell-touch-esp32-32e/Dashboard.cpp create mode 100644 sketches/doorbell-touch-esp32-32e/Dashboard.h diff --git a/sketches/doorbell-touch-esp32-32e/Dashboard.cpp b/sketches/doorbell-touch-esp32-32e/Dashboard.cpp new file mode 100644 index 0000000..0708886 --- /dev/null +++ b/sketches/doorbell-touch-esp32-32e/Dashboard.cpp @@ -0,0 +1,169 @@ +#include "Dashboard.h" + +// 16-bit color helpers +#define COL_BG 0x1082 // dark charcoal +#define COL_BAR 0x2104 // slightly lighter +#define COL_RED 0xF800 +#define COL_ORANGE 0xFBE0 +#define COL_GREEN 0x07E0 +#define COL_CYAN 0x07FF +#define COL_BLUE 0x001F +#define COL_PURPLE 0x780F +#define COL_YELLOW 0xFFE0 +#define COL_WHITE 0xFFFF +#define COL_GRAY 0x8410 +#define COL_DARK_TILE 0x18E3 + +// Tile color themes +static const uint16_t tileBG[] = { + COL_RED, // LAST_ALERT — red + COL_ORANGE, // STATS — orange + COL_CYAN, // NETWORK — cyan + COL_PURPLE, // MUTE — purple + COL_DARK_TILE, // HISTORY — dark + COL_DARK_TILE // SYSTEM — dark +}; + +static const uint16_t tileFG[] = { + COL_WHITE, COL_WHITE, 0x0000, COL_WHITE, COL_WHITE, COL_WHITE +}; + +Dashboard::Dashboard(TFT_eSPI& tft) + : _tft(tft), _sprite(&tft) +{ + // Initialize tile metadata + _tiles[TILE_LAST_ALERT] = { "!", "LAST ALERT", "none", "", 0, 0, true }; + _tiles[TILE_STATS] = { "#", "TODAY", "0 alerts", "", 0, 0, true }; + _tiles[TILE_NETWORK] = { "~", "NETWORK", "---", "", 0, 0, true }; + _tiles[TILE_MUTE] = { "M", "MUTE", "OFF", "", 0, 0, true }; + _tiles[TILE_HISTORY] = { ">", "HISTORY", "tap to view","", 0, 0, true }; + _tiles[TILE_SYSTEM] = { "*", "SYSTEM", "---", "", 0, 0, true }; + + for (int i = 0; i < TILE_COUNT; i++) { + _tiles[i].bgColor = tileBG[i]; + _tiles[i].fgColor = tileFG[i]; + } +} + +void Dashboard::begin() { + // Create sprite sized to one tile (reused for each) + _sprite.createSprite(TILE_W, TILE_H); + _sprite.setTextDatum(MC_DATUM); + _tft.fillScreen(COL_BG); +} + +void Dashboard::tilePosition(TileID id, int& x, int& y) { + int col = id % DASH_COLS; + int row = id / DASH_COLS; + x = DASH_MARGIN + col * (TILE_W + DASH_MARGIN); + y = DASH_TOP_BAR + DASH_MARGIN + row * (TILE_H + DASH_MARGIN); +} + +void Dashboard::drawTile(TileID id) { + TileData& t = _tiles[id]; + int tx, ty; + tilePosition(id, tx, ty); + + // Draw into sprite (off-screen) + _sprite.fillSprite(t.bgColor); + + // Rounded corner effect — draw border pixels + _sprite.drawRoundRect(0, 0, TILE_W, TILE_H, 8, COL_GRAY); + + // Icon — large character at top + _sprite.setTextColor(t.fgColor, t.bgColor); + _sprite.setTextFont(4); + _sprite.setTextSize(2); + _sprite.setTextDatum(TC_DATUM); + _sprite.drawString(t.icon, TILE_W / 2, 8); + + // Label — below icon + _sprite.setTextSize(1); + _sprite.setTextFont(2); + _sprite.setTextDatum(MC_DATUM); + _sprite.drawString(t.label, TILE_W / 2, TILE_H / 2 + 5); + + // Value — bottom area + _sprite.setTextFont(2); + _sprite.setTextDatum(BC_DATUM); + _sprite.drawString(t.value, TILE_W / 2, TILE_H - 25); + + // Sub text — very bottom + if (strlen(t.sub) > 0) { + _sprite.setTextFont(1); + _sprite.setTextDatum(BC_DATUM); + _sprite.drawString(t.sub, TILE_W / 2, TILE_H - 8); + } + + // Push sprite to screen in one operation — no flicker + _sprite.pushSprite(tx, ty); + + t.dirty = false; +} + +void Dashboard::drawTopBar(const char* time, int rssi, bool wifiOk) { + _tft.fillRect(0, 0, 480, DASH_TOP_BAR, COL_BAR); + + _tft.setTextColor(COL_WHITE, COL_BAR); + _tft.setTextFont(4); + _tft.setTextSize(1); + + // Title — left + _tft.setTextDatum(ML_DATUM); + _tft.drawString("KLUBHAUS ALERT", DASH_MARGIN, DASH_TOP_BAR / 2); + + // Time — right + _tft.setTextDatum(MR_DATUM); + _tft.drawString(time, 470, DASH_TOP_BAR / 2); + + // WiFi indicator — signal bars + int bars = 0; + if (wifiOk) { + if (rssi > -50) bars = 4; + else if (rssi > -60) bars = 3; + else if (rssi > -70) bars = 2; + else bars = 1; + } + + int barX = 370; + int barW = 6; + int barGap = 3; + for (int i = 0; i < 4; i++) { + int barH = 6 + i * 5; + int barY = DASH_TOP_BAR - 8 - barH; + uint16_t col = (i < bars) ? COL_GREEN : COL_GRAY; + _tft.fillRect(barX + i * (barW + barGap), barY, barW, barH, col); + } +} + +void Dashboard::drawAll() { + drawTopBar("--:--", 0, false); + for (int i = 0; i < TILE_COUNT; i++) { + drawTile((TileID)i); + } +} + +void Dashboard::updateTile(TileID id, const char* value, const char* sub) { + TileData& t = _tiles[id]; + strncpy(t.value, value, 31); + t.value[31] = '\0'; + if (sub) { + strncpy(t.sub, sub, 31); + t.sub[31] = '\0'; + } + t.dirty = true; + drawTile(id); // immediate redraw of just this tile +} + +int Dashboard::handleTouch(int x, int y) { + for (int i = 0; i < TILE_COUNT; i++) { + int tx, ty; + tilePosition((TileID)i, tx, ty); + if (x >= tx && x < tx + TILE_W && + y >= ty && y < ty + TILE_H) { + return i; + } + } + return -1; +} + diff --git a/sketches/doorbell-touch-esp32-32e/Dashboard.h b/sketches/doorbell-touch-esp32-32e/Dashboard.h new file mode 100644 index 0000000..2890127 --- /dev/null +++ b/sketches/doorbell-touch-esp32-32e/Dashboard.h @@ -0,0 +1,53 @@ +#pragma once +#include + +// Grid layout constants +#define DASH_COLS 3 +#define DASH_ROWS 2 +#define DASH_MARGIN 8 +#define DASH_TOP_BAR 40 + +// Tile dimensions (calculated for 480x320) +#define TILE_W ((480 - (DASH_COLS + 1) * DASH_MARGIN) / DASH_COLS) // ~148 +#define TILE_H ((320 - DASH_TOP_BAR - (DASH_ROWS + 1) * DASH_MARGIN) / DASH_ROWS) // ~128 + +// Tile IDs +enum TileID : uint8_t { + TILE_LAST_ALERT = 0, + TILE_STATS, + TILE_NETWORK, + TILE_MUTE, + TILE_HISTORY, + TILE_SYSTEM, + TILE_COUNT +}; + +struct TileData { + const char* icon; // emoji/symbol character + const char* label; // tile name + char value[32]; // dynamic value text + char sub[32]; // secondary line + uint16_t bgColor; + uint16_t fgColor; + bool dirty; // needs redraw +}; + +class Dashboard { +public: + Dashboard(TFT_eSPI& tft); + + void begin(); + void drawAll(); + void drawTopBar(const char* time, int rssi, bool wifiOk); + void updateTile(TileID id, const char* value, const char* sub = nullptr); + int handleTouch(int x, int y); // returns TileID or -1 + +private: + TFT_eSPI& _tft; + TFT_eSprite _sprite; + TileData _tiles[TILE_COUNT]; + + void drawTile(TileID id); + void tilePosition(TileID id, int& x, int& y); +}; +