Files
klubhaus-doorbell/boards/esp32-s3-lcd-43/DisplayDriverGFX.cpp

248 lines
5.9 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// boards/esp32-s3-lcd-43/DisplayDriverGFX.cpp
#include <Arduino.h>
#include "LovyanPins.h"
#include "board_config.h"
#include "DisplayDriverGFX.h"
// ── Globals ──
static LGFX* _gfx = nullptr;
// ── Forward declarations ──
static void initDisplay();
// ── Dimensions ──
static constexpr int DISP_W = 800;
static constexpr int DISP_H = 480;
// ── Display initialization ──
static void initDisplay() {
Serial.println("LovyanGFX init...");
_gfx = new LGFX();
_gfx->init();
_gfx->setRotation(1); // Landscape
_gfx->fillScreen(0x000000);
Serial.println("Display ready");
}
// ── Singleton ──
DisplayDriverGFX& DisplayDriverGFX::instance() {
static DisplayDriverGFX inst;
return inst;
}
// ── IDisplayDriver implementation ──
void DisplayDriverGFX::begin() {
initDisplay();
}
void DisplayDriverGFX::setBacklight(bool on) {
if (_gfx) {
// LovyanGFX handles backlight via setBrightness
_gfx->setBrightness(on ? 255 : 0);
}
}
int DisplayDriverGFX::width() {
return DISP_W;
}
int DisplayDriverGFX::height() {
return DISP_H;
}
// ── Touch handling ──
TouchEvent DisplayDriverGFX::readTouch() {
TouchEvent evt;
if (!_gfx) return evt;
int32_t x, y;
if (_gfx->getTouch(&x, &y)) {
evt.pressed = true;
evt.x = static_cast<int>(x);
evt.y = static_cast<int>(y);
// Track press start for hold detection
if (!_lastTouch.pressed) {
_pressStartMs = millis();
_isHolding = false;
}
}
_lastTouch = evt;
return evt;
}
int DisplayDriverGFX::dashboardTouch(int x, int y) {
// Dashboard tiles: 2 rows × 4 columns
constexpr int cols = 4;
constexpr int rows = 2;
constexpr int tileW = DISP_W / cols;
constexpr int tileH = DISP_H / rows;
if (x < 0 || x >= DISP_W || y < 0 || y >= DISP_H) {
return -1;
}
int col = x / tileW;
int row = y / tileH;
return row * cols + col;
}
HoldState DisplayDriverGFX::updateHold(unsigned long holdMs) {
HoldState state;
if (!_lastTouch.pressed) {
_isHolding = false;
return state;
}
unsigned long elapsed = millis() - _pressStartMs;
if (!_isHolding) {
_isHolding = true;
}
state.active = true;
state.progress = static_cast<float>(elapsed) / static_cast<float>(holdMs);
if (state.progress >= 1.0f) {
state.progress = 1.0f;
state.completed = true;
}
return state;
}
// ── Rendering ──
void DisplayDriverGFX::render(const ScreenState& state) {
if (!_gfx) return;
// Check if we need full redraw
if (state.screen != _lastScreen) {
_needsRedraw = true;
_lastScreen = state.screen;
}
switch (state.screen) {
case ScreenID::BOOT:
if (_needsRedraw) {
_gfx->fillScreen(0x000000);
_gfx->setTextColor(0xFFFF);
_gfx->setTextSize(2);
_gfx->setCursor(10, 10);
_gfx->print("KLUBHAUS BOOT");
_needsRedraw = false;
}
break;
case ScreenID::OFF:
if (_needsRedraw) {
_gfx->fillScreen(0x000000);
_needsRedraw = false;
}
break;
case ScreenID::ALERT:
drawAlert(state);
break;
case ScreenID::DASHBOARD:
if (_needsRedraw) {
drawDashboard(state);
_needsRedraw = false;
}
break;
}
}
void DisplayDriverGFX::drawAlert(const ScreenState& state) {
uint32_t elapsed = millis() - state.alertStartMs;
uint8_t pulse = static_cast<uint8_t>(180.0f + 75.0f * sinf(elapsed / 300.0f));
uint16_t bg = _gfx->color565(pulse, 0, 0);
_gfx->fillScreen(bg);
_gfx->setTextColor(0xFFFF, bg);
_gfx->setTextSize(3);
_gfx->setCursor(10, 20);
_gfx->print(state.alertTitle.length() > 0 ? state.alertTitle.c_str() : "ALERT");
_gfx->setTextSize(2);
_gfx->setCursor(10, 80);
_gfx->print(state.alertBody);
_gfx->setTextSize(1);
_gfx->setCursor(10, DISP_H - 20);
_gfx->print("Hold to silence...");
}
void DisplayDriverGFX::drawDashboard(const ScreenState& state) {
_gfx->fillScreen(0x001030); // Dark blue
// Header
_gfx->fillRect(0, 0, DISP_W, 30, 0x0220);
_gfx->setTextColor(0xFFFF);
_gfx->setTextSize(1);
_gfx->setCursor(5, 10);
_gfx->printf("KLUBHAUS");
// WiFi status
_gfx->setCursor(DISP_W - 100, 10);
_gfx->printf("WiFi:%s", state.wifiSsid.length() > 0 ? "ON" : "OFF");
// Tiles: 2 rows × 4 columns
constexpr int cols = 4;
constexpr int rows = 2;
constexpr int tileW = DISP_W / cols;
constexpr int tileH = (DISP_H - 30) / rows;
constexpr int margin = 8;
// Draw placeholder tiles (8 total for 2x4 grid)
const char* tileLabels[] = {"1", "2", "3", "4", "5", "6", "7", "8"};
for (int i = 0; i < 8; i++) {
int col = i % cols;
int row = i / cols;
int x = col * tileW + margin;
int y = 30 + row * tileH + margin;
int w = tileW - 2 * margin;
int h = tileH - 2 * margin;
// Tile background
_gfx->fillRoundRect(x, y, w, h, 8, 0x0220);
// Tile border
_gfx->drawRoundRect(x, y, w, h, 8, 0xFFFF);
// Tile number
_gfx->setTextColor(0xFFFF);
_gfx->setTextSize(2);
_gfx->setCursor(x + w/2 - 10, y + h/2 - 10);
_gfx->print(tileLabels[i]);
}
}
void DisplayDriverGFX::updateHint() {
if (!_gfx) return;
static uint32_t lastTime = 0;
uint32_t now = millis();
if (now - lastTime < 50) return;
lastTime = now;
float t = (now % 2000) / 2000.0f;
uint8_t v = static_cast<uint8_t>(30.0f + 30.0f * sinf(t * 2 * 3.14159f));
uint16_t col = ((v >> 3) << 11) | ((v >> 2) << 5) | (v >> 3);
_gfx->drawCircle(DISP_W / 2, DISP_H / 2, 50, col);
}