/**************************************************************************************************************************** ESP_WiFiManager.hpp For ESP8266 / ESP32 boards ESP_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/blob/master/libraries/DNSServer/examples/CaptivePortalAdvanced/ Modified from Tzapu https://github.com/tzapu/WiFiManager and from Ken Taylor https://github.com/kentaylor Built by Khoi Hoang https://github.com/khoih-prog/ESP_WiFiManager Licensed under MIT license Version: 1.11.0 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K Hoang 07/10/2019 Initial coding ... 1.8.0 K Hoang 29/12/2021 Fix `multiple-definitions` linker error and weird bug related to src_cpp 1.9.0 K Hoang 17/01/2022 Enable compatibility with old code to include only ESP_WiFiManager.h 1.10.0 K Hoang 10/02/2022 Add support to new ESP32-S3 1.10.1 K Hoang 11/02/2022 Add LittleFS support to ESP32-C3. Use core LittleFS instead of Lorol's LITTLEFS for v2.0.0+ 1.10.2 K Hoang 13/03/2022 Send CORS header in handleWifiSave() function 1.11.0 K Hoang 09/09/2022 Fix ESP32 chipID and add ESP_getChipOUI() *****************************************************************************************************************************/ #pragma once #ifndef ESP_WiFiManager_hpp #define ESP_WiFiManager_hpp #if !( defined(ESP8266) || defined(ESP32) ) #error This code is intended to run on the ESP8266 or ESP32 platform! Please check your Tools->Board setting. #elif ( ARDUINO_ESP32S2_DEV || ARDUINO_FEATHERS2 || ARDUINO_ESP32S2_THING_PLUS || ARDUINO_MICROS2 || \ ARDUINO_METRO_ESP32S2 || ARDUINO_MAGTAG29_ESP32S2 || ARDUINO_FUNHOUSE_ESP32S2 || \ ARDUINO_ADAFRUIT_FEATHER_ESP32S2_NOPSRAM ) #if (_WIFIMGR_LOGLEVEL_ > 3) #warning Using ESP32_S2. To follow library instructions to install esp32-s2 core and WebServer Patch #warning You have to select HUGE APP or 1.9-2.0 MB APP to be able to run Config Portal. Must use PSRAM #endif #define USING_ESP32_S2 true #elif ( ARDUINO_ESP32C3_DEV ) #if (_WIFIMGR_LOGLEVEL_ > 3) #if ( defined(ESP_ARDUINO_VERSION_MAJOR) && (ESP_ARDUINO_VERSION_MAJOR >= 2) ) #warning Using ESP32_C3 using core v2.0.0+. Either LittleFS, SPIFFS or EEPROM OK. #else #warning Using ESP32_C3 using core v1.0.6-. To follow library instructions to install esp32-c3 core. Only SPIFFS and EEPROM OK. #endif #endif // #warning You have to select Flash size 2MB and Minimal APP (1.3MB + 700KB) for some boards #define USING_ESP32_C3 true #elif ( defined(ARDUINO_ESP32S3_DEV) || defined(ARDUINO_ESP32_S3_BOX) || defined(ARDUINO_TINYS3) || \ defined(ARDUINO_PROS3) || defined(ARDUINO_FEATHERS3) ) #if (_WIFIMGR_LOGLEVEL_ > 3) #warning Using ESP32_S3. To install esp32-s3-support branch if using core v2.0.2- #endif #define USING_ESP32_S3 true #endif #define ESP_WIFIMANAGER_VERSION "ESP_WiFiManager v1.11.0" #define ESP_WIFIMANAGER_VERSION_MAJOR 1 #define ESP_WIFIMANAGER_VERSION_MINOR 11 #define ESP_WIFIMANAGER_VERSION_PATCH 0 #define ESP_WIFIMANAGER_VERSION_INT 1011000 #include "ESP_WiFiManager_Debug.h" #if ( defined(HTTP_PORT) && (HTTP_PORT < 65536) && (HTTP_PORT > 0) ) #if (_WIFIMGR_LOGLEVEL_ > 3) #warning Using custom HTTP_PORT #endif #define HTTP_PORT_TO_USE HTTP_PORT #else #if (_WIFIMGR_LOGLEVEL_ > 3) #warning Using default HTTP_PORT = 80 #endif #define HTTP_PORT_TO_USE 80 #endif //KH, for ESP32 #ifdef ESP8266 #include #include #else //ESP32 #include #include #endif #include #include #undef min #undef max #include //KH, for ESP32 #ifdef ESP8266 extern "C" { #include "user_interface.h" } #define ESP_getChipId() (ESP.getChipId()) #else //ESP32 #include uint32_t getChipID(); uint32_t getChipOUI(); #if defined(ESP_getChipId) #undef ESP_getChipId #endif #define ESP_getChipId() getChipID() // ((uint32_t)ESP.getEfuseMac()) #define ESP_getChipOUI() getChipOUI() // ((uint32_t)ESP.getEfuseMac()) #endif // New in v1.4.0 typedef struct { IPAddress _ap_static_ip; IPAddress _ap_static_gw; IPAddress _ap_static_sn; } WiFi_AP_IPConfig; // Thanks to @Amorphous for the feature and code // (https://community.blynk.cc/t/esp-wifimanager-for-esp32-and-esp8266/42257/13) // To enable to configure from sketch #if !defined(USE_CONFIGURABLE_DNS) #define USE_CONFIGURABLE_DNS false #endif typedef struct { IPAddress _sta_static_ip; IPAddress _sta_static_gw; IPAddress _sta_static_sn; IPAddress _sta_static_dns1; IPAddress _sta_static_dns2; } WiFi_STA_IPConfig; ////// #define WFM_LABEL_BEFORE 1 #define WFM_LABEL_AFTER 2 #define WFM_NO_LABEL 0 /** Handle CORS in pages */ // Default false for using only whenever necessary to avoid security issue when using CORS (Cross-Origin Resource Sharing) #ifndef USING_CORS_FEATURE // Contributed by AlesSt (https://github.com/AlesSt) to solve AJAX CORS protection problem of API redirects on client side // See more in https://github.com/khoih-prog/ESP_WiFiManager/issues/27 and https://en.wikipedia.org/wiki/Cross-origin_resource_sharing #define USING_CORS_FEATURE false #endif //KH //Mofidy HTTP_HEAD to WM_HTTP_HEAD_START to avoid conflict in Arduino esp8266 core 2.6.0+ const char WM_HTTP_200[] PROGMEM = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n"; const char WM_HTTP_HEAD_START[] PROGMEM = "{v}"; // KH, update from v1.0.10 const char WM_HTTP_STYLE[] PROGMEM = ""; ////// // KH, update from v1.1.0 const char WM_HTTP_SCRIPT[] PROGMEM = ""; ////// // From v1.0.9 to permit disable or configure NTP from sketch #ifndef USE_ESP_WIFIMANAGER_NTP // From v1.0.6 to enable NTP config #define USE_ESP_WIFIMANAGER_NTP true #endif #if USE_ESP_WIFIMANAGER_NTP #include "utils/TZ.h" const char WM_HTTP_SCRIPT_NTP_MSG[] PROGMEM = "

