From 83c271fed49bdbd7fd8846908c699ff15872fcb7 Mon Sep 17 00:00:00 2001 From: Shawn A Date: Fri, 25 Mar 2016 08:19:47 -0500 Subject: [PATCH 01/80] initial commit, signal meter and secure icons Adds sprite based icons, added some accessibility support since this uses background images. Can use classes `left` or `right` on parent div to align on either side of SSID, parent div `invert` css filters the icons to white. base64 image string has been formatted for easy replacement --- WiFiManager.cpp | 2 ++ WiFiManager.h | 9 ++++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/WiFiManager.cpp b/WiFiManager.cpp index 47414ca..4f234d4 100644 --- a/WiFiManager.cpp +++ b/WiFiManager.cpp @@ -444,6 +444,8 @@ void WiFiManager::handleWifi(boolean scan) { rssiQ += quality; item.replace("{v}", WiFi.SSID(indices[i])); item.replace("{r}", rssiQ); + Serial.print(map(quality,0,100,1,4)); + item.replace("{q}", (String)round(map(quality,0,100,1,4))); if (WiFi.encryptionType(indices[i]) != ENC_TYPE_NONE) { item.replace("{i}", "l"); } else { diff --git a/WiFiManager.h b/WiFiManager.h index 9f5bc41..5aeba9e 100644 --- a/WiFiManager.h +++ b/WiFiManager.h @@ -26,11 +26,14 @@ extern "C" { const char HTTP_200[] PROGMEM = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n"; const char HTTP_HEAD[] PROGMEM = "{v}"; -const char HTTP_STYLE[] PROGMEM = ""; +// const char HTTP_STYLE[] PROGMEM = ""; +const char HTTP_STYLE[] PROGMEM = ""; const char HTTP_SCRIPT[] PROGMEM = ""; -const char HTTP_HEAD_END[] PROGMEM = "
"; +const char HTTP_HEAD_END[] PROGMEM = "
"; const char HTTP_PORTAL_OPTIONS[] PROGMEM = "



"; -const char HTTP_ITEM[] PROGMEM = "
{v} {r}%
"; +const char HTTP_ITEM[] PROGMEM = "
{v}({r})
"; //const char HTTP_ITEM_PADLOCK[] PROGMEM = ""; const char HTTP_FORM_START[] PROGMEM = "


"; const char HTTP_FORM_PARAM[] PROGMEM = "
"; From 6708ddf37bc51a40fa86509486f9f4ae82a63867 Mon Sep 17 00:00:00 2001 From: Shawn A Date: Fri, 25 Mar 2016 08:48:49 -0500 Subject: [PATCH 02/80] remove debug --- WiFiManager.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/WiFiManager.cpp b/WiFiManager.cpp index 4f234d4..30c1c48 100644 --- a/WiFiManager.cpp +++ b/WiFiManager.cpp @@ -444,7 +444,6 @@ void WiFiManager::handleWifi(boolean scan) { rssiQ += quality; item.replace("{v}", WiFi.SSID(indices[i])); item.replace("{r}", rssiQ); - Serial.print(map(quality,0,100,1,4)); item.replace("{q}", (String)round(map(quality,0,100,1,4))); if (WiFi.encryptionType(indices[i]) != ENC_TYPE_NONE) { item.replace("{i}", "l"); From fd27cef1aeb11fce552f791794dfb41dff1de3b1 Mon Sep 17 00:00:00 2001 From: Shawn A Date: Fri, 25 Mar 2016 09:31:07 -0500 Subject: [PATCH 03/80] remove debug --- WiFiManager.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WiFiManager.h b/WiFiManager.h index 5aeba9e..6e6acfd 100644 --- a/WiFiManager.h +++ b/WiFiManager.h @@ -33,7 +33,7 @@ const char HTTP_STYLE[] PROGMEM = ""; -const char HTTP_STYLE[] PROGMEM = ""; +const char HTTP_STYLE[] PROGMEM = ""; const char HTTP_SCRIPT[] PROGMEM = ""; const char HTTP_HEAD_END[] PROGMEM = "
"; const char HTTP_PORTAL_OPTIONS[] PROGMEM = "


"; diff --git a/extras/png_signal_strength_master.png b/extras/png_signal_strength_master.png new file mode 100644 index 0000000000000000000000000000000000000000..f03c947f155f0552ce691a06424239ef80252812 GIT binary patch literal 59671 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 F{{Tv)bCCc5 literal 0 HcmV?d00001 diff --git a/extras/test.html b/extras/test.html new file mode 100644 index 0000000..58c913c --- /dev/null +++ b/extras/test.html @@ -0,0 +1,176 @@ + + + + + + Config ESP + + + + + + + + + + + + From 9c72bf98f778d38d6da1b4729ad5318318ba86e4 Mon Sep 17 00:00:00 2001 From: Shawn A Date: Thu, 9 Feb 2017 16:33:10 -0600 Subject: [PATCH 05/80] cleaning up source, comments --- WiFiManager.cpp | 148 ++++++++++++++++++++++++++++++++---------------- WiFiManager.h | 3 +- 2 files changed, 100 insertions(+), 51 deletions(-) diff --git a/WiFiManager.cpp b/WiFiManager.cpp index c9fa5aa..1f9abe8 100644 --- a/WiFiManager.cpp +++ b/WiFiManager.cpp @@ -12,6 +12,8 @@ #include "WiFiManager.h" +// PARAMETERS + WiFiManagerParameter::WiFiManagerParameter(const char *custom) { _id = NULL; _placeholder = NULL; @@ -64,9 +66,6 @@ const char* WiFiManagerParameter::getCustomHTML() { return _customHTML; } -WiFiManager::WiFiManager() { -} - void WiFiManager::addParameter(WiFiManagerParameter *p) { if(_paramsCount + 1 > WIFI_MANAGER_MAX_PARAMS) { @@ -82,7 +81,11 @@ void WiFiManager::addParameter(WiFiManagerParameter *p) { DEBUG_WM(p->getID()); } +WiFiManager::WiFiManager() { +} + void WiFiManager::setupConfigPortal() { + // setup dns and web servers dnsServer.reset(new DNSServer()); server.reset(new ESP8266WebServer(80)); @@ -91,28 +94,21 @@ void WiFiManager::setupConfigPortal() { DEBUG_WM(F("Configuring access point... ")); DEBUG_WM(_apName); - if (_apPassword != NULL) { - if (strlen(_apPassword) < 8 || strlen(_apPassword) > 63) { - // fail passphrase to short or long! - DEBUG_WM(F("Invalid AccessPoint password. Ignoring")); - _apPassword = NULL; - } - DEBUG_WM(_apPassword); - } - //optional soft ip config + // setup optional soft AP static ip config if (_ap_static_ip) { DEBUG_WM(F("Custom AP IP/GW/Subnet")); WiFi.softAPConfig(_ap_static_ip, _ap_static_gw, _ap_static_sn); } + // start soft AP with pass or anonymous if (_apPassword != NULL) { WiFi.softAP(_apName, _apPassword);//password option } else { WiFi.softAP(_apName); } - delay(500); // Without delay I've seen the IP address blank + delay(500); // slight delay to make sure we have a password DEBUG_WM(F("AP IP address: ")); DEBUG_WM(WiFi.softAPIP()); @@ -120,7 +116,7 @@ void WiFiManager::setupConfigPortal() { dnsServer->setErrorReplyCode(DNSReplyCode::NoError); dnsServer->start(DNS_PORT, "*", WiFi.softAPIP()); - /* Setup web pages: root, wifi config pages, SO captive portal detectors and not found. */ + /* Setup httpd callbacks, web pages: root, wifi config pages, SO captive portal detectors and not found. */ server->on("/", std::bind(&WiFiManager::handleRoot, this)); server->on("/wifi", std::bind(&WiFiManager::handleWifi, this, true)); server->on("/0wifi", std::bind(&WiFiManager::handleWifi, this, false)); @@ -129,9 +125,9 @@ void WiFiManager::setupConfigPortal() { server->on("/r", std::bind(&WiFiManager::handleReset, this)); //server->on("/fwlink", std::bind(&WiFiManager::handleRoot, this)); //Microsoft captive portal. Maybe not needed. Might be handled by notFound handler. server->onNotFound (std::bind(&WiFiManager::handleNotFound, this)); + server->begin(); // Web server start DEBUG_WM(F("HTTP server started")); - } boolean WiFiManager::autoConnect() { @@ -143,17 +139,17 @@ boolean WiFiManager::autoConnect(char const *apName, char const *apPassword) { DEBUG_WM(F("")); DEBUG_WM(F("AutoConnect")); - // read eeprom for ssid and pass + // read eeprom for ssid and pass, deprecated //String ssid = getSSID(); //String pass = getPassword(); - // attempt to connect; should it fail, fall back to AP + // attempt to connect using saved settings, on fail fallback to AP config portal WiFi.mode(WIFI_STA); if (connectWifi("", "") == WL_CONNECTED) { + //connected DEBUG_WM(F("IP Address:")); DEBUG_WM(WiFi.localIP()); - //connected return true; } @@ -178,34 +174,38 @@ boolean WiFiManager::startConfigPortal(char const *apName, char const *apPasswo WiFi.mode(WIFI_AP_STA); DEBUG_WM("SET AP STA"); - _apName = apName; + _apName = apName; _apPassword = apPassword; + if(!validApPassword) return; - //notify we entered AP mode + // do AP callback if set if ( _apcallback != NULL) { _apcallback(this); } connect = false; + + // init configportal setupConfigPortal(); + // blocking loop waiting for config while(1){ - // check if timeout + // check if timed out if(configPortalHasTimeout()) break; - //DNS + //DNS handler dnsServer->processNextRequest(); - //HTTP + //HTTP handler server->handleClient(); - + // Waiting for save... if (connect) { connect = false; delay(2000); DEBUG_WM(F("Connecting to new AP")); - // using user-provided _ssid, _pass in place of system-stored ssid and pass + // attempt sta connection to submitted _ssid, _pass if (connectWifi(_ssid, _pass) != WL_CONNECTED) { DEBUG_WM(F("Failed to connect.")); } else { @@ -238,28 +238,32 @@ boolean WiFiManager::startConfigPortal(char const *apName, char const *apPasswo return WiFi.status() == WL_CONNECTED; } - int WiFiManager::connectWifi(String ssid, String pass) { DEBUG_WM(F("Connecting as wifi client...")); - // check if we've got static_ip settings, if we do, use those. + // Setup static IP config if provided if (_sta_static_ip) { DEBUG_WM(F("Custom STA IP/GW/Subnet")); WiFi.config(_sta_static_ip, _sta_static_gw, _sta_static_sn); DEBUG_WM(WiFi.localIP()); } - //fix for auto connect racing issue + + // fix for auto connect racing issue + // @todo why is this here, what issue?, should be making sure its actually ssid == ssid should it not? if (WiFi.status() == WL_CONNECTED) { - DEBUG_WM("Already connected. Bailing out."); + DEBUG_WM("Already connected. Exiting"); return WL_CONNECTED; } - //check if we have ssid and pass and force those, if not, try with last saved values + + // if ssid argument provided connect to that if (ssid != "") { + DEBUG_WM("Connecting to new AP"); WiFi.begin(ssid.c_str(), pass.c_str()); } else { + // connect using saved ssid if there is one if (WiFi.SSID()) { - DEBUG_WM("Using last saved values, should be faster"); - //trying to fix connection in progress hanging + DEBUG_WM("Connecting to saved AP"); + // trying to fix connection in progress hanging, explain why ?? ETS_UART_INTR_DISABLE(); wifi_station_disconnect(); ETS_UART_INTR_ENABLE(); @@ -273,12 +277,15 @@ int WiFiManager::connectWifi(String ssid, String pass) { int connRes = waitForConnectResult(); DEBUG_WM ("Connection result: "); DEBUG_WM ( connRes ); - //not connected, WPS enabled, no pass - first attempt + + // do WPS, if WPS options enabled and not connected and no password was supplied + // @todo this seems like wrong place for this, is it a fallback or option? if (_tryWPS && connRes != WL_CONNECTED && pass == "") { startWPS(); - //should be connected at the end of WPS + // should be connected at the end of WPS connRes = waitForConnectResult(); } + return connRes; } @@ -331,16 +338,20 @@ void WiFiManager::startWPS() { return _pass; } */ + +// GETTERS String WiFiManager::getConfigPortalSSID() { return _apName; } +// SETTERS void WiFiManager::resetSettings() { - DEBUG_WM(F("settings invalidated")); - DEBUG_WM(F("THIS MAY CAUSE AP NOT TO START UP PROPERLY. YOU NEED TO COMMENT IT OUT AFTER ERASING THE DATA.")); + DEBUG_WM(F("SETTINGS ERASED")); + DEBUG_WM(F("THIS MAY CAUSE AP NOT TO START UP PROPERLY. YOU NEED TO COMMENT IT OUT AFTER ERASING THE DATA.")); // @todo WHUT? WiFi.disconnect(true); //delay(200); } + void WiFiManager::setTimeout(unsigned long seconds) { setConfigPortalTimeout(seconds); } @@ -377,10 +388,14 @@ void WiFiManager::setBreakAfterConfig(boolean shouldBreak) { _shouldBreakAfterConfig = shouldBreak; } -/** Handle root or redirect to captive portal */ + + +/** + * HTTPD CALLBACK root or redirect to captive portal + */ void WiFiManager::handleRoot() { DEBUG_WM(F("Handle root")); - if (captivePortal()) { // If caprive portal redirect instead of displaying the page. + if (captivePortal()) { // If captive portal redirect instead of displaying the page. return; } @@ -401,7 +416,9 @@ void WiFiManager::handleRoot() { } -/** Wifi config page handler */ +/** + * HTTPD CALLBACK Wifi config page handler + */ void WiFiManager::handleWifi(boolean scan) { String page = FPSTR(HTTP_HEAD); @@ -571,7 +588,9 @@ void WiFiManager::handleWifi(boolean scan) { DEBUG_WM(F("Sent config page")); } -/** Handle the WLAN save form and redirect to WLAN config page again */ +/** + * HTTPD CALLBACK save form and redirect to WLAN config page again + */ void WiFiManager::handleWifiSave() { DEBUG_WM(F("WiFi save")); @@ -629,7 +648,9 @@ void WiFiManager::handleWifiSave() { connect = true; //signal ready to connect/reset } -/** Handle the info page */ +/** + * HTTPD CALLBACK info page + */ void WiFiManager::handleInfo() { DEBUG_WM(F("Info")); @@ -669,7 +690,9 @@ void WiFiManager::handleInfo() { DEBUG_WM(F("Sent info page")); } -/** Handle the reset page */ +/** + * HTTPD CALLBACK reset page + */ void WiFiManager::handleReset() { DEBUG_WM(F("Reset")); @@ -689,17 +712,23 @@ void WiFiManager::handleReset() { delay(2000); } - - -//removed as mentioned here https://github.com/tzapu/WiFiManager/issues/114 -/*void WiFiManager::handle204() { +/** + * HTTPD CALLBACK 204 + * @note removed as mentioned here https://github.com/tzapu/WiFiManager/issues/114 + */ +/* +void WiFiManager::handle204() { DEBUG_WM(F("204 No Response")); server->sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); server->sendHeader("Pragma", "no-cache"); server->sendHeader("Expires", "-1"); server->send ( 204, "text/plain", ""); -}*/ +} +*/ +/** + * HTTPD CALLBACK 404 + */ void WiFiManager::handleNotFound() { if (captivePortal()) { // If captive portal redirect instead of displaying the error page. return; @@ -722,8 +751,11 @@ void WiFiManager::handleNotFound() { server->send ( 404, "text/plain", message ); } - -/** Redirect to captive portal if we got a request for another domain. Return true in that case so the page handler do not try to handle the request again. */ +/** + * HTTPD redirector + * Redirect to captive portal if we got a request for another domain. + * Return true in that case so the page handler do not try to handle the request again. + */ boolean WiFiManager::captivePortal() { if (!isIp(server->hostHeader()) ) { DEBUG_WM(F("Request redirected to captive portal")); @@ -735,6 +767,8 @@ boolean WiFiManager::captivePortal() { return false; } +// MORE SETTERS + //start up config portal callback void WiFiManager::setAPCallback( void (*func)(WiFiManager* myWiFiManager) ) { _apcallback = func; @@ -755,7 +789,7 @@ void WiFiManager::setRemoveDuplicateAPs(boolean removeDuplicates) { _removeDuplicateAPs = removeDuplicates; } - +// HELPERS template void WiFiManager::DEBUG_WM(Generic text) { @@ -798,3 +832,17 @@ String WiFiManager::toStringIp(IPAddress ip) { res += String(((ip >> 8 * 3)) & 0xFF); return res; } + +bool WiFiManager::validApPassword(){ + // check that ap password is valid, return false + if (_apPassword != NULL) { + if (strlen(_apPassword) < 8 || strlen(_apPassword) > 63) { + DEBUG_WM(F("AccessPoint password is INVALID")); + _apPassword = NULL; + return false; // @todo FATAL or fallback to NULL ? + } + DEBUG_WM(F("AccessPoint Password is VALID")); + DEBUG_WM(_apPassword); + } + return true; +} diff --git a/WiFiManager.h b/WiFiManager.h index 13dc620..86a215d 100644 --- a/WiFiManager.h +++ b/WiFiManager.h @@ -170,7 +170,8 @@ class WiFiManager void handle204(); boolean captivePortal(); boolean configPortalHasTimeout(); - + boolean validApPassword(); + // DNS server const byte DNS_PORT = 53; From 45a325bd3440cfb55f620a985098ab695d3ecb46 Mon Sep 17 00:00:00 2001 From: Shawn A Date: Thu, 9 Feb 2017 16:57:34 -0600 Subject: [PATCH 06/80] fix validippassword abstracted ap password validation passing a invalid password should be fatal , not fallback to null, but could be an optional define --- WiFiManager.cpp | 4 ++-- WiFiManager.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/WiFiManager.cpp b/WiFiManager.cpp index 1f9abe8..35f9b43 100644 --- a/WiFiManager.cpp +++ b/WiFiManager.cpp @@ -176,7 +176,7 @@ boolean WiFiManager::startConfigPortal(char const *apName, char const *apPasswo _apName = apName; _apPassword = apPassword; - if(!validApPassword) return; + if(!validApPassword()) return false; // do AP callback if set if ( _apcallback != NULL) { @@ -833,7 +833,7 @@ String WiFiManager::toStringIp(IPAddress ip) { return res; } -bool WiFiManager::validApPassword(){ +boolean WiFiManager::validApPassword(){ // check that ap password is valid, return false if (_apPassword != NULL) { if (strlen(_apPassword) < 8 || strlen(_apPassword) > 63) { diff --git a/WiFiManager.h b/WiFiManager.h index 86a215d..0fccf97 100644 --- a/WiFiManager.h +++ b/WiFiManager.h @@ -170,7 +170,6 @@ class WiFiManager void handle204(); boolean captivePortal(); boolean configPortalHasTimeout(); - boolean validApPassword(); // DNS server const byte DNS_PORT = 53; @@ -179,6 +178,7 @@ class WiFiManager int getRSSIasQuality(int RSSI); boolean isIp(String str); String toStringIp(IPAddress ip); + boolean validApPassword(); boolean connect; boolean _debug = true; From 2edd37eeb83dbce93be9d7aecb5308db190e17df Mon Sep 17 00:00:00 2001 From: Shawn A Date: Thu, 9 Feb 2017 17:02:33 -0600 Subject: [PATCH 07/80] oops typo --- WiFiManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WiFiManager.cpp b/WiFiManager.cpp index 35f9b43..23af06a 100644 --- a/WiFiManager.cpp +++ b/WiFiManager.cpp @@ -108,7 +108,7 @@ void WiFiManager::setupConfigPortal() { WiFi.softAP(_apName); } - delay(500); // slight delay to make sure we have a password + delay(500); // slight delay to make sure we have an AP IP DEBUG_WM(F("AP IP address: ")); DEBUG_WM(WiFi.softAPIP()); From c819d469022a42f0ac26f368eaa9faf131e26c72 Mon Sep 17 00:00:00 2001 From: Shawn A Date: Thu, 9 Feb 2017 17:10:15 -0600 Subject: [PATCH 08/80] allow for empty password --- WiFiManager.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/WiFiManager.cpp b/WiFiManager.cpp index 23af06a..dd9b964 100644 --- a/WiFiManager.cpp +++ b/WiFiManager.cpp @@ -835,6 +835,7 @@ String WiFiManager::toStringIp(IPAddress ip) { boolean WiFiManager::validApPassword(){ // check that ap password is valid, return false + if (_apPassword == "") _apPassword = NULL; if (_apPassword != NULL) { if (strlen(_apPassword) < 8 || strlen(_apPassword) > 63) { DEBUG_WM(F("AccessPoint password is INVALID")); From bd9c3b31fb60a1860cd29d7c3bd18cb8c39eb679 Mon Sep 17 00:00:00 2001 From: Shawn A Date: Fri, 10 Feb 2017 14:07:52 -0600 Subject: [PATCH 09/80] refactoring some larger functions * broke large functions into smaller , to allow new features to be hooked in easier * added persistent test to constructor destructor * testing turn off sta during portal , causes issues --- WiFiManager.cpp | 153 +++++++++++++++++++++++++++++------------------- WiFiManager.h | 7 ++- 2 files changed, 98 insertions(+), 62 deletions(-) diff --git a/WiFiManager.cpp b/WiFiManager.cpp index dd9b964..44897b8 100644 --- a/WiFiManager.cpp +++ b/WiFiManager.cpp @@ -81,18 +81,18 @@ void WiFiManager::addParameter(WiFiManagerParameter *p) { DEBUG_WM(p->getID()); } -WiFiManager::WiFiManager() { +// constructor +WiFiManager::WiFiManager() { + WiFi.persistent(false); } -void WiFiManager::setupConfigPortal() { - // setup dns and web servers - dnsServer.reset(new DNSServer()); - server.reset(new ESP8266WebServer(80)); +// destructor +WiFiManager::~WiFiManager() { + WiFi.persistent(true); // @todo restore original +} - DEBUG_WM(F("")); - _configPortalStart = millis(); - - DEBUG_WM(F("Configuring access point... ")); +bool WiFiManager::startAP(){ + DEBUG_WM(F("Configuring access point with with SSID ... ")); DEBUG_WM(_apName); // setup optional soft AP static ip config @@ -101,16 +101,31 @@ void WiFiManager::setupConfigPortal() { WiFi.softAPConfig(_ap_static_ip, _ap_static_gw, _ap_static_sn); } + bool ret = true; + // start soft AP with pass or anonymous if (_apPassword != NULL) { - WiFi.softAP(_apName, _apPassword);//password option + ret = WiFi.softAP(_apName, _apPassword);//password option } else { - WiFi.softAP(_apName); + DEBUG_WM(F("AP has anonymous access")); + ret = WiFi.softAP(_apName); } delay(500); // slight delay to make sure we have an AP IP DEBUG_WM(F("AP IP address: ")); DEBUG_WM(WiFi.softAPIP()); + return ret; +} + +void WiFiManager::setupConfigPortal() { + // setup dns and web servers + dnsServer.reset(new DNSServer()); + server.reset(new ESP8266WebServer(80)); + + DEBUG_WM(F("setupConfigPortal")); + _configPortalStart = millis(); + + if(!startAP()) DEBUG_WM("There was an error stating the AP"); /* Setup the DNS server redirecting all the domains to the apIP */ dnsServer->setErrorReplyCode(DNSReplyCode::NoError); @@ -144,9 +159,9 @@ boolean WiFiManager::autoConnect(char const *apName, char const *apPassword) { //String pass = getPassword(); // attempt to connect using saved settings, on fail fallback to AP config portal - WiFi.mode(WIFI_STA); + // WiFi.mode(WIFI_STA); // why, should not be needed - if (connectWifi("", "") == WL_CONNECTED) { + if (WiFi.status() == WL_CONNECTED || connectWifi("", "") == WL_CONNECTED) { //connected DEBUG_WM(F("IP Address:")); DEBUG_WM(WiFi.localIP()); @@ -171,7 +186,9 @@ boolean WiFiManager::startConfigPortal() { boolean WiFiManager::startConfigPortal(char const *apName, char const *apPassword) { //setup AP - WiFi.mode(WIFI_AP_STA); + // @todo AP_STA will constantly try to connect to old aps in background, if not allowing background configportal , maybe turn off STA + WiFi.mode(WIFI_AP); + // WiFi.mode(WIFI_AP_STA); DEBUG_WM("SET AP STA"); _apName = apName; @@ -183,17 +200,25 @@ boolean WiFiManager::startConfigPortal(char const *apName, char const *apPasswo _apcallback(this); } - connect = false; + connect = false; // global // init configportal setupConfigPortal(); + uint8_t ret; // blocking loop waiting for config while(1){ - // check if timed out - if(configPortalHasTimeout()) break; + if(configPortalHasTimeout()) return stopConfigPortal(); + ret = handleConfigPortal(); + if(ret == WL_CONNECTED) return true; + yield(); + } + return false; +} +//@todo need enums for return, using 3 esp enums for now +uint8_t WiFiManager::handleConfigPortal(){ //DNS handler dnsServer->processNextRequest(); //HTTP handler @@ -206,38 +231,44 @@ boolean WiFiManager::startConfigPortal(char const *apName, char const *apPasswo DEBUG_WM(F("Connecting to new AP")); // attempt sta connection to submitted _ssid, _pass - if (connectWifi(_ssid, _pass) != WL_CONNECTED) { - DEBUG_WM(F("Failed to connect.")); - } else { - //connected - WiFi.mode(WIFI_STA); - //notify that configuration has changed and any optional parameters should be saved - if ( _savecallback != NULL) { - //todo: check if any custom parameters actually exist, and check if they really changed maybe - _savecallback(); - } - break; + if (connectWifi(_ssid, _pass) == WL_CONNECTED) { + DEBUG_WM(F("Connect to new AP SUCCESS")); + stopConfigPortal(); + return WL_CONNECTED; // success } + DEBUG_WM(F("Failed to connect.")); + if (_shouldBreakAfterConfig) { - //flag set to exit after config after trying to connect - //notify that configuration has changed and any optional parameters should be saved - if ( _savecallback != NULL) { - //todo: check if any custom parameters actually exist, and check if they really changed maybe - _savecallback(); - } - break; + // this is more of an exiting callback than a save + stopConfigPortal(); // fail + return WL_CONNECT_FAILED; } } - yield(); + + return WL_IDLE_STATUS; +} + +boolean WiFiManager::stopConfigPortal(){ + // do save callback + if ( _savecallback != NULL) { + //todo: confirm or verify data was saved + _savecallback(); } + // @todo does this unload them enough ? server.reset(); dnsServer.reset(); - return WiFi.status() == WL_CONNECTED; + // turn off AP + DEBUG_WM(F("disconnect configportal")); + WiFi.softAPdisconnect(); + return false; } +// @todo refactor this up into seperate functions +// one for connecting to flash , one for new client +// clean up, flow is convoluted, and causes bugs int WiFiManager::connectWifi(String ssid, String pass) { DEBUG_WM(F("Connecting as wifi client...")); @@ -249,16 +280,18 @@ int WiFiManager::connectWifi(String ssid, String pass) { } // fix for auto connect racing issue - // @todo why is this here, what issue?, should be making sure its actually ssid == ssid should it not? - if (WiFi.status() == WL_CONNECTED) { + // @todo this had to be fixed, it will not let you set a new wifi, if still connected to another, as it just returns + if (WiFi.status() == WL_CONNECTED && (ssid=="" || WiFi.SSID() == ssid)) { DEBUG_WM("Already connected. Exiting"); return WL_CONNECTED; } // if ssid argument provided connect to that if (ssid != "") { - DEBUG_WM("Connecting to new AP"); + DEBUG_WM("Connecting to new AP"); + WiFi.persistent(true); WiFi.begin(ssid.c_str(), pass.c_str()); + WiFi.persistent(false); } else { // connect using saved ssid if there is one if (WiFi.SSID()) { @@ -273,7 +306,6 @@ int WiFiManager::connectWifi(String ssid, String pass) { DEBUG_WM("No saved credentials"); } } - int connRes = waitForConnectResult(); DEBUG_WM ("Connection result: "); DEBUG_WM ( connRes ); @@ -289,27 +321,28 @@ int WiFiManager::connectWifi(String ssid, String pass) { return connRes; } +// @todo uses _connectTimeout for wifi save also, add timeout argument to bypass? + +uint8_t WiFiManager::waitForConnectResult(uint16_t timeout) { + return waitForConnectResult(timeout * 1000); +} + uint8_t WiFiManager::waitForConnectResult() { - if (_connectTimeout == 0) { - return WiFi.waitForConnectResult(); - } else { - DEBUG_WM (F("Waiting for connection result with time out")); - unsigned long start = millis(); - boolean keepConnecting = true; - uint8_t status; - while (keepConnecting) { - status = WiFi.status(); - if (millis() > start + _connectTimeout) { - keepConnecting = false; - DEBUG_WM (F("Connection timed out")); - } - if (status == WL_CONNECTED || status == WL_CONNECT_FAILED) { - keepConnecting = false; - } - delay(100); + if (_connectTimeout == 0) return WiFi.waitForConnectResult(); + + DEBUG_WM (F("connectTimeout set, waiting for connect....")); + uint8_t status; + int timeout = millis() + _connectTimeout; + + while(millis() < timeout) { + status = WiFi.status(); + if (status == WL_CONNECTED || status == WL_CONNECT_FAILED) { + return status; } - return status; + DEBUG_WM ("."); + delay(100); } + return status; } void WiFiManager::startWPS() { @@ -388,8 +421,6 @@ void WiFiManager::setBreakAfterConfig(boolean shouldBreak) { _shouldBreakAfterConfig = shouldBreak; } - - /** * HTTPD CALLBACK root or redirect to captive portal */ diff --git a/WiFiManager.h b/WiFiManager.h index 0fccf97..40efb9b 100644 --- a/WiFiManager.h +++ b/WiFiManager.h @@ -74,6 +74,7 @@ class WiFiManager { public: WiFiManager(); + ~WiFiManager(); boolean autoConnect(); boolean autoConnect(char const *apName, char const *apPassword = NULL); @@ -130,6 +131,7 @@ class WiFiManager void setupConfigPortal(); void startWPS(); + bool startAP(); const char* _apName = "no-net"; const char* _apPassword = NULL; @@ -160,6 +162,7 @@ class WiFiManager int status = WL_IDLE_STATUS; int connectWifi(String ssid, String pass); uint8_t waitForConnectResult(); + uint8_t waitForConnectResult(uint16_t timeout); void handleRoot(); void handleWifi(boolean scan); @@ -170,7 +173,9 @@ class WiFiManager void handle204(); boolean captivePortal(); boolean configPortalHasTimeout(); - + boolean stopConfigPortal(); + uint8_t handleConfigPortal(); + // DNS server const byte DNS_PORT = 53; From e9b36f90845e6f09999310dde5b1e93fd01a5f12 Mon Sep 17 00:00:00 2001 From: Shawn A Date: Fri, 10 Feb 2017 15:05:45 -0600 Subject: [PATCH 10/80] fix breakafterconfig --- WiFiManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WiFiManager.cpp b/WiFiManager.cpp index 44897b8..7089b4a 100644 --- a/WiFiManager.cpp +++ b/WiFiManager.cpp @@ -211,7 +211,7 @@ boolean WiFiManager::startConfigPortal(char const *apName, char const *apPasswo // check if timed out if(configPortalHasTimeout()) return stopConfigPortal(); ret = handleConfigPortal(); - if(ret == WL_CONNECTED) return true; + if(ret == WL_CONNECTED || ret == WL_CONNECT_FAILED) return ret; yield(); } return false; From 65349cb54629c6431366ecc9066cd58cab614e93 Mon Sep 17 00:00:00 2001 From: Shawn A Date: Fri, 10 Feb 2017 15:13:48 -0600 Subject: [PATCH 11/80] fix WL_IDLE_STATUS return values for compatibility This might need better handling in future, since we only get back a bool of wether or not we are connected, not if configportal was saved etc. might be a problem. --- WiFiManager.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/WiFiManager.cpp b/WiFiManager.cpp index 7089b4a..ec2c806 100644 --- a/WiFiManager.cpp +++ b/WiFiManager.cpp @@ -209,9 +209,12 @@ boolean WiFiManager::startConfigPortal(char const *apName, char const *apPasswo // blocking loop waiting for config while(1){ // check if timed out - if(configPortalHasTimeout()) return stopConfigPortal(); + if(configPortalHasTimeout()){ + stopConfigPortal(); + return WiFi.status() == WL_CONNECTED; //returns connected bool, ok I guess + } ret = handleConfigPortal(); - if(ret == WL_CONNECTED || ret == WL_CONNECT_FAILED) return ret; + if(ret != WL_IDLE_STATUS) return ret == WL_CONNECTED; yield(); } return false; From 1a44dd53e63b94b94f0b6be9e9a94639d0969754 Mon Sep 17 00:00:00 2001 From: Shawn A Date: Fri, 10 Feb 2017 15:21:48 -0600 Subject: [PATCH 12/80] move apcallback to proper location This was actually being called before the ap was setup! --- WiFiManager.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/WiFiManager.cpp b/WiFiManager.cpp index ec2c806..cf6d397 100644 --- a/WiFiManager.cpp +++ b/WiFiManager.cpp @@ -114,6 +114,12 @@ bool WiFiManager::startAP(){ delay(500); // slight delay to make sure we have an AP IP DEBUG_WM(F("AP IP address: ")); DEBUG_WM(WiFi.softAPIP()); + + // do AP callback if set + if ( _apcallback != NULL) { + _apcallback(this); + } + return ret; } @@ -195,11 +201,6 @@ boolean WiFiManager::startConfigPortal(char const *apName, char const *apPasswo _apPassword = apPassword; if(!validApPassword()) return false; - // do AP callback if set - if ( _apcallback != NULL) { - _apcallback(this); - } - connect = false; // global // init configportal From 0b0eb6159c0b604bd9a2e41f75722249617398a4 Mon Sep 17 00:00:00 2001 From: Shawn A Date: Fri, 10 Feb 2017 15:29:32 -0600 Subject: [PATCH 13/80] moving blocks --- WiFiManager.cpp | 62 ++++++++++++++++++++++--------------------------- 1 file changed, 28 insertions(+), 34 deletions(-) diff --git a/WiFiManager.cpp b/WiFiManager.cpp index cf6d397..83b5cfc 100644 --- a/WiFiManager.cpp +++ b/WiFiManager.cpp @@ -91,6 +91,34 @@ WiFiManager::~WiFiManager() { WiFi.persistent(true); // @todo restore original } +// AUTOCONNECT +boolean WiFiManager::autoConnect() { + String ssid = "ESP" + String(ESP.getChipId()); + return autoConnect(ssid.c_str(), NULL); +} + +boolean WiFiManager::autoConnect(char const *apName, char const *apPassword) { + DEBUG_WM(F("")); + DEBUG_WM(F("AutoConnect")); + + // read eeprom for ssid and pass, deprecated + //String ssid = getSSID(); + //String pass = getPassword(); + + // attempt to connect using saved settings, on fail fallback to AP config portal + // WiFi.mode(WIFI_STA); // why, should not be needed + + if (WiFi.status() == WL_CONNECTED || connectWifi("", "") == WL_CONNECTED) { + //connected + DEBUG_WM(F("IP Address:")); + DEBUG_WM(WiFi.localIP()); + return true; + } + + return startConfigPortal(apName, apPassword); +} + +// CONFIG PORTAL bool WiFiManager::startAP(){ DEBUG_WM(F("Configuring access point with with SSID ... ")); DEBUG_WM(_apName); @@ -151,40 +179,6 @@ void WiFiManager::setupConfigPortal() { DEBUG_WM(F("HTTP server started")); } -boolean WiFiManager::autoConnect() { - String ssid = "ESP" + String(ESP.getChipId()); - return autoConnect(ssid.c_str(), NULL); -} - -boolean WiFiManager::autoConnect(char const *apName, char const *apPassword) { - DEBUG_WM(F("")); - DEBUG_WM(F("AutoConnect")); - - // read eeprom for ssid and pass, deprecated - //String ssid = getSSID(); - //String pass = getPassword(); - - // attempt to connect using saved settings, on fail fallback to AP config portal - // WiFi.mode(WIFI_STA); // why, should not be needed - - if (WiFi.status() == WL_CONNECTED || connectWifi("", "") == WL_CONNECTED) { - //connected - DEBUG_WM(F("IP Address:")); - DEBUG_WM(WiFi.localIP()); - return true; - } - - return startConfigPortal(apName, apPassword); -} - -boolean WiFiManager::configPortalHasTimeout(){ - if(_configPortalTimeout == 0 || wifi_softap_get_station_num() > 0){ - _configPortalStart = millis(); // kludge, bump configportal start time to skew timeouts - return false; - } - return (millis() > _configPortalStart + _configPortalTimeout); -} - boolean WiFiManager::startConfigPortal() { String ssid = "ESP" + String(ESP.getChipId()); return startConfigPortal(ssid.c_str(), NULL); From 612e18be35788369ecd44547c33862347f7a4eeb Mon Sep 17 00:00:00 2001 From: Shawn A Date: Fri, 10 Feb 2017 15:40:31 -0600 Subject: [PATCH 14/80] oops deleted a function --- WiFiManager.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/WiFiManager.cpp b/WiFiManager.cpp index 83b5cfc..49f5c16 100644 --- a/WiFiManager.cpp +++ b/WiFiManager.cpp @@ -151,6 +151,14 @@ bool WiFiManager::startAP(){ return ret; } +boolean WiFiManager::configPortalHasTimeout(){ + if(_configPortalTimeout == 0 || wifi_softap_get_station_num() > 0){ + _configPortalStart = millis(); // kludge, bump configportal start time to skew timeouts + return false; + } + return (millis() > _configPortalStart + _configPortalTimeout); +} + void WiFiManager::setupConfigPortal() { // setup dns and web servers dnsServer.reset(new DNSServer()); From 004f222f40e1d7f73d336389ebf5e42196ca8ae2 Mon Sep 17 00:00:00 2001 From: Shawn A Date: Fri, 10 Feb 2017 17:05:07 -0600 Subject: [PATCH 15/80] do not wait for conx is no ssid --- WiFiManager.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/WiFiManager.cpp b/WiFiManager.cpp index 49f5c16..4327cd8 100644 --- a/WiFiManager.cpp +++ b/WiFiManager.cpp @@ -267,8 +267,9 @@ boolean WiFiManager::stopConfigPortal(){ dnsServer.reset(); // turn off AP - DEBUG_WM(F("disconnect configportal")); - WiFi.softAPdisconnect(); + DEBUG_WM(F("disconnect configportal")); + // WiFi.mode(WIFI_STA); + WiFi.softAPdisconnect(false); return false; } @@ -277,6 +278,8 @@ boolean WiFiManager::stopConfigPortal(){ // clean up, flow is convoluted, and causes bugs int WiFiManager::connectWifi(String ssid, String pass) { DEBUG_WM(F("Connecting as wifi client...")); + + bool waitforconx = true; // Setup static IP config if provided if (_sta_static_ip) { @@ -300,7 +303,7 @@ int WiFiManager::connectWifi(String ssid, String pass) { WiFi.persistent(false); } else { // connect using saved ssid if there is one - if (WiFi.SSID()) { + if (WiFi.SSID() != "") { DEBUG_WM("Connecting to saved AP"); // trying to fix connection in progress hanging, explain why ?? ETS_UART_INTR_DISABLE(); @@ -310,9 +313,11 @@ int WiFiManager::connectWifi(String ssid, String pass) { WiFi.begin(); } else { DEBUG_WM("No saved credentials"); + waitforconx = false; } } - int connRes = waitForConnectResult(); + + int connRes = waitforconx ? waitForConnectResult() : WL_NO_SSID_AVAIL; DEBUG_WM ("Connection result: "); DEBUG_WM ( connRes ); From 29b2f02ae49f0cdfa726499afd0b3bfde1b6769d Mon Sep 17 00:00:00 2001 From: camelmasa Date: Mon, 20 Mar 2017 12:58:35 +0900 Subject: [PATCH 16/80] Fix broken links --- WiFiManager.cpp | 2 +- WiFiManager.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/WiFiManager.cpp b/WiFiManager.cpp index 4fba355..84d16fd 100644 --- a/WiFiManager.cpp +++ b/WiFiManager.cpp @@ -5,7 +5,7 @@ inspired by: http://www.esp8266.com/viewtopic.php?f=29&t=2520 https://github.com/chriscook8/esp-arduino-apboot - https://github.com/esp8266/Arduino/tree/esp8266/hardware/esp8266com/esp8266/libraries/DNSServer/examples/CaptivePortalAdvanced + https://github.com/esp8266/Arduino/tree/master/libraries/DNSServer/examples/CaptivePortalAdvanced Built by AlexT https://github.com/tzapu Licensed under MIT license **************************************************************/ diff --git a/WiFiManager.h b/WiFiManager.h index d86b904..4e45aed 100644 --- a/WiFiManager.h +++ b/WiFiManager.h @@ -5,7 +5,7 @@ inspired by: http://www.esp8266.com/viewtopic.php?f=29&t=2520 https://github.com/chriscook8/esp-arduino-apboot - https://github.com/esp8266/Arduino/tree/esp8266/hardware/esp8266com/esp8266/libraries/DNSServer/examples/CaptivePortalAdvanced + https://github.com/esp8266/Arduino/tree/master/libraries/DNSServer/examples/CaptivePortalAdvanced Built by AlexT https://github.com/tzapu Licensed under MIT license **************************************************************/ From ada93b721a235f4bae379e1804c32d962541b992 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mate=CC=8Cj=20Sychra?= Date: Wed, 3 May 2017 14:56:31 +0200 Subject: [PATCH 17/80] fix for #362 - Setting Content-length explicitly - https://github.com/tzapu/WiFiManager/issues/362 --- WiFiManager.cpp | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/WiFiManager.cpp b/WiFiManager.cpp index 4fba355..8a6c9d5 100644 --- a/WiFiManager.cpp +++ b/WiFiManager.cpp @@ -394,6 +394,7 @@ void WiFiManager::handleRoot() { page += FPSTR(HTTP_PORTAL_OPTIONS); page += FPSTR(HTTP_END); + server->sendHeader("Content-Length", String(page.length())); server->send(200, "text/html", page); } @@ -547,6 +548,7 @@ void WiFiManager::handleWifi(boolean scan) { page += FPSTR(HTTP_END); + server->sendHeader("Content-Length", String(page.length())); server->send(200, "text/html", page); @@ -604,6 +606,7 @@ void WiFiManager::handleWifiSave() { page += FPSTR(HTTP_SAVED); page += FPSTR(HTTP_END); + server->sendHeader("Content-Length", String(page.length())); server->send(200, "text/html", page); DEBUG_WM(F("Sent wifi save page")); @@ -646,6 +649,7 @@ void WiFiManager::handleInfo() { page += F(""); page += FPSTR(HTTP_END); + server->sendHeader("Content-Length", String(page.length())); server->send(200, "text/html", page); DEBUG_WM(F("Sent info page")); @@ -663,6 +667,8 @@ void WiFiManager::handleReset() { page += FPSTR(HTTP_HEAD_END); page += F("Module will reset in a few seconds."); page += FPSTR(HTTP_END); + + server->sendHeader("Content-Length", String(page.length())); server->send(200, "text/html", page); DEBUG_WM(F("Sent reset page")); @@ -671,17 +677,6 @@ void WiFiManager::handleReset() { delay(2000); } - - -//removed as mentioned here https://github.com/tzapu/WiFiManager/issues/114 -/*void WiFiManager::handle204() { - DEBUG_WM(F("204 No Response")); - server->sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); - server->sendHeader("Pragma", "no-cache"); - server->sendHeader("Expires", "-1"); - server->send ( 204, "text/plain", ""); -}*/ - void WiFiManager::handleNotFound() { if (captivePortal()) { // If captive portal redirect instead of displaying the error page. return; @@ -701,6 +696,7 @@ void WiFiManager::handleNotFound() { server->sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); server->sendHeader("Pragma", "no-cache"); server->sendHeader("Expires", "-1"); + server->sendHeader("Content-Length", String(message.length())); server->send ( 404, "text/plain", message ); } From 58eb7113e76d8ff83364b381472b14bedcc8a368 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Skytt=C3=A4?= Date: Sun, 9 Jul 2017 17:11:31 +0300 Subject: [PATCH 18/80] Spelling fixes --- WiFiManager.h | 6 +++--- examples/OnDemandConfigPortal/OnDemandConfigPortal.ino | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/WiFiManager.h b/WiFiManager.h index 4e45aed..fa479d2 100644 --- a/WiFiManager.h +++ b/WiFiManager.h @@ -79,12 +79,12 @@ class WiFiManager void resetSettings(); //sets timeout before webserver loop ends and exits even if there has been no setup. - //usefully for devices that failed to connect at some point and got stuck in a webserver loop + //useful for devices that failed to connect at some point and got stuck in a webserver loop //in seconds setConfigPortalTimeout is a new name for setTimeout void setConfigPortalTimeout(unsigned long seconds); void setTimeout(unsigned long seconds); - //sets timeout for which to attempt connecting, usefull if you get a lot of failed connects + //sets timeout for which to attempt connecting, useful if you get a lot of failed connects void setConnectTimeout(unsigned long seconds); @@ -101,7 +101,7 @@ class WiFiManager void setSaveConfigCallback( void (*func)(void) ); //adds a custom parameter void addParameter(WiFiManagerParameter *p); - //if this is set, it will exit after config, even if connection is unsucessful. + //if this is set, it will exit after config, even if connection is unsuccessful. void setBreakAfterConfig(boolean shouldBreak); //if this is set, try WPS setup when starting (this will delay config portal for up to 2 mins) //TODO diff --git a/examples/OnDemandConfigPortal/OnDemandConfigPortal.ino b/examples/OnDemandConfigPortal/OnDemandConfigPortal.ino index edf4f46..2f2d0cc 100644 --- a/examples/OnDemandConfigPortal/OnDemandConfigPortal.ino +++ b/examples/OnDemandConfigPortal/OnDemandConfigPortal.ino @@ -5,7 +5,7 @@ #include #include //https://github.com/tzapu/WiFiManager -// select wich pin will trigger the configuraton portal when set to LOW +// select which pin will trigger the configuration portal when set to LOW // ESP-01 users please note: the only pins available (0 and 2), are shared // with the bootloader, so always set them HIGH at power-up #define TRIGGER_PIN 0 From 9637d9473e40067ab84e4969a7bdb236d5e414e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=AAnis=20Volpato=20Martins?= Date: Wed, 12 Jul 2017 22:07:33 -0300 Subject: [PATCH 19/80] Improve wishlist in README --- README.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index a329f2b..d2c65fd 100644 --- a/README.md +++ b/README.md @@ -43,16 +43,16 @@ First attempt at a library. Lots more changes and fixes to do. Contributions are ![ESP8266 WiFi Captive Portal Homepage](http://i.imgur.com/YPvW9eql.png) ![ESP8266 WiFi Captive Portal Configuration](http://i.imgur.com/oicWJ4gl.png) ## Wishlist -- ~~remove dependency on EEPROM library~~ -- ~~move HTML Strings to PROGMEM~~ -- ~~cleanup and streamline code~~ (although this is ongoing) -- if timeout is set, extend it when a page is fetched in AP mode -- ~~add ability to configure more parameters than ssid/password~~ -- ~~maybe allow setting ip of ESP after reboot~~ -- ~~add to Arduino Library Manager~~ -- ~~add to PlatformIO~~ -- add multiple sets of network credentials -- ~~allow users to customize CSS~~ +- [x] remove dependency on EEPROM library +- [x] move HTML Strings to PROGMEM +- [x] cleanup and streamline code (although this is ongoing) +- [ ] if timeout is set, extend it when a page is fetched in AP mode +- [x] add ability to configure more parameters than ssid/password +- [x] maybe allow setting ip of ESP after reboot +- [x] add to Arduino Library Manager +- [x] add to PlatformIO +- [ ] add multiple sets of network credentials +- [x] allow users to customize CSS ## Quick Start From 06d75bf7b1084a7e35ce0a7191d6471445cda909 Mon Sep 17 00:00:00 2001 From: tzapu Date: Thu, 13 Jul 2017 09:39:28 +0300 Subject: [PATCH 20/80] Update README.md --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d2c65fd..10da7f6 100644 --- a/README.md +++ b/README.md @@ -46,13 +46,16 @@ First attempt at a library. Lots more changes and fixes to do. Contributions are - [x] remove dependency on EEPROM library - [x] move HTML Strings to PROGMEM - [x] cleanup and streamline code (although this is ongoing) -- [ ] if timeout is set, extend it when a page is fetched in AP mode +- [x] if timeout is set, extend it when a page is fetched in AP mode - [x] add ability to configure more parameters than ssid/password - [x] maybe allow setting ip of ESP after reboot - [x] add to Arduino Library Manager - [x] add to PlatformIO - [ ] add multiple sets of network credentials - [x] allow users to customize CSS +- [ ] ESP32 support or instructions +- [ ] rewrite documentation for simplicity, based on scenarios/goals +- [ ] rely on the SDK's built in auto connect more than forcing a connect ## Quick Start From cee18a7ce36ca6793f3e9fe91b0cb231e5901702 Mon Sep 17 00:00:00 2001 From: Derek123456 Date: Tue, 22 Aug 2017 10:30:53 +0800 Subject: [PATCH 21/80] Update WiFiManager.h Changed to --- WiFiManager.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WiFiManager.h b/WiFiManager.h index fa479d2..5b37e44 100644 --- a/WiFiManager.h +++ b/WiFiManager.h @@ -29,7 +29,7 @@ const char HTTP_HEAD_END[] PROGMEM = "
{v} {r}%
"; const char HTTP_FORM_START[] PROGMEM = "


"; -const char HTTP_FORM_PARAM[] PROGMEM = "
"; +const char HTTP_FORM_PARAM[] PROGMEM = "
"; const char HTTP_FORM_END[] PROGMEM = "
"; const char HTTP_SCAN_LINK[] PROGMEM = "
"; const char HTTP_SAVED[] PROGMEM = "
Credentials Saved
Trying to connect ESP to network.
If it fails reconnect to AP to try again
"; From 68e32a10c01406b398ecb956ca514f43e2ab6ec2 Mon Sep 17 00:00:00 2001 From: Derek123456 Date: Tue, 22 Aug 2017 10:32:52 +0800 Subject: [PATCH 22/80] Update WiFiManager.cpp https://github.com/tzapu/WiFiManager/issues/392 Increased size of parLength to 5 so the custom parameter length variable can be up to 9999 long. --- WiFiManager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WiFiManager.cpp b/WiFiManager.cpp index 57ce031..97c9eea 100644 --- a/WiFiManager.cpp +++ b/WiFiManager.cpp @@ -485,7 +485,7 @@ void WiFiManager::handleWifi(boolean scan) { } page += FPSTR(HTTP_FORM_START); - char parLength[2]; + char parLength[5]; // add the extra parameters to the form for (int i = 0; i < _paramsCount; i++) { if (_params[i] == NULL) { @@ -497,7 +497,7 @@ void WiFiManager::handleWifi(boolean scan) { pitem.replace("{i}", _params[i]->getID()); pitem.replace("{n}", _params[i]->getID()); pitem.replace("{p}", _params[i]->getPlaceholder()); - snprintf(parLength, 2, "%d", _params[i]->getValueLength()); + snprintf(parLength, 5, "%d", _params[i]->getValueLength()); pitem.replace("{l}", parLength); pitem.replace("{v}", _params[i]->getValue()); pitem.replace("{c}", _params[i]->getCustomHTML()); From 179c0309a5c1a894bdc8707d901ffb6677888bbf Mon Sep 17 00:00:00 2001 From: Shawn A Date: Sat, 26 Aug 2017 23:13:37 -0500 Subject: [PATCH 23/80] comments, autoconnect fix fix sta+ap mode preservaton in autoconnect --- WiFiManager.cpp | 84 ++++++++++++------- WiFiManager.h | 2 + .../AutoConnectWithTimeout/platformio.ini | 18 ++++ 3 files changed, 75 insertions(+), 29 deletions(-) create mode 100644 examples/AutoConnectWithTimeout/platformio.ini diff --git a/WiFiManager.cpp b/WiFiManager.cpp index 4327cd8..c56fcb5 100644 --- a/WiFiManager.cpp +++ b/WiFiManager.cpp @@ -106,15 +106,16 @@ boolean WiFiManager::autoConnect(char const *apName, char const *apPassword) { //String pass = getPassword(); // attempt to connect using saved settings, on fail fallback to AP config portal - // WiFi.mode(WIFI_STA); // why, should not be needed + if((WiFi.getMode() & WIFI_STA) == 0) wifimode(WIFI_STA); // if STA not on, turn it on + // if already connected, or try stored connect if (WiFi.status() == WL_CONNECTED || connectWifi("", "") == WL_CONNECTED) { //connected DEBUG_WM(F("IP Address:")); DEBUG_WM(WiFi.localIP()); return true; } - + // not connected start configportal return startConfigPortal(apName, apPassword); } @@ -131,7 +132,7 @@ bool WiFiManager::startAP(){ bool ret = true; - // start soft AP with pass or anonymous + // start soft AP with password or anonymous if (_apPassword != NULL) { ret = WiFi.softAP(_apName, _apPassword);//password option } else { @@ -139,7 +140,7 @@ bool WiFiManager::startAP(){ ret = WiFi.softAP(_apName); } - delay(500); // slight delay to make sure we have an AP IP + delay(500); // slight delay to make sure we get an AP IP DEBUG_WM(F("AP IP address: ")); DEBUG_WM(WiFi.softAPIP()); @@ -167,7 +168,7 @@ void WiFiManager::setupConfigPortal() { DEBUG_WM(F("setupConfigPortal")); _configPortalStart = millis(); - if(!startAP()) DEBUG_WM("There was an error stating the AP"); + if(!startAP()) DEBUG_WM("There was an error stating the AP"); // @bug startAP returns unreliable success status /* Setup the DNS server redirecting all the domains to the apIP */ dnsServer->setErrorReplyCode(DNSReplyCode::NoError); @@ -194,10 +195,15 @@ boolean WiFiManager::startConfigPortal() { boolean WiFiManager::startConfigPortal(char const *apName, char const *apPassword) { //setup AP - // @todo AP_STA will constantly try to connect to old aps in background, if not allowing background configportal , maybe turn off STA - WiFi.mode(WIFI_AP); - // WiFi.mode(WIFI_AP_STA); - DEBUG_WM("SET AP STA"); + if(!WiFi.isConnected()){ + // this fixes most ap problems, simply doing mode(WIFI_AP) does not work if sta connection is hanging + wifioff(); + wifimode(WIFI_AP); + DEBUG_WM("Disabling STA"); + } + else wifimode(WIFI_AP_STA); + + DEBUG_WM("Enabling AP"); _apName = apName; _apPassword = apPassword; @@ -223,7 +229,7 @@ boolean WiFiManager::startConfigPortal(char const *apName, char const *apPasswo return false; } -//@todo need enums for return, using 3 esp enums for now +//using esp enums returns for now, should be fine uint8_t WiFiManager::handleConfigPortal(){ //DNS handler dnsServer->processNextRequest(); @@ -262,15 +268,21 @@ boolean WiFiManager::stopConfigPortal(){ _savecallback(); } - // @todo does this unload them enough ? + //DNS handler + dnsServer->processNextRequest(); + //HTTP handler + server->handleClient(); + + // @todo does this free memory? server.reset(); dnsServer.reset(); // turn off AP DEBUG_WM(F("disconnect configportal")); - // WiFi.mode(WIFI_STA); - WiFi.softAPdisconnect(false); - return false; + bool ret = WiFi.softAPdisconnect(false); + if(!ret)DEBUG_WM(F("disconnect configportal error")); + wifimode(WIFI_STA); // set back to sta, @todo use preservation variable + return ret; } // @todo refactor this up into seperate functions @@ -288,12 +300,8 @@ int WiFiManager::connectWifi(String ssid, String pass) { DEBUG_WM(WiFi.localIP()); } - // fix for auto connect racing issue - // @todo this had to be fixed, it will not let you set a new wifi, if still connected to another, as it just returns - if (WiFi.status() == WL_CONNECTED && (ssid=="" || WiFi.SSID() == ssid)) { - DEBUG_WM("Already connected. Exiting"); - return WL_CONNECTED; - } + wifimode(WIFI_AP_STA); // turn sta back on so begin does not call enablesta->mode + wifioff(); // disconnect before begin, in case anything is hung // if ssid argument provided connect to that if (ssid != "") { @@ -305,11 +313,6 @@ int WiFiManager::connectWifi(String ssid, String pass) { // connect using saved ssid if there is one if (WiFi.SSID() != "") { DEBUG_WM("Connecting to saved AP"); - // trying to fix connection in progress hanging, explain why ?? - ETS_UART_INTR_DISABLE(); - wifi_station_disconnect(); - ETS_UART_INTR_ENABLE(); - WiFi.begin(); } else { DEBUG_WM("No saved credentials"); @@ -339,8 +342,11 @@ uint8_t WiFiManager::waitForConnectResult(uint16_t timeout) { } uint8_t WiFiManager::waitForConnectResult() { - if (_connectTimeout == 0) return WiFi.waitForConnectResult(); - + if (_connectTimeout == 0){ + DEBUG_WM (F("connectTimeout not set, ESP waitForConnectResult...")); + return WiFi.waitForConnectResult(); + } + DEBUG_WM (F("connectTimeout set, waiting for connect....")); uint8_t status; int timeout = millis() + _connectTimeout; @@ -880,12 +886,32 @@ boolean WiFiManager::validApPassword(){ if (_apPassword == "") _apPassword = NULL; if (_apPassword != NULL) { if (strlen(_apPassword) < 8 || strlen(_apPassword) > 63) { - DEBUG_WM(F("AccessPoint password is INVALID")); + DEBUG_WM(F("AccessPoint set password is INVALID")); _apPassword = NULL; return false; // @todo FATAL or fallback to NULL ? } - DEBUG_WM(F("AccessPoint Password is VALID")); + DEBUG_WM(F("AccessPoint set password is VALID")); DEBUG_WM(_apPassword); } return true; } + +// set mode without persistent +bool WiFiManager::wifimode(WiFiMode_t m) { + if(wifi_get_opmode() == (uint8) m) { + return true; + } + bool ret; + ETS_UART_INTR_DISABLE(); + ret = wifi_set_opmode_current(m); + ETS_UART_INTR_ENABLE(); + return ret; +} + +// sta disconnect without persistent +bool WiFiManager::wifioff() { + DEBUG_WM(F("wifi disconncting")); + if((WiFi.getMode() & WIFI_STA) != 0) { + return wifi_station_disconnect(); + } +} \ No newline at end of file diff --git a/WiFiManager.h b/WiFiManager.h index 40efb9b..c2bf7da 100644 --- a/WiFiManager.h +++ b/WiFiManager.h @@ -175,6 +175,8 @@ class WiFiManager boolean configPortalHasTimeout(); boolean stopConfigPortal(); uint8_t handleConfigPortal(); + bool wifimode(WiFiMode_t m); + bool wifioff(); // DNS server const byte DNS_PORT = 53; diff --git a/examples/AutoConnectWithTimeout/platformio.ini b/examples/AutoConnectWithTimeout/platformio.ini new file mode 100644 index 0000000..7e80288 --- /dev/null +++ b/examples/AutoConnectWithTimeout/platformio.ini @@ -0,0 +1,18 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; http://docs.platformio.org/page/projectconf.html + +[env:nodemcuv2] +platform=espressif8266 +board=nodemcuv2 +framework=arduino + +[platformio] +src_dir=AutoConnectWithTimeout +lib_dir=/Users/shawn/projects/microcontrollers/dev/libraries From 0aecffc8226d38ae165615ff10690243fcc2a9a4 Mon Sep 17 00:00:00 2001 From: Shawn A Date: Sat, 26 Aug 2017 23:26:48 -0500 Subject: [PATCH 24/80] fix configportal loop flow removed returns for better flow --- WiFiManager.cpp | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/WiFiManager.cpp b/WiFiManager.cpp index c56fcb5..ef9a5f2 100644 --- a/WiFiManager.cpp +++ b/WiFiManager.cpp @@ -202,7 +202,7 @@ boolean WiFiManager::startConfigPortal(char const *apName, char const *apPasswo DEBUG_WM("Disabling STA"); } else wifimode(WIFI_AP_STA); - + DEBUG_WM("Enabling AP"); _apName = apName; @@ -213,20 +213,28 @@ boolean WiFiManager::startConfigPortal(char const *apName, char const *apPasswo // init configportal setupConfigPortal(); - uint8_t ret; + uint8_t state; + bool result = false; // blocking loop waiting for config while(1){ + // check if timed out if(configPortalHasTimeout()){ stopConfigPortal(); - return WiFi.status() == WL_CONNECTED; //returns connected bool, ok I guess + result = WiFi.status() == WL_CONNECTED; //returns connected bool, ok I guess + break; + } + + state = handleConfigPortal(); + + if(state != WL_IDLE_STATUS){ + result == WL_CONNECTED; + break; } - ret = handleConfigPortal(); - if(ret != WL_IDLE_STATUS) return ret == WL_CONNECTED; yield(); } - return false; + return result; } //using esp enums returns for now, should be fine From 47d9082e18dc9a9f7670f1970b5a7ba4aec2c8cd Mon Sep 17 00:00:00 2001 From: Shawn A Date: Sun, 27 Aug 2017 00:11:04 -0500 Subject: [PATCH 25/80] testing non blocking portal calling process in loop not fast enough results in corrupt webpages, not sure why. --- WiFiManager.cpp | 12 ++++++++++++ WiFiManager.h | 4 ++++ 2 files changed, 16 insertions(+) diff --git a/WiFiManager.cpp b/WiFiManager.cpp index ef9a5f2..57c4dc5 100644 --- a/WiFiManager.cpp +++ b/WiFiManager.cpp @@ -209,6 +209,7 @@ boolean WiFiManager::startConfigPortal(char const *apName, char const *apPasswo _apPassword = apPassword; if(!validApPassword()) return false; + configPortalActive = true; connect = false; // global // init configportal @@ -216,6 +217,8 @@ boolean WiFiManager::startConfigPortal(char const *apName, char const *apPasswo uint8_t state; bool result = false; + if(!_configPortalIsBlocking) return result; + // blocking loop waiting for config while(1){ @@ -237,6 +240,14 @@ boolean WiFiManager::startConfigPortal(char const *apName, char const *apPasswo return result; } +boolean WiFiManager::process(){ + if(configPortalActive){ + uint8_t state = handleConfigPortal(); + return state == WL_CONNECTED; + } + return false; +} + //using esp enums returns for now, should be fine uint8_t WiFiManager::handleConfigPortal(){ //DNS handler @@ -290,6 +301,7 @@ boolean WiFiManager::stopConfigPortal(){ bool ret = WiFi.softAPdisconnect(false); if(!ret)DEBUG_WM(F("disconnect configportal error")); wifimode(WIFI_STA); // set back to sta, @todo use preservation variable + configPortalActive = false; return ret; } diff --git a/WiFiManager.h b/WiFiManager.h index c2bf7da..c466c9c 100644 --- a/WiFiManager.h +++ b/WiFiManager.h @@ -82,6 +82,7 @@ class WiFiManager //if you want to always start the config portal, without trying to connect first boolean startConfigPortal(); boolean startConfigPortal(char const *apName, char const *apPassword = NULL); + boolean process(); // get the AP name of the config portal, so it can be used in the callback String getConfigPortalSSID(); @@ -148,11 +149,13 @@ class WiFiManager IPAddress _sta_static_gw; IPAddress _sta_static_sn; + // option parameters int _paramsCount = 0; int _minimumQuality = -1; boolean _removeDuplicateAPs = true; boolean _shouldBreakAfterConfig = false; boolean _tryWPS = false; + boolean _configPortalIsBlocking = false; const char* _customHeadElement = ""; @@ -188,6 +191,7 @@ class WiFiManager boolean validApPassword(); boolean connect; + boolean configPortalActive = false; boolean _debug = true; void (*_apcallback)(WiFiManager*) = NULL; From c4563802f3110b315ca5f8482c191462a4941cbf Mon Sep 17 00:00:00 2001 From: Shawn A Date: Sun, 27 Aug 2017 09:32:31 -0500 Subject: [PATCH 26/80] renable blocking --- WiFiManager.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WiFiManager.h b/WiFiManager.h index 52b74fe..eb56a04 100644 --- a/WiFiManager.h +++ b/WiFiManager.h @@ -155,7 +155,7 @@ class WiFiManager boolean _removeDuplicateAPs = true; boolean _shouldBreakAfterConfig = false; boolean _tryWPS = false; - boolean _configPortalIsBlocking = false; + boolean _configPortalIsBlocking = true; const char* _customHeadElement = ""; From f2580463e43bc1bc8f1e929916c52e36edcb6c7f Mon Sep 17 00:00:00 2001 From: Shawn A Date: Sun, 27 Aug 2017 10:55:26 -0500 Subject: [PATCH 27/80] fix const char corruptions #400 fixing non blocking, non blocking does not handle timeouts for the user --- WiFiManager.cpp | 27 ++++++++++++++++----------- WiFiManager.h | 6 +++--- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/WiFiManager.cpp b/WiFiManager.cpp index a0428e8..b43feae 100644 --- a/WiFiManager.cpp +++ b/WiFiManager.cpp @@ -133,11 +133,11 @@ bool WiFiManager::startAP(){ bool ret = true; // start soft AP with password or anonymous - if (_apPassword != NULL) { - ret = WiFi.softAP(_apName, _apPassword);//password option + if (_apPassword != "") { + ret = WiFi.softAP(_apName.c_str(), _apPassword.c_str());//password option } else { DEBUG_WM(F("AP has anonymous access")); - ret = WiFi.softAP(_apName); + ret = WiFi.softAP(_apName.c_str()); } delay(500); // slight delay to make sure we get an AP IP @@ -205,7 +205,7 @@ boolean WiFiManager::startConfigPortal(char const *apName, char const *apPasswo DEBUG_WM("Enabling AP"); - _apName = apName; + _apName = apName; // @todo check valid apname ? _apPassword = apPassword; if(!validApPassword()) return false; @@ -217,8 +217,12 @@ boolean WiFiManager::startConfigPortal(char const *apName, char const *apPasswo uint8_t state; bool result = false; - if(!_configPortalIsBlocking) return result; + if(!_configPortalIsBlocking){ + DEBUG_WM(F("Config Portal Running, non blocking/processing")); + return result; + } + DEBUG_WM(F("Config Portal Running, blocking")); // blocking loop waiting for config while(1){ @@ -815,7 +819,8 @@ void WiFiManager::handleNotFound() { * Return true in that case so the page handler do not try to handle the request again. */ boolean WiFiManager::captivePortal() { - if (!isIp(server->hostHeader()) ) { +DEBUG_WM(server->hostHeader()); + if (!isIp(server->hostHeader()) && false) { DEBUG_WM(F("Request redirected to captive portal")); server->sendHeader("Location", String("http://") + toStringIp(server->client().localIP()), true); server->send ( 302, "text/plain", ""); // Empty content inhibits Content-length header so we have to close the socket ourselves. @@ -893,12 +898,12 @@ String WiFiManager::toStringIp(IPAddress ip) { boolean WiFiManager::validApPassword(){ // check that ap password is valid, return false - if (_apPassword == "") _apPassword = NULL; - if (_apPassword != NULL) { - if (strlen(_apPassword) < 8 || strlen(_apPassword) > 63) { + if (_apPassword == NULL) _apPassword = ""; + if (_apPassword != "") { + if (_apPassword.length() < 8 || _apPassword.length() > 63) { DEBUG_WM(F("AccessPoint set password is INVALID")); - _apPassword = NULL; - return false; // @todo FATAL or fallback to NULL ? + _apPassword = ""; + return false; // @todo FATAL or fallback to empty ? } DEBUG_WM(F("AccessPoint set password is VALID")); DEBUG_WM(_apPassword); diff --git a/WiFiManager.h b/WiFiManager.h index eb56a04..d156ecf 100644 --- a/WiFiManager.h +++ b/WiFiManager.h @@ -134,8 +134,8 @@ class WiFiManager void startWPS(); bool startAP(); - const char* _apName = "no-net"; - const char* _apPassword = NULL; + String _apName = "no-net"; + String _apPassword = ""; String _ssid = ""; String _pass = ""; unsigned long _configPortalTimeout = 0; @@ -155,7 +155,7 @@ class WiFiManager boolean _removeDuplicateAPs = true; boolean _shouldBreakAfterConfig = false; boolean _tryWPS = false; - boolean _configPortalIsBlocking = true; + boolean _configPortalIsBlocking = false; const char* _customHeadElement = ""; From 8592647cbfd990af8fec8528ad0e5d47e6d8e50d Mon Sep 17 00:00:00 2001 From: Shawn A Date: Sun, 27 Aug 2017 10:57:43 -0500 Subject: [PATCH 28/80] fraking tabs --- WiFiManager.cpp | 30 +++++++++++++++--------------- WiFiManager.h | 4 ++-- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/WiFiManager.cpp b/WiFiManager.cpp index b43feae..05a8f9b 100644 --- a/WiFiManager.cpp +++ b/WiFiManager.cpp @@ -70,10 +70,10 @@ void WiFiManager::addParameter(WiFiManagerParameter *p) { if(_paramsCount + 1 > WIFI_MANAGER_MAX_PARAMS) { //Max parameters exceeded! - DEBUG_WM("WIFI_MANAGER_MAX_PARAMS exceeded, increase number (in WiFiManager.h) before adding more parameters!"); - DEBUG_WM("Skipping parameter with ID:"); - DEBUG_WM(p->getID()); - return; + DEBUG_WM("WIFI_MANAGER_MAX_PARAMS exceeded, increase number (in WiFiManager.h) before adding more parameters!"); + DEBUG_WM("Skipping parameter with ID:"); + DEBUG_WM(p->getID()); + return; } _params[_paramsCount] = p; _paramsCount++; @@ -115,7 +115,7 @@ boolean WiFiManager::autoConnect(char const *apName, char const *apPassword) { DEBUG_WM(WiFi.localIP()); return true; } - // not connected start configportal + // not connected start configportal return startConfigPortal(apName, apPassword); } @@ -209,7 +209,7 @@ boolean WiFiManager::startConfigPortal(char const *apName, char const *apPasswo _apPassword = apPassword; if(!validApPassword()) return false; - configPortalActive = true; + configPortalActive = true; connect = false; // global // init configportal @@ -218,8 +218,8 @@ boolean WiFiManager::startConfigPortal(char const *apName, char const *apPasswo bool result = false; if(!_configPortalIsBlocking){ - DEBUG_WM(F("Config Portal Running, non blocking/processing")); - return result; + DEBUG_WM(F("Config Portal Running, non blocking/processing")); + return result; } DEBUG_WM(F("Config Portal Running, blocking")); @@ -236,8 +236,8 @@ boolean WiFiManager::startConfigPortal(char const *apName, char const *apPasswo state = handleConfigPortal(); if(state != WL_IDLE_STATUS){ - result == WL_CONNECTED; - break; + result == WL_CONNECTED; + break; } yield(); } @@ -245,11 +245,11 @@ boolean WiFiManager::startConfigPortal(char const *apName, char const *apPasswo } boolean WiFiManager::process(){ - if(configPortalActive){ - uint8_t state = handleConfigPortal(); - return state == WL_CONNECTED; - } - return false; + if(configPortalActive){ + uint8_t state = handleConfigPortal(); + return state == WL_CONNECTED; + } + return false; } //using esp enums returns for now, should be fine diff --git a/WiFiManager.h b/WiFiManager.h index d156ecf..e76b1ab 100644 --- a/WiFiManager.h +++ b/WiFiManager.h @@ -134,8 +134,8 @@ class WiFiManager void startWPS(); bool startAP(); - String _apName = "no-net"; - String _apPassword = ""; + String _apName = "no-net"; + String _apPassword = ""; String _ssid = ""; String _pass = ""; unsigned long _configPortalTimeout = 0; From 8185eaa00226f31b5b65456a6fc5cec3ea5ae395 Mon Sep 17 00:00:00 2001 From: Shawn A Date: Sun, 27 Aug 2017 11:04:05 -0500 Subject: [PATCH 29/80] add blocking setter @todo add example --- WiFiManager.cpp | 5 +++++ WiFiManager.h | 6 ++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/WiFiManager.cpp b/WiFiManager.cpp index 05a8f9b..450e162 100644 --- a/WiFiManager.cpp +++ b/WiFiManager.cpp @@ -852,6 +852,11 @@ void WiFiManager::setRemoveDuplicateAPs(boolean removeDuplicates) { _removeDuplicateAPs = removeDuplicates; } +//if this is true, remove duplicated Access Points - defaut true +void WiFiManager::setConfigPortalBlocking(boolean shoudlBlock) { + _configPortalIsBlocking = shoudlBlock; +} + // HELPERS template diff --git a/WiFiManager.h b/WiFiManager.h index e76b1ab..e69c6eb 100644 --- a/WiFiManager.h +++ b/WiFiManager.h @@ -114,7 +114,9 @@ class WiFiManager void addParameter(WiFiManagerParameter *p); //if this is set, it will exit after config, even if connection is unsuccessful. void setBreakAfterConfig(boolean shouldBreak); - //if this is set, try WPS setup when starting (this will delay config portal for up to 2 mins) + //if this is set, portal will be blocking and wait until save or exit, is false user must manually process to handle config portal + void setConfigPortalBlocking(boolean shouldBlock); + //TODO //if this is set, customise style void setCustomHeadElement(const char* element); @@ -155,7 +157,7 @@ class WiFiManager boolean _removeDuplicateAPs = true; boolean _shouldBreakAfterConfig = false; boolean _tryWPS = false; - boolean _configPortalIsBlocking = false; + boolean _configPortalIsBlocking = true; const char* _customHeadElement = ""; From d1ab6ec6db8289d944c9f94ec8b389470c6b8389 Mon Sep 17 00:00:00 2001 From: Shawn A Date: Mon, 28 Aug 2017 21:00:15 -0500 Subject: [PATCH 30/80] left in debug --- WiFiManager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WiFiManager.cpp b/WiFiManager.cpp index 320af11..a7d63a8 100644 --- a/WiFiManager.cpp +++ b/WiFiManager.cpp @@ -820,8 +820,8 @@ void WiFiManager::handleNotFound() { * Return true in that case so the page handler do not try to handle the request again. */ boolean WiFiManager::captivePortal() { -DEBUG_WM(server->hostHeader()); - if (!isIp(server->hostHeader()) && false) { + DEBUG_WM(server->hostHeader()); + if (!isIp(server->hostHeader())) { DEBUG_WM(F("Request redirected to captive portal")); server->sendHeader("Location", String("http://") + toStringIp(server->client().localIP()), true); server->send ( 302, "text/plain", ""); // Empty content inhibits Content-length header so we have to close the socket ourselves. From 04a9dda74b98cb14aa8ee892604f11fed5d4dd4c Mon Sep 17 00:00:00 2001 From: Shawn A Date: Tue, 29 Aug 2017 16:31:59 -0500 Subject: [PATCH 31/80] testing mode and persistent presrvation You should be able to have sta+softap mode with your own softap, and starting config portal will not overwrite your default softap, but i cannot find a way to reconnect other than rebooting the device --- WiFiManager.cpp | 22 +++++++++++++++++----- WiFiManager.h | 11 ++++++++++- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/WiFiManager.cpp b/WiFiManager.cpp index a7d63a8..a068068 100644 --- a/WiFiManager.cpp +++ b/WiFiManager.cpp @@ -82,13 +82,17 @@ void WiFiManager::addParameter(WiFiManagerParameter *p) { } // constructor -WiFiManager::WiFiManager() { - WiFi.persistent(false); +WiFiManager::WiFiManager() { + _usermode = WiFi.getMode(); + WiFi.persistent(false); // disable persistent so scannetworks and mode switching is not saved } // destructor WiFiManager::~WiFiManager() { - WiFi.persistent(true); // @todo restore original + if(_userpersistent) WiFi.persistent(true); // reenable persistent + WiFi.mode(_usermode); + DEBUG_WM(_usermode); + DEBUG_WM(F("unloading")); } // AUTOCONNECT @@ -121,7 +125,7 @@ boolean WiFiManager::autoConnect(char const *apName, char const *apPassword) { // CONFIG PORTAL bool WiFiManager::startAP(){ - DEBUG_WM(F("Configuring access point with with SSID ... ")); + DEBUG_WM(F("Configuring access point with with SSID of ... ")); DEBUG_WM(_apName); // setup optional soft AP static ip config @@ -303,7 +307,7 @@ boolean WiFiManager::stopConfigPortal(){ // turn off AP DEBUG_WM(F("disconnect configportal")); bool ret = WiFi.softAPdisconnect(false); - if(!ret)DEBUG_WM(F("disconnect configportal error")); + if(!ret)DEBUG_WM(F("disconnect configportal - softAPdisconnect failed")); wifimode(WIFI_STA); // set back to sta, @todo use preservation variable configPortalActive = false; return ret; @@ -422,7 +426,9 @@ String WiFiManager::getConfigPortalSSID() { void WiFiManager::resetSettings() { DEBUG_WM(F("SETTINGS ERASED")); DEBUG_WM(F("THIS MAY CAUSE AP NOT TO START UP PROPERLY. YOU NEED TO COMMENT IT OUT AFTER ERASING THE DATA.")); // @todo WHUT? + WiFi.persistent(true); WiFi.disconnect(true); + WiFi.persistent(false); //delay(200); } @@ -858,6 +864,12 @@ void WiFiManager::setConfigPortalBlocking(boolean shoudlBlock) { _configPortalIsBlocking = shoudlBlock; } +//wrapper for ESP wifi.persistent so we can remember it as WIFi._persistent is protected +void WiFiManager::setRestorePersistent(boolean persistent) { + _userpersistent = persistent; + if(!persistent) DEBUG_WM(F("persistent is off")); +} + // HELPERS template diff --git a/WiFiManager.h b/WiFiManager.h index 72956b0..843ddb1 100644 --- a/WiFiManager.h +++ b/WiFiManager.h @@ -125,11 +125,13 @@ class WiFiManager //if this is set, portal will be blocking and wait until save or exit, is false user must manually process to handle config portal void setConfigPortalBlocking(boolean shouldBlock); - //TODO + //if this is set, customise style void setCustomHeadElement(const char* element); //if this is true, remove duplicated Access Points - defaut true void setRemoveDuplicateAPs(boolean removeDuplicates); + //wrapper for esp wifi persistent, set esp wifi persistent through wifimanager so we can save status and restore as needed + void setRestorePersistent(boolean persistent); private: std::unique_ptr dnsServer; @@ -144,6 +146,11 @@ class WiFiManager void startWPS(); bool startAP(); + // typedef enum WiFiMode + // { + // WIFI_OFF = 0, WIFI_STA = 1, WIFI_AP = 2, WIFI_AP_STA = 3 + // } WiFiMode_t; + String _apName = "no-net"; String _apPassword = ""; String _ssid = ""; @@ -151,6 +158,8 @@ class WiFiManager unsigned long _configPortalTimeout = 0; unsigned long _connectTimeout = 0; unsigned long _configPortalStart = 0; + boolean _userpersistent = true; + WiFiMode_t _usermode = WIFI_OFF; IPAddress _ap_static_ip; IPAddress _ap_static_gw; From 9160b5bd3b8b1974dc6a39af2ab629b09a1779e4 Mon Sep 17 00:00:00 2001 From: Shawn A Date: Wed, 30 Aug 2017 09:15:36 -0500 Subject: [PATCH 32/80] maxlength null string fix pr#244 --- WiFiManager.cpp | 9 ++++----- WiFiManager.h | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/WiFiManager.cpp b/WiFiManager.cpp index a068068..b94e0fc 100644 --- a/WiFiManager.cpp +++ b/WiFiManager.cpp @@ -41,7 +41,7 @@ void WiFiManagerParameter::init(const char *id, const char *placeholder, const c _value[i] = 0; } if (defaultValue != NULL) { - strncpy(_value, defaultValue, length); + strncpy(_value, defaultValue, length+1); // length+1 due to null terminated string } _customHTML = custom; @@ -84,7 +84,7 @@ void WiFiManager::addParameter(WiFiManagerParameter *p) { // constructor WiFiManager::WiFiManager() { _usermode = WiFi.getMode(); - WiFi.persistent(false); // disable persistent so scannetworks and mode switching is not saved + WiFi.persistent(false); // disable persistent so scannetworks and mode switching do not cause overwrites } // destructor @@ -309,6 +309,7 @@ boolean WiFiManager::stopConfigPortal(){ bool ret = WiFi.softAPdisconnect(false); if(!ret)DEBUG_WM(F("disconnect configportal - softAPdisconnect failed")); wifimode(WIFI_STA); // set back to sta, @todo use preservation variable + WiFi.mode(_usermode); configPortalActive = false; return ret; } @@ -575,7 +576,7 @@ void WiFiManager::handleWifi(boolean scan) { page += item; delay(0); } else { - DEBUG_WM(F("Skipping due to quality")); + DEBUG_WM(F("Skipping , does not meet _minimumQuality")); } } @@ -659,13 +660,11 @@ void WiFiManager::handleWifi(boolean scan) { page += FPSTR(HTTP_FORM_END); page += FPSTR(HTTP_SCAN_LINK); - page += FPSTR(HTTP_END); server->sendHeader("Content-Length", String(page.length())); server->send(200, "text/html", page); - DEBUG_WM(F("Sent config page")); } diff --git a/WiFiManager.h b/WiFiManager.h index 843ddb1..433ddd9 100644 --- a/WiFiManager.h +++ b/WiFiManager.h @@ -36,7 +36,7 @@ const char HTTP_HEAD_END[] PROGMEM = "



"; const char HTTP_ITEM[] PROGMEM = ""; //const char HTTP_ITEM_PADLOCK[] PROGMEM = ""; -const char HTTP_FORM_START[] PROGMEM = "


"; +const char HTTP_FORM_START[] PROGMEM = "

"; const char HTTP_FORM_LABEL[] PROGMEM = ""; const char HTTP_FORM_PARAM[] PROGMEM = "
"; const char HTTP_FORM_END[] PROGMEM = "
"; From 63dd714805c5e818b975fa773a3b549f5e7a884d Mon Sep 17 00:00:00 2001 From: Shawn A Date: Wed, 30 Aug 2017 09:22:40 -0500 Subject: [PATCH 33/80] oops missed one pr#244 --- WiFiManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WiFiManager.cpp b/WiFiManager.cpp index b94e0fc..3d97c04 100644 --- a/WiFiManager.cpp +++ b/WiFiManager.cpp @@ -686,7 +686,7 @@ void WiFiManager::handleWifiSave() { //read parameter String value = server->arg(_params[i]->getID()).c_str(); //store it in array - value.toCharArray(_params[i]->_value, _params[i]->_length); + value.toCharArray(_params[i]->_value, _params[i]->_length+1); // length+1 null terminated DEBUG_WM(F("Parameter")); DEBUG_WM(_params[i]->getID()); DEBUG_WM(value); From 606a1c312884eadc9e17bb0b69a6af24e8de3562 Mon Sep 17 00:00:00 2001 From: Shawn A Date: Wed, 30 Aug 2017 10:00:29 -0500 Subject: [PATCH 34/80] removed escaped quotes --- WiFiManager.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/WiFiManager.h b/WiFiManager.h index 433ddd9..4bfaaa9 100644 --- a/WiFiManager.h +++ b/WiFiManager.h @@ -22,8 +22,8 @@ extern "C" { #include "user_interface.h" } -const char HTTP_HEAD[] PROGMEM = "{v}"; -// const char HTTP_STYLE[] PROGMEM = ""; +const char HTTP_HEAD[] PROGMEM = "{v}"; +// const char HTTP_STYLE[] PROGMEM = ""; const char HTTP_STYLE[] PROGMEM = ""; const char HTTP_SCRIPT[] PROGMEM = ""; const char HTTP_HEAD_END[] PROGMEM = "
"; -const char HTTP_PORTAL_OPTIONS[] PROGMEM = "



"; +const char HTTP_PORTAL_OPTIONS[] PROGMEM = "



"; const char HTTP_ITEM[] PROGMEM = ""; //const char HTTP_ITEM_PADLOCK[] PROGMEM = ""; const char HTTP_FORM_START[] PROGMEM = "


"; -const char HTTP_FORM_LABEL[] PROGMEM = ""; +const char HTTP_FORM_LABEL[] PROGMEM = ""; const char HTTP_FORM_PARAM[] PROGMEM = "
"; const char HTTP_FORM_END[] PROGMEM = "
"; -const char HTTP_SCAN_LINK[] PROGMEM = "
"; +const char HTTP_SCAN_LINK[] PROGMEM = "
"; const char HTTP_SAVED[] PROGMEM = "
Credentials Saved
Trying to connect ESP to network.
If it fails reconnect to AP to try again
"; const char HTTP_END[] PROGMEM = "
"; From 66c653ff94bb83f08b317b94b022dafb64ecfc5d Mon Sep 17 00:00:00 2001 From: Shawn A Date: Wed, 30 Aug 2017 10:00:48 -0500 Subject: [PATCH 35/80] convert to post --- WiFiManager.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WiFiManager.h b/WiFiManager.h index 4bfaaa9..9500450 100644 --- a/WiFiManager.h +++ b/WiFiManager.h @@ -33,7 +33,7 @@ const char HTTP_STYLE[] PROGMEM = ""; const char HTTP_SCRIPT[] PROGMEM = ""; const char HTTP_HEAD_END[] PROGMEM = "
"; -const char HTTP_PORTAL_OPTIONS[] PROGMEM = "



"; +const char HTTP_PORTAL_OPTIONS[] PROGMEM = "



"; const char HTTP_ITEM[] PROGMEM = ""; //const char HTTP_ITEM_PADLOCK[] PROGMEM = ""; const char HTTP_FORM_START[] PROGMEM = "


"; From 6e43c1eb41c87aca254e85418118c51c0d94268b Mon Sep 17 00:00:00 2001 From: Shawn A Date: Wed, 30 Aug 2017 10:24:04 -0500 Subject: [PATCH 36/80] change all forms to POST #409 --- WiFiManager.cpp | 3 ++- WiFiManager.h | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/WiFiManager.cpp b/WiFiManager.cpp index 3d97c04..33500f7 100644 --- a/WiFiManager.cpp +++ b/WiFiManager.cpp @@ -382,6 +382,7 @@ uint8_t WiFiManager::waitForConnectResult() { while(millis() < timeout) { status = WiFi.status(); + // @todo detect additional states, connect happens, then dhcp then get ip, there is some delay here, make sure not to timeout if waiting on IP if (status == WL_CONNECTED || status == WL_CONNECT_FAILED) { return status; } @@ -673,7 +674,7 @@ void WiFiManager::handleWifi(boolean scan) { */ void WiFiManager::handleWifiSave() { DEBUG_WM(F("WiFi save")); - + //SAVE/connect here _ssid = server->arg("s").c_str(); _pass = server->arg("p").c_str(); diff --git a/WiFiManager.h b/WiFiManager.h index 9500450..2949d2b 100644 --- a/WiFiManager.h +++ b/WiFiManager.h @@ -33,10 +33,10 @@ const char HTTP_STYLE[] PROGMEM = ""; const char HTTP_SCRIPT[] PROGMEM = ""; const char HTTP_HEAD_END[] PROGMEM = "
"; -const char HTTP_PORTAL_OPTIONS[] PROGMEM = "


"; +const char HTTP_PORTAL_OPTIONS[] PROGMEM = "



"; const char HTTP_ITEM[] PROGMEM = ""; //const char HTTP_ITEM_PADLOCK[] PROGMEM = ""; -const char HTTP_FORM_START[] PROGMEM = "


"; +const char HTTP_FORM_START[] PROGMEM = "

"; const char HTTP_FORM_LABEL[] PROGMEM = ""; const char HTTP_FORM_PARAM[] PROGMEM = "
"; const char HTTP_FORM_END[] PROGMEM = "
"; From 5f288438435b471007fca38d66d5c461cfbd7303 Mon Sep 17 00:00:00 2001 From: Shawn A Date: Wed, 30 Aug 2017 14:01:37 -0500 Subject: [PATCH 37/80] Cleaned some html csss --- WiFiManager.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/WiFiManager.h b/WiFiManager.h index 2949d2b..7005227 100644 --- a/WiFiManager.h +++ b/WiFiManager.h @@ -24,7 +24,7 @@ extern "C" { const char HTTP_HEAD[] PROGMEM = "{v}"; // const char HTTP_STYLE[] PROGMEM = ""; -const char HTTP_STYLE[] PROGMEM = ""; const char HTTP_SCRIPT[] PROGMEM = ""; const char HTTP_HEAD_END[] PROGMEM = "
"; -const char HTTP_PORTAL_OPTIONS[] PROGMEM = "



"; +const char HTTP_PORTAL_OPTIONS[] PROGMEM = "



"; const char HTTP_ITEM[] PROGMEM = ""; //const char HTTP_ITEM_PADLOCK[] PROGMEM = ""; const char HTTP_FORM_START[] PROGMEM = "


"; const char HTTP_FORM_LABEL[] PROGMEM = ""; const char HTTP_FORM_PARAM[] PROGMEM = "
"; -const char HTTP_FORM_END[] PROGMEM = "
"; +const char HTTP_FORM_END[] PROGMEM = "
"; const char HTTP_SCAN_LINK[] PROGMEM = "
"; const char HTTP_SAVED[] PROGMEM = "
Credentials Saved
Trying to connect ESP to network.
If it fails reconnect to AP to try again
"; const char HTTP_END[] PROGMEM = "
"; From 2c357d7e8683444e27195a9cef037c1c67fd4ffe Mon Sep 17 00:00:00 2001 From: Shawn A Date: Thu, 31 Aug 2017 10:21:21 -0500 Subject: [PATCH 38/80] changing non form back to GET for now #409 --- WiFiManager.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WiFiManager.h b/WiFiManager.h index 7005227..4d25252 100644 --- a/WiFiManager.h +++ b/WiFiManager.h @@ -33,7 +33,7 @@ const char HTTP_STYLE[] PROGMEM = ""; const char HTTP_SCRIPT[] PROGMEM = ""; const char HTTP_HEAD_END[] PROGMEM = "
"; -const char HTTP_PORTAL_OPTIONS[] PROGMEM = "



"; +const char HTTP_PORTAL_OPTIONS[] PROGMEM = "



"; const char HTTP_ITEM[] PROGMEM = ""; //const char HTTP_ITEM_PADLOCK[] PROGMEM = ""; const char HTTP_FORM_START[] PROGMEM = "


"; From 6525a0bd3d1960df68cebd9d3b6411e16ee14e17 Mon Sep 17 00:00:00 2001 From: Shawn A Date: Thu, 31 Aug 2017 13:57:11 -0500 Subject: [PATCH 39/80] fixed comment wording --- WiFiManager.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WiFiManager.h b/WiFiManager.h index 4d25252..f021a23 100644 --- a/WiFiManager.h +++ b/WiFiManager.h @@ -130,7 +130,7 @@ class WiFiManager void setCustomHeadElement(const char* element); //if this is true, remove duplicated Access Points - defaut true void setRemoveDuplicateAPs(boolean removeDuplicates); - //wrapper for esp wifi persistent, set esp wifi persistent through wifimanager so we can save status and restore as needed + //setter for ESP wifi.persistent so we can remember it and restore user preference, as WIFi._persistent is protected void setRestorePersistent(boolean persistent); private: From a93b601cd7e349db43c98a439896e34569d67fc7 Mon Sep 17 00:00:00 2001 From: Shawn A Date: Thu, 31 Aug 2017 13:57:23 -0500 Subject: [PATCH 40/80] again --- WiFiManager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WiFiManager.cpp b/WiFiManager.cpp index 33500f7..d84e753 100644 --- a/WiFiManager.cpp +++ b/WiFiManager.cpp @@ -674,7 +674,7 @@ void WiFiManager::handleWifi(boolean scan) { */ void WiFiManager::handleWifiSave() { DEBUG_WM(F("WiFi save")); - + //SAVE/connect here _ssid = server->arg("s").c_str(); _pass = server->arg("p").c_str(); @@ -864,7 +864,7 @@ void WiFiManager::setConfigPortalBlocking(boolean shoudlBlock) { _configPortalIsBlocking = shoudlBlock; } -//wrapper for ESP wifi.persistent so we can remember it as WIFi._persistent is protected +//setter for ESP wifi.persistent so we can remember it and restore user preference, as WIFi._persistent is protected void WiFiManager::setRestorePersistent(boolean persistent) { _userpersistent = persistent; if(!persistent) DEBUG_WM(F("persistent is off")); From 81b37dfffce6cff343257549ab08fa000f8e0963 Mon Sep 17 00:00:00 2001 From: Shawn A Date: Fri, 8 Sep 2017 16:08:35 -0500 Subject: [PATCH 41/80] adds some style, clean up css, adds new tokens #415 --- WiFiManager.cpp | 30 ++++- WiFiManager.h | 20 +++- extras/WiFiManager.template.html | 196 +++++++++++++++++++++++++------ extras/test.html | 8 +- 4 files changed, 201 insertions(+), 53 deletions(-) diff --git a/WiFiManager.cpp b/WiFiManager.cpp index d84e753..e758692 100644 --- a/WiFiManager.cpp +++ b/WiFiManager.cpp @@ -560,15 +560,18 @@ void WiFiManager::handleWifi(boolean scan) { DEBUG_WM(WiFi.SSID(indices[i])); DEBUG_WM(WiFi.RSSI(indices[i])); int quality = getRSSIasQuality(WiFi.RSSI(indices[i])); + uint8_t enc_type = WiFi.encryptionType(indices[i]); if (_minimumQuality == -1 || _minimumQuality < quality) { String item = FPSTR(HTTP_ITEM); String rssiQ; rssiQ += quality; - item.replace("{v}", WiFi.SSID(indices[i])); - item.replace("{r}", rssiQ); - item.replace("{q}", (String)round(map(quality,0,100,1,4))); - if (WiFi.encryptionType(indices[i]) != ENC_TYPE_NONE) { + item.replace("{v}", WiFi.SSID(indices[i])); // ssid no encoding + item.replace("{R}", (String)WiFi.RSSI(indices[i])); // rssi + item.replace("{e}", encryptionTypeStr(enc_type)); + item.replace("{r}", rssiQ); // qualitypercent + item.replace("{q}", (String)round(map(quality,0,100,1,4))); //quality icon 1-4 + if (enc_type != ENC_TYPE_NONE) { item.replace("{i}", "l"); } else { item.replace("{i}", ""); @@ -616,7 +619,7 @@ void WiFiManager::handleWifi(boolean scan) { snprintf(parLength, 5, "%d", _params[i]->getValueLength()); pitem.replace("{l}", parLength); pitem.replace("{v}", _params[i]->getValue()); - pitem.replace("{c}", _params[i]->getCustomHTML()); + pitem.replace("{c}", _params[i]->getCustomHTML()); // additional attributes not html } else { pitem = _params[i]->getCustomHTML(); } @@ -929,6 +932,23 @@ boolean WiFiManager::validApPassword(){ return true; } +String WiFiManager::encryptionTypeStr(uint8_t authmode) { + switch(authmode) { + case ENC_TYPE_NONE: + return "None"; + case ENC_TYPE_WEP: + return "WEP"; + case ENC_TYPE_TKIP: + return "WPA_PSK"; + case ENC_TYPE_CCMP: + return "WPA2_PSK"; + case ENC_TYPE_AUTO: + return "WPA_WPA2_PSK"; + default: + return "Unknown"; + } +} + // set mode without persistent bool WiFiManager::wifimode(WiFiMode_t m) { if(wifi_get_opmode() == (uint8) m) { diff --git a/WiFiManager.h b/WiFiManager.h index f021a23..f6ad1ec 100644 --- a/WiFiManager.h +++ b/WiFiManager.h @@ -24,15 +24,24 @@ extern "C" { const char HTTP_HEAD[] PROGMEM = "{v}"; // const char HTTP_STYLE[] PROGMEM = ""; -const char HTTP_STYLE[] PROGMEM = ""; + const char HTTP_SCRIPT[] PROGMEM = ""; -const char HTTP_HEAD_END[] PROGMEM = "
"; +const char HTTP_HEAD_END[] PROGMEM = "
"; const char HTTP_PORTAL_OPTIONS[] PROGMEM = "


"; const char HTTP_ITEM[] PROGMEM = ""; //const char HTTP_ITEM_PADLOCK[] PROGMEM = ""; @@ -40,8 +49,8 @@ const char HTTP_FORM_START[] PROGMEM = "
{p}"; const char HTTP_FORM_PARAM[] PROGMEM = "
"; const char HTTP_FORM_END[] PROGMEM = "
"; -const char HTTP_SCAN_LINK[] PROGMEM = "
"; -const char HTTP_SAVED[] PROGMEM = "
Credentials Saved
Trying to connect ESP to network.
If it fails reconnect to AP to try again
"; +const char HTTP_SCAN_LINK[] PROGMEM = "
"; +const char HTTP_SAVED[] PROGMEM = "
Saving Credentials
Trying to connect ESP to network.
If it fails reconnect to AP to try again
"; const char HTTP_END[] PROGMEM = "
"; #define WIFI_MANAGER_MAX_PARAMS 10 @@ -208,6 +217,7 @@ class WiFiManager boolean isIp(String str); String toStringIp(IPAddress ip); boolean validApPassword(); + String encryptionTypeStr(uint8_t authmode); boolean connect; boolean configPortalActive = false; diff --git a/extras/WiFiManager.template.html b/extras/WiFiManager.template.html index e15593b..2a49be3 100644 --- a/extras/WiFiManager.template.html +++ b/extras/WiFiManager.template.html @@ -6,15 +6,125 @@ {v} - + "; diff --git a/extras/WiFiManager.template.html b/extras/WiFiManager.template.html index 2a49be3..09ae94b 100644 --- a/extras/WiFiManager.template.html +++ b/extras/WiFiManager.template.html @@ -124,6 +124,13 @@ a:hover { color: #d9534f; } +dt { + font-weight: bold; +} +dd { + margin: 0; + padding: 0 0 0.5em 0; +} @@ -189,8 +196,18 @@ a:hover {

Information

Connected to AP: homeAP

Connect Failed

An error occured
- - + +

/info


+
+
Chip ID
123456
+
Flash Chip ID
1234556
+
IDE Flash Size
4194304 bytes
+
Real Flash Size
4194304 bytes
+
Soft AP IP
192.168.4.1
+
Soft AP MAC
00:00:00:00:00:00
+
Station MAC
00:00:00:00:00:00
+
+
From 64798ebbf1519f54a787a30a9cd0bb7d4a44e2b8 Mon Sep 17 00:00:00 2001 From: Shawn A Date: Sun, 10 Sep 2017 21:17:07 -0500 Subject: [PATCH 43/80] pimped out info pagee We can simply this later, or make an option string to display specific things --- WiFiManager.cpp | 113 ++++++++++++++++++++++++++++++++++++++++++++++-- WiFiManager.h | 1 + 2 files changed, 110 insertions(+), 4 deletions(-) diff --git a/WiFiManager.cpp b/WiFiManager.cpp index cc6edb0..0fe2c24 100644 --- a/WiFiManager.cpp +++ b/WiFiManager.cpp @@ -749,29 +749,114 @@ void WiFiManager::handleInfo() { page += FPSTR(HTTP_STYLE); page += _customHeadElement; page += FPSTR(HTTP_HEAD_END); - page += F("
"); + + page += "
"; + reportStatus(page); + page +="
"; + // @todo add versioning here + + page += F("

esp8266


"); page += F("
Chip ID
"); page += ESP.getChipId(); page += F("
"); + page += F("
Flash Chip ID
"); page += ESP.getFlashChipId(); page += F("
"); + page += F("
IDE Flash Size
"); page += ESP.getFlashChipSize(); page += F(" bytes
"); - page += F("
Real Flash Size
"); + + page += F("
Actual Flash Size
"); page += ESP.getFlashChipRealSize(); page += F(" bytes
"); - page += F("
Soft AP IP
"); + + page += F("
SDK Version
"); + page += system_get_sdk_version(); + page += F("
"); + + page += F("
Core Version
"); + page += ESP.getCoreVersion(); + page += F("
"); + + page += F("
Boot Version
"); + page += system_get_boot_version(); + page += F("
"); + + page += F("
CPU Frequency
"); + page += ESP.getCpuFreqMHz(); + page += F("MHz
"); + + // heap is dynamic, would have to have a known starting for graph + // uint32_t heapstart = 47000; + // page += F("
Memory Heap
"); + // page += F("Used/Total bytes
"); + // page += (heapstart)-ESP.getFreeHeap(); + // page += "/"; + // page += (heapstart); + // page += "
"; + // page += F("
"); + + page += F("
Memory - Free Heap
"); + page += ESP.getFreeHeap(); + page += F(" bytes available
"); + + page += F("
Memory - Sketch Size
"); + page += F("Used/Total bytes
"); + page += (ESP.getSketchSize()+ESP.getFreeSketchSpace())-ESP.getFreeSketchSpace(); + page += "/"; + page += (ESP.getSketchSize()+ESP.getFreeSketchSpace()); + page += F("
"); + + page += F("
Last reset reason
"); + page += ESP.getResetReason(); + page += F("
"); + + page += F("

WiFi


Access Point IP
"); page += WiFi.softAPIP().toString(); page += F("
"); - page += F("
Soft AP MAC
"); + + page += F("
Access Point MAC
"); page += WiFi.softAPmacAddress(); page += F("
"); + + page += F("
SSID
"); + page += WiFi.SSID(); + page += F("
"); + + page += F("
Station IP
"); + page += WiFi.localIP().toString(); + page += F("
"); + page += F("
Station MAC
"); page += WiFi.macAddress(); page += F("
"); + page += F("
"); + + page += F("

Available Pages


"); + page += F(""); + page += F(""); + page += F(""); + page += F(""); + page += F(""); + page += F(""); + page += F(""); + page += F(""); + page += F(""); + page += F(""); + page += F(""); + page += F(""); + page += F(""); + page += F(""); + // @todo add callback here + page += F("
PageFunction
/Menu page.
/wifiShow WiFi scan results and enter WiFi configuration.
/wifisaveSave WiFi configuration information and configure device. Needs variables supplied.
/closeClose the configuration server and configuration WiFi network.
/iInformation page
/rDelete WiFi configuration and reboot. ESP device will not reconnect to a network until new WiFi configuration data is entered.
"); + page += F("

More information about WiFiManager at https://github.com/tzapu/WiFiManager."); page += FPSTR(HTTP_END); server->sendHeader("Content-Length", String(page.length())); @@ -780,6 +865,26 @@ void WiFiManager::handleInfo() { DEBUG_WM(F("Sent info page")); } +void WiFiManager::reportStatus(String &page){ + if (WiFi.SSID() != ""){ + page += F("Configured to connect to access point "); + page += WiFi.SSID(); + if (WiFi.status()==WL_CONNECTED){ + page += F(" and currently connected on IP "); + page += WiFi.localIP().toString(); + page += F(""); + } + else { + page += F(" but not currently connected to network."); + } + } + else { + page += F("No network currently configured."); + } +} + /** * HTTPD CALLBACK reset page */ diff --git a/WiFiManager.h b/WiFiManager.h index 40e5c0e..c5cde8e 100644 --- a/WiFiManager.h +++ b/WiFiManager.h @@ -221,6 +221,7 @@ class WiFiManager String toStringIp(IPAddress ip); boolean validApPassword(); String encryptionTypeStr(uint8_t authmode); + void reportStatus(String &page); boolean connect; boolean configPortalActive = false; From c6f7ac001253f3421b8606d2d92a86e3efcdb8e1 Mon Sep 17 00:00:00 2001 From: Shawn A Date: Thu, 14 Sep 2017 10:26:51 -0500 Subject: [PATCH 44/80] added uptime to info still subject to rollover --- WiFiManager.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/WiFiManager.cpp b/WiFiManager.cpp index 0fe2c24..78ec234 100644 --- a/WiFiManager.cpp +++ b/WiFiManager.cpp @@ -756,6 +756,11 @@ void WiFiManager::handleInfo() { // @todo add versioning here page += F("

esp8266


"); + + page += F("
Uptime
"); + page += (String)(system_get_time() / 1000000 / 60) + " mins "; + page += (String)((system_get_time() / 1000000)%60) + " secs"; + page += F("
Chip ID
"); page += ESP.getChipId(); page += F("
"); From e43a7d0b75db446744bb0d46c3d6cd08fb544359 Mon Sep 17 00:00:00 2001 From: Shawn A Date: Thu, 14 Sep 2017 15:38:12 -0500 Subject: [PATCH 45/80] modify templates, remove placeholders adds labels to all inputs, deprecates {p} placeholders, adds {t} to ttitles --- WiFiManager.cpp | 117 +++++++++++++++++++++++++++++------------------- WiFiManager.h | 8 ++-- 2 files changed, 76 insertions(+), 49 deletions(-) diff --git a/WiFiManager.cpp b/WiFiManager.cpp index 78ec234..d545b36 100644 --- a/WiFiManager.cpp +++ b/WiFiManager.cpp @@ -592,78 +592,103 @@ void WiFiManager::handleWifi(boolean scan) { } } - page += FPSTR(HTTP_FORM_START); - char parLength[5]; - // add the extra parameters to the form - for (int i = 0; i < _paramsCount; i++) { - if (_params[i] == NULL) { - break; - } + String pitem = FPSTR(HTTP_FORM_START); + pitem.replace("{v}", WiFi.SSID()); + page += pitem; - //String pitem = FPSTR(HTTP_FORM_PARAM); - String pitem; - switch (_params[i]->getLabelPlacement()) { - case WFM_LABEL_BEFORE: - pitem = FPSTR(HTTP_FORM_LABEL); - pitem += FPSTR(HTTP_FORM_PARAM); - break; - case WFM_LABEL_AFTER: - pitem = FPSTR(HTTP_FORM_PARAM); - pitem += FPSTR(HTTP_FORM_LABEL); - break; - default: - // WFM_NO_LABEL - pitem = FPSTR(HTTP_FORM_PARAM); - break; - } - if (_params[i]->getID() != NULL) { - pitem.replace("{i}", _params[i]->getID()); - pitem.replace("{n}", _params[i]->getID()); - pitem.replace("{p}", _params[i]->getPlaceholder()); - snprintf(parLength, 5, "%d", _params[i]->getValueLength()); - pitem.replace("{l}", parLength); - pitem.replace("{v}", _params[i]->getValue()); - pitem.replace("{c}", _params[i]->getCustomHTML()); // additional attributes not html - } else { - pitem = _params[i]->getCustomHTML(); - } - page += pitem; - } - if (_params[0] != NULL) { - page += "
"; + if(_paramsCount > 0){ + + page += FPSTR(HTTP_FORM_PARAM_START); + + char parLength[5]; + // add the extra parameters to the form + for (int i = 0; i < _paramsCount; i++) { + if (_params[i] == NULL) { + break; + } + + String pitem; + switch (_params[i]->getLabelPlacement()) { + case WFM_LABEL_BEFORE: + pitem = FPSTR(HTTP_FORM_LABEL); + pitem += FPSTR(HTTP_FORM_PARAM); + break; + case WFM_LABEL_AFTER: + pitem = FPSTR(HTTP_FORM_PARAM); + pitem += FPSTR(HTTP_FORM_LABEL); + break; + default: + // WFM_NO_LABEL + pitem = FPSTR(HTTP_FORM_PARAM); + break; + } + if (_params[i]->getID() != NULL) { + pitem.replace("{i}", _params[i]->getID()); + pitem.replace("{n}", _params[i]->getID()); + pitem.replace("{p}", "{t}"); + pitem.replace("{t}", _params[i]->getPlaceholder()); + snprintf(parLength, 5, "%d", _params[i]->getValueLength()); + pitem.replace("{l}", parLength); + pitem.replace("{v}", _params[i]->getValue()); + pitem.replace("{c}", _params[i]->getCustomHTML()); // additional attributes not html + } else { + pitem = _params[i]->getCustomHTML(); + } + + page += pitem; + } + if (_params[0] != NULL) { + page += FPSTR(HTTP_FORM_PARAM_END); + } } if (_sta_show_static_fields || _sta_static_ip) { - String item = FPSTR(HTTP_FORM_PARAM); + // @todo how do we get these settings from memory , wifi_get_ip_info does not seem to reveal if struct ip_info is static or not + // @todo move titles to params for i18n + + String item = FPSTR(HTTP_FORM_LABEL); + item += FPSTR(HTTP_FORM_PARAM); item.replace("{i}", "ip"); item.replace("{n}", "ip"); - item.replace("{p}", "Static IP"); + item.replace("{p}", "{t}"); + item.replace("{t}", "Static IP"); item.replace("{l}", "15"); item.replace("{v}", (_sta_static_ip ? _sta_static_ip.toString() : "")); + // IPAddress sta_ip = WiFi.localIP(); + // item.replace("{p}", sta_ip.toString()); + page += item; - item = FPSTR(HTTP_FORM_PARAM); + item = FPSTR(HTTP_FORM_LABEL); + item += FPSTR(HTTP_FORM_PARAM); item.replace("{i}", "gw"); item.replace("{n}", "gw"); - item.replace("{p}", "Static Gateway"); + item.replace("{p}", "{t}"); + item.replace("{t}", "Static Gateway"); item.replace("{l}", "15"); item.replace("{v}", (_sta_static_gw ? _sta_static_gw.toString() : "")); + // IPAddress sta_gateway = WiFi.gatewayIP(); + // item.replace("{p}", sta_gateway.toString()); + page += item; - item = FPSTR(HTTP_FORM_PARAM); + item = FPSTR(HTTP_FORM_LABEL); + item += FPSTR(HTTP_FORM_PARAM); item.replace("{i}", "sn"); item.replace("{n}", "sn"); - item.replace("{p}", "Subnet"); + item.replace("{p}", "{t}"); + item.replace("{t}", "Subnet"); item.replace("{l}", "15"); item.replace("{v}", (_sta_static_sn ? _sta_static_sn.toString() : "")); + // IPAddress sta_subnet = WiFi.subnetMask(); + // item.replace("{p}", sta_subnet.toString()); page += item; - - page += "
"; + page += "
"; // @todo remove these, use css } page += FPSTR(HTTP_FORM_END); diff --git a/WiFiManager.h b/WiFiManager.h index c5cde8e..01ac0f6 100644 --- a/WiFiManager.h +++ b/WiFiManager.h @@ -47,10 +47,12 @@ const char HTTP_HEAD_END[] PROGMEM = "
"; const char HTTP_PORTAL_OPTIONS[] PROGMEM = "



"; const char HTTP_ITEM[] PROGMEM = ""; //const char HTTP_ITEM_PADLOCK[] PROGMEM = ""; -const char HTTP_FORM_START[] PROGMEM = "


"; -const char HTTP_FORM_LABEL[] PROGMEM = ""; -const char HTTP_FORM_PARAM[] PROGMEM = "
"; +const char HTTP_FORM_START[] PROGMEM = "

"; const char HTTP_FORM_END[] PROGMEM = "
"; +const char HTTP_FORM_LABEL[] PROGMEM = ""; +const char HTTP_FORM_PARAM_START[] PROGMEM = "
"; +const char HTTP_FORM_PARAM[] PROGMEM = "
"; +const char HTTP_FORM_PARAM_END[] PROGMEM = "
"; const char HTTP_SCAN_LINK[] PROGMEM = "
"; const char HTTP_SAVED[] PROGMEM = "
Saving Credentials
Trying to connect ESP to network.
If it fails reconnect to AP to try again
"; const char HTTP_END[] PROGMEM = "
"; From 37dbd667f4f7cdd7da25d00b2cdd32e1a4679627 Mon Sep 17 00:00:00 2001 From: Shawn A Date: Thu, 14 Sep 2017 15:38:42 -0500 Subject: [PATCH 46/80] add staticfield setter --- WiFiManager.cpp | 5 +++++ WiFiManager.h | 2 ++ 2 files changed, 7 insertions(+) diff --git a/WiFiManager.cpp b/WiFiManager.cpp index d545b36..d896650 100644 --- a/WiFiManager.cpp +++ b/WiFiManager.cpp @@ -1012,6 +1012,11 @@ void WiFiManager::setRestorePersistent(boolean persistent) { if(!persistent) DEBUG_WM(F("persistent is off")); } +void WiFiManager::setShowStaticFields(boolean alwaysShow){ + _sta_show_static_fields = alwaysShow; +} + + // HELPERS template diff --git a/WiFiManager.h b/WiFiManager.h index 01ac0f6..788a004 100644 --- a/WiFiManager.h +++ b/WiFiManager.h @@ -145,6 +145,8 @@ class WiFiManager void setRemoveDuplicateAPs(boolean removeDuplicates); //setter for ESP wifi.persistent so we can remember it and restore user preference, as WIFi._persistent is protected void setRestorePersistent(boolean persistent); + //if true, always show static net inputs, IP, subnet, gateway, if false, only show when already set. + void setShowStaticFields(boolean alwaysShow); private: std::unique_ptr dnsServer; From 4d18bfb113c1eb565f197f1b3f8c246760e477bc Mon Sep 17 00:00:00 2001 From: Shawn A Date: Thu, 14 Sep 2017 16:34:16 -0500 Subject: [PATCH 47/80] abstracted outputters --- WiFiManager.cpp | 134 +++++++++++++++++++++++++++--------------------- WiFiManager.h | 4 ++ 2 files changed, 80 insertions(+), 58 deletions(-) diff --git a/WiFiManager.cpp b/WiFiManager.cpp index d896650..5e6fdc5 100644 --- a/WiFiManager.cpp +++ b/WiFiManager.cpp @@ -514,6 +514,29 @@ void WiFiManager::handleWifi(boolean scan) { page += FPSTR(HTTP_HEAD_END); if (scan) { + page += getScanItemOut(); + } + + String pitem = FPSTR(HTTP_FORM_START); + pitem.replace("{v}", WiFi.SSID()); + page += pitem; + + page += getStaticOut(); + page += getParamOut(); + + page += FPSTR(HTTP_FORM_END); + page += FPSTR(HTTP_SCAN_LINK); + page += FPSTR(HTTP_END); + + server->sendHeader("Content-Length", String(page.length())); + server->send(200, "text/html", page); + + DEBUG_WM(F("Sent config page")); +} + +String WiFiManager::getScanItemOut(){ + String page; + int n = WiFi.scanNetworks(); DEBUG_WM(F("Scan done")); if (n == 0) { @@ -590,59 +613,11 @@ void WiFiManager::handleWifi(boolean scan) { } page += "
"; } - } - - String pitem = FPSTR(HTTP_FORM_START); - pitem.replace("{v}", WiFi.SSID()); - page += pitem; - - - if(_paramsCount > 0){ - - page += FPSTR(HTTP_FORM_PARAM_START); - - char parLength[5]; - // add the extra parameters to the form - for (int i = 0; i < _paramsCount; i++) { - if (_params[i] == NULL) { - break; - } - - String pitem; - switch (_params[i]->getLabelPlacement()) { - case WFM_LABEL_BEFORE: - pitem = FPSTR(HTTP_FORM_LABEL); - pitem += FPSTR(HTTP_FORM_PARAM); - break; - case WFM_LABEL_AFTER: - pitem = FPSTR(HTTP_FORM_PARAM); - pitem += FPSTR(HTTP_FORM_LABEL); - break; - default: - // WFM_NO_LABEL - pitem = FPSTR(HTTP_FORM_PARAM); - break; - } - if (_params[i]->getID() != NULL) { - pitem.replace("{i}", _params[i]->getID()); - pitem.replace("{n}", _params[i]->getID()); - pitem.replace("{p}", "{t}"); - pitem.replace("{t}", _params[i]->getPlaceholder()); - snprintf(parLength, 5, "%d", _params[i]->getValueLength()); - pitem.replace("{l}", parLength); - pitem.replace("{v}", _params[i]->getValue()); - pitem.replace("{c}", _params[i]->getCustomHTML()); // additional attributes not html - } else { - pitem = _params[i]->getCustomHTML(); - } - - page += pitem; - } - if (_params[0] != NULL) { - page += FPSTR(HTTP_FORM_PARAM_END); - } - } + return page; +} +String WiFiManager::getStaticOut(){ + String page; if (_sta_show_static_fields || _sta_static_ip) { // @todo how do we get these settings from memory , wifi_get_ip_info does not seem to reveal if struct ip_info is static or not @@ -690,15 +665,58 @@ void WiFiManager::handleWifi(boolean scan) { page += item; page += "
"; // @todo remove these, use css } + return page; +} - page += FPSTR(HTTP_FORM_END); - page += FPSTR(HTTP_SCAN_LINK); - page += FPSTR(HTTP_END); +String WiFiManager::getParamOut(){ + String page; - server->sendHeader("Content-Length", String(page.length())); - server->send(200, "text/html", page); + if(_paramsCount > 0){ - DEBUG_WM(F("Sent config page")); + page += FPSTR(HTTP_FORM_PARAM_START); + + char parLength[5]; + // add the extra parameters to the form + for (int i = 0; i < _paramsCount; i++) { + if (_params[i] == NULL) { + break; + } + + String pitem; + switch (_params[i]->getLabelPlacement()) { + case WFM_LABEL_BEFORE: + pitem = FPSTR(HTTP_FORM_LABEL); + pitem += FPSTR(HTTP_FORM_PARAM); + break; + case WFM_LABEL_AFTER: + pitem = FPSTR(HTTP_FORM_PARAM); + pitem += FPSTR(HTTP_FORM_LABEL); + break; + default: + // WFM_NO_LABEL + pitem = FPSTR(HTTP_FORM_PARAM); + break; + } + if (_params[i]->getID() != NULL) { + pitem.replace("{i}", _params[i]->getID()); + pitem.replace("{n}", _params[i]->getID()); + pitem.replace("{p}", "{t}"); + pitem.replace("{t}", _params[i]->getPlaceholder()); + snprintf(parLength, 5, "%d", _params[i]->getValueLength()); + pitem.replace("{l}", parLength); + pitem.replace("{v}", _params[i]->getValue()); + pitem.replace("{c}", _params[i]->getCustomHTML()); // additional attributes not html + } else { + pitem = _params[i]->getCustomHTML(); + } + + page += pitem; + } + if (_params[0] != NULL) { + page += FPSTR(HTTP_FORM_PARAM_END); + } + } + return page; } /** diff --git a/WiFiManager.h b/WiFiManager.h index 788a004..7535feb 100644 --- a/WiFiManager.h +++ b/WiFiManager.h @@ -216,6 +216,10 @@ class WiFiManager bool wifimode(WiFiMode_t m); bool wifioff(); + String getParamOut(); + String getScanItemOut(); + String getStaticOut(); + // DNS server const byte DNS_PORT = 53; From 31ab2b67d0120267ffc4c79bef118cb97bfb061d Mon Sep 17 00:00:00 2001 From: Shawn A Date: Thu, 14 Sep 2017 17:09:17 -0500 Subject: [PATCH 48/80] ability to disable captiveportal Sometimes the redirect to captive portal is unwanted or a nuisance, eg wehn using as a webserver etc. --- WiFiManager.cpp | 8 +++++++- WiFiManager.h | 3 +++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/WiFiManager.cpp b/WiFiManager.cpp index 5e6fdc5..7b9ddb4 100644 --- a/WiFiManager.cpp +++ b/WiFiManager.cpp @@ -536,7 +536,7 @@ void WiFiManager::handleWifi(boolean scan) { String WiFiManager::getScanItemOut(){ String page; - + int n = WiFi.scanNetworks(); DEBUG_WM(F("Scan done")); if (n == 0) { @@ -987,6 +987,9 @@ void WiFiManager::handleNotFound() { */ boolean WiFiManager::captivePortal() { DEBUG_WM(server->hostHeader()); + + if(!_enableCaptivePortal) return true; // skip redirections + if (!isIp(server->hostHeader())) { DEBUG_WM(F("Request redirected to captive portal")); server->sendHeader("Location", String("http://") + toStringIp(server->client().localIP()), true); @@ -1034,6 +1037,9 @@ void WiFiManager::setShowStaticFields(boolean alwaysShow){ _sta_show_static_fields = alwaysShow; } +void WiFiManager::setCaptivePortalEnable(boolean enabled){ + _enableCaptivePortal = enabled; +} // HELPERS diff --git a/WiFiManager.h b/WiFiManager.h index 7535feb..0be462f 100644 --- a/WiFiManager.h +++ b/WiFiManager.h @@ -148,6 +148,8 @@ class WiFiManager //if true, always show static net inputs, IP, subnet, gateway, if false, only show when already set. void setShowStaticFields(boolean alwaysShow); + void setCaptivePortalEnable(boolean enabled); + private: std::unique_ptr dnsServer; std::unique_ptr server; @@ -175,6 +177,7 @@ class WiFiManager unsigned long _configPortalStart = 0; boolean _userpersistent = true; WiFiMode_t _usermode = WIFI_OFF; + boolean _enableCaptivePortal = true; IPAddress _ap_static_ip; IPAddress _ap_static_gw; From 80470fb553011d72e1d27b3455b5099907193aa1 Mon Sep 17 00:00:00 2001 From: Shawn A Date: Sat, 16 Sep 2017 10:39:51 -0500 Subject: [PATCH 49/80] addd token precheck flags This helps skip token replacements that are not even used, replace is slow, this is an optimization mostly for large sets of params or aps. --- WiFiManager.cpp | 63 ++++++++++++++++++++++++++++++++----------------- WiFiManager.h | 5 ++-- 2 files changed, 45 insertions(+), 23 deletions(-) diff --git a/WiFiManager.cpp b/WiFiManager.cpp index 7b9ddb4..c4286bc 100644 --- a/WiFiManager.cpp +++ b/WiFiManager.cpp @@ -581,27 +581,36 @@ String WiFiManager::getScanItemOut(){ } } + // token precheck, to speed up replacements on large ap lists + String HTTP_ITEM_STR = FPSTR(HTTP_ITEM); + bool tok_r = HTTP_ITEM_STR.indexOf("{r}") > 0; + bool tok_R = HTTP_ITEM_STR.indexOf("{R}") > 0; + bool tok_e = HTTP_ITEM_STR.indexOf("{e}") > 0; + bool tok_q = HTTP_ITEM_STR.indexOf("{q}") > 0; + bool tok_i = HTTP_ITEM_STR.indexOf("{i}") > 0; + //display networks in page for (int i = 0; i < n; i++) { if (indices[i] == -1) continue; // skip dups - DEBUG_WM(WiFi.SSID(indices[i])); - DEBUG_WM(WiFi.RSSI(indices[i])); - int quality = getRSSIasQuality(WiFi.RSSI(indices[i])); + + DEBUG_WM("AP: " + (String)WiFi.RSSI(indices[i]) + " " + (String)WiFi.SSID(indices[i])); + + int rssiperc = getRSSIasQuality(WiFi.RSSI(indices[i])); uint8_t enc_type = WiFi.encryptionType(indices[i]); - if (_minimumQuality == -1 || _minimumQuality < quality) { + if (_minimumQuality == -1 || _minimumQuality < rssiperc) { String item = FPSTR(HTTP_ITEM); - String rssiQ; - rssiQ += quality; item.replace("{v}", WiFi.SSID(indices[i])); // ssid no encoding - item.replace("{R}", (String)WiFi.RSSI(indices[i])); // rssi - item.replace("{e}", encryptionTypeStr(enc_type)); - item.replace("{r}", rssiQ); // qualitypercent - item.replace("{q}", (String)round(map(quality,0,100,1,4))); //quality icon 1-4 - if (enc_type != ENC_TYPE_NONE) { - item.replace("{i}", "l"); - } else { - item.replace("{i}", ""); + if(tok_e) item.replace("{e}", encryptionTypeStr(enc_type)); + if(tok_r) item.replace("{r}", (String)rssiperc); // rssi percentage 0-100 + if(tok_R) item.replace("{R}", (String)WiFi.RSSI(indices[i])); // rssi db + if(tok_q) item.replace("{q}", (String)round(map(rssiperc,0,100,1,4))); //quality icon 1-4 + if(tok_i){ + if (enc_type != ENC_TYPE_NONE) { + item.replace("{i}", "l"); + } else { + item.replace("{i}", ""); + } } //DEBUG_WM(item); page += item; @@ -613,6 +622,7 @@ String WiFiManager::getScanItemOut(){ } page += "
"; } + return page; } @@ -673,6 +683,16 @@ String WiFiManager::getParamOut(){ if(_paramsCount > 0){ + String HTTP_PARAM_temp = FPSTR(HTTP_FORM_LABEL); + HTTP_PARAM_temp += FPSTR(HTTP_FORM_PARAM); + bool tok_i = HTTP_PARAM_temp.indexOf("{i}") > 0; + bool tok_n = HTTP_PARAM_temp.indexOf("{n}") > 0; + bool tok_p = HTTP_PARAM_temp.indexOf("{p}") > 0; + bool tok_t = HTTP_PARAM_temp.indexOf("{t}") > 0; + bool tok_l = HTTP_PARAM_temp.indexOf("{l}") > 0; + bool tok_v = HTTP_PARAM_temp.indexOf("{v}") > 0; + bool tok_c = HTTP_PARAM_temp.indexOf("{c}") > 0; + page += FPSTR(HTTP_FORM_PARAM_START); char parLength[5]; @@ -698,14 +718,14 @@ String WiFiManager::getParamOut(){ break; } if (_params[i]->getID() != NULL) { - pitem.replace("{i}", _params[i]->getID()); - pitem.replace("{n}", _params[i]->getID()); - pitem.replace("{p}", "{t}"); - pitem.replace("{t}", _params[i]->getPlaceholder()); + if(tok_i)pitem.replace("{i}", _params[i]->getID()); + if(tok_n)pitem.replace("{n}", _params[i]->getID()); + if(tok_p)pitem.replace("{p}", "{t}"); + if(tok_t)pitem.replace("{t}", _params[i]->getPlaceholder()); snprintf(parLength, 5, "%d", _params[i]->getValueLength()); - pitem.replace("{l}", parLength); - pitem.replace("{v}", _params[i]->getValue()); - pitem.replace("{c}", _params[i]->getCustomHTML()); // additional attributes not html + if(tok_l)pitem.replace("{l}", parLength); + if(tok_v)pitem.replace("{v}", _params[i]->getValue()); + if(tok_c)pitem.replace("{c}", _params[i]->getCustomHTML()); // meant for additional attributes, not html } else { pitem = _params[i]->getCustomHTML(); } @@ -716,6 +736,7 @@ String WiFiManager::getParamOut(){ page += FPSTR(HTTP_FORM_PARAM_END); } } + return page; } diff --git a/WiFiManager.h b/WiFiManager.h index 0be462f..95ce014 100644 --- a/WiFiManager.h +++ b/WiFiManager.h @@ -45,11 +45,12 @@ const char HTTP_STYLE[] PROGMEM = ""; const char HTTP_STYLE[] PROGMEM = ""; const char HTTP_STYLE[] PROGMEM = "