refactor(display): improve touch handling and code formatting

This commit is contained in:
2026-02-17 03:10:52 -08:00
parent 2907dac33d
commit d61c9c60bf
5 changed files with 128 additions and 92 deletions

View File

@@ -1,8 +1,10 @@
// boards/esp32-s3-lcd-43/DisplayDriverGFX.cpp // boards/esp32-s3-lcd-43/DisplayDriverGFX.cpp
#include <Arduino.h> #include "DisplayDriverGFX.h"
#include "LovyanPins.h" #include "LovyanPins.h"
#include "board_config.h" #include "board_config.h"
#include "DisplayDriverGFX.h"
#include <Arduino.h>
// ── Globals ── // ── Globals ──
static LGFX* _gfx = nullptr; static LGFX* _gfx = nullptr;
@@ -18,9 +20,12 @@ static constexpr int DISP_H = 480;
static void initDisplay() { static void initDisplay() {
Serial.println("LovyanGFX init..."); Serial.println("LovyanGFX init...");
// Note: LovyanGFX handles I2C internally (port 1 for touch, port 0 for CH422G)
// No need to call Wire.begin() or Wire1.begin()
_gfx = new LGFX(); _gfx = new LGFX();
_gfx->init(); _gfx->init();
_gfx->setRotation(1); // Landscape _gfx->setRotation(0); // Landscape
_gfx->fillScreen(0x000000); _gfx->fillScreen(0x000000);
Serial.println("Display ready"); Serial.println("Display ready");
@@ -36,44 +41,41 @@ DisplayDriverGFX& DisplayDriverGFX::instance() {
void DisplayDriverGFX::begin() { void DisplayDriverGFX::begin() {
initDisplay(); initDisplay();
// Turn on backlight immediately
setBacklight(true);
} }
void DisplayDriverGFX::setBacklight(bool on) { void DisplayDriverGFX::setBacklight(bool on) {
if (_gfx) { if(_gfx) {
// LovyanGFX handles backlight via setBrightness // LovyanGFX handles backlight via setBrightness
_gfx->setBrightness(on ? 255 : 0); _gfx->setBrightness(on ? 255 : 0);
} }
} }
int DisplayDriverGFX::width() { int DisplayDriverGFX::width() { return DISP_W; }
return DISP_W;
}
int DisplayDriverGFX::height() { int DisplayDriverGFX::height() { return DISP_H; }
return DISP_H;
}
// ── Touch handling ── // ── Touch handling ──
TouchEvent DisplayDriverGFX::readTouch() { TouchEvent DisplayDriverGFX::readTouch() {
TouchEvent evt; TouchEvent evt;
if(!_gfx)
if (!_gfx) return evt; return evt;
int32_t x, y; int32_t x, y;
if (_gfx->getTouch(&x, &y)) { bool pressed = _gfx->getTouch(&x, &y);
evt.pressed = true;
// Only report NEW touches (debounce - ignore held touches)
evt.pressed = pressed && !_lastTouch.pressed;
if(pressed) {
evt.x = static_cast<int>(x); evt.x = static_cast<int>(x);
evt.y = static_cast<int>(y); evt.y = static_cast<int>(y);
_pressStartMs = millis();
// Track press start for hold detection
if (!_lastTouch.pressed) {
_pressStartMs = millis();
_isHolding = false;
}
} }
_lastTouch = evt; _lastTouch.pressed = pressed;
return evt; return evt;
} }
@@ -84,7 +86,7 @@ int DisplayDriverGFX::dashboardTouch(int x, int y) {
constexpr int tileW = DISP_W / cols; constexpr int tileW = DISP_W / cols;
constexpr int tileH = DISP_H / rows; constexpr int tileH = DISP_H / rows;
if (x < 0 || x >= DISP_W || y < 0 || y >= DISP_H) { if(x < 0 || x >= DISP_W || y < 0 || y >= DISP_H) {
return -1; return -1;
} }
@@ -97,21 +99,21 @@ int DisplayDriverGFX::dashboardTouch(int x, int y) {
HoldState DisplayDriverGFX::updateHold(unsigned long holdMs) { HoldState DisplayDriverGFX::updateHold(unsigned long holdMs) {
HoldState state; HoldState state;
if (!_lastTouch.pressed) { if(!_lastTouch.pressed) {
_isHolding = false; _isHolding = false;
return state; return state;
} }
unsigned long elapsed = millis() - _pressStartMs; unsigned long elapsed = millis() - _pressStartMs;
if (!_isHolding) { if(!_isHolding) {
_isHolding = true; _isHolding = true;
} }
state.active = true; state.active = true;
state.progress = static_cast<float>(elapsed) / static_cast<float>(holdMs); state.progress = static_cast<float>(elapsed) / static_cast<float>(holdMs);
if (state.progress >= 1.0f) { if(state.progress >= 1.0f) {
state.progress = 1.0f; state.progress = 1.0f;
state.completed = true; state.completed = true;
} }
@@ -122,43 +124,44 @@ HoldState DisplayDriverGFX::updateHold(unsigned long holdMs) {
// ── Rendering ── // ── Rendering ──
void DisplayDriverGFX::render(const ScreenState& state) { void DisplayDriverGFX::render(const ScreenState& state) {
if (!_gfx) return; if(!_gfx)
return;
// Check if we need full redraw // Check if we need full redraw
if (state.screen != _lastScreen) { if(state.screen != _lastScreen) {
_needsRedraw = true; _needsRedraw = true;
_lastScreen = state.screen; _lastScreen = state.screen;
} }
switch (state.screen) { switch(state.screen) {
case ScreenID::BOOT: case ScreenID::BOOT:
if (_needsRedraw) { if(_needsRedraw) {
_gfx->fillScreen(0x000000); _gfx->fillScreen(0x000000);
_gfx->setTextColor(0xFFFF); _gfx->setTextColor(0xFFFF);
_gfx->setTextSize(2); _gfx->setTextSize(2);
_gfx->setCursor(10, 10); _gfx->setCursor(10, 10);
_gfx->print("KLUBHAUS BOOT"); _gfx->print("KLUBHAUS BOOT");
_needsRedraw = false; _needsRedraw = false;
} }
break; break;
case ScreenID::OFF: case ScreenID::OFF:
if (_needsRedraw) { if(_needsRedraw) {
_gfx->fillScreen(0x000000); _gfx->fillScreen(0x000000);
_needsRedraw = false; _needsRedraw = false;
} }
break; break;
case ScreenID::ALERT: case ScreenID::ALERT:
drawAlert(state); drawAlert(state);
break; break;
case ScreenID::DASHBOARD: case ScreenID::DASHBOARD:
if (_needsRedraw) { if(_needsRedraw) {
drawDashboard(state); drawDashboard(state);
_needsRedraw = false; _needsRedraw = false;
} }
break; break;
} }
} }
@@ -184,10 +187,11 @@ void DisplayDriverGFX::drawAlert(const ScreenState& state) {
} }
void DisplayDriverGFX::drawDashboard(const ScreenState& state) { void DisplayDriverGFX::drawDashboard(const ScreenState& state) {
_gfx->fillScreen(0x001030); // Dark blue _gfx->fillScreen(0x001030); // Dark blue
// Header // Header
_gfx->fillRect(0, 0, DISP_W, 30, 0x0220); _gfx->fillRect(0, 0, DISP_W, 30, 0x1A1A); // Dark gray
_gfx->setFont(&fonts::Font0); // Built-in minimal font
_gfx->setTextColor(0xFFFF); _gfx->setTextColor(0xFFFF);
_gfx->setTextSize(1); _gfx->setTextSize(1);
_gfx->setCursor(5, 10); _gfx->setCursor(5, 10);
@@ -197,7 +201,6 @@ void DisplayDriverGFX::drawDashboard(const ScreenState& state) {
_gfx->setCursor(DISP_W - 100, 10); _gfx->setCursor(DISP_W - 100, 10);
_gfx->printf("WiFi:%s", state.wifiSsid.length() > 0 ? "ON" : "OFF"); _gfx->printf("WiFi:%s", state.wifiSsid.length() > 0 ? "ON" : "OFF");
// Tiles: 2 rows × 4 columns // Tiles: 2 rows × 4 columns
constexpr int cols = 4; constexpr int cols = 4;
constexpr int rows = 2; constexpr int rows = 2;
@@ -206,9 +209,9 @@ void DisplayDriverGFX::drawDashboard(const ScreenState& state) {
constexpr int margin = 8; constexpr int margin = 8;
// Draw placeholder tiles (8 total for 2x4 grid) // Draw placeholder tiles (8 total for 2x4 grid)
const char* tileLabels[] = {"1", "2", "3", "4", "5", "6", "7", "8"}; const char* tileLabels[] = { "1", "2", "3", "4", "5", "6", "7", "8" };
for (int i = 0; i < 8; i++) { for(int i = 0; i < 8; i++) {
int col = i % cols; int col = i % cols;
int row = i / cols; int row = i / cols;
@@ -226,17 +229,20 @@ void DisplayDriverGFX::drawDashboard(const ScreenState& state) {
// Tile number // Tile number
_gfx->setTextColor(0xFFFF); _gfx->setTextColor(0xFFFF);
_gfx->setTextSize(2); _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]); _gfx->print(tileLabels[i]);
} }
} }
void DisplayDriverGFX::updateHint() { void DisplayDriverGFX::updateHint() {
if (!_gfx) return; if(!_gfx)
return;
static uint32_t lastTime = 0; static uint32_t lastTime = 0;
uint32_t now = millis(); uint32_t now = millis();
if (now - lastTime < 50) return; // Fixed: throttle to 100ms instead of 50ms
if(now - lastTime < 100)
return;
lastTime = now; lastTime = now;
float t = (now % 2000) / 2000.0f; float t = (now % 2000) / 2000.0f;

View File

@@ -2,14 +2,15 @@
// Klubhaus Doorbell — ESP32-S3-Touch-LCD-4.3 target // Klubhaus Doorbell — ESP32-S3-Touch-LCD-4.3 target
// //
#include <KlubhausCore.h> #include "DisplayDriverGFX.h"
#include "board_config.h" #include "board_config.h"
#include "secrets.h" #include "secrets.h"
#include "DisplayDriverGFX.h"
#include <KlubhausCore.h>
DisplayDriverGFX gfxDriver; DisplayDriverGFX gfxDriver;
DisplayManager display(&gfxDriver); DisplayManager display(&gfxDriver);
DoorbellLogic logic(&display); DoorbellLogic logic(&display);
void setup() { void setup() {
Serial.begin(115200); Serial.begin(115200);
@@ -19,36 +20,52 @@ void setup() {
logic.finishBoot(); logic.finishBoot();
} }
void loop() { // void loop() {
// ── State machine tick ── TouchEvent evt = display.readTouch();
logic.update();
// ── Render current screen ── logic.update();
display.render(logic.getScreenState()); display.render(logic.getScreenState());
// ── Touch handling ── const ScreenState& st = logic.getScreenState();
const ScreenState& st = logic.getScreenState();
if (st.deviceState == DeviceState::ALERTING) { if(st.deviceState == DeviceState::ALERTING) {
HoldState h = display.updateHold(HOLD_TO_SILENCE_MS); HoldState h = display.updateHold(HOLD_TO_SILENCE_MS);
if (h.completed) { if(h.completed) {
logic.silenceAlert(); logic.silenceAlert();
}
if (!h.active) {
display.updateHint();
}
} else {
TouchEvent evt = display.readTouch();
if (evt.pressed && st.screen == ScreenID::DASHBOARD) {
int tile = display.dashboardTouch(evt.x, evt.y);
if (tile >= 0) Serial.printf("[DASH] Tile %d tapped\n", tile);
}
} }
if(!h.active) {
// ── Serial console ── display.updateHint();
if (Serial.available()) { }
String cmd = Serial.readStringUntil('\n'); } else if(evt.pressed) {
cmd.trim(); if(st.screen == ScreenID::OFF) {
if (cmd.length() > 0) logic.onSerialCommand(cmd); // Tap in OFF mode → wake to DASHBOARD
Serial.println("[TOUCH] OFF → DASHBOARD");
logic.setScreen(ScreenID::DASHBOARD);
display.setBacklight(true);
} else if(st.screen == ScreenID::DASHBOARD) {
int tile = display.dashboardTouch(evt.x, evt.y);
if(tile >= 0) {
Serial.printf("[DASH] Tile %d tapped\n", tile);
// TODO: Handle tile actions
}
} else if(st.screen == ScreenID::ALERT) {
// Tap in ALERT mode → could dismiss or show more info
Serial.println("[TOUCH] ALERT tap");
// For now, let's make tap do nothing (hold to silence only)
// Or: logic.dismissAlert();
} }
} }
// Serial console (unchanged)
if(Serial.available()) {
// ...
}
}
// ── Serial console ──
if(Serial.available()) {
String cmd = Serial.readStringUntil('\n');
cmd.trim();
if(cmd.length() > 0)
logic.onSerialCommand(cmd);
}
}

View File

@@ -258,3 +258,13 @@ void DoorbellLogic::onSerialCommand(const String& cmd) {
} }
else Serial.println(F("[CMD] alert|silence|reboot|dashboard|off|status")); else Serial.println(F("[CMD] alert|silence|reboot|dashboard|off|status"));
} }
void DoorbellLogic::setScreen(ScreenID s) {
Serial.printf("[SCREEN] Set to %s\n", screenIdStr(s));
_state.screen = s;
// Auto-manage backlight based on screen
bool needsBacklight = (s != ScreenID::OFF);
_display->setBacklight(needsBacklight);
_state.backlightOn = needsBacklight;
}

View File

@@ -34,6 +34,7 @@ private:
void flushStatus(const String& message); void flushStatus(const String& message);
void heartbeat(); void heartbeat();
void transition(DeviceState s); void transition(DeviceState s);
void setScreen(ScreenID s);
String topicUrl(const char* base); String topicUrl(const char* base);
DisplayManager* _display; DisplayManager* _display;

2
vendor/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
*
!.gitignore