Your Timezone is :

"; const char WM_HTTP_SCRIPT_NTP_HIDDEN[] PROGMEM = "

"; #if ESP8266 #if !(USE_CLOUDFLARE_NTP) #undef USE_CLOUDFLARE_NTP #define USE_CLOUDFLARE_NTP true #if (_WIFIMGR_LOGLEVEL_ > 3) #warning Forcing USE_CLOUDFLARE_NTP for ESP8266 as low memory can cause blank page #endif #endif #endif // To permit disable or configure NTP from sketch #ifndef USE_CLOUDFLARE_NTP #define USE_CLOUDFLARE_NTP false #endif #if USE_CLOUDFLARE_NTP const char WM_HTTP_SCRIPT_NTP[] PROGMEM = ""; #else const char WM_HTTP_SCRIPT_NTP[] PROGMEM = ""; #endif #else const char WM_HTTP_SCRIPT_NTP_MSG[] PROGMEM = ""; const char WM_HTTP_SCRIPT_NTP_HIDDEN[] PROGMEM = ""; const char WM_HTTP_SCRIPT_NTP[] PROGMEM = ""; #endif // KH, update from v1.0.10 const char WM_HTTP_HEAD_END[] PROGMEM = "
"; const char WM_FLDSET_START[] PROGMEM = "
"; const char WM_FLDSET_END[] PROGMEM = "
"; ////// const char WM_HTTP_PORTAL_OPTIONS[] PROGMEM = "



