#include "DisplayDriverTFT.h" #include extern DisplayManager display; void DisplayDriverTFT::begin() { // Backlight pinMode(PIN_LCD_BL, OUTPUT); digitalWrite(PIN_LCD_BL, LOW); _tft.init(); _tft.setRotation(DISPLAY_ROTATION); _tft.fillScreen(TFT_BLACK); Serial.printf("[GFX] Display OK: %dx%d\n", DISPLAY_WIDTH, DISPLAY_HEIGHT); Serial.flush(); // Debug: check if touch controller is responding uint16_t z = _tft.getTouchRawZ(); Serial.printf("[TOUCH] Raw Z=%d (non-zero = controller detected)\n", z); Serial.flush(); ScreenState st; st.screen = ScreenID::BOOT; st.bootStage = BootStage::SPLASH; drawBoot(st); digitalWrite(PIN_LCD_BL, HIGH); Serial.println("[GFX] Backlight ON"); Serial.flush(); } void DisplayDriverTFT::setBacklight(bool on) { digitalWrite(PIN_LCD_BL, on ? HIGH : LOW); } // ── Rendering ─────────────────────────────────────────────── void DisplayDriverTFT::render(const ScreenState& st) { if(st.screen != _lastScreen || (st.screen == ScreenID::BOOT && st.bootStage != _lastBootStage)) { _needsRedraw = true; _lastScreen = st.screen; _lastBootStage = st.bootStage; } switch(st.screen) { case ScreenID::BOOT: if(_needsRedraw) { drawBoot(st); _needsRedraw = false; } break; case ScreenID::ALERT: drawAlert(st); break; case ScreenID::DASHBOARD: if(_needsRedraw) { drawDashboard(st); _needsRedraw = false; } break; case ScreenID::OFF: if(_needsRedraw) { _tft.fillScreen(TFT_BLACK); _needsRedraw = false; } break; } } void DisplayDriverTFT::drawBoot(const ScreenState& st) { BootStage stage = st.bootStage; _tft.fillScreen(TFT_BLACK); _tft.setTextColor(TFT_WHITE, TFT_BLACK); _tft.setTextSize(2); _tft.setCursor(10, 10); _tft.printf("KLUBHAUS v%s", FW_VERSION); _tft.setTextSize(1); _tft.setCursor(10, 40); _tft.print(BOARD_NAME); // Show boot stage status _tft.setCursor(10, 70); switch(stage) { case BootStage::SPLASH: _tft.print("Initializing..."); break; case BootStage::INIT_DISPLAY: _tft.print("Display OK"); break; case BootStage::INIT_NETWORK: _tft.print("Network init..."); break; case BootStage::CONNECTING_WIFI: _tft.print("Connecting WiFi..."); break; case BootStage::READY: _tft.print("All systems go!"); break; case BootStage::DONE: _tft.print("Ready!"); break; } } void DisplayDriverTFT::drawAlert(const ScreenState& st) { uint32_t elapsed = millis() - st.alertStartMs; uint8_t pulse = 180 + (uint8_t)(75.0f * sinf(elapsed / 300.0f)); uint16_t bg = _tft.color565(pulse, 0, 0); _tft.fillScreen(bg); _tft.setTextColor(TFT_WHITE, bg); _tft.setTextSize(3); _tft.setCursor(10, 20); _tft.print(st.alertTitle.length() > 0 ? st.alertTitle : "ALERT"); _tft.setTextSize(2); _tft.setCursor(10, 80); _tft.print(st.alertBody); _tft.setTextSize(1); _tft.setCursor(10, DISPLAY_HEIGHT - 20); _tft.print("Hold to silence..."); } void DisplayDriverTFT::drawDashboard(const ScreenState& st) { _tft.fillScreen(TFT_BLACK); // Header _tft.setTextColor(TFT_WHITE, TFT_BLACK); _tft.setTextSize(1); _tft.setCursor(5, 5); _tft.printf("KLUBHAUS"); // WiFi indicator _tft.setCursor(DISPLAY_WIDTH - 50, 5); _tft.printf("WiFi:%s", st.wifiSsid.length() > 0 ? "ON" : "OFF"); // Get tile layouts from library helper int tileCount = display.calculateDashboardLayouts(30, 8); const TileLayout* layouts = display.getTileLayouts(); const char* tileLabels[] = { "Alert", "Silent", "Status", "Reboot" }; const uint16_t tileColors[] = { 0x0280, 0x0400, 0x0440, 0x0100 }; for(int i = 0; i < tileCount && i < 4; i++) { const TileLayout& lay = layouts[i]; int x = lay.x; int y = lay.y; int w = lay.w; int h = lay.h; // Tile background _tft.fillRoundRect(x, y, w, h, 8, tileColors[i]); // Tile border _tft.drawRoundRect(x, y, w, h, 8, TFT_WHITE); // Tile label _tft.setTextColor(TFT_WHITE); _tft.setTextSize(2); int textLen = strlen(tileLabels[i]); int textW = textLen * 12; _tft.setCursor(x + w/2 - textW/2, y + h/2 - 10); _tft.print(tileLabels[i]); } } // ── Touch ─────────────────────────────────────────────────── TouchEvent DisplayDriverTFT::readTouch() { TouchEvent evt; uint16_t tx, ty; uint8_t touched = _tft.getTouch(&tx, &ty, 100); // Detect transitions (press/release) if(touched && !_touchWasPressed) { // Press transition: finger just touched down evt.pressed = true; _touchDownX = tx; _touchDownY = ty; evt.downX = _touchDownX; evt.downY = _touchDownY; } else if(!touched && _touchWasPressed) { // Release transition: finger just lifted evt.released = true; evt.downX = _touchDownX; evt.downY = _touchDownY; } // Current position if still touched if(touched) { evt.x = tx; evt.y = ty; evt.downX = _touchDownX; evt.downY = _touchDownY; } // Track previous state for next call _touchWasPressed = touched; return evt; } uint16_t DisplayDriverTFT::getRawTouchZ() { return _tft.getTouchRawZ(); } void DisplayDriverTFT::transformTouch(int* x, int* y) { // Resistive touch panel is rotated 90° vs display - swap coordinates int temp = *x; *x = *y; *y = temp; } HoldState DisplayDriverTFT::updateHold(unsigned long holdMs) { HoldState h; TouchEvent t = readTouch(); if(t.pressed) { if(!_holdActive) { _holdActive = true; _holdStartMs = millis(); h.started = true; } uint32_t held = millis() - _holdStartMs; h.active = true; h.progress = constrain((float)held / (float)holdMs, 0.0f, 1.0f); h.completed = (held >= holdMs); // Simple progress bar at bottom of screen int barW = (int)(DISPLAY_WIDTH * h.progress); _tft.fillRect(0, DISPLAY_HEIGHT - 8, barW, 8, TFT_WHITE); _tft.fillRect(barW, DISPLAY_HEIGHT - 8, DISPLAY_WIDTH - barW, 8, TFT_DARKGREY); } else { if(_holdActive) { // Clear the progress bar when released _tft.fillRect(0, DISPLAY_HEIGHT - 8, DISPLAY_WIDTH, 8, TFT_DARKGREY); } _holdActive = false; } return h; } void DisplayDriverTFT::updateHint(int x, int y, bool active) { float period = active ? 500.0f : 2000.0f; float t = fmodf(millis(), period) / period; uint8_t v = static_cast(30.0f + 30.0f * sinf(t * 2.0f * PI)); uint16_t col = _tft.color565(v, v, v); _tft.drawRect(x - 40, y - 20, 80, 40, col); }