Эх сурвалжийг харах

changed WiFiManager to work with Async Server

Alan Steremberg 8 жил өмнө
commit
fa18623ccc

+ 804 - 0
ESPAsyncWiFiManager.cpp

@@ -0,0 +1,804 @@
+/**************************************************************
+   AsyncWiFiManager is a library for the ESP8266/Arduino platform
+   (https://github.com/esp8266/Arduino) to enable easy
+   configuration and reconfiguration of WiFi credentials using a Captive Portal
+   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
+   Built by AlexT https://github.com/tzapu
+   Licensed under MIT license
+ **************************************************************/
+
+#include "ESPAsyncWiFiManager.h"
+
+AsyncWiFiManagerParameter::AsyncWiFiManagerParameter(const char *custom) {
+  _id = NULL;
+  _placeholder = NULL;
+  _length = 0;
+  _value = NULL;
+
+  _customHTML = custom;
+}
+
+AsyncWiFiManagerParameter::AsyncWiFiManagerParameter(const char *id, const char *placeholder, const char *defaultValue, int length) {
+  init(id, placeholder, defaultValue, length, "");
+}
+
+AsyncWiFiManagerParameter::AsyncWiFiManagerParameter(const char *id, const char *placeholder, const char *defaultValue, int length, const char *custom) {
+  init(id, placeholder, defaultValue, length, custom);
+}
+
+void AsyncWiFiManagerParameter::init(const char *id, const char *placeholder, const char *defaultValue, int length, const char *custom) {
+  _id = id;
+  _placeholder = placeholder;
+  _length = length;
+  _value = new char[length + 1];
+  for (int i = 0; i < length; i++) {
+    _value[i] = 0;
+  }
+  if (defaultValue != NULL) {
+    strncpy(_value, defaultValue, length);
+  }
+
+  _customHTML = custom;
+}
+
+const char* AsyncWiFiManagerParameter::getValue() {
+  return _value;
+}
+const char* AsyncWiFiManagerParameter::getID() {
+  return _id;
+}
+const char* AsyncWiFiManagerParameter::getPlaceholder() {
+  return _placeholder;
+}
+int AsyncWiFiManagerParameter::getValueLength() {
+  return _length;
+}
+const char* AsyncWiFiManagerParameter::getCustomHTML() {
+  return _customHTML;
+}
+
+AsyncWiFiManager::AsyncWiFiManager(AsyncWebServer *server, DNSServer *dns) :server(server), dnsServer(dns) {
+  wifiSSIDs = NULL;
+wifiSSIDscan=true;
+}
+
+void AsyncWiFiManager::addParameter(AsyncWiFiManagerParameter *p) {
+  _params[_paramsCount] = p;
+  _paramsCount++;
+  DEBUG_WM("Adding parameter");
+  DEBUG_WM(p->getID());
+}
+
+void AsyncWiFiManager::setupConfigPortal() {
+ // dnsServer.reset(new DNSServer());
+ // server.reset(new ESP8266WebServer(80));
+
+  DEBUG_WM(F(""));
+  _configPortalStart = millis();
+
+  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
+  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
+  } else {
+    WiFi.softAP(_apName);
+  }
+
+  delay(500); // Without delay I've seen the IP address blank
+  DEBUG_WM(F("AP IP address: "));
+  DEBUG_WM(WiFi.softAPIP());
+
+  /* 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. */
+  server->on("/", std::bind(&AsyncWiFiManager::handleRoot, this,std::placeholders::_1)).setFilter(ON_AP_FILTER);
+  server->on("/wifi", std::bind(&AsyncWiFiManager::handleWifi, this, std::placeholders::_1,true)).setFilter(ON_AP_FILTER);
+  server->on("/0wifi", std::bind(&AsyncWiFiManager::handleWifi, this,std::placeholders::_1, false)).setFilter(ON_AP_FILTER);
+  server->on("/wifisave", std::bind(&AsyncWiFiManager::handleWifiSave,this,std::placeholders::_1)).setFilter(ON_AP_FILTER);
+  server->on("/i", std::bind(&AsyncWiFiManager::handleInfo,this, std::placeholders::_1)).setFilter(ON_AP_FILTER);
+  server->on("/r", std::bind(&AsyncWiFiManager::handleReset, this,std::placeholders::_1)).setFilter(ON_AP_FILTER);
+  //server->on("/generate_204", std::bind(&AsyncWiFiManager::handle204, this));  //Android/Chrome OS captive portal check.
+  server->on("/fwlink", std::bind(&AsyncWiFiManager::handleRoot, this,std::placeholders::_1)).setFilter(ON_AP_FILTER);  //Microsoft captive portal. Maybe not needed. Might be handled by notFound handler.
+  server->onNotFound (std::bind(&AsyncWiFiManager::handleNotFound,this,std::placeholders::_1));
+  server->begin(); // Web server start
+  DEBUG_WM(F("HTTP server started"));
+
+}
+
+boolean AsyncWiFiManager::autoConnect() {
+  String ssid = "ESP" + String(ESP.getChipId());
+  return autoConnect(ssid.c_str(), NULL);
+}
+
+boolean AsyncWiFiManager::autoConnect(char const *apName, char const *apPassword) {
+  DEBUG_WM(F(""));
+  DEBUG_WM(F("AutoConnect"));
+
+  // 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);
+}
+
+void AsyncWiFiManager::scan()
+{
+
+if (wifiSSIDscan)
+{
+  delay(100);
+}
+
+if (wifiSSIDscan)
+{
+    int n = WiFi.scanNetworks();
+    DEBUG_WM(F("Scan done"));
+    if (n == 0) {
+      DEBUG_WM(F("No networks found"));
+     // page += F("No networks found. Refresh to scan again.");
+    } else {
+
+
+if (wifiSSIDscan)
+{
+/* WE SHOULD MOVE THIS IN PLACE ATOMICALLY */
+  if (wifiSSIDs) delete [] wifiSSIDs;
+  wifiSSIDs = new WiFiResult[n];
+  wifiSSIDCount = n;
+
+  for (int i=0;i<n;i++)
+  {  
+       wifiSSIDs[i].duplicate=false;
+
+  	bool res=WiFi.getNetworkInfo(i, wifiSSIDs[i].SSID, wifiSSIDs[i].encryptionType, wifiSSIDs[i].RSSI, wifiSSIDs[i].BSSID, wifiSSIDs[i].channel, wifiSSIDs[i].isHidden);
+  }
+      
+
+      // RSSI SORT
+
+      // old sort
+      for (int i = 0; i < n; i++) {
+        for (int j = i + 1; j < n; j++) {
+          if (wifiSSIDs[j].RSSI > wifiSSIDs[i].RSSI) {
+            std::swap(wifiSSIDs[i], wifiSSIDs[j]);
+          }
+        }
+      }
+
+
+      // remove duplicates ( must be RSSI sorted )
+      if (_removeDuplicateAPs) {
+        String cssid;
+        for (int i = 0; i < n; i++) {
+          if (wifiSSIDs[i].duplicate == true) continue;
+          cssid = wifiSSIDs[i].SSID;
+          for (int j = i + 1; j < n; j++) {
+            if (cssid == wifiSSIDs[j].SSID) {
+              DEBUG_WM("DUP AP: " +wifiSSIDs[j].SSID);              
+              wifiSSIDs[j].duplicate=true; // set dup aps to NULL
+            }
+          }
+        }
+      }
+
+}
+}
+}
+}
+boolean  AsyncWiFiManager::startConfigPortal(char const *apName, char const *apPassword) {
+  //setup AP
+  WiFi.mode(WIFI_AP_STA);
+  DEBUG_WM("SET AP STA");
+
+  _apName = apName;
+  _apPassword = apPassword;
+
+  //notify we entered AP mode
+  if ( _apcallback != NULL) {
+    _apcallback(this);
+  }
+
+  connect = false;
+  setupConfigPortal();
+  int scannow= -1 ;
+  while (_configPortalTimeout == 0 || millis() < _configPortalStart + _configPortalTimeout) {
+    //DNS
+    dnsServer->processNextRequest();
+
+//
+//  we should do a scan every so often here
+//
+if ( millis() > scannow + 10000)
+{
+DEBUG_WM(F("About to scan()"));
+scan();
+scannow= millis() ;
+}
+
+
+    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
+      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 (_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;
+      }
+    }
+    yield();
+  }
+
+ // server.reset();
+ // dnsServer.reset();
+
+  return  WiFi.status() == WL_CONNECTED;
+}
+
+
+int AsyncWiFiManager::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.
+  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();
+
+      WiFi.begin();
+    } else {
+      DEBUG_WM("No saved credentials");
+    }
+  }
+
+  int connRes = waitForConnectResult();
+  DEBUG_WM ("Connection result: ");
+  DEBUG_WM ( connRes );
+  //not connected, WPS enabled, no pass - first attempt
+  if (_tryWPS && connRes != WL_CONNECTED && pass == "") {
+    startWPS();
+    //should be connected at the end of WPS
+    connRes = waitForConnectResult();
+  }
+  return connRes;
+}
+
+uint8_t AsyncWiFiManager::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);
+    }
+    return status;
+  }
+}
+
+void AsyncWiFiManager::startWPS() {
+  DEBUG_WM("START WPS");
+  WiFi.beginWPSConfig();
+  DEBUG_WM("END WPS");
+}
+/*
+  String AsyncWiFiManager::getSSID() {
+  if (_ssid == "") {
+    DEBUG_WM(F("Reading SSID"));
+    _ssid = WiFi.SSID();
+    DEBUG_WM(F("SSID: "));
+    DEBUG_WM(_ssid);
+  }
+  return _ssid;
+  }
+
+  String AsyncWiFiManager::getPassword() {
+  if (_pass == "") {
+    DEBUG_WM(F("Reading Password"));
+    _pass = WiFi.psk();
+    DEBUG_WM("Password: " + _pass);
+    //DEBUG_WM(_pass);
+  }
+  return _pass;
+  }
+*/
+String AsyncWiFiManager::getConfigPortalSSID() {
+  return _apName;
+}
+
+void AsyncWiFiManager::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."));
+  WiFi.disconnect(true);
+  //delay(200);
+}
+void AsyncWiFiManager::setTimeout(unsigned long seconds) {
+  setConfigPortalTimeout(seconds);
+}
+
+void AsyncWiFiManager::setConfigPortalTimeout(unsigned long seconds) {
+  _configPortalTimeout = seconds * 1000;
+}
+
+void AsyncWiFiManager::setConnectTimeout(unsigned long seconds) {
+  _connectTimeout = seconds * 1000;
+}
+
+void AsyncWiFiManager::setDebugOutput(boolean debug) {
+  _debug = debug;
+}
+
+void AsyncWiFiManager::setAPStaticIPConfig(IPAddress ip, IPAddress gw, IPAddress sn) {
+  _ap_static_ip = ip;
+  _ap_static_gw = gw;
+  _ap_static_sn = sn;
+}
+
+void AsyncWiFiManager::setSTAStaticIPConfig(IPAddress ip, IPAddress gw, IPAddress sn) {
+  _sta_static_ip = ip;
+  _sta_static_gw = gw;
+  _sta_static_sn = sn;
+}
+
+void AsyncWiFiManager::setMinimumSignalQuality(int quality) {
+  _minimumQuality = quality;
+}
+
+void AsyncWiFiManager::setBreakAfterConfig(boolean shouldBreak) {
+  _shouldBreakAfterConfig = shouldBreak;
+}
+
+/** Handle root or redirect to captive portal */
+void AsyncWiFiManager::handleRoot(AsyncWebServerRequest *request) {
+  DEBUG_WM(F("Handle root"));
+  if (captivePortal(request)) { // If caprive portal redirect instead of displaying the page.
+    return;
+  }
+
+  String page = FPSTR(WFM_HTTP_HEAD);
+  page.replace("{v}", "Options");
+  page += FPSTR(HTTP_SCRIPT);
+  page += FPSTR(HTTP_STYLE);
+  page += _customHeadElement;
+  page += FPSTR(HTTP_HEAD_END);
+  page += "<h1>";
+  page += _apName;
+  page += "</h1>";
+  page += F("<h3>AsyncWiFiManager</h3>");
+  page += FPSTR(HTTP_PORTAL_OPTIONS);
+  page += FPSTR(HTTP_END);
+
+  request->send(200, "text/html", page);
+
+}
+
+/** Wifi config page handler */
+void AsyncWiFiManager::handleWifi(AsyncWebServerRequest *request,boolean scan) {
+
+  String page = FPSTR(WFM_HTTP_HEAD);
+  page.replace("{v}", "Config ESP");
+  page += FPSTR(HTTP_SCRIPT);
+  page += FPSTR(HTTP_STYLE);
+  page += _customHeadElement;
+  page += FPSTR(HTTP_HEAD_END);
+
+  if (scan) {
+  wifiSSIDscan=false;
+  
+   int n = wifiSSIDCount;
+   
+  
+    DEBUG_WM(F("Scan done"));
+    if (n == 0) {
+      DEBUG_WM(F("No networks found"));
+      page += F("No networks found. Refresh to scan again.");
+    } else {
+
+     
+
+      //display networks in page
+      for (int i = 0; i < n; i++) {
+        if (wifiSSIDs[i].duplicate == true) continue; // skip dups
+        DEBUG_WM(wifiSSIDs[i].SSID);
+        
+        DEBUG_WM(wifiSSIDs[i].RSSI);
+        int quality = getRSSIasQuality(wifiSSIDs[i].RSSI);
+
+        if (_minimumQuality == -1 || _minimumQuality < quality) {
+          String item = FPSTR(HTTP_ITEM);
+          String rssiQ;
+          rssiQ += quality;
+          item.replace("{v}", wifiSSIDs[i].SSID);
+          item.replace("{r}", rssiQ);
+          if (wifiSSIDs[i].encryptionType != 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"));
+        }
+
+      }
+      page += "<br/>";
+    }
+  }
+  wifiSSIDscan=true;
+
+  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);
+    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 += "<br/>";
+  }
+
+  if (_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.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.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.toString());
+
+    page += item;
+
+    page += "<br/>";
+  }
+
+  page += FPSTR(HTTP_FORM_END);
+  page += FPSTR(HTTP_SCAN_LINK);
+
+  page += FPSTR(HTTP_END);
+
+  request->send(200, "text/html", page);
+
+
+  DEBUG_WM(F("Sent config page"));
+}
+
+/** Handle the WLAN save form and redirect to WLAN config page again */
+void AsyncWiFiManager::handleWifiSave(AsyncWebServerRequest *request) {
+  DEBUG_WM(F("WiFi save"));
+
+  //SAVE/connect here
+  _ssid = request->arg("s").c_str();
+  _pass = request->arg("p").c_str();
+
+  //parameters
+  for (int i = 0; i < _paramsCount; i++) {
+    if (_params[i] == NULL) {
+      break;
+    }
+    //read parameter
+    String value = request->arg(_params[i]->getID()).c_str();
+    //store it in array
+    value.toCharArray(_params[i]->_value, _params[i]->_length);
+    DEBUG_WM(F("Parameter"));
+    DEBUG_WM(_params[i]->getID());
+    DEBUG_WM(value);
+  }
+
+  if (request->hasArg("ip")) {
+    DEBUG_WM(F("static ip"));
+    DEBUG_WM(request->arg("ip"));
+    //_sta_static_ip.fromString(request->arg("ip"));
+    String ip = request->arg("ip");
+    optionalIPFromString(&_sta_static_ip, ip.c_str());
+  }
+  if (request->hasArg("gw")) {
+    DEBUG_WM(F("static gateway"));
+    DEBUG_WM(request->arg("gw"));
+    String gw = request->arg("gw");
+    optionalIPFromString(&_sta_static_gw, gw.c_str());
+  }
+  if (request->hasArg("sn")) {
+    DEBUG_WM(F("static netmask"));
+    DEBUG_WM(request->arg("sn"));
+    String sn = request->arg("sn");
+    optionalIPFromString(&_sta_static_sn, sn.c_str());
+  }
+
+  String page = FPSTR(WFM_HTTP_HEAD);
+  page.replace("{v}", "Credentials Saved");
+  page += FPSTR(HTTP_SCRIPT);
+  page += FPSTR(HTTP_STYLE);
+  page += _customHeadElement;
+  page += FPSTR(HTTP_HEAD_END);
+  page += FPSTR(HTTP_SAVED);
+  page += FPSTR(HTTP_END);
+
+  request->send(200, "text/html", page);
+
+  DEBUG_WM(F("Sent wifi save page"));
+
+  connect = true; //signal ready to connect/reset
+}
+
+/** Handle the info page */
+void AsyncWiFiManager::handleInfo(AsyncWebServerRequest *request) {
+  DEBUG_WM(F("Info"));
+
+  String page = FPSTR(WFM_HTTP_HEAD);
+  page.replace("{v}", "Info");
+  page += FPSTR(HTTP_SCRIPT);
+  page += FPSTR(HTTP_STYLE);
+  page += _customHeadElement;
+  page += FPSTR(HTTP_HEAD_END);
+  page += F("<dl>");
+  page += F("<dt>Chip ID</dt><dd>");
+  page += ESP.getChipId();
+  page += F("</dd>");
+  page += F("<dt>Flash Chip ID</dt><dd>");
+  page += ESP.getFlashChipId();
+  page += F("</dd>");
+  page += F("<dt>IDE Flash Size</dt><dd>");
+  page += ESP.getFlashChipSize();
+  page += F(" bytes</dd>");
+  page += F("<dt>Real Flash Size</dt><dd>");
+  page += ESP.getFlashChipRealSize();
+  page += F(" bytes</dd>");
+  page += F("<dt>Soft AP IP</dt><dd>");
+  page += WiFi.softAPIP().toString();
+  page += F("</dd>");
+  page += F("<dt>Soft AP MAC</dt><dd>");
+  page += WiFi.softAPmacAddress();
+  page += F("</dd>");
+  page += F("<dt>Station MAC</dt><dd>");
+  page += WiFi.macAddress();
+  page += F("</dd>");
+  page += F("</dl>");
+  page += FPSTR(HTTP_END);
+
+  request->send(200, "text/html", page);
+
+  DEBUG_WM(F("Sent info page"));
+}
+
+/** Handle the reset page */
+void AsyncWiFiManager::handleReset(AsyncWebServerRequest *request) {
+  DEBUG_WM(F("Reset"));
+
+  String page = FPSTR(WFM_HTTP_HEAD);
+  page.replace("{v}", "Info");
+  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);
+  request->send(200, "text/html", page);
+
+  DEBUG_WM(F("Sent reset page"));
+  delay(5000);
+  ESP.reset();
+  delay(2000);
+}
+
+
+
+//removed as mentioned here https://github.com/tzapu/AsyncWiFiManager/issues/114
+/*void AsyncWiFiManager::handle204(AsyncWebServerRequest *request) {
+  DEBUG_WM(F("204 No Response"));
+  request->sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
+  request->sendHeader("Pragma", "no-cache");
+  request->sendHeader("Expires", "-1");
+  request->send ( 204, "text/plain", "");
+  
+}*/
+
+void AsyncWiFiManager::handleNotFound(AsyncWebServerRequest *request) {
+  if (captivePortal(request)) { // If captive portal redirect instead of displaying the error page.
+    return;
+  }
+  String message = "File Not Found\n\n";
+  message += "URI: ";
+  message += request->url();
+  message += "\nMethod: ";
+  message += ( request->method() == HTTP_GET ) ? "GET" : "POST";
+  message += "\nArguments: ";
+  message += request->args();
+  message += "\n";
+
+  for ( uint8_t i = 0; i < request->args(); i++ ) {
+    message += " " + request->argName ( i ) + ": " + request->arg ( i ) + "\n";
+  }
+  AsyncWebServerResponse *response = request->beginResponse(404,"text/plain",message);
+  response->addHeader("Cache-Control", "no-cache, no-store, must-revalidate");
+  response->addHeader("Pragma", "no-cache");
+  response->addHeader("Expires", "-1");
+  request->send (response );
+}
+
+
+/** 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 AsyncWiFiManager::captivePortal(AsyncWebServerRequest *request) {
+  if (!isIp(request->host()) ) {
+    DEBUG_WM(F("Request redirected to captive portal"));
+	AsyncWebServerResponse *response = request->beginResponse(302,"text/plain","");
+    response->addHeader("Location", String("http://") + toStringIp(request->client()->localIP()));
+    request->send ( response);
+    return true;
+  }
+  return false;
+}
+
+//start up config portal callback
+void AsyncWiFiManager::setAPCallback( void (*func)(AsyncWiFiManager* myAsyncWiFiManager) ) {
+  _apcallback = func;
+}
+
+//start up save config callback
+void AsyncWiFiManager::setSaveConfigCallback( void (*func)(void) ) {
+  _savecallback = func;
+}
+
+//sets a custom element to add to head, like a new style tag
+void AsyncWiFiManager::setCustomHeadElement(const char* element) {
+  _customHeadElement = element;
+}
+
+//if this is true, remove duplicated Access Points - defaut true
+void AsyncWiFiManager::setRemoveDuplicateAPs(boolean removeDuplicates) {
+  _removeDuplicateAPs = removeDuplicates;
+}
+
+
+
+template <typename Generic>
+void AsyncWiFiManager::DEBUG_WM(Generic text) {
+  if (_debug) {
+    Serial.print("*WM: ");
+    Serial.println(text);
+  }
+}
+
+int AsyncWiFiManager::getRSSIasQuality(int RSSI) {
+  int quality = 0;
+
+  if (RSSI <= -100) {
+    quality = 0;
+  } else if (RSSI >= -50) {
+    quality = 100;
+  } else {
+    quality = 2 * (RSSI + 100);
+  }
+  return quality;
+}
+
+/** Is this an IP? */
+boolean AsyncWiFiManager::isIp(String str) {
+  for (int i = 0; i < str.length(); i++) {
+    int c = str.charAt(i);
+    if (c != '.' && (c < '0' || c > '9')) {
+      return false;
+    }
+  }
+  return true;
+}
+
+/** IP to String? */
+String AsyncWiFiManager::toStringIp(IPAddress ip) {
+  String res = "";
+  for (int i = 0; i < 3; i++) {
+    res += String((ip >> (8 * i)) & 0xFF) + ".";
+  }
+  res += String(((ip >> 8 * 3)) & 0xFF);
+  return res;
+}