"; const char WM_HTTP_ITEM[] PROGMEM = "
{v} {r}%
"; const char JSON_ITEM[] PROGMEM = "{\"SSID\":\"{v}\", \"Encryption\":{i}, \"Quality\":\"{r}\"}"; // KH, update from v1.1.0 const char WM_HTTP_FORM_START[] PROGMEM = "
"; ////// // KH, add from v1.0.10 const char WM_HTTP_FORM_LABEL_BEFORE[] PROGMEM = "
"; const char WM_HTTP_FORM_LABEL_AFTER[] PROGMEM = "
"; ////// const char WM_HTTP_FORM_LABEL[] PROGMEM = ""; const char WM_HTTP_FORM_PARAM[] PROGMEM = ""; const char WM_HTTP_FORM_END[] PROGMEM = "
"; // KH, update from v1.1.0 const char WM_HTTP_SAVED[] PROGMEM = "
Credentials Saved
Try connecting ESP to the {x}/{x1} network. Wait around 10 seconds then check if it's OK.

The {v} AP will run on the same WiFi channel of the {x}/{x1} AP. You may have to manually reconnect to the {v} AP.

"; ////// const char WM_HTTP_END[] PROGMEM = "
"; //KH, from v1.1.0 const char WM_HTTP_HEAD_CL[] PROGMEM = "Content-Length"; const char WM_HTTP_HEAD_CT[] PROGMEM = "text/html"; const char WM_HTTP_HEAD_CT2[] PROGMEM = "text/plain"; //KH Add repeatedly used const const char WM_HTTP_CACHE_CONTROL[] PROGMEM = "Cache-Control"; const char WM_HTTP_NO_STORE[] PROGMEM = "no-cache, no-store, must-revalidate"; const char WM_HTTP_PRAGMA[] PROGMEM = "Pragma"; const char WM_HTTP_NO_CACHE[] PROGMEM = "no-cache"; const char WM_HTTP_EXPIRES[] PROGMEM = "Expires"; const char WM_HTTP_CORS[] PROGMEM = "Access-Control-Allow-Origin"; const char WM_HTTP_CORS_ALLOW_ALL[] PROGMEM = "*"; #if USE_AVAILABLE_PAGES const char WM_HTTP_AVAILABLE_PAGES[] PROGMEM = "

Available Pages

PageFunction
/Menu page.
/wifiShow WiFi scan results and enter WiFi configuration.
/wifisaveSave WiFi configuration information and configure device. Needs variables supplied.
/closeClose the configuration server and configuration WiFi network.
/iThis page.
/rDelete WiFi configuration and reboot. ESP device will not reconnect to a network until new WiFi configuration data is entered.
/stateCurrent device state in JSON format. Interface for programmatic WiFi configuration.
/scanRun a WiFi scan and return results in JSON format. Interface for programmatic WiFi configuration.
"; #else const char WM_HTTP_AVAILABLE_PAGES[] PROGMEM = ""; #endif //KH #define WIFI_MANAGER_MAX_PARAMS 20 ///////////////////////////////////////////////////////////////////////////// // New in v1.4.0 typedef struct { const char *_id; const char *_placeholder; char *_value; int _length; int _labelPlacement; } WMParam_Data; ////// class ESP_WMParameter { public: ESP_WMParameter(const char *custom); ESP_WMParameter(const char *id, const char *placeholder, const char *defaultValue, const int& length, const char *custom = "", const int& labelPlacement = WFM_LABEL_BEFORE); // New in v1.4.0 ESP_WMParameter(const WMParam_Data& WMParam_data); ////// ~ESP_WMParameter(); // New in v1.4.0 void setWMParam_Data(const WMParam_Data& WMParam_data); void getWMParam_Data(WMParam_Data& WMParam_data); ////// const char *getID(); const char *getValue(); const char *getPlaceholder(); int getValueLength(); int getLabelPlacement(); const char *getCustomHTML(); private: WMParam_Data _WMParam_data; const char *_customHTML; void init(const char *id, const char *placeholder, const char *defaultValue, const int& length, const char *custom, const int& labelPlacement); friend class ESP_WiFiManager; }; #define USE_DYNAMIC_PARAMS true #define DEFAULT_PORTAL_TIMEOUT 60000L // From v1.0.10 to permit disable/enable StaticIP configuration in Config Portal from sketch. Valid only if DHCP is used. // You have to explicitly specify false to disable the feature. #ifndef USE_STATIC_IP_CONFIG_IN_CP #define USE_STATIC_IP_CONFIG_IN_CP true #endif class ESP_WiFiManager { public: ESP_WiFiManager(const char *iHostname = ""); ~ESP_WiFiManager(); // Update feature from v1.0.11. Can use with STA staticIP now bool autoConnect(); bool autoConnect(char const *apName, char const *apPassword = NULL); ////// //if you want to start the config portal bool startConfigPortal(); bool 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(); // get the AP password of the config portal, so it can be used in the callback String getConfigPortalPW(); 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(const unsigned long& seconds); void setTimeout(const unsigned long& seconds); //sets timeout for which to attempt connecting, usefull if you get a lot of failed connects void setConnectTimeout(const unsigned long& seconds); void setDebugOutput(bool debug); //defaults to not showing anything under 8% signal quality if called void setMinimumSignalQuality(const int& quality = 8); // KH, new from v1.0.10 to enable dynamic/random channel int setConfigPortalChannel(const int& channel = 1); ////// //sets a custom ip /gateway /subnet configuration void setAPStaticIPConfig(const IPAddress& ip, const IPAddress& gw, const IPAddress& sn); // New in v1.4.0 void setAPStaticIPConfig(const WiFi_AP_IPConfig& WM_AP_IPconfig); void getAPStaticIPConfig(WiFi_AP_IPConfig& WM_AP_IPconfig); ////// //sets config for a static IP void setSTAStaticIPConfig(const IPAddress& ip, const IPAddress& gw, const IPAddress& sn); // New in v1.4.0 void setSTAStaticIPConfig(const WiFi_STA_IPConfig& WM_STA_IPconfig); void getSTAStaticIPConfig(WiFi_STA_IPConfig& WM_STA_IPconfig); ////// #if USE_CONFIGURABLE_DNS void setSTAStaticIPConfig(const IPAddress& ip, const IPAddress& gw, const IPAddress& sn, const IPAddress& dns_address_1, const IPAddress& dns_address_2); #endif //called when AP mode and config portal is started void setAPCallback(void(*func)(ESP_WiFiManager*)); //called when settings have been changed and connection was successful void setSaveConfigCallback(void(*func)()); #if USE_DYNAMIC_PARAMS //adds a custom parameter bool addParameter(ESP_WMParameter *p); #else //adds a custom parameter void addParameter(ESP_WMParameter *p); #endif //if this is set, it will exit after config, even if connection is unsucessful. void setBreakAfterConfig(bool 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(bool removeDuplicates); //Scan for WiFiNetworks in range and sort by signal strength //space for indices array allocated on the heap and should be freed when no longer required int scanWifiNetworks(int **indicesptr); // return SSID of router in STA mode got from config portal. NULL if no user's input //KH String getSSID() { return _ssid; } // return password of router in STA mode got from config portal. NULL if no user's input //KH String getPW() { return _pass; } // New from v1.1.0 // return SSID of router in STA mode got from config portal. NULL if no user's input //KH String getSSID1() { return _ssid1; } // return password of router in STA mode got from config portal. NULL if no user's input //KH String getPW1() { return _pass1; } #define MAX_WIFI_CREDENTIALS 2 String getSSID(const uint8_t& index) { if (index == 0) return _ssid; else if (index == 1) return _ssid1; else return String(""); } String getPW(const uint8_t& index) { if (index == 0) return _pass; else if (index == 1) return _pass1; else return String(""); } ////// // New from v1.1.1, for configure CORS Header, default to WM_HTTP_CORS_ALLOW_ALL = "*" #if USING_CORS_FEATURE void setCORSHeader(const char* CORSHeaders) { _CORS_Header = CORSHeaders; LOGWARN1(F("Set CORS Header to : "), _CORS_Header); } const char* getCORSHeader() { return _CORS_Header; } #endif //returns the list of Parameters ESP_WMParameter** getParameters(); // returns the Parameters Count int getParametersCount(); const char* getStatus(const int& status); #ifdef ESP32 String getStoredWiFiSSID(); String getStoredWiFiPass(); #endif String WiFi_SSID() { #ifdef ESP8266 return WiFi.SSID(); #else return getStoredWiFiSSID(); #endif } String WiFi_Pass() { #ifdef ESP8266 return WiFi.psk(); #else return getStoredWiFiPass(); #endif } void setHostname() { if (RFC952_hostname[0] != 0) { #if ESP8266 WiFi.hostname(RFC952_hostname); #else // Check cores/esp32/esp_arduino_version.h and cores/esp32/core_version.h //#if ( ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(2, 0, 0) ) //(ESP_ARDUINO_VERSION_MAJOR >= 2) #if ( defined(ESP_ARDUINO_VERSION_MAJOR) && (ESP_ARDUINO_VERSION_MAJOR >= 2) ) WiFi.setHostname(RFC952_hostname); #else // Still have bug in ESP32_S2 for old core. If using WiFi.setHostname() => WiFi.localIP() always = 255.255.255.255 if ( String(ARDUINO_BOARD) != "ESP32S2_DEV" ) { // See https://github.com/espressif/arduino-esp32/issues/2537 WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE); WiFi.setHostname(RFC952_hostname); } #endif #endif } } #if USE_ESP_WIFIMANAGER_NTP String getTimezoneName() { return _timezoneName; } void setTimezoneName(const String& inTimezoneName) { _timezoneName = inTimezoneName; } ////// //See: https://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html // EST5EDT,M3.2.0,M11.1.0 (for America/New_York) // EST5EDT is the name of the time zone // EST is the abbreviation used when DST is off // 6 hours is the time difference from GMT // EDT is the abbreviation used when DST is on // ,M3 is the third month // .2 is the second occurrence of the day in the month // .0 is Sunday // ,M11 is the eleventh month // .1 is the first occurrence of the day in the month // .0 is Sunday const char * getTZ(const char * timezoneName) { //const char TZ_NAME[][TIMEZONE_MAX_LEN] for (uint16_t index = 0; index < sizeof(TZ_NAME) / TIMEZONE_MAX_LEN; index++) { if ( !strncmp(timezoneName, (TZ_NAME[index]), strlen((TZ_NAME[index])) ) ) { yield(); return (ESP_TZ_NAME[index]); } } return ""; } const char * getTZ(const String& timezoneName) { return getTZ(timezoneName.c_str()); } #endif private: std::unique_ptr dnsServer; //KH, for ESP32 #ifdef ESP8266 std::unique_ptr server; #else //ESP32 std::unique_ptr server; #endif #define RFC952_HOSTNAME_MAXLEN 24 char RFC952_hostname[RFC952_HOSTNAME_MAXLEN + 1]; char* getRFC952_hostname(const char* iHostname); void setupConfigPortal(); void startWPS(); //const char* getStatus(const int& status); const char* _apName = "no-net"; const char* _apPassword = NULL; String _ssid = ""; String _pass = ""; // New from v1.1.0 String _ssid1 = ""; String _pass1 = ""; ////// #if USE_ESP_WIFIMANAGER_NTP // Timezone info String _timezoneName = ""; #endif unsigned long _configPortalTimeout = 0; unsigned long _connectTimeout = 0; unsigned long _configPortalStart = 0; int numberOfNetworks; int *networkIndices; // KH, new from v1.0.10 to enable dynamic/random channel // default to channel 1 #define MIN_WIFI_CHANNEL 1 #define MAX_WIFI_CHANNEL 11 // Channel 12,13 is flaky, because of bad number 13 ;-) int _WiFiAPChannel = 1; ////// // New in v1.4.0 WiFi_AP_IPConfig _WiFi_AP_IPconfig; WiFi_STA_IPConfig _WiFi_STA_IPconfig = { IPAddress(0, 0, 0, 0), IPAddress(192, 168, 2, 1), IPAddress(255, 255, 255, 0), IPAddress(192, 168, 2, 1), IPAddress(8, 8, 8, 8) }; ////// int _paramsCount = 0; int _minimumQuality = -1; bool _removeDuplicateAPs = true; bool _shouldBreakAfterConfig = false; bool _tryWPS = false; const char* _customHeadElement = ""; int status = WL_IDLE_STATUS; // New from v1.1.0, for configure CORS Header, default to WM_HTTP_CORS_ALLOW_ALL = "*" #if USING_CORS_FEATURE const char* _CORS_Header = WM_HTTP_CORS_ALLOW_ALL; //"*"; #endif ////// // New v1.0.8 void setWifiStaticIP(); // New v1.1.0 int reconnectWifi(); ////// // New v1.0.11 int connectWifi(const String& ssid = "", const String& pass = ""); ////// uint8_t waitForConnectResult(); void handleRoot(); void handleWifi(); void handleWifiSave(); void handleServerClose(); void handleInfo(); void handleState(); void handleScan(); void handleReset(); void handleNotFound(); bool captivePortal(); void reportStatus(String& page); // DNS server const byte DNS_PORT = 53; //helpers int getRSSIasQuality(const int& RSSI); bool isIp(const String& str); String toStringIp(const IPAddress& ip); bool connect; bool stopConfigPortal = false; bool _debug = false; //true; void(*_apcallback) (ESP_WiFiManager*) = NULL; void(*_savecallback)() = NULL; #if USE_DYNAMIC_PARAMS int _max_params; ESP_WMParameter** _params; #else ESP_WMParameter* _params[WIFI_MANAGER_MAX_PARAMS]; #endif template void DEBUG_WM(Generic text); template auto optionalIPFromString(T *obj, const char *s) -> decltype(obj->fromString(s)) { return obj->fromString(s); } auto optionalIPFromString(...) -> bool { LOGINFO("NO fromString METHOD ON IPAddress, you need ESP8266 core 2.1.0 or newer for Custom IP configuration to work."); return false; } }; #endif // ESP_WiFiManager_h