From 90eed9d5ff2ec21d578874f9f24e5e0f169c0bbe Mon Sep 17 00:00:00 2001 From: cottongin Date: Mon, 3 Aug 2026 11:25:41 -0400 Subject: [PATCH] fix: single boot refresh with QR code, async WiFi scan, skip light sleep in AP mode Co-authored-by: Cursor --- data/setup.html | 15 ++++++++++++-- platformio.ini | 1 + src/display_manager.cpp | 45 ++++++++++++++++++++++++++++++++++++++++- src/display_manager.h | 2 ++ src/main.cpp | 10 ++++----- src/web_server.cpp | 25 +++++++++++++++++++++-- 6 files changed, 88 insertions(+), 10 deletions(-) diff --git a/data/setup.html b/data/setup.html index 2473104..347b428 100644 --- a/data/setup.html +++ b/data/setup.html @@ -46,13 +46,24 @@ async function scanNetworks() { try { const res = await fetch('/api/wifi/scan'); - const networks = await res.json(); + const data = await res.json(); + if (data.scanning) { + // Scan in progress — retry after 2s + setTimeout(scanNetworks, 2000); + return; + } const select = document.getElementById('wifiSsid'); - select.innerHTML = networks.map(n => + if (data.length === 0) { + select.innerHTML = ''; + setTimeout(scanNetworks, 3000); + return; + } + select.innerHTML = data.map(n => `` ).join(''); } catch (e) { console.error('Scan failed:', e); + setTimeout(scanNetworks, 3000); } } diff --git a/platformio.ini b/platformio.ini index 8867597..1667bce 100644 --- a/platformio.ini +++ b/platformio.ini @@ -22,6 +22,7 @@ lib_deps = https://github.com/m5stack/M5PM1.git mathieucarbou/ESP Async WebServer @ ^3.0.6 bblanchon/ArduinoJson @ ^7.0.0 + ricmoo/QRCode @ ^0.0.1 lib_ignore = WebServer diff --git a/src/display_manager.cpp b/src/display_manager.cpp index 0d55a7c..4062699 100644 --- a/src/display_manager.cpp +++ b/src/display_manager.cpp @@ -21,8 +21,51 @@ void DisplayManager::begin(PowerManager& power) { Serial.printf("[display] Initialized: %dx%d, rotation=%d\n", _display->width(), _display->height(), _display->getRotation()); +} - showMessage("Immich Frame", "Starting up..."); +void DisplayManager::showSetupScreen() { + _power->enableEPDPower(); + + _display->fillScreen(TFT_WHITE); + + // Generate QR code for WiFi auto-connect + // Format: WIFI:T:nopass;S:PaperColor-Setup;; + const char* qrData = "WIFI:T:nopass;S:PaperColor-Setup;;"; + QRCode qrcode; + uint8_t qrcodeData[qrcode_getBufferSize(6)]; + qrcode_initText(&qrcode, qrcodeData, 6, ECC_LOW, qrData); + + // Draw QR code centered vertically, left-of-center horizontally + int qrSize = qrcode.size; + int scale = 4; // pixels per QR module + int qrPixels = qrSize * scale; + int qrX = (DISPLAY_WIDTH / 3) - (qrPixels / 2); + int qrY = (DISPLAY_HEIGHT - qrPixels) / 2; + + for (int y = 0; y < qrSize; y++) { + for (int x = 0; x < qrSize; x++) { + uint16_t color = qrcode_getModule(&qrcode, x, y) ? TFT_BLACK : TFT_WHITE; + _display->fillRect(qrX + x * scale, qrY + y * scale, scale, scale, color); + } + } + + // Draw text instructions to the right of QR code + int textX = DISPLAY_WIDTH / 2 + 40; + _display->setTextColor(TFT_BLACK); + _display->setTextDatum(middle_left); + _display->setTextSize(2); + _display->drawString("PaperColor", textX, DISPLAY_HEIGHT / 2 - 60); + _display->setTextSize(1); + _display->drawString("Scan QR to connect", textX, DISPLAY_HEIGHT / 2 - 20); + _display->drawString("or join WiFi:", textX, DISPLAY_HEIGHT / 2 + 10); + _display->setTextSize(1.5); + _display->drawString("PaperColor-Setup", textX, DISPLAY_HEIGHT / 2 + 40); + _display->setTextSize(1); + _display->drawString("Then open:", textX, DISPLAY_HEIGHT / 2 + 70); + _display->drawString("192.168.4.1", textX, DISPLAY_HEIGHT / 2 + 95); + + triggerRefresh(); + _power->disableEPDPower(); } void DisplayManager::showImage(const ProcessedImage& img) { diff --git a/src/display_manager.h b/src/display_manager.h index 028e39e..f7ef462 100644 --- a/src/display_manager.h +++ b/src/display_manager.h @@ -2,6 +2,7 @@ #include #include +#include #include "config.h" #include "image_pipeline.h" #include "immich_client.h" @@ -12,6 +13,7 @@ class PowerManager; class DisplayManager { public: void begin(PowerManager& power); + void showSetupScreen(); void showImage(const ProcessedImage& img); void showMessage(const char* title, const char* body); void showMetadata(const AssetInfo& info, uint8_t metaFlags, MetaPosition pos); diff --git a/src/main.cpp b/src/main.cpp index 4c7e5fa..dfe5b88 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -199,19 +199,19 @@ void setup() { webServer.begin(settingsManager, immichClient, powerManager, wifiManager); webServer.setActionCallback(webActionCallback); + // Single display update after all init is complete if (wifiManager.isAPMode()) { - displayManager.showMessage("Setup Required", - "Connect to PaperColor-Setup WiFi"); + displayManager.showSetupScreen(); } else { displayManager.showMessage("Immich Frame", "Loading photos..."); // Create display task on Core 1 xTaskCreatePinnedToCore(displayTask, "display", 32768, nullptr, 1, &displayTaskHandle, 1); - } - // Enable light sleep for power saving - powerManager.enableLightSleep(); + // Enable light sleep only in station mode (not supported in AP mode) + powerManager.enableLightSleep(); + } Serial.println("[main] Boot complete"); } diff --git a/src/web_server.cpp b/src/web_server.cpp index 59f6de3..9eb7186 100644 --- a/src/web_server.cpp +++ b/src/web_server.cpp @@ -46,9 +46,25 @@ void AppWebServer::setupCaptivePortal() { request->send(LittleFS, "/setup.html", "text/html"); }); - // Handle WiFi scan + // Kick off initial async WiFi scan + WiFi.scanNetworks(true); + + // Handle WiFi scan (non-blocking — returns cached results or "scanning" status) _server.on("/api/wifi/scan", HTTP_GET, [](AsyncWebServerRequest* request) { - int n = WiFi.scanNetworks(); + int n = WiFi.scanComplete(); + if (n == WIFI_SCAN_RUNNING) { + // Scan in progress — tell client to retry + request->send(200, "application/json", "{\"scanning\":true}"); + return; + } + if (n == WIFI_SCAN_FAILED) { + // No scan started or failed — kick one off + WiFi.scanNetworks(true); + request->send(200, "application/json", "{\"scanning\":true}"); + return; + } + + // Scan complete — return results JsonDocument doc; JsonArray arr = doc.to(); for (int i = 0; i < n; i++) { @@ -59,6 +75,11 @@ void AppWebServer::setupCaptivePortal() { } String response; serializeJson(doc, response); + + // Start a fresh scan for next request + WiFi.scanDelete(); + WiFi.scanNetworks(true); + request->send(200, "application/json", response); });