+ 216 - 0
ESPAsyncWiFiManager.h

@@ -0,0 +1,216 @@
+/**************************************************************
+   WiFiManager is a library for the ESP8266/Arduino platform
+   (https://github.com/esp8266/Arduino) to enable easy
+   configuration and reconfiguration of WiFi credentials using a Captive Portal
+   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
+   Built by AlexT https://github.com/tzapu
+   Licensed under MIT license
+ **************************************************************/
+
+#ifndef ESPAsyncWiFiManager_h
+#define ESPAsyncWiFiManager_h
+
+#include <ESP8266WiFi.h>
+#include <ESPAsyncWebServer.h>
+#include <DNSServer.h>
+#include <memory>
+
+extern "C" {
+  #include "user_interface.h"
+}
+
+const char WFM_HTTP_HEAD[] PROGMEM            = "<!DOCTYPE html><html lang=\"en\"><head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1, user-scalable=no\"/><title>{v}</title>";
+const char HTTP_STYLE[] PROGMEM           = "<style>.c{text-align: center;} div,input{padding:5px;font-size:1em;} input{width:95%;} body{text-align: center;font-family:verdana;} button{border:0;border-radius:0.3rem;background-color:#1fa3ec;color:#fff;line-height:2.4rem;font-size:1.2rem;width:100%;} .q{float: right;width: 64px;text-align: right;} .l{background: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAALVBMVEX///8EBwfBwsLw8PAzNjaCg4NTVVUjJiZDRUUUFxdiZGSho6OSk5Pg4eFydHTCjaf3AAAAZElEQVQ4je2NSw7AIAhEBamKn97/uMXEGBvozkWb9C2Zx4xzWykBhFAeYp9gkLyZE0zIMno9n4g19hmdY39scwqVkOXaxph0ZCXQcqxSpgQpONa59wkRDOL93eAXvimwlbPbwwVAegLS1HGfZAAAAABJRU5ErkJggg==\") no-repeat left center;background-size: 1em;}</style>";
+const char HTTP_SCRIPT[] PROGMEM          = "<script>function c(l){document.getElementById('s').value=l.innerText||l.textContent;document.getElementById('p').focus();}</script>";
+const char HTTP_HEAD_END[] PROGMEM        = "</head><body><div style='text-align:left;display:inline-block;min-width:260px;'>";
+const char HTTP_PORTAL_OPTIONS[] PROGMEM  = "<form action=\"/wifi\" method=\"get\"><button>Configure WiFi</button></form><br/><form action=\"/0wifi\" method=\"get\"><button>Configure WiFi (No Scan)</button></form><br/><form action=\"/i\" method=\"get\"><button>Info</button></form><br/><form action=\"/r\" method=\"post\"><button>Reset</button></form>";
+const char HTTP_ITEM[] PROGMEM            = "<div><a href='#p' onclick='c(this)'>{v}</a>&nbsp;<span class='q {i}'>{r}%</span></div>";
+const char HTTP_FORM_START[] PROGMEM      = "<form method='get' action='wifisave'><input id='s' name='s' length=32 placeholder='SSID'><br/><input id='p' name='p' length=64 type='password' placeholder='password'><br/>";
+const char HTTP_FORM_PARAM[] PROGMEM      = "<br/><input id='{i}' name='{n}' length={l} placeholder='{p}' value='{v}' {c}>";
+const char HTTP_FORM_END[] PROGMEM        = "<br/><button type='submit'>save</button></form>";
+const char HTTP_SCAN_LINK[] PROGMEM       = "<br/><div class=\"c\"><a href=\"/wifi\">Scan</a></div>";
+const char HTTP_SAVED[] PROGMEM           = "<div>Credentials Saved<br />Trying to connect ESP to network.<br />If it fails reconnect to AP to try again</div>";
+const char HTTP_END[] PROGMEM             = "</div></body></html>";
+
+#define WIFI_MANAGER_MAX_PARAMS 10
+
+class AsyncWiFiManagerParameter {
+  public:
+    AsyncWiFiManagerParameter(const char *custom);
+    AsyncWiFiManagerParameter(const char *id, const char *placeholder, const char *defaultValue, int length);
+    AsyncWiFiManagerParameter(const char *id, const char *placeholder, const char *defaultValue, int length, const char *custom);
+
+    const char *getID();
+    const char *getValue();
+    const char *getPlaceholder();
+    int         getValueLength();
+    const char *getCustomHTML();
+  private:
+    const char *_id;
+    const char *_placeholder;
+    char       *_value;
+    int         _length;
+    const char *_customHTML;
+
+    void init(const char *id, const char *placeholder, const char *defaultValue, int length, const char *custom);
+
+    friend class AsyncWiFiManager;
+};
+
+
+class WiFiResult
+{
+public:
+    bool duplicate;
+	String SSID;
+	uint8_t encryptionType;
+	int32_t RSSI;
+	uint8_t* BSSID;
+	int32_t channel;
+	bool isHidden;
+	
+	WiFiResult()
+	{
+	}
+	
+	
+};
+
+class AsyncWiFiManager
+{
+  public:
+    AsyncWiFiManager(AsyncWebServer * server, DNSServer *dns);
+
+    void          scan();
+
+    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(char const *apName, char const *apPassword = NULL);
+
+    // 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
+    //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
+    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);
+    //sets a custom ip /gateway /subnet configuration
+    void          setAPStaticIPConfig(IPAddress ip, IPAddress gw, IPAddress sn);
+    //sets config for a static IP
+    void          setSTAStaticIPConfig(IPAddress ip, IPAddress gw, IPAddress sn);
+    //called when AP mode and config portal is started
+    void          setAPCallback( void (*func)(AsyncWiFiManager*) );
+    //called when settings have been changed and connection was successful
+    void          setSaveConfigCallback( void (*func)(void) );
+    //adds a custom parameter
+    void          addParameter(AsyncWiFiManagerParameter *p);
+    //if this is set, it will exit after config, even if connection is unsucessful.
+    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, customise style
+    void          setCustomHeadElement(const char* element);
+    //if this is true, remove duplicated Access Points - defaut true
+    void          setRemoveDuplicateAPs(boolean removeDuplicates);
+
+  private:
+    DNSServer      *dnsServer;
+    AsyncWebServer *server;
+
+    //const int     WM_DONE                 = 0;
+    //const int     WM_WAIT                 = 10;
+
+    //const String  HTTP_HEAD = "<!DOCTYPE html><html lang=\"en\"><head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"/><title>{v}</title>";
+
+    void          setupConfigPortal();
+    void          startWPS();
+
+    const char*   _apName                 = "no-net";
+    const char*   _apPassword             = NULL;
+    String        _ssid                   = "";
+    String        _pass                   = "";
+    unsigned long _configPortalTimeout    = 0;
+    unsigned long _connectTimeout         = 0;
+    unsigned long _configPortalStart      = 0;
+
+    IPAddress     _ap_static_ip;
+    IPAddress     _ap_static_gw;
+    IPAddress     _ap_static_sn;
+    IPAddress     _sta_static_ip;
+    IPAddress     _sta_static_gw;
+    IPAddress     _sta_static_sn;
+
+    int           _paramsCount            = 0;
+    int           _minimumQuality         = -1;
+    boolean       _removeDuplicateAPs     = true;
+    boolean       _shouldBreakAfterConfig = false;
+    boolean       _tryWPS                 = false;
+
+    const char*   _customHeadElement      = "";
+
+    //String        getEEPROMString(int start, int len);
+    //void          setEEPROMString(int start, int len, String string);
+
+    int           status = WL_IDLE_STATUS;
+    int           connectWifi(String ssid, String pass);
+    uint8_t       waitForConnectResult();
+
+    void          handleRoot(AsyncWebServerRequest *);
+    void          handleWifi(AsyncWebServerRequest*,boolean scan);
+    void          handleWifiSave(AsyncWebServerRequest*);
+    void          handleInfo(AsyncWebServerRequest*);
+    void          handleReset(AsyncWebServerRequest*);
+    void          handleNotFound(AsyncWebServerRequest*);
+    void          handle204(AsyncWebServerRequest*);
+    boolean       captivePortal(AsyncWebServerRequest*);
+
+    // DNS server
+    const byte    DNS_PORT = 53;
+
+    //helpers
+    int           getRSSIasQuality(int RSSI);
+    boolean       isIp(String str);
+    String        toStringIp(IPAddress ip);
+
+    boolean       connect;
+    boolean       _debug = true;
+
+    WiFiResult    *wifiSSIDs;
+    int           wifiSSIDCount;
+    boolean       wifiSSIDscan;
+    
+    void (*_apcallback)(AsyncWiFiManager*) = NULL;
+    void (*_savecallback)(void) = NULL;
+
+    AsyncWiFiManagerParameter* _params[WIFI_MANAGER_MAX_PARAMS];
+
+    template <typename Generic>
+    void          DEBUG_WM(Generic text);
+
+    template <class T>
+    auto optionalIPFromString(T *obj, const char *s) -> decltype(  obj->fromString(s)  ) {
+      return  obj->fromString(s);
+    }
+    auto optionalIPFromString(...) -> bool {
+      DEBUG_WM("NO fromString METHOD ON IPAddress, you need ESP8266 core 2.1.0 or newer for Custom IP configuration to work.");
+      return false;
+    }
+};
+
+#endif

