#include "LovyanPins.h" #include "board_config.h" #include #include // Global display instance static LGFX* _gfx = nullptr; static ESP_IOExpander* _expander = nullptr; // Forward declarations void initExpander(); void initDisplay(); // ── Expander initialization (from Westcott) ── void initExpander() { Serial.println("IO expander init..."); #include #include #include #include "LovyanPins.h" #include "board_config.h" #include "DisplayDriverGFX.h" // ── Globals ── static LGFX* _gfx = nullptr; static ESP_IOExpander* _expander = nullptr; // ── Forward declarations ── static void initExpander(); static void initDisplay(); // ── Dimensions ── static constexpr int DISP_W = 800; static constexpr int DISP_H = 480; // ── Expander initialization ── static void initExpander() { Serial.println("IO expander init..."); // Initialize I2C for expander Wire.begin(TOUCH_SDA, TOUCH_SCL); _expander = new ESP_IOExpander_CH422G( (i2c_port_t)I2C_NUM_0, ESP_IO_EXPANDER_I2C_CH422G_ADDRESS ); _expander->init(); _expander->begin(); // Set all pins to output _expander->multiPinMode(TP_RST | LCD_BL | LCD_RST | SD_CS | USB_SEL, OUTPUT); // Reset sequence _expander->digitalWrite(LCD_RST, LOW); delay(50); _expander->digitalWrite(LCD_RST, HIGH); delay(150); // Turn on backlight _expander->digitalWrite(LCD_BL, HIGH); Serial.println("IO expander ready"); } // ── 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() { initExpander(); initDisplay(); } void DisplayDriverGFX::setBacklight(bool on) { if (_expander) { _expander->digitalWrite(LCD_BL, on ? HIGH : LOW); } } 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(x); evt.y = static_cast(y); // Track press start 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) { // Start tracking hold _isHolding = true; } state.active = true; state.progress = static_cast(elapsed) / static_cast(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; // Clear with background color _gfx->fillScreen(0x001030); // Dark blue if (!state.showDashboard) { // Show alert/message screen renderAlert(state); return; } // Draw dashboard tiles constexpr int cols = 4; constexpr int rows = 2; constexpr int tileW = DISP_W / cols; constexpr int tileH = DISP_H / rows; constexpr int margin = 8; for (int i = 0; i < state.dashTiles.size(); i++) { int col = i % cols; int row = i / cols; int x = col * tileW + margin; int y = row * tileH + margin; int w = tileW - 2 * margin; int h = tileH - 2 * margin; // Tile background uint16_t tileColor = state.dashTiles[i].active ? 0x04A0 : 0x0220; _gfx->fillRoundRect(x, y, w, h, 8, tileColor); // Tile border _gfx->drawRoundRect(x, y, w, h, 8, 0xFFFF); // Tile label _gfx->setTextColor(0xFFFF); _gfx->setTextSize(2); _gfx->setCursor(x + 10, y + 10); _gfx->print(state.dashTiles[i].label); } // Draw WiFi status _gfx->setTextSize(1); _gfx->setCursor(DISP_W - 100, 10); _gfx->printf("WiFi: %s", state.wifiConnected ? "ON" : "OFF"); } 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(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); } // Helper for alert rendering (implement as needed) void DisplayDriverGFX::renderAlert(const ScreenState& state) { // Placeholder - implement based on your ScreenState fields _gfx->setTextColor(0xFFFF); _gfx->setTextSize(3); _gfx->setCursor(200, 200); _gfx->print("Alert Mode"); }