refactor(display): improve touch handling and code formatting
This commit is contained in:
@@ -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);
|
||||||
|
|
||||||
// Track press start for hold detection
|
|
||||||
if (!_lastTouch.pressed) {
|
|
||||||
_pressStartMs = millis();
|
_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,17 +124,18 @@ 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);
|
||||||
@@ -143,7 +146,7 @@ void DisplayDriverGFX::render(const ScreenState& state) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case ScreenID::OFF:
|
case ScreenID::OFF:
|
||||||
if (_needsRedraw) {
|
if(_needsRedraw) {
|
||||||
_gfx->fillScreen(0x000000);
|
_gfx->fillScreen(0x000000);
|
||||||
_needsRedraw = false;
|
_needsRedraw = false;
|
||||||
}
|
}
|
||||||
@@ -154,7 +157,7 @@ void DisplayDriverGFX::render(const ScreenState& state) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case ScreenID::DASHBOARD:
|
case ScreenID::DASHBOARD:
|
||||||
if (_needsRedraw) {
|
if(_needsRedraw) {
|
||||||
drawDashboard(state);
|
drawDashboard(state);
|
||||||
_needsRedraw = false;
|
_needsRedraw = false;
|
||||||
}
|
}
|
||||||
@@ -187,7 +190,8 @@ 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;
|
||||||
|
|||||||
@@ -2,10 +2,11 @@
|
|||||||
// 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);
|
||||||
@@ -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) {
|
if(!h.active) {
|
||||||
display.updateHint();
|
display.updateHint();
|
||||||
}
|
}
|
||||||
} else {
|
} else if(evt.pressed) {
|
||||||
TouchEvent evt = display.readTouch();
|
if(st.screen == ScreenID::OFF) {
|
||||||
if (evt.pressed && st.screen == ScreenID::DASHBOARD) {
|
// 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);
|
int tile = display.dashboardTouch(evt.x, evt.y);
|
||||||
if (tile >= 0) Serial.printf("[DASH] Tile %d tapped\n", tile);
|
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 console ──
|
Serial.println("[TOUCH] ALERT tap");
|
||||||
if (Serial.available()) {
|
// For now, let's make tap do nothing (hold to silence only)
|
||||||
String cmd = Serial.readStringUntil('\n');
|
// Or: logic.dismissAlert();
|
||||||
cmd.trim();
|
|
||||||
if (cmd.length() > 0) logic.onSerialCommand(cmd);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
2
vendor/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
*
|
||||||
|
!.gitignore
|
||||||
Reference in New Issue
Block a user