+ 22 - 0
LICENSE

@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 tzapu
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+

+ 369 - 0
README.md

@@ -0,0 +1,369 @@
+# WiFiManager
+ESP8266 WiFi Connection manager with fallback web configuration portal
+
+[![Build Status](https://travis-ci.org/tzapu/WiFiManager.svg?branch=master)](https://travis-ci.org/tzapu/WiFiManager)
+
+The configuration portal is of the captive variety, so on various devices it will present the configuration dialogue as soon as you connect to the created access point.
+
+First attempt at a library. Lots more changes and fixes to do. Contributions are welcome.
+
+#### This works with the ESP8266 Arduino platform with a recent stable release(2.0.0 or newer) https://github.com/esp8266/Arduino
+
+## Contents
+ - [How it works](#how-it-works)
+ - [Wishlist](#wishlist)
+ - [Quick start](#quick-start)
+   - Installing
+     - [Through Library Manager](#install-through-library-manager)
+     - [From Github](#checkout-from-github)
+   - [Using](#using)
+ - [Documentation](#documentation)
+   - [Access Point Password](#password-protect-the-configuration-access-point)
+   - [Callbacks](#callbacks)
+   - [Configuration Portal Timeout](#configuration-portal-timeout)
+   - [On Demand Configuration](#on-demand-configuration-portal)
+   - [Custom Parameters](#custom-parameters)
+   - [Custom IP Configuration](#custom-ip-configuration)
+   - [Filter Low Quality Networks](#filter-networks)
+   - [Debug Output](#debug)
+ - [Troubleshooting](#troubleshooting)
+ - [Releases](#releases)
+ - [Contributors](#contributions-and-thanks)
+
+
+## How It Works
+- when your ESP starts up, it sets it up in Station mode and tries to connect to a previously saved Access Point
+- if this is unsuccessful (or no previous network saved) it moves the ESP into Access Point mode and spins up a DNS and WebServer (default ip 192.168.4.1)
+- using any wifi enabled device with a browser (computer, phone, tablet) connect to the newly created Access Point
+- because of the Captive Portal and the DNS server you will either get a 'Join to network' type of popup or get any domain you try to access redirected to the configuration portal
+- choose one of the access points scanned, enter password, click save
+- ESP will try to connect. If successful, it relinquishes control back to your app. If not, reconnect to AP and reconfigure.
+
+## How It Looks
+![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~~
+
+## Quick Start
+
+### Installing
+You can either install through the Arduino Library Manager or checkout the latest changes or a release from github
+
+#### Install through Library Manager
+__Currently version 0.8+ works with release 2.0.0 or newer of the [ESP8266 core for Arduino](https://github.com/esp8266/Arduino)__
+ - in Arduino IDE got to Sketch/Include Library/Manage Libraries
+  ![Manage Libraries](http://i.imgur.com/9BkEBkR.png)
+
+ - search for WiFiManager
+  ![WiFiManager package](http://i.imgur.com/18yIai8.png)
+
+ - click Install and start [using it](#using)
+
+####  Checkout from github
+__Github version works with release 2.0.0 or newer of the [ESP8266 core for Arduino](https://github.com/esp8266/Arduino)__
+- Checkout library to your Arduino libraries folder
+
+### Using
+- Include in your sketch
+```cpp
+#include <ESP8266WiFi.h>          //ESP8266 Core WiFi Library (you most likely already have this in your sketch)
+
+#include <DNSServer.h>            //Local DNS Server used for redirecting all requests to the configuration portal
+#include <ESP8266WebServer.h>     //Local WebServer used to serve the configuration portal
+#include <WiFiManager.h>          //https://github.com/tzapu/WiFiManager WiFi Configuration Magic
+```
+
+- Initialize library, in your setup function add
+```cpp
+WiFiManager wifiManager;
+```
+
+- Also in the setup function add
+```cpp
+//first parameter is name of access point, second is the password
+wifiManager.autoConnect("AP-NAME", "AP-PASSWORD");
+```
+if you just want an unsecured access point
+```cpp
+wifiManager.autoConnect("AP-NAME");
+```
+or if you want to use and auto generated name from 'ESP' and the esp's Chip ID use
+```cpp
+wifiManager.autoConnect();
+```
+
+After you write your sketch and start the ESP, it will try to connect to WiFi. If it fails it starts in Access Point mode.
+While in AP mode, connect to it then open a browser to the gateway IP, default 192.168.4.1, configure wifi, save and it should reboot and connect.
+
+Also see [examples](https://github.com/tzapu/WiFiManager/tree/master/examples).
+
+## Documentation
+
+#### Password protect the configuration Access Point
+You can and should password protect the configuration access point.  Simply add the password as a second parameter to `autoConnect`.
+A short password seems to have unpredictable results so use one that's around 8 characters or more in length.
+The guidelines are that a wifi password must consist of 8 to 63 ASCII-encoded characters in the range of 32 to 126 (decimal)
+```cpp
+wifiManager.autoConnect("AutoConnectAP", "password")
+```
+
+#### Callbacks
+##### Enter Config mode
+Use this if you need to do something when your device enters configuration mode on failed WiFi connection attempt.
+Before `autoConnect()`
+```cpp
+wifiManager.setAPCallback(configModeCallback);
+```
+`configModeCallback` declaration and example
+```cpp
+void configModeCallback (WiFiManager *myWiFiManager) {
+  Serial.println("Entered config mode");
+  Serial.println(WiFi.softAPIP());
+
+  Serial.println(myWiFiManager->getConfigPortalSSID());
+}
+```
+
+##### Save settings
+This gets called when custom parameters have been set **AND** a connection has been established. Use it to set a flag, so when all the configuration finishes, you can save the extra parameters somewhere.
+
+See [AutoConnectWithFSParameters Example](https://github.com/tzapu/WiFiManager/tree/master/examples/AutoConnectWithFSParameters).
+```cpp
+wifiManager.setSaveConfigCallback(saveConfigCallback);
+```
+`saveConfigCallback` declaration and example
+```cpp
+//flag for saving data
+bool shouldSaveConfig = false;
+
+//callback notifying us of the need to save config
+void saveConfigCallback () {
+  Serial.println("Should save config");
+  shouldSaveConfig = true;
+}
+```
+
+#### Configuration Portal Timeout
+If you need to set a timeout so the ESP doesn't hang waiting to be configured, for instance after a power failure, you can add
+```cpp
+wifiManager.setConfigPortalTimeout(180);
+```
+which will wait 3 minutes (180 seconds). When the time passes, the autoConnect function will return, no matter the outcome.
+Check for connection and if it's still not established do whatever is needed (on some modules I restart them to retry, on others I enter deep sleep)
+
+#### On Demand Configuration Portal
+If you would rather start the configuration portal on demand rather than automatically on a failed connection attempt, then this is for you.
+
+Instead of calling `autoConnect()` which does all the connecting and failover configuration portal setup for you, you need to use `startConfigPortal()`. __Do not use BOTH.__
+
+Example usage
+```cpp
+void loop() {
+  // is configuration portal requested?
+  if ( digitalRead(TRIGGER_PIN) == LOW ) {
+    WiFiManager wifiManager;
+    wifiManager.startConfigPortal("OnDemandAP");
+    Serial.println("connected...yeey :)");
+  }
+}
+```
+See example for a more complex version. [OnDemandConfigPortal](https://github.com/tzapu/WiFiManager/tree/master/examples/OnDemandConfigPortal)
+
+#### Custom Parameters
+You can use WiFiManager to collect more parameters than just SSID and password.
+This could be helpful for configuring stuff like MQTT host and port, [blynk](http://www.blynk.cc) or [emoncms](http://emoncms.org) tokens, just to name a few.
+**You are responsible for saving and loading these custom values.** The library just collects and displays the data for you as a convenience.
+Usage scenario would be:
+- load values from somewhere (EEPROM/FS) or generate some defaults
+- add the custom parameters to WiFiManager using
+ ```cpp
+ // id/name, placeholder/prompt, default, length
+ WiFiManagerParameter custom_mqtt_server("server", "mqtt server", mqtt_server, 40);
+ wifiManager.addParameter(&custom_mqtt_server);
+
+ ```
+- if connection to AP fails, configuration portal starts and you can set /change the values (or use on demand configuration portal)
+- once configuration is done and connection is established [save config callback]() is called
+- once WiFiManager returns control to your application, read and save the new values using the `WiFiManagerParameter` object.
+ ```cpp
+ mqtt_server = custom_mqtt_server.getValue();
+ ```  
+This feature is a lot more involved than all the others, so here are some examples to fully show how it is done.
+You should also take a look at adding custom HTML to your form.
+
+- Save and load custom parameters to file system in json form [AutoConnectWithFSParameters](https://github.com/tzapu/WiFiManager/tree/master/examples/AutoConnectWithFSParameters)
+- *Save and load custom parameters to EEPROM* (not done yet)
+
+#### Custom IP Configuration
+You can set a custom IP for both AP (access point, config mode) and STA (station mode, client mode, normal project state)
+
+##### Custom Access Point IP Configuration
+This will set your captive portal to a specific IP should you need/want such a feature. Add the following snippet before `autoConnect()`
+```cpp
+//set custom ip for portal
+wifiManager.setAPStaticIPConfig(IPAddress(10,0,1,1), IPAddress(10,0,1,1), IPAddress(255,255,255,0));
+```
+
+##### Custom Station (client) Static IP Configuration
+This will make use the specified IP configuration instead of using DHCP in station mode.
+```cpp
+wifiManager.setSTAStaticIPConfig(IPAddress(192,168,0,99), IPAddress(192,168,0,1), IPAddress(255,255,255,0));
+```
+There are a couple of examples in the examples folder that show you how to set a static IP and even how to configure it through the web configuration portal.
+
+#### Custom HTML, CSS, Javascript
+There are various ways in which you can inject custom HTML, CSS or Javascript into the configuration portal.
+The options are:
+- inject custom head element
+You can use this to any html bit to the head of the configuration portal. If you add a `<style>` element, bare in mind it overwrites the included css, not replaces.
+```cpp
+wifiManager.setCustomHeadElement("<style>html{filter: invert(100%); -webkit-filter: invert(100%);}</style>");
+```
+- inject a custom bit of html in the configuration form
+```cpp
+WiFiManagerParameter custom_text("<p>This is just a text paragraph</p>");
+wifiManager.addParameter(&custom_text);
+```
+- inject a custom bit of html in a configuration form element
+Just add the bit you want added as the last parameter to the custom parameter constructor.
+```cpp
+WiFiManagerParameter custom_mqtt_server("server", "mqtt server", "iot.eclipse", 40, " readonly");
+```
+
+#### Filter Networks
+You can filter networks based on signal quality and show/hide duplicate networks.
+
+- If you would like to filter low signal quality networks you can tell WiFiManager to not show networks below an arbitrary quality %;
+```cpp
+wifiManager.setMinimumSignalQuality(10);
+```
+will not show networks under 10% signal quality. If you omit the parameter it defaults to 8%;
+
+- You can also remove or show duplicate networks (default is remove).
+Use this function to show (or hide) all networks.
+```cpp
+wifiManager.setRemoveDuplicateAPs(false);
+```
+
+#### Debug
+Debug is enabled by default on Serial. To disable add before autoConnect
+```cpp
+wifiManager.setDebugOutput(false);
+```
+
+## Troubleshooting
+If you get compilation errors, more often than not, you may need to install a newer version of the ESP8266 core for Arduino.
+
+Changes added on 0.8 should make the latest trunk work without compilation errors. Tested down to ESP8266 core 2.0.0. **Please update to version 0.8**
+
+I am trying to keep releases working with release versions of the core, so they can be installed through boards manager, but if you checkout the latest version directly from github, sometimes, the library will only work if you update the ESP8266 core to the latest version because I am using some newly added function.
+
+If you connect to the created configuration Access Point but the configuration portal does not show up, just open a browser and type in the IP of the web portal, by default `192.168.4.1`.
+
+If trying to connect ends up in an endless loop, try to add `setConnectTimeout(60)` before `autoConnect();`. The parameter is timeout to try connecting in seconds.
+
+## Releases
+#### 0.12
+- removed 204 header response
+- fixed incompatibility with other libs using isnan and other std:: functions without namespace
+
+##### 0.11
+- a lot more reliable reconnecting to networks
+- custom html in custom parameters (for read only params)
+- custom html in custom parameter form (like labels)
+- custom head element (like custom css)
+- sort networks based on signal quality
+- remove duplicate networks
+
+##### 0.10
+- some css changes
+- bug fixes and speed improvements
+- added an alternative to waitForConnectResult() for debugging
+- changed `setTimeout(seconds)` to `setConfigPortalTimeout(seconds)`
+
+##### 0.9
+ - fixed support for encoded characters in ssid/pass
+
+##### 0.8
+ - made it compile on older versions of ESP8266 core as well, tested down to 2.0.0
+ - added simple example for Custom IP
+
+##### 0.7
+ - added static IP in station mode
+ - added example of persisting custom IP to FS config.json
+ - more option on portal homepage
+ - added on PlatformIO
+
+##### 0.6
+ - custom parameters
+ - prettier
+ - on demand config portal
+ - commit #100 :D
+
+##### 0.5
+ - Added to Arduino Boards Manager - Thanks Max
+ - moved most stuff to PROGMEM
+ - added signal quality and a nice little padlock to show which networks are encrypted
+
+##### v0.4 - all of it user contributed changes - Thank you
+ - added ability to password protect the configuration Access Point
+ - callback for enter configuration mode
+ - memory allocation improvements
+
+##### v0.3
+ - removed the need for EEPROM and works with the 2.0.0 and above stable release of the ESP8266 for Arduino IDE package
+ - removed restart on save of credentials
+ - updated examples
+
+##### v0.2
+needs the latest staging version (or at least a recent release of the staging version) to work
+
+##### v0.1
+works with the staging release ver. 1.6.5-1044-g170995a, built on Aug 10, 2015 of the ESP8266 Arduino library.
+
+
+### Contributions and thanks
+The support and help I got from the community has been nothing short of phenomenal. I can't thank you guys enough. This is my first real attept in developing open source stuff and I must say, now I understand why people are so dedicated to it, it is because of all the wonderful people involved.
+
+__THANK YOU__
+
+[Shawn A](https://github.com/tablatronix)
+
+[Maximiliano Duarte](https://github.com/domonetic)
+
+[alltheblinkythings](https://github.com/alltheblinkythings)
+
+[Niklas Wall](https://github.com/niklaswall)
+
+[Jakub Piasecki](https://github.com/zaporylie)
+
+[Peter Allan](https://github.com/alwynallan)
+
+[John Little](https://github.com/j0hnlittle)
+
+[markaswift](https://github.com/markaswift)
+
+[franklinvv](https://github.com/franklinvv)
+
+[Alberto Ricci Bitti](https://github.com/riccibitti)
+
+[SebiPanther](https://github.com/SebiPanther)
+
+[jonathanendersby](https://github.com/jonathanendersby)
+
+[walthercarsten](https://github.com/walthercarsten)
+
+Sorry if i have missed anyone.
+
+#### Inspiration
+- http://www.esp8266.com/viewtopic.php?f=29&t=2520

BIN
examples/.DS_Store


+ 39 - 0
examples/AutoConnect/AutoConnect.ino

@@ -0,0 +1,39 @@
+#include <ESP8266WiFi.h>          //https://github.com/esp8266/Arduino
+
+//needed for library
+#include <DNSServer.h>
+#include <ESPAsyncWebServer.h>
+#include <ESPAsyncWiFiManager.h>         //https://github.com/tzapu/WiFiManager
+
+AsyncWebServer server(80);
+DNSServer dns;
+void setup() {
+    // put your setup code here, to run once:
+    Serial.begin(115200);
+
+    //WiFiManager
+    //Local intialization. Once its business is done, there is no need to keep it around
+    AsyncWiFiManager wifiManager(&server,&dns);
+    //reset saved settings
+    //wifiManager.resetSettings();
+    
+    //set custom ip for portal
+    //wifiManager.setAPConfig(IPAddress(10,0,1,1), IPAddress(10,0,1,1), IPAddress(255,255,255,0));
+
+    //fetches ssid and pass from eeprom and tries to connect
+    //if it does not connect it starts an access point with the specified name
+    //here  "AutoConnectAP"
+    //and goes into a blocking loop awaiting configuration
+    wifiManager.autoConnect("AutoConnectAP");
+    //or use this for auto generated name ESP + ChipID
+    //wifiManager.autoConnect();
+
+    
+    //if you get here you have connected to the WiFi
+    Serial.println("connected...yeey :)");
+}
+
+void loop() {
+    // put your main code here, to run repeatedly:
+    
+}

+ 156 - 0
examples/AutoConnectWithFSParameters/AutoConnectWithFSParameters.ino

@@ -0,0 +1,156 @@
+#include <FS.h>                   //this needs to be first, or it all crashes and burns...
+
+#include <ESP8266WiFi.h>          //https://github.com/esp8266/Arduino
+
+//needed for library
+#include <DNSServer.h>
+#include <ESP8266WebServer.h>
+#include <WiFiManager.h>          //https://github.com/tzapu/WiFiManager
+
+#include <ArduinoJson.h>          //https://github.com/bblanchon/ArduinoJson
+
+//define your default values here, if there are different values in config.json, they are overwritten.
+char mqtt_server[40];
+char mqtt_port[6] = "8080";
+char blynk_token[34] = "YOUR_BLYNK_TOKEN";
+
+//flag for saving data
+bool shouldSaveConfig = false;
+
+//callback notifying us of the need to save config
+void saveConfigCallback () {
+  Serial.println("Should save config");
+  shouldSaveConfig = true;
+}
+
+
+void setup() {
+  // put your setup code here, to run once:
+  Serial.begin(115200);
+  Serial.println();
+
+  //clean FS, for testing
+  //SPIFFS.format();
+
+  //read configuration from FS json
+  Serial.println("mounting FS...");
+
+  if (SPIFFS.begin()) {
+    Serial.println("mounted file system");
+    if (SPIFFS.exists("/config.json")) {
+      //file exists, reading and loading
+      Serial.println("reading config file");
+      File configFile = SPIFFS.open("/config.json", "r");
+      if (configFile) {
+        Serial.println("opened config file");
+        size_t size = configFile.size();
+        // Allocate a buffer to store contents of the file.
+        std::unique_ptr<char[]> buf(new char[size]);
+
+        configFile.readBytes(buf.get(), size);
+        DynamicJsonBuffer jsonBuffer;
+        JsonObject& json = jsonBuffer.parseObject(buf.get());
+        json.printTo(Serial);
+        if (json.success()) {
+          Serial.println("\nparsed json");
+
+          strcpy(mqtt_server, json["mqtt_server"]);
+          strcpy(mqtt_port, json["mqtt_port"]);
+          strcpy(blynk_token, json["blynk_token"]);
+
+        } else {
+          Serial.println("failed to load json config");
+        }
+      }
+    }
+  } else {
+    Serial.println("failed to mount FS");
+  }
+  //end read
+
+
+
+  // The extra parameters to be configured (can be either global or just in the setup)
+  // After connecting, parameter.getValue() will get you the configured value
+  // id/name placeholder/prompt default length
+  WiFiManagerParameter custom_mqtt_server("server", "mqtt server", mqtt_server, 40);
+  WiFiManagerParameter custom_mqtt_port("port", "mqtt port", mqtt_port, 5);
+  WiFiManagerParameter custom_blynk_token("blynk", "blynk token", blynk_token, 32);
+
+  //WiFiManager
+  //Local intialization. Once its business is done, there is no need to keep it around
+  WiFiManager wifiManager;
+
+  //set config save notify callback
+  wifiManager.setSaveConfigCallback(saveConfigCallback);
+
+  //set static ip
+  wifiManager.setSTAStaticIPConfig(IPAddress(10,0,1,99), IPAddress(10,0,1,1), IPAddress(255,255,255,0));
+  
+  //add all your parameters here
+  wifiManager.addParameter(&custom_mqtt_server);
+  wifiManager.addParameter(&custom_mqtt_port);
+  wifiManager.addParameter(&custom_blynk_token);
+
+  //reset settings - for testing
+  //wifiManager.resetSettings();
+
+  //set minimu quality of signal so it ignores AP's under that quality
+  //defaults to 8%
+  //wifiManager.setMinimumSignalQuality();
+  
+  //sets timeout until configuration portal gets turned off
+  //useful to make it all retry or go to sleep
+  //in seconds
+  //wifiManager.setTimeout(120);
+
+  //fetches ssid and pass and tries to connect
+  //if it does not connect it starts an access point with the specified name
+  //here  "AutoConnectAP"
+  //and goes into a blocking loop awaiting configuration
+  if (!wifiManager.autoConnect("AutoConnectAP", "password")) {
+    Serial.println("failed to connect and hit timeout");
+    delay(3000);
+    //reset and try again, or maybe put it to deep sleep
+    ESP.reset();
+    delay(5000);
+  }
+
+  //if you get here you have connected to the WiFi
+  Serial.println("connected...yeey :)");
+
+  //read updated parameters
+  strcpy(mqtt_server, custom_mqtt_server.getValue());
+  strcpy(mqtt_port, custom_mqtt_port.getValue());
+  strcpy(blynk_token, custom_blynk_token.getValue());
+
+  //save the custom parameters to FS
+  if (shouldSaveConfig) {
+    Serial.println("saving config");
+    DynamicJsonBuffer jsonBuffer;
+    JsonObject& json = jsonBuffer.createObject();
+    json["mqtt_server"] = mqtt_server;
+    json["mqtt_port"] = mqtt_port;
+    json["blynk_token"] = blynk_token;
+
+    File configFile = SPIFFS.open("/config.json", "w");
+    if (!configFile) {
+      Serial.println("failed to open config file for writing");
+    }
+
+    json.printTo(Serial);
+    json.printTo(configFile);
+    configFile.close();
+    //end save
+  }
+
+  Serial.println("local ip");
+  Serial.println(WiFi.localIP());
+
+}
+
+void loop() {
+  // put your main code here, to run repeatedly:
+
+
+}

+ 188 - 0
examples/AutoConnectWithFSParametersAndCustomIP/AutoConnectWithFSParametersAndCustomIP.ino

@@ -0,0 +1,188 @@
+#include <FS.h>                   //this needs to be first, or it all crashes and burns...
+
+#include <ESP8266WiFi.h>          //https://github.com/esp8266/Arduino
+
+//needed for library
+#include <DNSServer.h>
+#include <ESP8266WebServer.h>
+#include <WiFiManager.h>          //https://github.com/tzapu/WiFiManager
+
+#include <ArduinoJson.h>          //https://github.com/bblanchon/ArduinoJson
+
+//define your default values here, if there are different values in config.json, they are overwritten.
+//length should be max size + 1 
+char mqtt_server[40];
+char mqtt_port[6] = "8080";
+char blynk_token[33] = "YOUR_BLYNK_TOKEN";
+//default custom static IP
+char static_ip[16] = "10.0.1.56";
+char static_gw[16] = "10.0.1.1";
+char static_sn[16] = "255.255.255.0";
+
+//flag for saving data
+bool shouldSaveConfig = false;
+
+//callback notifying us of the need to save config
+void saveConfigCallback () {
+  Serial.println("Should save config");
+  shouldSaveConfig = true;
+}
+
+void setup() {
+  // put your setup code here, to run once:
+  Serial.begin(115200);
+  Serial.println();
+
+  //clean FS, for testing
+  //SPIFFS.format();
+
+  //read configuration from FS json
+  Serial.println("mounting FS...");
+
+  if (SPIFFS.begin()) {
+    Serial.println("mounted file system");
+    if (SPIFFS.exists("/config.json")) {
+      //file exists, reading and loading
+      Serial.println("reading config file");
+      File configFile = SPIFFS.open("/config.json", "r");
+      if (configFile) {
+        Serial.println("opened config file");
+        size_t size = configFile.size();
+        // Allocate a buffer to store contents of the file.
+        std::unique_ptr<char[]> buf(new char[size]);
+
+        configFile.readBytes(buf.get(), size);
+        DynamicJsonBuffer jsonBuffer;
+        JsonObject& json = jsonBuffer.parseObject(buf.get());
+        json.printTo(Serial);
+        if (json.success()) {
+          Serial.println("\nparsed json");
+
+          strcpy(mqtt_server, json["mqtt_server"]);
+          strcpy(mqtt_port, json["mqtt_port"]);
+          strcpy(blynk_token, json["blynk_token"]);
+
+          if(json["ip"]) {
+            Serial.println("setting custom ip from config");
+            //static_ip = json["ip"];
+            strcpy(static_ip, json["ip"]);
+            strcpy(static_gw, json["gateway"]);
+            strcpy(static_sn, json["subnet"]);
+            //strcat(static_ip, json["ip"]);
+            //static_gw = json["gateway"];
+            //static_sn = json["subnet"];
+            Serial.println(static_ip);
+/*            Serial.println("converting ip");
+            IPAddress ip = ipFromCharArray(static_ip);
+            Serial.println(ip);*/
+          } else {
+            Serial.println("no custom ip in config");
+          }
+        } else {
+          Serial.println("failed to load json config");
+        }
+      }
+    }
+  } else {
+    Serial.println("failed to mount FS");
+  }
+  //end read
+  Serial.println(static_ip);
+  Serial.println(blynk_token);
+  Serial.println(mqtt_server);
+
+
+  // The extra parameters to be configured (can be either global or just in the setup)
+  // After connecting, parameter.getValue() will get you the configured value
+  // id/name placeholder/prompt default length
+  WiFiManagerParameter custom_mqtt_server("server", "mqtt server", mqtt_server, 40);
+  WiFiManagerParameter custom_mqtt_port("port", "mqtt port", mqtt_port, 5);
+  WiFiManagerParameter custom_blynk_token("blynk", "blynk token", blynk_token, 34);
+
+  //WiFiManager
+  //Local intialization. Once its business is done, there is no need to keep it around
+  WiFiManager wifiManager;
+
+  //set config save notify callback
+  wifiManager.setSaveConfigCallback(saveConfigCallback);
+
+  //set static ip
+  IPAddress _ip,_gw,_sn;
+  _ip.fromString(static_ip);
+  _gw.fromString(static_gw);
+  _sn.fromString(static_sn);
+
+  wifiManager.setSTAStaticIPConfig(_ip, _gw, _sn);
+  
+  //add all your parameters here
+  wifiManager.addParameter(&custom_mqtt_server);
+  wifiManager.addParameter(&custom_mqtt_port);
+  wifiManager.addParameter(&custom_blynk_token);
+
+  //reset settings - for testing
+  //wifiManager.resetSettings();
+
+  //set minimu quality of signal so it ignores AP's under that quality
+  //defaults to 8%
+  wifiManager.setMinimumSignalQuality();
+  
+  //sets timeout until configuration portal gets turned off
+  //useful to make it all retry or go to sleep
+  //in seconds
+  //wifiManager.setTimeout(120);
+
+  //fetches ssid and pass and tries to connect
+  //if it does not connect it starts an access point with the specified name
+  //here  "AutoConnectAP"
+  //and goes into a blocking loop awaiting configuration
+  if (!wifiManager.autoConnect("AutoConnectAP", "password")) {
+    Serial.println("failed to connect and hit timeout");
+    delay(3000);
+    //reset and try again, or maybe put it to deep sleep
+    ESP.reset();
+    delay(5000);
+  }
+
+  //if you get here you have connected to the WiFi
+  Serial.println("connected...yeey :)");
+
+  //read updated parameters
+  strcpy(mqtt_server, custom_mqtt_server.getValue());
+  strcpy(mqtt_port, custom_mqtt_port.getValue());
+  strcpy(blynk_token, custom_blynk_token.getValue());
+
+  //save the custom parameters to FS
+  if (shouldSaveConfig) {
+    Serial.println("saving config");
+    DynamicJsonBuffer jsonBuffer;
+    JsonObject& json = jsonBuffer.createObject();
+    json["mqtt_server"] = mqtt_server;
+    json["mqtt_port"] = mqtt_port;
+    json["blynk_token"] = blynk_token;
+
+    json["ip"] = WiFi.localIP().toString();
+    json["gateway"] = WiFi.gatewayIP().toString();
+    json["subnet"] = WiFi.subnetMask().toString();
+
+    File configFile = SPIFFS.open("/config.json", "w");
+    if (!configFile) {
+      Serial.println("failed to open config file for writing");
+    }
+
+    json.prettyPrintTo(Serial);
+    json.printTo(configFile);
+    configFile.close();
+    //end save
+  }
+
+  Serial.println("local ip");
+  Serial.println(WiFi.localIP());
+  Serial.println(WiFi.gatewayIP());
+  Serial.println(WiFi.subnetMask());
+}
+
+void loop() {
+  // put your main code here, to run repeatedly:
+
+
+}

+ 47 - 0
examples/AutoConnectWithFeedback/AutoConnectWithFeedback.ino

@@ -0,0 +1,47 @@
+#include <ESP8266WiFi.h>          //https://github.com/esp8266/Arduino
+
+//needed for library
+#include <DNSServer.h>
+#include <ESP8266WebServer.h>
+#include "WiFiManager.h"          //https://github.com/tzapu/WiFiManager
+
+void configModeCallback (WiFiManager *myWiFiManager) {
+  Serial.println("Entered config mode");
+  Serial.println(WiFi.softAPIP());
+  //if you used auto generated SSID, print it
+  Serial.println(myWiFiManager->getConfigPortalSSID());
+}
+
+void setup() {
+  // put your setup code here, to run once:
+  Serial.begin(115200);
+  
+  //WiFiManager
+  //Local intialization. Once its business is done, there is no need to keep it around
+  WiFiManager wifiManager;
+  //reset settings - for testing
+  //wifiManager.resetSettings();
+
+  //set callback that gets called when connecting to previous WiFi fails, and enters Access Point mode
+  wifiManager.setAPCallback(configModeCallback);
+
+  //fetches ssid and pass and tries to connect
+  //if it does not connect it starts an access point with the specified name
+  //here  "AutoConnectAP"
+  //and goes into a blocking loop awaiting configuration
+  if(!wifiManager.autoConnect()) {
+    Serial.println("failed to connect and hit timeout");
+    //reset and try again, or maybe put it to deep sleep
+    ESP.reset();
+    delay(1000);
+  } 
+
+  //if you get here you have connected to the WiFi
+  Serial.println("connected...yeey :)");
+ 
+}
+
+void loop() {
+  // put your main code here, to run repeatedly:
+
+}

+ 68 - 0
examples/AutoConnectWithFeedbackLED/AutoConnectWithFeedbackLED.ino

@@ -0,0 +1,68 @@
+#include <ESP8266WiFi.h>          //https://github.com/esp8266/Arduino
+
+//needed for library
+#include <DNSServer.h>
+#include <ESP8266WebServer.h>
+#include <WiFiManager.h>          //https://github.com/tzapu/WiFiManager
+
+//for LED status
+#include <Ticker.h>
+Ticker ticker;
+
+void tick()
+{
+  //toggle state
+  int state = digitalRead(BUILTIN_LED);  // get the current state of GPIO1 pin
+  digitalWrite(BUILTIN_LED, !state);     // set pin to the opposite state
+}
+
+//gets called when WiFiManager enters configuration mode
+void configModeCallback (WiFiManager *myWiFiManager) {
+  Serial.println("Entered config mode");
+  Serial.println(WiFi.softAPIP());
+  //if you used auto generated SSID, print it
+  Serial.println(myWiFiManager->getConfigPortalSSID());
+  //entered config mode, make led toggle faster
+  ticker.attach(0.2, tick);
+}
+
+void setup() {
+  // put your setup code here, to run once:
+  Serial.begin(115200);
+  
+  //set led pin as output
+  pinMode(BUILTIN_LED, OUTPUT);
+  // start ticker with 0.5 because we start in AP mode and try to connect
+  ticker.attach(0.6, tick);
+
+  //WiFiManager
+  //Local intialization. Once its business is done, there is no need to keep it around
+  WiFiManager wifiManager;
+  //reset settings - for testing
+  //wifiManager.resetSettings();
+
+  //set callback that gets called when connecting to previous WiFi fails, and enters Access Point mode
+  wifiManager.setAPCallback(configModeCallback);
+
+  //fetches ssid and pass and tries to connect
+  //if it does not connect it starts an access point with the specified name
+  //here  "AutoConnectAP"
+  //and goes into a blocking loop awaiting configuration
+  if (!wifiManager.autoConnect()) {
+    Serial.println("failed to connect and hit timeout");
+    //reset and try again, or maybe put it to deep sleep
+    ESP.reset();
+    delay(1000);
+  }
+
+  //if you get here you have connected to the WiFi
+  Serial.println("connected...yeey :)");
+  ticker.detach();
+  //keep LED on
+  digitalWrite(BUILTIN_LED, LOW);
+}
+
+void loop() {
+  // put your main code here, to run repeatedly:
+
+}

+ 49 - 0
examples/AutoConnectWithReset/AutoConnectWithReset.ino

@@ -0,0 +1,49 @@
+#include <FS.h>                   //this needs to be first, or it all crashes and burns...
+
+#include <ESP8266WiFi.h>          //https://github.com/esp8266/Arduino
+
+//needed for library
+#include <DNSServer.h>
+#include <ESP8266WebServer.h>
+#include <WiFiManager.h>          //https://github.com/tzapu/WiFiManager
+
+void setup() {
+  // put your setup code here, to run once:
+  Serial.begin(115200);
+  Serial.println();
+
+  //WiFiManager
+  //Local intialization. Once its business is done, there is no need to keep it around
+  WiFiManager wifiManager;
+
+  //exit after config instead of connecting
+  wifiManager.setBreakAfterConfig(true);
+
+  //reset settings - for testing
+  //wifiManager.resetSettings();
+
+
+  //tries to connect to last known settings
+  //if it does not connect it starts an access point with the specified name
+  //here  "AutoConnectAP" with password "password"
+  //and goes into a blocking loop awaiting configuration
+  if (!wifiManager.autoConnect("AutoConnectAP", "password")) {
+    Serial.println("failed to connect, we should reset as see if it connects");
+    delay(3000);
+    ESP.reset();
+    delay(5000);
+  }
+
+  //if you get here you have connected to the WiFi
+  Serial.println("connected...yeey :)");
+
+
+  Serial.println("local ip");
+  Serial.println(WiFi.localIP());
+}
+
+void loop() {
+  // put your main code here, to run repeatedly:
+
+
+}

+ 75 - 0
examples/AutoConnectWithStaticIP/AutoConnectWithStaticIP.ino

@@ -0,0 +1,75 @@
+#include <FS.h>                   //this needs to be first, or it all crashes and burns...
+
+#include <ESP8266WiFi.h>          //https://github.com/esp8266/Arduino
+
+//needed for library
+#include <DNSServer.h>
+#include <ESP8266WebServer.h>
+#include <WiFiManager.h>          //https://github.com/tzapu/WiFiManager
+
+
+/**************************************************************************************
+ * this example shows how to set a static IP configuration for the ESP
+ * although the IP shows in the config portal, the changes will revert 
+ * to the IP set in the source file.
+ * if you want the ability to configure and persist the new IP configuration
+ * look at the FS examples, which save the config to file
+ *************************************************************************************/
+
+
+
+//default custom static IP
+//char static_ip[16] = "10.0.1.59";
+//char static_gw[16] = "10.0.1.1";
+//char static_sn[16] = "255.255.255.0";
+
+void setup() {
+  // put your setup code here, to run once:
+  Serial.begin(115200);
+  Serial.println();
+
+  //WiFiManager
+  //Local intialization. Once its business is done, there is no need to keep it around
+  WiFiManager wifiManager;
+
+  //reset settings - for testing
+  //wifiManager.resetSettings();
+
+  //set static ip 
+  //the commented bit only works for ESP8266 core 2.1.0 or newer
+  /*IPAddress _ip,_gw,_sn;
+  _ip.fromString(static_ip);
+  _gw.fromString(static_gw);
+  _sn.fromString(static_sn);
+*/
+  IPAddress _ip = IPAddress(10, 0, 1, 78);
+  IPAddress _gw = IPAddress(10, 0, 1, 1);
+  IPAddress _sn = IPAddress(255, 255, 255, 0);
+  
+  wifiManager.setSTAStaticIPConfig(_ip, _gw, _sn);
+
+
+  //tries to connect to last known settings
+  //if it does not connect it starts an access point with the specified name
+  //here  "AutoConnectAP" with password "password"
+  //and goes into a blocking loop awaiting configuration
+  if (!wifiManager.autoConnect("AutoConnectAP", "password")) {
+    Serial.println("failed to connect, we should reset as see if it connects");
+    delay(3000);
+    ESP.reset();
+    delay(5000);
+  }
+
+  //if you get here you have connected to the WiFi
+  Serial.println("connected...yeey :)");
+
+
+  Serial.println("local ip");
+  Serial.println(WiFi.localIP());
+}
+
+void loop() {
+  // put your main code here, to run repeatedly:
+
+
+}

+ 45 - 0
examples/AutoConnectWithTimeout/AutoConnectWithTimeout.ino

@@ -0,0 +1,45 @@
+#include <ESP8266WiFi.h>          //https://github.com/esp8266/Arduino
+
+//needed for library
+#include <DNSServer.h>
+#include <ESP8266WebServer.h>
+#include <WiFiManager.h>          //https://github.com/tzapu/WiFiManager
+
+
+
+void setup() {
+  // put your setup code here, to run once:
+  Serial.begin(115200);
+  
+  //WiFiManager
+  //Local intialization. Once its business is done, there is no need to keep it around
+  WiFiManager wifiManager;
+  //reset settings - for testing
+  //wifiManager.resetSettings();
+
+  //sets timeout until configuration portal gets turned off
+  //useful to make it all retry or go to sleep
+  //in seconds
+  wifiManager.setTimeout(180);
+  
+  //fetches ssid and pass and tries to connect
+  //if it does not connect it starts an access point with the specified name
+  //here  "AutoConnectAP"
+  //and goes into a blocking loop awaiting configuration
+  if(!wifiManager.autoConnect("AutoConnectAP")) {
+    Serial.println("failed to connect and hit timeout");
+    delay(3000);
+    //reset and try again, or maybe put it to deep sleep
+    ESP.reset();
+    delay(5000);
+  } 
+
+  //if you get here you have connected to the WiFi
+  Serial.println("connected...yeey :)");
+ 
+}
+
+void loop() {
+  // put your main code here, to run repeatedly:
+
+}

+ 60 - 0
examples/OnDemandConfigPortal/OnDemandConfigPortal.ino

@@ -0,0 +1,60 @@
+#include <ESP8266WiFi.h>          //https://github.com/esp8266/Arduino
+
+//needed for library
+#include <ESP8266WebServer.h>
+#include <DNSServer.h>
+#include <WiFiManager.h>          //https://github.com/tzapu/WiFiManager
+
+// select wich pin will trigger the configuraton 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
+
+
+void setup() {
+  // put your setup code here, to run once:
+  Serial.begin(115200);
+  Serial.println("\n Starting");
+
+  pinMode(TRIGGER_PIN, INPUT);
+}
+
+
+void loop() {
+  // is configuration portal requested?
+  if ( digitalRead(TRIGGER_PIN) == LOW ) {
+    //WiFiManager
+    //Local intialization. Once its business is done, there is no need to keep it around
+    WiFiManager wifiManager;
+
+    //reset settings - for testing
+    //wifiManager.resetSettings();
+
+    //sets timeout until configuration portal gets turned off
+    //useful to make it all retry or go to sleep
+    //in seconds
+    //wifiManager.setTimeout(120);
+
+    //it starts an access point with the specified name
+    //here  "AutoConnectAP"
+    //and goes into a blocking loop awaiting configuration
+
+    //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);
+    
+    if (!wifiManager.startConfigPortal("OnDemandAP")) {
+      Serial.println("failed to connect and hit timeout");
+      delay(3000);
+      //reset and try again, or maybe put it to deep sleep
+      ESP.reset();
+      delay(5000);
+    }
+
+    //if you get here you have connected to the WiFi
+    Serial.println("connected...yeey :)");
+  }
+
+
+  // put your main code here, to run repeatedly:
+
+}

+ 39 - 0
keywords.txt

@@ -0,0 +1,39 @@
+#######################################
+# Syntax Coloring Map For WifiManager
+#######################################
+
+#######################################
+# Datatypes (KEYWORD1)
+#######################################
+
+WiFiManager	KEYWORD1
+WiFiManagerParameter KEYWORD1
+
+
+#######################################
+# Methods and Functions (KEYWORD2)
+#######################################
+autoConnect	KEYWORD2
+getSSID	KEYWORD2
+getPassword	KEYWORD2
+getConfigPortalSSID KEYWORD2
+resetSettings	KEYWORD2
+setConfigPortalTimeout	KEYWORD2
+setConnectTimeout KEYWORD2
+setDebugOutput	KEYWORD2
+setMinimumSignalQuality KEYWORD2
+setAPStaticIPConfig	KEYWORD2
+setSTAStaticIPConfig KEYWORD2
+setAPCallback	KEYWORD2
+setSaveConfigCallback KEYWORD2
+addParameter KEYWORD2
+getID KEYWORD2
+getValue KEYWORD2
+getPlaceholder KEYWORD2
+getValueLength KEYWORD2
+
+#######################################
+# Constants (LITERAL1)
+#######################################
+
+#	LITERAL1

+ 13 - 0
library.json

@@ -0,0 +1,13 @@
+{
+  "name": "ESPAsyncWifiManager",
+  "keywords": "wifi, wi-fi",
+  "description": "ESP8266 WiFi Connection manager with fallback web configuration portal",
+  "repository":
+  {
+    "type": "git",
+    "url": "https://github.com/tzapu/WiFiManager.git"
+  },
+  "frameworks": "arduino",
+  "platforms": "espressif",
+  "version": "0.12"
+}

+ 9 - 0
library.properties

@@ -0,0 +1,9 @@
+name=ESPAsyncWiFiManager
+version=0.12
+author=tzapu
+maintainer=tzapu
+sentence=ESP8266 WiFi Connection manager with fallback web configuration portal
+paragraph=Library for configuring ESP8266 modules WiFi credentials at runtime.
+category=Communication
+url=https://github.com/tzapu/WiFiManager.git
+architectures=esp8266