#include #include #include #include #include "display_config.h" #include "renderer_lcd.h" #include "config.h" #include "captive_portal.h" // ── Display instance ──────────────────────────────────────────────────────── static LGFX_WT32_SC01 lcd; // ── Status bar at bottom ──────────────────────────────────────────────────── static void drawStatusBar(const char* status, uint32_t color = LCD_LABEL) { lcd.fillRect(0, 300, 480, 20, 0x0000); lcd.setTextSize(1.0f); lcd.setTextColor(color); lcd.setTextDatum(lgfx::bottom_left); lcd.drawString(status, 4, 318); } // ── Show error full screen ────────────────────────────────────────────────── static void showError(const char* message) { Serial.printf("[Error] %s\n", message); lcd.fillScreen(0x0000); lcd.setTextSize(2.0f); lcd.setTextColor(TFT_RED); lcd.setTextDatum(lgfx::middle_center); lcd.drawString("FEHLER", 240, 140); lcd.setTextSize(1.2f); lcd.setTextColor(LCD_TEXT); lcd.drawString(message, 240, 170); } // ── Show setup instructions on LCD ────────────────────────────────────────── static void showSetupScreen() { lcd.fillScreen(0x0000); lcd.setTextSize(2.0f); lcd.setTextColor(LCD_ACCENT); lcd.setTextDatum(lgfx::middle_center); lcd.drawString("EPaper Setup", 240, 80); lcd.setTextSize(1.2f); lcd.setTextColor(LCD_TEXT); lcd.drawString("Mit 'EPaper-Setup' WLAN verbinden", 240, 140); lcd.drawString("und http://192.168.4.1 oeffnen", 240, 165); } // ── Fetch and render ──────────────────────────────────────────────────────── static bool fetchAndRender(Config& config) { String url = config.api_url + "/api/display/" + config.display_id; Serial.printf("[HTTP] GET %s\n", url.c_str()); drawStatusBar("Daten laden...", LCD_ACCENT); HTTPClient http; http.begin(url); http.setTimeout(10000); if (config.api_token.length() > 0) { http.addHeader("Authorization", "Bearer " + config.api_token); } int httpCode = http.GET(); if (httpCode <= 0 || httpCode >= 400) { Serial.printf("[HTTP] Error: %d\n", httpCode); http.end(); String errMsg = "HTTP Fehler: " + String(httpCode); drawStatusBar(errMsg.c_str(), TFT_RED); return false; } String payload = http.getString(); http.end(); Serial.printf("[HTTP] Payload length: %d\n", payload.length()); // Parse JSON JsonDocument doc; DeserializationError err = deserializeJson(doc, payload); if (err) { Serial.printf("[JSON] Parse error: %s\n", err.c_str()); String errMsg = "JSON Fehler: " + String(err.c_str()); drawStatusBar(errMsg.c_str(), TFT_RED); return false; } // Extract refresh_sec int refreshSec = doc["refresh_sec"] | config.refresh_sec; if (refreshSec < 5) refreshSec = 30; config.refresh_sec = refreshSec; // Render JsonArray elements = doc["elements"].as(); lcd.fillScreen(LCD_BG); renderElementsLCD(lcd, elements); // Status bar with last update time char timeBuf[40]; unsigned long sec = millis() / 1000; snprintf(timeBuf, sizeof(timeBuf), "Aktualisiert | Refresh: %ds", refreshSec); drawStatusBar(timeBuf, LCD_LABEL); Serial.println("[Render] Done"); return true; } // ── Setup ─────────────────────────────────────────────────────────────────── void setup() { Serial.begin(115200); delay(2000); Serial.println("\n\n[Boot] WT32-SC01 Plus LCD Display starting..."); // 1. Init LCD lcd.init(); lcd.setRotation(1); // Landscape: 480x320 lcd.setBrightness(200); lcd.fillScreen(0x0000); lcd.setTextSize(1.5f); lcd.setTextColor(LCD_TEXT); lcd.setTextDatum(lgfx::middle_center); lcd.drawString("Starte...", 240, 160); // 2. Load config from NVS Config config; config.load(); // 3. If not configured → captive portal if (!config.isConfigured()) { Serial.println("[Boot] Not configured, starting captive portal"); showSetupScreen(); startCaptivePortal(config); // never returns } // 4. Connect WiFi lcd.fillScreen(0x0000); lcd.drawString("WiFi verbinden...", 240, 160); Serial.printf("[WiFi] Connecting to %s\n", config.wifi_ssid.c_str()); WiFi.mode(WIFI_STA); WiFi.begin(config.wifi_ssid.c_str(), config.wifi_password.c_str()); int attempts = 0; while (WiFi.status() != WL_CONNECTED && attempts < 30) { delay(500); Serial.print("."); ++attempts; } Serial.println(); if (WiFi.status() != WL_CONNECTED) { showError("WiFi Verbindung fehlgeschlagen"); delay(10000); ESP.restart(); return; } Serial.printf("[WiFi] Connected, IP: %s\n", WiFi.localIP().toString().c_str()); // 5. Initial fetch if (!fetchAndRender(config)) { showError("Daten konnten nicht geladen werden"); } } // ── Loop: periodic refresh (no deep sleep for LCD) ────────────────────────── static unsigned long lastUpdate = 0; static Config loopConfig; static bool configLoaded = false; void loop() { if (!configLoaded) { loopConfig.load(); configLoaded = true; lastUpdate = millis(); } unsigned long now = millis(); unsigned long intervalMs = (unsigned long)loopConfig.refresh_sec * 1000UL; if (now - lastUpdate >= intervalMs) { lastUpdate = now; if (WiFi.status() != WL_CONNECTED) { Serial.println("[WiFi] Reconnecting..."); WiFi.reconnect(); delay(5000); } if (WiFi.status() == WL_CONNECTED) { fetchAndRender(loopConfig); } else { drawStatusBar("WiFi getrennt!", TFT_RED); } } // Touch handling: tap to force refresh lgfx::touch_point_t tp; if (lcd.getTouch(&tp)) { Serial.println("[Touch] Manual refresh triggered"); drawStatusBar("Manuell aktualisieren...", LCD_ACCENT); delay(300); // debounce if (WiFi.status() == WL_CONNECTED) { fetchAndRender(loopConfig); } lastUpdate = millis(); } delay(50); // small delay for touch responsiveness }