diff --git a/README.md b/README.md
index a329f2b..10da7f6 100644
--- a/README.md
+++ b/README.md
@@ -43,16 +43,19 @@ First attempt at a library. Lots more changes and fixes to do. Contributions are
 
## 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)
+- [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
diff --git a/WiFiManager.cpp b/WiFiManager.cpp
index e5998dc..61321aa 100644
--- a/WiFiManager.cpp
+++ b/WiFiManager.cpp
@@ -5,20 +5,22 @@
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
**************************************************************/
#include "WiFiManager.h"
+// PARAMETERS
+
WiFiManagerParameter::WiFiManagerParameter(const char *custom) {
- _id = NULL;
- _placeholder = NULL;
- _length = 0;
- _value = NULL;
+ _id = NULL;
+ _placeholder = NULL;
+ _length = 0;
+ _value = NULL;
_labelPlacement = WFM_LABEL_BEFORE;
- _customHTML = custom;
+ _customHTML = custom;
}
WiFiManagerParameter::WiFiManagerParameter(const char *id, const char *placeholder, const char *defaultValue, int length) {
@@ -34,16 +36,16 @@ WiFiManagerParameter::WiFiManagerParameter(const char *id, const char *placehold
}
void WiFiManagerParameter::init(const char *id, const char *placeholder, const char *defaultValue, int length, const char *custom, int labelPlacement) {
- _id = id;
- _placeholder = placeholder;
- _length = length;
+ _id = id;
+ _placeholder = placeholder;
+ _length = length;
_labelPlacement = labelPlacement;
- _value = new char[length + 1];
+ _value = new char[length + 1];
for (int i = 0; i < length; i++) {
_value[i] = 0;
}
if (defaultValue != NULL) {
- strncpy(_value, defaultValue, length);
+ strncpy(_value, defaultValue, length+1); // length+1 due to null terminated string
}
_customHTML = custom;
@@ -68,17 +70,14 @@ const char* WiFiManagerParameter::getCustomHTML() {
return _customHTML;
}
-WiFiManager::WiFiManager() {
-}
-
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++;
@@ -86,92 +85,147 @@ void WiFiManager::addParameter(WiFiManagerParameter *p) {
DEBUG_WM(p->getID());
}
-void WiFiManager::setupConfigPortal() {
- dnsServer.reset(new DNSServer());
- server.reset(new ESP8266WebServer(80));
+// constructors
+WiFiManager::WiFiManager(Stream& consolePort):_debugPort(consolePort) {
+ WiFiManager();
+}
+WiFiManager::WiFiManager():_debugPort(Serial) {
+ _usermode = WiFi.getMode();
+ WiFi.persistent(false); // disable persistent so scannetworks and mode switching do not cause overwrites
+}
+
+// destructor
+WiFiManager::~WiFiManager() {
+ if(_userpersistent) WiFi.persistent(true); // reenable persistent, there is no getter we rely on _userpersistent
+ WiFi.mode(_usermode);
+ DEBUG_WM(F("unloading"));
+}
+
+// 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(""));
- _configPortalStart = millis();
+ DEBUG_WM(F("AutoConnect"));
- 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);
+ // attempt to connect using saved settings, on fail fallback to AP config portal
+ WiFi_enableSTA(true);
+
+ // 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);
+}
- //optional soft ip config
+// CONFIG PORTAL
+bool WiFiManager::startAP(){
+ DEBUG_WM(F("Configuring access point with with SSID of ... "));
+ DEBUG_WM(_apName);
+
+ // 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);
}
- if (_apPassword != NULL) {
- WiFi.softAP(_apName, _apPassword);//password option
+ bool ret = true;
+
+ // start soft AP with password or anonymous
+ if (_apPassword != "") {
+ Serial.println((String)_apPassword.c_str());
+ ret = WiFi.softAP(_apName.c_str(), _apPassword.c_str());//password option
} else {
- WiFi.softAP(_apName);
+ DEBUG_WM(F("AP has anonymous access"));
+ ret = WiFi.softAP(_apName.c_str());
}
- delay(500); // Without delay I've seen the IP address blank
+ debugSoftAPConfig();
+
+ if(!ret) DEBUG_WM("There was an error starting the AP"); // @bug startAP returns unreliable success status
+
+ delay(500); // slight delay to make sure we get 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;
+}
+
+void WiFiManager::startWebPortal() {
+ if(configPortalActive || webPortalActive) return;
+ setupConfigPortal();
+ webPortalActive = true;
+}
+
+void WiFiManager::stopWebPortal() {
+ if(!configPortalActive && !webPortalActive) return;
+ DEBUG_WM(F("Stopping Web Portal"));
+ webPortalActive = false;
+ stopConfigPortal();
+}
+
+boolean WiFiManager::configPortalHasTimeout(){
+ if(_configPortalTimeout == 0 || wifi_softap_get_station_num() > 0){
+ if(millis() - timer > 30000){
+ timer = millis();
+ DEBUG_WM("NUM CLIENTS: " + (String)wifi_softap_get_station_num());
+ }
+ _configPortalStart = millis(); // kludge, bump configportal start time to skew timeouts
+ return false;
+ }
+
+ if(millis() > _configPortalStart + _configPortalTimeout){
+ DEBUG_WM(F("config portal has timed out"));
+ return true;
+ } else {
+ if((millis() - timer) > 1000){
+ timer = millis();
+ DEBUG_WM("Portal Timeout In " + (String)((_configPortalStart + _configPortalTimeout-millis())/1000) + " seconds");
+ }
+ }
+
+ return false;
+}
+
+void WiFiManager::setupConfigPortal() {
+
+ DEBUG_WM("Starting Web Portal");
+
+ // setup dns and web servers
+ dnsServer.reset(new DNSServer());
+ server.reset(new ESP8266WebServer(80));
+
/* Setup the DNS server redirecting all the domains to the apIP */
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));
server->on("/wifisave", std::bind(&WiFiManager::handleWifiSave, this));
server->on("/i", std::bind(&WiFiManager::handleInfo, this));
server->on("/r", std::bind(&WiFiManager::handleReset, this));
+ server->on("/exit", std::bind(&WiFiManager::handleExit, this));
+ server->on("/erase", std::bind(&WiFiManager::handleErase, 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(boolean showStaticIPFields) {
- String ssid = "ESP" + String(ESP.getChipId());
- return autoConnect(ssid.c_str(), NULL, showStaticIPFields);
-}
-
-boolean WiFiManager::autoConnect(char const *apName, char const *apPassword, boolean showStaticIPFields) {
- DEBUG_WM(F(""));
- DEBUG_WM(F("AutoConnect"));
-
- _sta_show_static_fields = showStaticIPFields;
-
- // read eeprom for ssid and pass
- //String ssid = getSSID();
- //String pass = getPassword();
-
- // attempt to connect; should it fail, fall back to AP
- WiFi.mode(WIFI_STA);
-
- if (connectWifi("", "") == WL_CONNECTED) {
- DEBUG_WM(F("IP Address:"));
- DEBUG_WM(WiFi.localIP());
- //connected
- 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() {
@@ -181,134 +235,232 @@ boolean WiFiManager::startConfigPortal() {
boolean WiFiManager::startConfigPortal(char const *apName, char const *apPassword) {
//setup AP
- WiFi.mode(WIFI_AP_STA);
- DEBUG_WM("SET AP STA");
+
+ bool disableSTA = false; // debug, always disable sta
- _apName = apName;
- _apPassword = apPassword;
-
- //notify we entered AP mode
- if ( _apcallback != NULL) {
- _apcallback(this);
+ // HANDLE issues with STA connections, shutdown sta if not connected, or else this will hang channel scanning and softap will not respond
+ // @todo sometimes still cannot connect to AP for no known reason, no events in log either
+ if(!WiFi.isConnected() || disableSTA){
+ // this fixes most ap problems, however, simply doing mode(WIFI_AP) does not work if sta connection is hanging, must `wifi_station_disconnect`
+ WiFi_Disconnect();
+ WiFi_enableSTA(false);
+ DEBUG_WM("Disabling STA");
+ }
+ else {
+ // @todo even if sta is connected, it is possible that softap connections will fail, IOS says "invalid password", windows says "cannot connect to this network" researching
+ WiFi_enableSTA(true);
}
- connect = false;
+ DEBUG_WM("Enabling AP");
+
+ _apName = apName; // @todo check valid apname ?
+ _apPassword = apPassword;
+ if(!validApPassword()) return false;
+
+ // init configportal globals to known states
+ configPortalActive = true;
+ bool result = connect = abort = false; // loop flags, connect true success, abort true break
+ uint8_t state;
+
+ DEBUG_WM(F("setupConfigPortal"));
+ _configPortalStart = millis();
+
+ // start access point
+ startAP();
+
+ // init configportal
setupConfigPortal();
+ if(!_configPortalIsBlocking){
+ DEBUG_WM(F("Config Portal Running, non blocking/processing"));
+ return result;
+ }
+
+ DEBUG_WM(F("Config Portal Running, blocking, waiting for clients..."));
+ // blocking loop waiting for config
while(1){
- // check if timeout
- if(configPortalHasTimeout()) break;
+ // if timed out or abort, break
+ if(configPortalHasTimeout() || abort){
+ DEBUG_WM(F("configportal abort"));
+ stopConfigPortal();
+ result = abort ? portalAbortResult : portalTimeoutResult; // false, false
+ break;
+ }
- //DNS
+ state = handleConfigPortal();
+
+ // status change, break
+ if(state != WL_IDLE_STATUS){
+ result = state == WL_CONNECTED; // true if connected
+ break;
+ }
+
+ yield(); // watchdog
+ }
+
+ DEBUG_WM("config portal exiting");
+ return result;
+}
+
+boolean WiFiManager::process(){
+ if(webPortalActive || (configPortalActive && !_configPortalIsBlocking)){
+ 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
dnsServer->processNextRequest();
- //HTTP
+ //HTTP handler
server->handleClient();
-
+ // Waiting for save...
if (connect) {
connect = false;
delay(2000);
- DEBUG_WM(F("Connecting to new AP"));
+ DEBUG_WM(F("Connecting save WiFi"));
- // using user-provided _ssid, _pass in place of system-stored ssid and 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;
+ // attempt sta connection to submitted _ssid, _pass
+ 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();
+ return WL_CONNECT_FAILED; // fail
+ }
+ else{
+ // sta off to stabilize AP on connect failure
+ WiFi_Disconnect();
+ WiFi_enableSTA(false);
+ DEBUG_WM("Disabling STA");
}
}
- yield();
+
+ return WL_IDLE_STATUS;
+}
+
+boolean WiFiManager::stopConfigPortal(){
+ if(webPortalActive) return false;
+
+ // do save callback, @todo move out of here
+ if ( _savecallback != NULL) {
+ //todo: confirm or verify data was saved
+ _savecallback();
}
+ //DNS handler
+ dnsServer->processNextRequest();
+ //HTTP handler
+ server->handleClient();
+
+ // @todo what is the proper way to shutdown and free the server up
+ server->stop();
server.reset();
dnsServer.reset();
- return WiFi.status() == WL_CONNECTED;
+ if(!configPortalActive) return false;
+
+ // turn off AP
+ // @todo bug *WM: disconnect configportal
+ // [APdisconnect] set_config failed!
+ // *WM: disconnect configportal - softAPdisconnect failed
+ DEBUG_WM(F("disconnect configportal"));
+ bool ret = WiFi.softAPdisconnect(false);
+ if(!ret)DEBUG_WM(F("disconnect configportal - softAPdisconnect failed"));
+ WiFi_Mode(_usermode); // restore users wifi mode
+ configPortalActive = false;
+ return ret;
}
-
+// @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..."));
+
+ bool waitforconx = true;
- // 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
- if (WiFi.status() == WL_CONNECTED) {
- DEBUG_WM("Already connected. Bailing out.");
- return WL_CONNECTED;
- }
- //check if we have ssid and pass and force those, if not, try with last saved values
- if (ssid != "") {
- WiFi.begin(ssid.c_str(), pass.c_str());
- } else {
- if (WiFi.SSID()) {
- DEBUG_WM("Using last saved values, should be faster");
- //trying to fix connection in progress hanging
- ETS_UART_INTR_DISABLE();
- wifi_station_disconnect();
- ETS_UART_INTR_ENABLE();
+ // make sure sta is on before `begin` so it does not call enablesta->mode while persistent is ON ( which would save WM AP state to eeprom !)
+ WiFi_Disconnect(); // disconnect before begin, in case anything is hung
+
+ // if ssid argument provided connect to that
+ if (ssid != "") {
+ DEBUG_WM("Connecting to new AP");
+ WiFi_enableSTA(true,storeSTAmode); // storeSTAmode will also toggle STA on in default opmode (persistent) if true (default)
+ WiFi.persistent(true);
+ WiFi.begin(ssid.c_str(), pass.c_str());
+ WiFi.persistent(false);
+ //@todo catch failures in set_config
+ } else {
+ // connect using saved ssid if there is one
+ if (WiFi.SSID() != "") {
+ DEBUG_WM("Connecting to saved AP");
+ WiFi_enableSTA(true,storeSTAmode);
WiFi.begin();
} else {
- DEBUG_WM("No saved credentials");
+ DEBUG_WM("No saved credentials, skipping wifi");
+ waitforconx = false;
}
}
- int connRes = waitForConnectResult();
+ uint8_t connRes = waitforconx ? waitForConnectResult() : WL_NO_SSID_AVAIL;
DEBUG_WM ("Connection result: ");
- DEBUG_WM ( connRes );
- //not connected, WPS enabled, no pass - first attempt
+ DEBUG_WM ( getWLStatusString(connRes) );
+
+ // 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;
}
+// @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) {
+ if (_connectTimeout == 0){
+ DEBUG_WM (F("connectTimeout not set, ESP waitForConnectResult..."));
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);
- }
- return status;
}
+
+ DEBUG_WM (F("connectTimeout set, waiting for connect...."));
+ uint8_t status;
+ int timeout = millis() + _connectTimeout;
+
+ 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;
+ }
+ DEBUG_WM (".");
+ delay(100);
+ }
+ return status;
}
void WiFiManager::startWPS() {
@@ -316,37 +468,22 @@ void WiFiManager::startWPS() {
WiFi.beginWPSConfig();
DEBUG_WM("END WPS");
}
-/*
- String WiFiManager::getSSID() {
- if (_ssid == "") {
- DEBUG_WM(F("Reading SSID"));
- _ssid = WiFi.SSID();
- DEBUG_WM(F("SSID: "));
- DEBUG_WM(_ssid);
- }
- return _ssid;
- }
- String WiFiManager::getPassword() {
- if (_pass == "") {
- DEBUG_WM(F("Reading Password"));
- _pass = WiFi.psk();
- DEBUG_WM("Password: " + _pass);
- //DEBUG_WM(_pass);
- }
- 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.persistent(true);
WiFi.disconnect(true);
+ WiFi.persistent(false);
//delay(200);
}
+
void WiFiManager::setTimeout(unsigned long seconds) {
setConfigPortalTimeout(seconds);
}
@@ -383,10 +520,13 @@ 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.
+ DEBUG_WM(F("<- HTTP Root"));
+
+ if (captivePortal()) { // If captive portal redirect instead of displaying the page.
return;
}
@@ -397,19 +537,27 @@ void WiFiManager::handleRoot() {
page += _customHeadElement;
page += FPSTR(HTTP_HEAD_END);
page += "
";
- page += _apName;
+ page += configPortalActive ? _apName : WiFi.localIP().toString();
page += " ";
page += F("WiFiManager ");
page += FPSTR(HTTP_PORTAL_OPTIONS);
+ reportStatus(page);
page += FPSTR(HTTP_END);
+
+ server->sendHeader("Content-Length", String(page.length()));
server->send(200, "text/html", page);
+ // server->close(); // testing reliability fix for content length mismatches during mutiple flood hits
}
-/** Wifi config page handler */
+/**
+ * HTTPD CALLBACK Wifi config page handler
+ */
void WiFiManager::handleWifi(boolean scan) {
+ DEBUG_WM("<- HTTP Wifi");
+
String page = FPSTR(HTTP_HEAD);
page.replace("{v}", "Config ESP");
page += FPSTR(HTTP_SCRIPT);
@@ -418,6 +566,33 @@ 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);
+ reportStatus(page);
+ page += FPSTR(HTTP_END);
+
+ server->sendHeader("Content-Length", String(page.length()));
+ server->send(200, "text/html", page);
+ // server->close(); // testing reliability fix for content length mismatches during mutiple flood hits
+
+ // Serial.println(page);
+
+ DEBUG_WM(F("Sent config page"));
+}
+
+String WiFiManager::getScanItemOut(){
+ String page;
+
int n = WiFi.scanNetworks();
DEBUG_WM(F("Scan done"));
if (n == 0) {
@@ -462,124 +637,176 @@ void WiFiManager::handleWifi(boolean scan) {
}
}
+ // 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]));
- if (_minimumQuality == -1 || _minimumQuality < quality) {
+ 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 < rssiperc) {
String item = FPSTR(HTTP_ITEM);
- String rssiQ;
- rssiQ += quality;
- item.replace("{v}", WiFi.SSID(indices[i]));
- item.replace("{r}", rssiQ);
- if (WiFi.encryptionType(indices[i]) != ENC_TYPE_NONE) {
- item.replace("{i}", "l");
- } else {
- item.replace("{i}", "");
+ item.replace("{v}", WiFi.SSID(indices[i])); // ssid no encoding
+ 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;
delay(0);
} else {
- DEBUG_WM(F("Skipping due to quality"));
+ DEBUG_WM(F("Skipping , does not meet _minimumQuality"));
}
}
page += " ";
}
- }
- page += FPSTR(HTTP_FORM_START);
- char parLength[2];
- // add the extra parameters to the form
- for (int i = 0; i < _paramsCount; i++) {
- if (_params[i] == NULL) {
- break;
- }
-
- //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, 2, "%d", _params[i]->getValueLength());
- pitem.replace("{l}", parLength);
- pitem.replace("{v}", _params[i]->getValue());
- pitem.replace("{c}", _params[i]->getCustomHTML());
- } else {
- pitem = _params[i]->getCustomHTML();
- }
-
- page += pitem;
- }
- if (_params[0] != NULL) {
- page += " ";
- }
-
- if (_sta_show_static_fields || _sta_static_ip) {
-
- String item = FPSTR(HTTP_FORM_PARAM);
- item.replace("{i}", "ip");
- item.replace("{n}", "ip");
- item.replace("{p}", "Static IP");
- item.replace("{l}", "15");
- item.replace("{v}", (_sta_static_ip ? _sta_static_ip.toString() : ""));
-
- page += item;
-
- item = FPSTR(HTTP_FORM_PARAM);
- item.replace("{i}", "gw");
- item.replace("{n}", "gw");
- item.replace("{p}", "Static Gateway");
- item.replace("{l}", "15");
- item.replace("{v}", (_sta_static_gw ? _sta_static_gw.toString() : ""));
-
- page += item;
-
- item = FPSTR(HTTP_FORM_PARAM);
- item.replace("{i}", "sn");
- item.replace("{n}", "sn");
- item.replace("{p}", "Subnet");
- item.replace("{l}", "15");
- item.replace("{v}", (_sta_static_sn ? _sta_static_sn.toString() : ""));
-
- page += item;
-
- page += " ";
- }
-
- page += FPSTR(HTTP_FORM_END);
- page += FPSTR(HTTP_SCAN_LINK);
-
- page += FPSTR(HTTP_END);
-
- server->send(200, "text/html", page);
-
-
- DEBUG_WM(F("Sent config page"));
+ return page;
}
-/** Handle the WLAN save form and redirect to WLAN config page again */
+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
+ // @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}", "{t}");
+ item.replace("{t}", "Static IP");
+ item.replace("{l}", "15");
+ item.replace("{v}", (_sta_static_ip ? _sta_static_ip.toString() : ""));
+ item.replace("{c}", "");
+
+ // IPAddress sta_ip = WiFi.localIP();
+ // item.replace("{p}", sta_ip.toString());
+
+ page += item;
+
+ item = FPSTR(HTTP_FORM_LABEL);
+ item += FPSTR(HTTP_FORM_PARAM);
+ item.replace("{i}", "gw");
+ item.replace("{n}", "gw");
+ item.replace("{p}", "{t}");
+ item.replace("{t}", "Static Gateway");
+ item.replace("{l}", "15");
+ item.replace("{v}", (_sta_static_gw ? _sta_static_gw.toString() : ""));
+ item.replace("{c}", "");
+
+ // IPAddress sta_gateway = WiFi.gatewayIP();
+ // item.replace("{p}", sta_gateway.toString());
+
+ page += item;
+
+ item = FPSTR(HTTP_FORM_LABEL);
+ item += FPSTR(HTTP_FORM_PARAM);
+ item.replace("{i}", "sn");
+ item.replace("{n}", "sn");
+ item.replace("{p}", "{t}");
+ item.replace("{t}", "Subnet");
+ item.replace("{l}", "15");
+ item.replace("{v}", (_sta_static_sn ? _sta_static_sn.toString() : ""));
+ item.replace("{c}", "");
+
+ // IPAddress sta_subnet = WiFi.subnetMask();
+ // item.replace("{p}", sta_subnet.toString());
+ page += item;
+ page += " "; // @todo remove these, use css
+ }
+ return page;
+}
+
+String WiFiManager::getParamOut(){
+ String page;
+
+ 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];
+ // add the extra parameters to the form
+ for (int i = 0; i < _paramsCount; i++) {
+ if (_params[i] == NULL) {
+ break;
+ }
+
+ // label before or after, this could probably be done via floats however
+ 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 no ID use customhtml for item, else generate from param string
+ if (_params[i]->getID() != NULL) {
+ 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());
+ 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();
+ }
+
+ page += pitem;
+ }
+ if (_params[0] != NULL) {
+ page += FPSTR(HTTP_FORM_PARAM_END);
+ }
+ }
+
+ return page;
+}
+
+/**
+ * HTTPD CALLBACK save form and redirect to WLAN config page again
+ */
void WiFiManager::handleWifiSave() {
- DEBUG_WM(F("WiFi save"));
+ DEBUG_WM(F("HTTP WiFi save"));
//SAVE/connect here
_ssid = server->arg("s").c_str();
@@ -593,7 +820,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);
@@ -628,6 +855,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"));
@@ -635,9 +863,11 @@ 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"));
+ DEBUG_WM(F("HTTP Info"));
String page = FPSTR(HTTP_HEAD);
page.replace("{v}", "Info");
@@ -645,66 +875,257 @@ void WiFiManager::handleInfo() {
page += FPSTR(HTTP_STYLE);
page += _customHeadElement;
page += FPSTR(HTTP_HEAD_END);
- page += F("");
+
+ reportStatus(page);
+
+ // @todo add versioning here
+
+ page += F("esp8266 ");
+
+ // subject to rollover!
+ 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(" ");
+
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("BSSID ");
+ page += WiFi.BSSIDstr();
+ page += F(" ");
+
+ page += F("Station IP ");
+ page += WiFi.localIP().toString();
+ page += F(" ");
+
+ page += F("Station Gateway ");
+ page += WiFi.gatewayIP().toString();
+ page += F(" ");
+
+ page += F("Station Subnet ");
+ page += WiFi.subnetMask().toString();
+ page += F(" ");
+
+ page += F("DNS Server ");
+ page += WiFi.dnsIP().toString();
+ page += F(" ");
+
+ page += F("Hostname ");
+ page += WiFi.hostname();
+ page += F(" ");
+
page += F("Station MAC ");
page += WiFi.macAddress();
page += F(" ");
+
+ page += F("Connected ");
+ page += WiFi.isConnected() ? "Yes" : "No";
+ page += F(" ");
+
+ page += F("Autoconnect ");
+ page += WiFi.getAutoConnect() ? "Enabled" : "Disabled";
+ page += F(" ");
page += F(" ");
+
+ page += " ";
+
+ page += F("Available Pages ");
+ page += F("");
+ page += F("Page Function ");
+ page += F("/ ");
+ page += F("Menu page. ");
+ page += F("/wifi ");
+ page += F("Show WiFi scan results and enter WiFi configuration. ");
+ page += F("/wifisave ");
+ page += F("Save WiFi configuration information and configure device. Needs variables supplied. ");
+ page += F("/close ");
+ page += F("Close the configuration server and configuration WiFi network. ");
+ page += F("/i ");
+ page += F("Information page ");
+ page += F("/r ");
+ page += F("Delete WiFi configuration and reboot. ESP device will not reconnect to a network until new WiFi configuration data is entered. ");
+ // @todo add callback here
+ page += F("
");
+ page += F("
More information about WiFiManager at https://github.com/tzapu/WiFiManager .");
page += FPSTR(HTTP_END);
+ server->sendHeader("Content-Length", String(page.length()));
server->send(200, "text/html", page);
DEBUG_WM(F("Sent info page"));
}
-/** Handle the reset page */
-void WiFiManager::handleReset() {
- DEBUG_WM(F("Reset"));
+
+/**
+ * HTTPD CALLBACK root or redirect to captive portal
+ */
+void WiFiManager::handleExit() {
+ DEBUG_WM(F("<- HTTP Exit"));
String page = FPSTR(HTTP_HEAD);
- page.replace("{v}", "Info");
+ page.replace("{v}", "Exit");
+ page += FPSTR(HTTP_SCRIPT);
+ page += FPSTR(HTTP_STYLE);
+ page += _customHeadElement;
+ page += FPSTR(HTTP_HEAD_END);
+
+ page += "Exiting";
+ server->sendHeader("Content-Length", String(page.length()));
+ server->send(200, "text/html", page);
+ abort = true;
+}
+
+void WiFiManager::reportStatus(String &page){
+
+ if (WiFi.SSID() != ""){
+ if (WiFi.status()==WL_CONNECTED){
+ page += "";
+ page += F("
Connected to ");
+ page += WiFi.SSID();
+ page += F("
with IP ");
+ page += WiFi.localIP().toString();
+ page += F(" ");
+ } else {
+ page += "
";
+ page += "
Not Connected to ";
+ page += WiFi.SSID();
+ }
+ } else {
+ page += "
";
+ page += "No AP set";
+ }
+ page += "
";
+}
+
+/**
+ * HTTPD CALLBACK reset page
+ */
+void WiFiManager::handleReset() {
+ DEBUG_WM(F("<- HTTP Reset"));
+
+ String page = FPSTR(HTTP_HEAD);
+ page.replace("{v}", "Reset");
page += FPSTR(HTTP_SCRIPT);
page += FPSTR(HTTP_STYLE);
page += _customHeadElement;
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"));
- delay(5000);
+ DEBUG_WM(F("RESETTING ESP"));
+ delay(1000);
ESP.reset();
- delay(2000);
}
+/**
+ * HTTPD CALLBACK erase page
+ */
+void WiFiManager::handleErase() {
+ DEBUG_WM(F("<- HTTP Erase"));
+ String page = FPSTR(HTTP_HEAD);
+ page.replace("{v}", "Erase");
+ page += FPSTR(HTTP_SCRIPT);
+ page += FPSTR(HTTP_STYLE);
+ page += _customHeadElement;
+ page += FPSTR(HTTP_HEAD_END);
-//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", "");
-}*/
+ bool ret = ESP_eraseConfig();
+ if(!ret) DEBUG_WM(F("ERASE FAILED"));
+
+ // bool ret = reset = true;
+
+ if(ret) page += F("Module will reset in a few seconds.");
+ else {
+ page += F("An Error Occured");
+ DEBUG_WM(F("WiFi.eraseConfig reported an error"));
+ }
+
+ page += FPSTR(HTTP_END);
+ server->sendHeader("Content-Length", String(page.length()));
+ server->send(200, "text/html", page);
+ delay(2000);
+
+ if(ret){
+ DEBUG_WM(F("RESETTING ESP"));
+ ESP.reset();
+ }
+}
void WiFiManager::handleNotFound() {
if (captivePortal()) { // If captive portal redirect instead of displaying the error page.
@@ -725,14 +1146,22 @@ 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 );
}
-
-/** 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"));
+ DEBUG_WM("-> " + server->hostHeader());
+
+ if(!_enableCaptivePortal) return false; // skip redirections
+
+ 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.
server->client().stop(); // Stop is needed because we sent no content length
@@ -741,6 +1170,8 @@ boolean WiFiManager::captivePortal() {
return false;
}
+// MORE SETTERS
+
//start up config portal callback
void WiFiManager::setAPCallback( void (*func)(WiFiManager* myWiFiManager) ) {
_apcallback = func;
@@ -761,16 +1192,87 @@ void WiFiManager::setRemoveDuplicateAPs(boolean removeDuplicates) {
_removeDuplicateAPs = removeDuplicates;
}
+//if this is true, remove duplicated Access Points - defaut true
+void WiFiManager::setConfigPortalBlocking(boolean shoudlBlock) {
+ _configPortalIsBlocking = shoudlBlock;
+}
+//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"));
+}
+
+void WiFiManager::setShowStaticFields(boolean alwaysShow){
+ _sta_show_static_fields = alwaysShow;
+}
+
+void WiFiManager::setCaptivePortalEnable(boolean enabled){
+ _enableCaptivePortal = enabled;
+}
+
+// HELPERS
template
void WiFiManager::DEBUG_WM(Generic text) {
if (_debug) {
- Serial.print("*WM: ");
- Serial.println(text);
+ _debugPort.print("*WM: ");
+ _debugPort.print(text);
+ _debugPort.print("\n");
}
}
+template
+void WiFiManager::DEBUG_WM(Generic text,Genericb textb) {
+ if (_debug) {
+ _debugPort.print("*WM: ");
+ _debugPort.print(text);
+ _debugPort.print(" ");
+ _debugPort.print(textb);
+ _debugPort.print("\n");
+ }
+}
+
+/*
+ String WiFiManager::getSSID() {
+ if (_ssid == "") {
+ DEBUG_WM(F("Reading SSID"));
+ _ssid = WiFi.SSID();
+ DEBUG_WM(F("SSID: "));
+ DEBUG_WM(_ssid);
+ }
+ return _ssid;
+ }
+
+ String WiFiManager::getPassword() {
+ if (_pass == "") {
+ DEBUG_WM(F("Reading Password"));
+ _pass = WiFi.psk();
+ DEBUG_WM("Password: " + _pass);
+ //DEBUG_WM(_pass);
+ }
+ return _pass;
+ }
+*/
+
+void WiFiManager::debugSoftAPConfig(){
+ softap_config config;
+ wifi_softap_get_config(&config);
+
+ DEBUG_WM(F("SoftAP Configuration"));
+ DEBUG_WM(F("--------------------"));
+ DEBUG_WM(F("ssid: "),(char *) config.ssid);
+ DEBUG_WM(F("password: "),(char *) config.password);
+ DEBUG_WM(F("ssid_len: "),config.ssid_len);
+ DEBUG_WM(F("channel: "),config.channel);
+ DEBUG_WM(F("authmode: "),config.authmode);
+ DEBUG_WM(F("ssid_hidden: "),config.ssid_hidden);
+ DEBUG_WM(F("max_connection: "),config.max_connection);
+ DEBUG_WM(F("beacon_interval: "),(String)config.beacon_interval + "(ms)");
+ DEBUG_WM(F("--------------------"));
+}
+
+
int WiFiManager::getRSSIasQuality(int RSSI) {
int quality = 0;
@@ -804,3 +1306,117 @@ String WiFiManager::toStringIp(IPAddress ip) {
res += String(((ip >> 8 * 3)) & 0xFF);
return res;
}
+
+boolean WiFiManager::validApPassword(){
+ // check that ap password is valid, return false
+ if (_apPassword == NULL) _apPassword = "";
+ if (_apPassword != "") {
+ if (_apPassword.length() < 8 || _apPassword.length() > 63) {
+ DEBUG_WM(F("AccessPoint set password is INVALID"));
+ _apPassword = "";
+ return false; // @todo FATAL or fallback to empty ?
+ }
+ DEBUG_WM(F("AccessPoint set password is VALID"));
+ DEBUG_WM(_apPassword);
+ }
+ return true;
+}
+
+String WiFiManager::getWLStatusString(uint8_t status){
+ const char * const WIFI_STA_STATUS[]
+ {
+ "WL_IDLE_STATUS",
+ "WL_NO_SSID_AVAIL",
+ "WL_SCAN_COMPLETED",
+ "WL_CONNECTED",
+ "WL_CONNECT_FAILED",
+ "WL_CONNECTION_LOST",
+ "WL_DISCONNECTED"
+ };
+
+ if(status >=0 && status <=6) return WIFI_STA_STATUS[status];
+ return "UNKNOWN";
+}
+
+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 ignores WiFi.persistent
+bool WiFiManager::WiFi_Mode(WiFiMode_t m,bool persistent) {
+ if((wifi_get_opmode() == (uint8) m ) && !persistent) {
+ return true;
+ }
+ bool ret;
+ ETS_UART_INTR_DISABLE();
+ if(persistent) ret = wifi_set_opmode(m);
+ else ret = wifi_set_opmode_current(m);
+ ETS_UART_INTR_ENABLE();
+
+ return ret;
+}
+bool WiFiManager::WiFi_Mode(WiFiMode_t m) {
+ return WiFi_Mode(m,false);
+}
+
+// sta disconnect without persistent
+bool WiFiManager::WiFi_Disconnect() {
+ if((WiFi.getMode() & WIFI_STA) != 0) {
+ bool ret;
+ DEBUG_WM(F("wifi station disconnect"));
+ ETS_UART_INTR_DISABLE();
+ ret = wifi_station_disconnect();
+ ETS_UART_INTR_ENABLE();
+ return ret;
+ }
+}
+
+// toggle STA without persistent
+bool WiFiManager::WiFi_enableSTA(bool enable,bool persistent) {
+
+ WiFiMode_t currentMode = WiFi.getMode();
+ bool isEnabled = ((currentMode & WIFI_STA) != 0);
+
+ if((isEnabled != enable) || persistent) {
+ if(enable) {
+ if(persistent) DEBUG_WM(F("enableSTA PERSISTENT ON"));
+ return WiFi_Mode((WiFiMode_t)(currentMode | WIFI_STA),persistent);
+ } else {
+ return WiFi_Mode((WiFiMode_t)(currentMode & (~WIFI_STA)),persistent);
+ }
+ } else {
+ return true;
+ }
+}
+bool WiFiManager::WiFi_enableSTA(bool enable) {
+ return WiFi_enableSTA(enable,false);
+}
+
+// erase config BUG polyfill
+// https://github.com/esp8266/Arduino/pull/3635
+bool WiFiManager::ESP_eraseConfig(void) {
+ // return ESP.eraseConfig();
+
+ const size_t cfgSize = 0x4000;
+ size_t cfgAddr = ESP.getFlashChipSize() - cfgSize;
+
+ for (size_t offset = 0; offset < cfgSize; offset += SPI_FLASH_SEC_SIZE) {
+ if (!ESP.flashEraseSector((cfgAddr + offset) / SPI_FLASH_SEC_SIZE)) {
+ return false;
+ }
+ }
+ return true;
+}
\ No newline at end of file
diff --git a/WiFiManager.h b/WiFiManager.h
index 00e8bd3..826be09 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
**************************************************************/
@@ -22,21 +22,43 @@ 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_ITEM[] PROGMEM = "
";
-const char HTTP_FORM_START[] 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_HEAD_END[] PROGMEM = "
";
+const char HTTP_PORTAL_OPTIONS[] PROGMEM = "
";
+const char HTTP_ITEM[] PROGMEM = "
";
+// const char HTTP_ITEM[] PROGMEM = "
"; // test all tokens
+//const char HTTP_ITEM_PADLOCK[] PROGMEM = "
";
+const char HTTP_FORM_START[] PROGMEM = "
";
+const char HTTP_FORM_LABEL[] PROGMEM = "
{t} ";
+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 = "
";
-#define WIFI_MANAGER_MAX_PARAMS 20
+#define WIFI_MANAGER_MAX_PARAMS 10
#define WFM_LABEL_BEFORE 1
#define WFM_LABEL_AFTER 2
@@ -73,30 +95,34 @@ class WiFiManagerParameter {
class WiFiManager
{
public:
+ WiFiManager(Stream& consolePort);
WiFiManager();
+ ~WiFiManager();
- boolean autoConnect(boolean showStaticIPFields = false);
- boolean autoConnect(char const *apName, char const *apPassword = NULL, boolean showStaticIPFields = false);
+ boolean autoConnect();
+ boolean autoConnect(char const *apName, char const *apPassword = NULL);
//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();
+ void startWebPortal();
+ void stopWebPortal();
// get the AP name of the config portal, so it can be used in the callback
String getConfigPortalSSID();
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
+ //sets timeout before AP,webserver loop ends and exits even if there has been no setup.
+ //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);
+ void setTimeout(unsigned long seconds); // @deprecated, alias
- //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);
-
void setDebugOutput(boolean debug);
//defaults to not showing anything under 8% signal quality if called
void setMinimumSignalQuality(int quality = 8);
@@ -110,14 +136,22 @@ 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
+ //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);
+
+
//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);
+ //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);
+ //if true disable captive portal redirection
+ void setCaptivePortalEnable(boolean enabled);
private:
std::unique_ptr
dnsServer;
@@ -130,14 +164,23 @@ class WiFiManager
void setupConfigPortal();
void startWPS();
+ bool startAP();
- const char* _apName = "no-net";
- const char* _apPassword = NULL;
+ // 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 = "";
String _pass = "";
unsigned long _configPortalTimeout = 0;
unsigned long _connectTimeout = 0;
unsigned long _configPortalStart = 0;
+ boolean _userpersistent = true;
+ WiFiMode_t _usermode = WIFI_OFF;
+ boolean _enableCaptivePortal = true;
IPAddress _ap_static_ip;
IPAddress _ap_static_gw;
@@ -147,11 +190,13 @@ class WiFiManager
IPAddress _sta_static_sn;
boolean _sta_show_static_fields = false;
+ // option parameters
int _paramsCount = 0;
int _minimumQuality = -1;
boolean _removeDuplicateAPs = true;
boolean _shouldBreakAfterConfig = false;
boolean _tryWPS = false;
+ boolean _configPortalIsBlocking = true;
const char* _customHeadElement = "";
@@ -161,6 +206,8 @@ class WiFiManager
int status = WL_IDLE_STATUS;
int connectWifi(String ssid, String pass);
uint8_t waitForConnectResult();
+ uint8_t waitForConnectResult(uint16_t timeout);
+ String getWLStatusString(uint8_t status);
void handleRoot();
void handleWifi(boolean scan);
@@ -168,9 +215,24 @@ class WiFiManager
void handleInfo();
void handleReset();
void handleNotFound();
- void handle204();
+ void handleExit();
+ void handleErase();
+
boolean captivePortal();
boolean configPortalHasTimeout();
+ boolean stopConfigPortal();
+ uint8_t handleConfigPortal();
+ bool WiFi_Mode(WiFiMode_t m);
+ bool WiFi_Mode(WiFiMode_t m,bool persistent);
+ bool WiFi_Disconnect();
+ bool WiFi_enableSTA(bool enable);
+ bool WiFi_enableSTA(bool enable,bool persistent);
+ bool ESP_eraseConfig();
+ void debugSoftAPConfig();
+
+ String getParamOut();
+ String getScanItemOut();
+ String getStaticOut();
// DNS server
const byte DNS_PORT = 53;
@@ -179,17 +241,31 @@ class WiFiManager
int getRSSIasQuality(int RSSI);
boolean isIp(String str);
String toStringIp(IPAddress ip);
+ boolean validApPassword();
+ String encryptionTypeStr(uint8_t authmode);
+ void reportStatus(String &page);
boolean connect;
- boolean _debug = true;
+ boolean abort;
+ boolean reset = false;
+ boolean configPortalActive = false;
+ boolean webPortalActive = false;
+ boolean _debug = true;
+ boolean portalTimeoutResult = false;
+ boolean portalAbortResult = false;
+ boolean storeSTAmode = true; // option store persistent STA mode in connectwifi
+ int timer = 0;
void (*_apcallback)(WiFiManager*) = NULL;
void (*_savecallback)(void) = NULL;
+ Stream& _debugPort; // debug output stream ref
WiFiManagerParameter* _params[WIFI_MANAGER_MAX_PARAMS];
template
void DEBUG_WM(Generic text);
+ template
+ void DEBUG_WM(Generic text,Genericb textb);
template
auto optionalIPFromString(T *obj, const char *s) -> decltype( obj->fromString(s) ) {
diff --git a/examples/AutoConnectWithFSParameters/AutoConnectWithFSParameters.ino b/examples/AutoConnectWithFSParameters/AutoConnectWithFSParameters.ino
index 1d58e5c..1891808 100644
--- a/examples/AutoConnectWithFSParameters/AutoConnectWithFSParameters.ino
+++ b/examples/AutoConnectWithFSParameters/AutoConnectWithFSParameters.ino
@@ -102,7 +102,7 @@ void setup() {
//sets timeout until configuration portal gets turned off
//useful to make it all retry or go to sleep
//in seconds
- //wifiManager.setTimeout(120);
+ //wifiManager.setConfigPortalTimeout(120);
//fetches ssid and pass and tries to connect
//if it does not connect it starts an access point with the specified name
diff --git a/examples/AutoConnectWithFSParametersAndCustomIP/AutoConnectWithFSParametersAndCustomIP.ino b/examples/AutoConnectWithFSParametersAndCustomIP/AutoConnectWithFSParametersAndCustomIP.ino
index 3f391ae..135b99f 100644
--- a/examples/AutoConnectWithFSParametersAndCustomIP/AutoConnectWithFSParametersAndCustomIP.ino
+++ b/examples/AutoConnectWithFSParametersAndCustomIP/AutoConnectWithFSParametersAndCustomIP.ino
@@ -129,7 +129,7 @@ void setup() {
//sets timeout until configuration portal gets turned off
//useful to make it all retry or go to sleep
//in seconds
- //wifiManager.setTimeout(120);
+ //wifiManager.setConfigPortalTimeout(120);
//fetches ssid and pass and tries to connect
//if it does not connect it starts an access point with the specified name
diff --git a/examples/AutoConnectWithTimeout/AutoConnectWithTimeout.ino b/examples/AutoConnectWithTimeout/AutoConnectWithTimeout.ino
index c90cf10..da77cbe 100644
--- a/examples/AutoConnectWithTimeout/AutoConnectWithTimeout.ino
+++ b/examples/AutoConnectWithTimeout/AutoConnectWithTimeout.ino
@@ -20,7 +20,7 @@ void setup() {
//sets timeout until configuration portal gets turned off
//useful to make it all retry or go to sleep
//in seconds
- wifiManager.setTimeout(180);
+ wifiManager.setConfigPortalTimeout(180);
//fetches ssid and pass and tries to connect
//if it does not connect it starts an access point with the specified name
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
diff --git a/examples/OnDemandConfigPortal/OnDemandConfigPortal.ino b/examples/OnDemandConfigPortal/OnDemandConfigPortal.ino
index edf4f46..1292c35 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
@@ -33,7 +33,7 @@ void loop() {
//sets timeout until configuration portal gets turned off
//useful to make it all retry or go to sleep
//in seconds
- //wifiManager.setTimeout(120);
+ //wifiManager.setConfigPortalTimeout(120);
//it starts an access point with the specified name
//here "AutoConnectAP"
@@ -42,6 +42,9 @@ void loop() {
//WITHOUT THIS THE AP DOES NOT SEEM TO WORK PROPERLY WITH SDK 1.5 , update to at least 1.5.1
//WiFi.mode(WIFI_STA);
+ // disable captive portal redirection
+ // wifiManager.setCaptivePortalEnable(false);
+
if (!wifiManager.startConfigPortal("OnDemandAP")) {
Serial.println("failed to connect and hit timeout");
delay(3000);
diff --git a/examples/OnDemandWebPortal/OnDemandWebPortal.ino b/examples/OnDemandWebPortal/OnDemandWebPortal.ino
new file mode 100644
index 0000000..50f5657
--- /dev/null
+++ b/examples/OnDemandWebPortal/OnDemandWebPortal.ino
@@ -0,0 +1,57 @@
+/**
+ * OnDemandWebPortal.ino
+ * example of running the webportal manually, independantly from the captiveportal
+ * trigger pin will start a webportal for 120 seconds then turn it off.
+ *
+ */
+
+#include //https://github.com/esp8266/Arduino
+
+//needed for library
+#include
+#include
+#include //https://github.com/tzapu/WiFiManager
+
+// 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
+
+// set your ap credentials
+const char *ssid = "********";
+const char *password = "********";
+
+int timeout = 120; // seconds
+
+WiFiManager wifiManager;
+
+bool webportalrunning = false;
+int start = millis();
+
+void setup() {
+ // put your setup code here, to run once:
+ Serial.begin(115200);
+ Serial.println("\n Starting");
+ pinMode(TRIGGER_PIN, INPUT_PULLUP);
+}
+
+
+void loop() {
+
+ if(webportalrunning){
+ wifiManager.process();
+ if(millis()-start > timeout*1000){
+ Serial.println("webportaltimeout");
+ webportalrunning = false;
+ wifiManager.stopWebPortal(); // auto off
+ }
+ }
+
+ // is configuration portal requested?
+ if ( digitalRead(TRIGGER_PIN) == LOW && !webportalrunning) {
+ wifiManager.startWebPortal();
+ webportalrunning = true;
+ start = millis();
+ }
+ // put your main code here, to run repeatedly:
+}
diff --git a/extras/WiFiManager.template.html b/extras/WiFiManager.template.html
index e15593b..09ae94b 100644
--- a/extras/WiFiManager.template.html
+++ b/extras/WiFiManager.template.html
@@ -6,15 +6,132 @@
{v}
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+