From a0a7b7e26331900852e579cab20d439624a4be79 Mon Sep 17 00:00:00 2001 From: Alex Hope-O'Connor Date: Wed, 5 Nov 2025 20:47:10 +1000 Subject: [PATCH 1/2] Refactor WiFi scanning JavaScript to use raw string literal template - Remove async parameter from WiFi_scanNetworks() method signatures - Move JavaScript code to templates/WiFiPollingJS.h using R"rawliteral" format - Fix JavaScript syntax errors by eliminating quote escaping issues - Change scan status route from /wifi/scanstatus to /wifistatus to avoid route conflicts - Cleaner code structure with JavaScript in separate template file --- lib/WiFiManager/include/WiFiManager.h | 2 - .../include/templates/WiFiPollingJS.h | 112 ++++++++++++++++++ lib/WiFiManager/include/wm_consts_en.h | 2 +- lib/WiFiManager/src/WiFiManager.cpp | 108 +---------------- 4 files changed, 115 insertions(+), 109 deletions(-) create mode 100644 lib/WiFiManager/include/templates/WiFiPollingJS.h diff --git a/lib/WiFiManager/include/WiFiManager.h b/lib/WiFiManager/include/WiFiManager.h index a562351..7f671dd 100644 --- a/lib/WiFiManager/include/WiFiManager.h +++ b/lib/WiFiManager/include/WiFiManager.h @@ -782,8 +782,6 @@ protected: String WiFi_psk(bool persistent = true) const; bool WiFi_scanNetworks(); bool WiFi_scanNetworks(bool force); // Always async - returns false if scan started but not complete - bool WiFi_scanNetworks(bool force,bool async); // async parameter ignored - always async - bool WiFi_scanNetworks(unsigned int cachetime,bool async); // async parameter ignored - always async bool WiFi_scanNetworks(unsigned int cachetime); void WiFi_scanComplete(int networksFound); bool WiFiSetCountry(); diff --git a/lib/WiFiManager/include/templates/WiFiPollingJS.h b/lib/WiFiManager/include/templates/WiFiPollingJS.h new file mode 100644 index 0000000..46c6a2b --- /dev/null +++ b/lib/WiFiManager/include/templates/WiFiPollingJS.h @@ -0,0 +1,112 @@ +/** + * WiFiPollingJS.h + * JavaScript code for WiFi network scanning with AJAX polling + * This is embedded in the WiFi configuration page for async WiFi scanning + */ + +#ifndef _WIFI_POLLING_JS_H_ +#define _WIFI_POLLING_JS_H_ + +#include + +const char PROGMEM WIFI_POLLING_JS[] = R"rawliteral( + +)rawliteral"; + +#endif // _WIFI_POLLING_JS_H_ + diff --git a/lib/WiFiManager/include/wm_consts_en.h b/lib/WiFiManager/include/wm_consts_en.h index cf703fb..d728709 100644 --- a/lib/WiFiManager/include/wm_consts_en.h +++ b/lib/WiFiManager/include/wm_consts_en.h @@ -59,7 +59,7 @@ const char R_exit[] PROGMEM = "/exit"; const char R_close[] PROGMEM = "/close"; const char R_erase[] PROGMEM = "/erase"; const char R_status[] PROGMEM = "/status"; -const char R_scanstatus[] PROGMEM = "/wifi/scanstatus"; +const char R_scanstatus[] PROGMEM = "/wifistatus"; const char R_update[] PROGMEM = "/update"; const char R_updatedone[] PROGMEM = "/u"; diff --git a/lib/WiFiManager/src/WiFiManager.cpp b/lib/WiFiManager/src/WiFiManager.cpp index d3c4744..5b231e1 100644 --- a/lib/WiFiManager/src/WiFiManager.cpp +++ b/lib/WiFiManager/src/WiFiManager.cpp @@ -11,6 +11,7 @@ */ #include "WiFiManager.h" +#include "templates/WiFiPollingJS.h" #if defined(ESP8266) || defined(ESP32) @@ -1501,104 +1502,7 @@ void WiFiManager::handleWifi(AsyncWebServerRequest *request, boolean scan) { reportStatus(page); // Add JavaScript for AJAX polling - page += F(""); + page += FPSTR(WIFI_POLLING_JS); page += getHTTPEnd(); @@ -1675,17 +1579,9 @@ bool WiFiManager::WiFi_scanNetworks(){ return WiFi_scanNetworks(false); } -bool WiFiManager::WiFi_scanNetworks(unsigned int cachetime,bool async){ - // async parameter ignored - always async now - return WiFi_scanNetworks(millis()-_lastscan > cachetime); -} bool WiFiManager::WiFi_scanNetworks(unsigned int cachetime){ return WiFi_scanNetworks(millis()-_lastscan > cachetime); } -bool WiFiManager::WiFi_scanNetworks(bool force,bool async){ - // async parameter ignored - always async now - return WiFi_scanNetworks(force); -} bool WiFiManager::WiFi_scanNetworks(bool force){ #ifdef WM_DEBUG_LEVEL // DEBUG_WM(WM_DEBUG_DEV,_numNetworks,(millis()-_lastscan )); From f119b02815e66c68bf8f43f6bb943d5f9db036dd Mon Sep 17 00:00:00 2001 From: Alex Hope-O'Connor Date: Wed, 5 Nov 2025 23:19:26 +1000 Subject: [PATCH 2/2] Fix async WiFi scanning and captive portal redirects - Fix captive portal redirect to preserve URL path and query params - Fix password field rendering for WiFi networks (encryption detection) - Add WiFiPollingJS template for AJAX polling (currently disabled due to size limit) - Remove blocking WiFi scan operations - Add debug output for response handling --- .../include/templates/WiFiPollingJS.h | 11 +++-- lib/WiFiManager/include/wm_strings_en.h | 9 ++-- lib/WiFiManager/src/WiFiManager.cpp | 42 +++++++++++++++++-- 3 files changed, 52 insertions(+), 10 deletions(-) diff --git a/lib/WiFiManager/include/templates/WiFiPollingJS.h b/lib/WiFiManager/include/templates/WiFiPollingJS.h index 46c6a2b..f761772 100644 --- a/lib/WiFiManager/include/templates/WiFiPollingJS.h +++ b/lib/WiFiManager/include/templates/WiFiPollingJS.h @@ -66,12 +66,17 @@ function updateNetworkList(data) { } else if(data.networks && data.networks.length > 0) { data.networks.forEach(function(network) { let qualityPercent = network.quality + '%'; - let encrypted = network.enc_type !== 0 ? '🔒' : ''; + // Map quality 0-100 to icon level 1-4 + let qualityIcon = Math.round((network.quality / 100) * 3) + 1; + if(qualityIcon < 1) qualityIcon = 1; + if(qualityIcon > 4) qualityIcon = 4; + // Encryption class: 'l' if encrypted, empty if open + let encClass = network.enc_type !== 0 ? 'l' : ''; let ssidEscaped = escapeHtml(network.ssid); + // Match original template structure: icon div with quality + encryption class, then percentage div html += '
' + ssidEscaped + ''; - html += ''; + html += ''; html += '
' + qualityPercent + '
'; - html += encrypted; html += '
'; }); } diff --git a/lib/WiFiManager/include/wm_strings_en.h b/lib/WiFiManager/include/wm_strings_en.h index da6bfac..edc5ee6 100644 --- a/lib/WiFiManager/include/wm_strings_en.h +++ b/lib/WiFiManager/include/wm_strings_en.h @@ -31,10 +31,13 @@ const char HTTP_HEAD_START[] PROGMEM = "" const char HTTP_SCRIPT[] PROGMEM = ""; // @todo add button states, disable on click , show ack , spinner etc const char HTTP_HEAD_END[] PROGMEM = "
"; // {c} = _bodyclass diff --git a/lib/WiFiManager/src/WiFiManager.cpp b/lib/WiFiManager/src/WiFiManager.cpp index d70b5f7..8232760 100644 --- a/lib/WiFiManager/src/WiFiManager.cpp +++ b/lib/WiFiManager/src/WiFiManager.cpp @@ -1382,7 +1382,14 @@ void WiFiManager::handleRoot(AsyncWebServerRequest *request) { void WiFiManager::handleWifi(AsyncWebServerRequest *request, boolean scan) { #ifdef WM_DEBUG_LEVEL DEBUG_WM(WM_DEBUG_VERBOSE, F("<- HTTP Wifi")); + DEBUG_WM(WM_DEBUG_DEV, F("handleWifi called, scan="), scan ? "true" : "false"); #endif + if (captivePortal(request)) { + #ifdef WM_DEBUG_LEVEL + DEBUG_WM(WM_DEBUG_DEV, F("Captive portal redirect")); + #endif + return; // If captive portal redirect instead of displaying the page + } handleRequest(request); String page = getHTTPHead(FPSTR(S_titlewifi), FPSTR(C_wifi)); // @token titlewifi if (scan) { @@ -1441,15 +1448,29 @@ void WiFiManager::handleWifi(AsyncWebServerRequest *request, boolean scan) { if(_showBack) page += FPSTR(HTTP_BACKBTN); reportStatus(page); - // Add JavaScript for AJAX polling - page += FPSTR(WIFI_POLLING_JS); + // TODO: Known issue - Adding WIFI_POLLING_JS causes silent failure (empty response) + // This is likely due to: + // 1. String size limit: Page is already ~9-10KB, adding ~2.5KB JS may exceed ESPAsyncWebServer limits + // 2. Memory fragmentation: Large String append operations on ESP8266 can cause heap fragmentation + // 3. PROGMEM access: FPSTR() on large PROGMEM strings may have timing issues with async operations + // 4. Response size: ESPAsyncWebServer may silently fail or truncate responses exceeding ~12KB + // Workaround: Temporarily disabled until we can optimize the page size or use chunked responses + // page += FPSTR(WIFI_POLLING_JS); page += getHTTPEnd(); + #ifdef WM_DEBUG_LEVEL + DEBUG_WM(WM_DEBUG_DEV, F("Page length: "), String(page.length())); + DEBUG_WM(WM_DEBUG_DEV, F("_numNetworks: "), String(_numNetworks)); + DEBUG_WM(WM_DEBUG_DEV, F("_scanInProgress: "), _scanInProgress ? "true" : "false"); + DEBUG_WM(WM_DEBUG_DEV, F("_lastscan: "), String(_lastscan)); + DEBUG_WM(WM_DEBUG_DEV, F("About to send response")); + #endif + request->send(200, FPSTR(HTTP_HEAD_CT), page); #ifdef WM_DEBUG_LEVEL - DEBUG_WM(WM_DEBUG_DEV, F("Sent config page")); + DEBUG_WM(WM_DEBUG_DEV, F("Response sent")); #endif } @@ -2638,8 +2659,21 @@ boolean WiFiManager::captivePortal(AsyncWebServerRequest *request) { #ifdef WM_DEBUG_LEVEL DEBUG_WM(WM_DEBUG_VERBOSE, F("<- Request redirected to captive portal")); DEBUG_WM(WM_DEBUG_DEV, "serverLoc " + serverLoc); + DEBUG_WM(WM_DEBUG_DEV, "Original URL " + request->url()); #endif - request->redirect((String)F("http://") + serverLoc); + // Preserve the original path in the redirect + String redirectUrl = (String)F("http://") + serverLoc + request->url(); + if (request->params() > 0) { + redirectUrl += F("?"); + for (size_t i = 0; i < request->params(); i++) { + if (i > 0) redirectUrl += F("&"); + redirectUrl += request->getParam(i)->name() + F("=") + request->getParam(i)->value(); + } + } + #ifdef WM_DEBUG_LEVEL + DEBUG_WM(WM_DEBUG_DEV, "Redirect URL " + redirectUrl); + #endif + request->redirect(redirectUrl); return true; } return false;