- Why do we need this ESPAsync_WiFiManager library
- Changelog
- Features
- Prerequisites
- Installation
- HOWTO Fix
Multiple Definitions
Linker Error - HOWTO Use analogRead() with ESP32 running WiFi and/or BlueTooth (BT/BLE)
- How It Works
- HOWTO Basic configurations
- 1. Using default for every configurable parameter
- 2. Using many configurable parameters
- 3. Using STA-mode DHCP, but don't like to change to static IP or display in Config Portal
- 4. Using STA-mode DHCP, but permit to change to static IP and display in Config Portal
- 5. Using STA-mode StaticIP, and be able to change to DHCP IP and display in Config Portal
- 6. Using STA-mode StaticIP and configurable DNS, and be able to change to DHCP IP and display in Config Portal
- 7. Using STA-mode StaticIP and auto DNS, and be able to change to DHCP IP and display in Config Portal
- 8. Not using NTP to avoid issue with some WebBrowsers, especially in CellPhone or Tablets.
- 9. Using NTP feature with CloudFlare. System can hang until you have Internet access for CloudFlare.
- 10. Using NTP feature without CloudFlare to avoid system hang if no Internet access for CloudFlare.
- 11. Using random AP-mode channel to avoid conflict
- 12. Using fixed AP-mode channel, for example channel 3
- 13. Setting STA-mode static IP
- 14. Using AUTOCONNECT_NO_INVALIDATE feature
- 15. Using CORS (Cross-Origin Resource Sharing) feature
- 16. Using MultiWiFi auto(Re)connect feature
- HOWTO Open Config Portal
- HOWTO Add Dynamic Parameters
- 1. Determine the variables to be configured via Config Portal (CP)
- 2. Initialize the variables to prepare for Config Portal (CP)
- 2.1 Use the following simple constructor for simple variables such as
thingspeakApiKey
,pinSda
andpinScl
- 2.2 For example, to create a new
ESPAsync_WMParameter
objectp_thingspeakApiKey
forthingspeakApiKey
- 2.3 Use the more complex following constructor for variables such as
sensorDht22
- 2.4 For example, to create a new
ESPAsync_WMParameter
objectp_sensorDht22
forsensorDht22
- 2.1 Use the following simple constructor for simple variables such as
- 3. Add the variables to Config Portal (CP)
- 4. Save the variables configured in Config Portal (CP)
- 5. Write to FS (SPIFFS, LittleFS, etc.) using JSON format
- 6. Read from FS using JSON format
- So, how it works?
- Documentation
- Examples
- Medium Complexity
- Async_ConfigOnSwitch
- Async_ConfigOnSwitchFS
- Async_ConfigOnStartup
- Async_ConfigOnDoubleReset (now support ArduinoJson 6.0.0+ as well as 5.13.5-)
- Async_ConfigPortalParamsOnSwitch (now support ArduinoJson 6.0.0+ as well as 5.13.5-)
- Async_AutoConnect
- Async_AutoConnectWithFeedback
- Async_AutoConnectWithFeedbackLED
- Async_AutoConnectWithFSParameters
- Async_ConfigOnSwitchFS_MQTT_Ptr
- Async_AutoConnectWithFSParametersAndCustomIP
- ModelessConnect
- ModelessWithInterrupts
- Async_ESP32_FSWebServer
- Async_ESP32_FSWebServer_DRD
- Async_ESP_FSWebServer
- Async_ESP_FSWebServer_DRD
- Async_ConfigOnDRD_FS_MQTT_Ptr
- High Complexity
- Simplest Possible
- Medium Complexity
- Example Async_ConfigOnDRD_FS_MQTT_Ptr
- Debug Terminal Output Samples
- Debug
- Troubleshooting
- Issues
- Releases
- Contributions and Thanks
- Contributing
- License and credits
- Copyright
Why do we need this Async ESPAsync_WiFiManager library
- Using asynchronous network means that you can handle more than one connection at the same time
- You are called once the request is ready and parsed
- When you send the response, you are immediately ready to handle other connections while the server is taking care of sending the response in the background
- Speed is OMG
- Easy to use API, HTTP Basic and Digest MD5 Authentication (default), ChunkedResponse
- Easily extensible to handle any type of content
- Supports Continue 100
- Async WebSocket plugin offering different locations without extra servers or ports
- Async EventSource (Server-Sent Events) plugin to send events to the browser
- URL Rewrite plugin for conditional and permanent url rewrites
- ServeStatic plugin that supports cache, Last-Modified, default index and more
- Simple template processing engine to handle templates
To appreciate the power of the ESPAsyncWebServer and underlying Async libraries, please compare the more efficient Async_ESP32_FSWebServer example example with the complicated twin ESP32_FSWebServer.
- Fix bug.
- Fix compiler warnings.
- Fix staticIP not saved in examples. See ESP32 static IP not saved after restarting the device
- Add structures and functions to handle AP and STA IPs.
- Add complex examples
- Async_ConfigOnDRD_FS_MQTT_Ptr_Complex to demo usage of std::map
- Async_ConfigOnDRD_FS_MQTT_Ptr_Medium.
- Add simple minimal examples
- Async_ConfigOnDRD_ESP32_minimal
- Async_ConfigOnDRD_ESP8266_minimal
- Async_AutoConnect_ESP32_minimal
- Async_AutoConnect_ESP8266_minimal
- Modify Version String
- Add Table of Contents
- Add LittleFS support to ESP32-related examples to use LittleFS_esp32 Library
- Add Version String
- Restore cpp code besides Impl.h code to use in case of
multiple definition
linker error. SeeChange Implementation to seperate *.h and *.cpp file instead of *.h and *-Impl.h
andSupport building in PlatformIO PR
. Also have a look at HOWTO Fix Multiple Definitions Linker Error - Fix bug /close does not close the config portal.
- Fix bug in examples.
- Add MultiWiFi feature to auto(Re)connect to the best WiFi at runtime
- Fix bug, typo and minor improvement.
- Completely enhanced examples to use new MultiWiFi feature.
- Add setCORSHeader function to allow configurable CORS Header. See Using CORS feature
- Bump up to v1.1.1 to sync with ESP_WiFiManager v1.1.1.
- Initial coding to use ESPAsyncWebServer instead of (ESP8266)WebServer.
- Add more features and error checking to many examples.
- Add example Async_ConfigOnDRD_FS_MQTT_Ptr
- Bump up to v1.0.11 to sync with ESP_WiFiManager v1.0.11.
This library is based on, modified, bug-fixed and improved from:
Tzapu's WiFiManager
Ken Taylor's WiFiManager
Alan Steremberg's ESPAsyncWiFiManager
Khoi Hoang's ESP_WiFiManager
to use the better and faster asynchronous ESPAsyncWebServer instead of (ESP8266)WebServer.
This is an ESP32 / ESP8266
WiFi Connection Manager with fallback Web ConfigPortal. This Library is used for configuring ESP32, ESP8266 modules' (WiFi / Dynamic) Credentials at runtime. You can also specify static DNS servers, personalized HostName, fixed or random AP channel. Now with CORS feature.
Thanks to this ESPAsync_WiFiManager library is based on and sync'ed with ESP_WiFiManager
, all the features currently supported by ESP_WiFiManager
will be available. Please have a look at ESP_WiFiManager
for those too-many-to-list features.
Arduino IDE 1.8.13+
for ArduinoESP8266 Core 2.7.4+
for ESP8266-based boards.ESP32 Core 1.0.4+
for ESP32-based boardsESPAsyncWebServer v1.2.3+
for all ESP32/ESP8266-based boards.ESPAsyncTCP v1.2.2+
for ESP8266-based boards.AsyncTCP v1.1.1+
for ESP32-based boardsESP_DoubleResetDetector v1.1.0+
if using DRD feature. To install, check. Use v1.1.0+ if using LittleFS for EP32.
LittleFS_esp32 v1.0.5+
for ESP32-based boards using LittleFS.
The best and easiest way is to use Arduino Library Manager
. Search for ESPAsync_WiFiManager
, then select / install the latest version. You can also use this link for more detailed instructions.
- Navigate to ESPAsync_WiFiManager page.
- Download the latest release
ESPAsync_WiFiManager-master.zip
. - Extract the zip file to
ESPAsync_WiFiManager-master
directory - Copy the whole
ESPAsync_WiFiManager-master
folder to Arduino libraries' directory such as~/Arduino/libraries/
.
- Install VS Code
- Install PlatformIO
- Install ESPAsync_WiFiManager library by using Library Manager. Search for ESPAsync_WiFiManager in Platform.io Author's Libraries
- Use included platformio.ini file from examples to ensure that all dependent libraries will installed automatically. Please visit documentation for the other options and examples at Project Configuration File
The current library implementation, using xyz-Impl.h instead of standard xyz.cpp, possibly creates certain Multiple Definitions
Linker error in certain use cases. Although it's simple to just modify several lines of code, either in the library or in the application, the library is adding 2 more source directories
- scr_h for new h-only files
- src_cpp for standard h/cpp files
besides the standard src directory.
To use the old standard cpp way, locate this library' directory, then just
- Delete the all the files in src directory.
- Copy all the files in src_cpp directory into src.
- Close then reopen the application code in Arduino IDE, etc. to recompile from scratch.
To re-use the new h-only way, just
- Delete the all the files in src directory.
- Copy the files in src_h directory into src.
- Close then reopen the application code in Arduino IDE, etc. to recompile from scratch.
Please have a look at ESP_WiFiManager Issue 39: Not able to read analog port when using the autoconnect example to have more detailed description and solution of the issue.
- ADC1 controls ADC function for pins GPIO32-GPIO39
- ADC2 controls ADC function for pins GPIO0, 2, 4, 12-15, 25-27
Look in file adc_common.c
In ADC2, there're two locks used for different cases:
lock shared with app and Wi-Fi: ESP32: When Wi-Fi using the ADC2, we assume it will never stop, so app checks the lock and returns immediately if failed. ESP32S2: The controller's control over the ADC is determined by the arbiter. There is no need to control by lock.
lock shared between tasks: when several tasks sharing the ADC2, we want to guarantee all the requests will be handled. Since conversions are short (about 31us), app returns the lock very soon, we use a spinlock to stand there waiting to do conversions one by one.
adc2_spinlock should be acquired first, then adc2_wifi_lock or rtc_spinlock.
- In order to use ADC2 for other functions, we have to acquire complicated firmware locks and very difficult to do
- So, it's not advisable to use ADC2 with WiFi/BlueTooth (BT/BLE).
- Use ADC1, and pins GPIO32-GPIO39
- If somehow it's a must to use those pins serviced by ADC2 (GPIO0, 2, 4, 12, 13, 14, 15, 25, 26 and 27), use the fix mentioned at the end of ESP_WiFiManager Issue 39: Not able to read analog port when using the autoconnect example to work with ESP32 WiFi/BlueTooth (BT/BLE).
- The Async_ConfigOnSwitch example shows how it works and should be used as the basis for a sketch that uses this library.
- The concept of Async_ConfigOnSwitch is that a new
ESP32 / ESP8266
will start a WiFi ConfigPortal when powered up and save the configuration data in non volatile memory. Thereafter, the ConfigPortal will only be started again if a button is pushed on theESP32 / ESP8266
module. - Using any WiFi enabled device with a browser (computer, phone, tablet) connect to the newly created Access Point (AP) using configurable SSID and Password (specified in sketch)
// SSID and PW for Config Portal
String ssid = "ESP_" + String(ESP_getChipId(), HEX);
const char* password = "your_password";
then connect WebBrowser to configurable ConfigPortal IP address, default is 192.168.4.1
- Choose one of the access points scanned, enter password, click Save.
- ESP will restart, then try to connect to the WiFi netwotk using STA-only mode, without running the ConfigPortal WebServer and WiFi AP. See Accessing manager after connection.
- Include in your sketch
#ifdef ESP32
#include <esp_wifi.h>
#include <WiFi.h>
#include <WiFiClient.h>
// From v1.1.1
#include <WiFiMulti.h>
WiFiMulti wifiMulti;
// LittleFS has higher priority than SPIFFS
#define USE_LITTLEFS true
#define USE_SPIFFS false
#if USE_LITTLEFS
// Use LittleFS
#include "FS.h"
// The library will be depreciated after being merged to future major Arduino esp32 core release 2.x
// At that time, just remove this library inclusion
#include <LITTLEFS.h> // https://github.com/lorol/LITTLEFS
FS* filesystem = &LITTLEFS;
#define FileFS LITTLEFS
#define FS_Name "LittleFS"
#elif USE_SPIFFS
#include <SPIFFS.h>
FS* filesystem = &SPIFFS;
#define FileFS SPIFFS
#define FS_Name "SPIFFS"
#else
// +Use FFat
#include <FFat.h>
FS* filesystem = &FFat;
#define FileFS FFat
#define FS_Name "FFat"
#endif
//////
#define ESP_getChipId() ((uint32_t)ESP.getEfuseMac())
#define LED_BUILTIN 2
#define LED_ON HIGH
#define LED_OFF LOW
#else
#include <ESP8266WiFi.h> //https://github.com/esp8266/Arduino
//needed for library
#include <DNSServer.h>
// From v1.1.1
#include <ESP8266WiFiMulti.h>
ESP8266WiFiMulti wifiMulti;
#define USE_LITTLEFS true
#if USE_LITTLEFS
#include <LittleFS.h>
FS* filesystem = &LittleFS;
#define FileFS LittleFS
#define FS_Name "LittleFS"
#else
FS* filesystem = &SPIFFS;
#define FileFS SPIFFS
#define FS_Name "SPIFFS"
#endif
//////
#define ESP_getChipId() (ESP.getChipId())
#define LED_ON LOW
#define LED_OFF HIGH
#endif
// SSID and PW for Config Portal
String ssid = "ESP_" + String(ESP_getChipId(), HEX);
const char* password = "your_password";
// SSID and PW for your Router
String Router_SSID;
String Router_Pass;
// From v1.1.0
// You only need to format the filesystem once
//#define FORMAT_FILESYSTEM true
#define FORMAT_FILESYSTEM false
#define MIN_AP_PASSWORD_SIZE 8
#define SSID_MAX_LEN 32
//From v1.0.10, WPA2 passwords can be up to 63 characters long.
#define PASS_MAX_LEN 64
typedef struct
{
char wifi_ssid[SSID_MAX_LEN];
char wifi_pw [PASS_MAX_LEN];
} WiFi_Credentials;
typedef struct
{
String wifi_ssid;
String wifi_pw;
} WiFi_Credentials_String;
#define NUM_WIFI_CREDENTIALS 2
typedef struct
{
WiFi_Credentials WiFi_Creds [NUM_WIFI_CREDENTIALS];
} WM_Config;
WM_Config WM_config;
#define CONFIG_FILENAME F("/wifi_cred.dat")
//////
#include <ESPAsync_WiFiManager.h> //https://github.com/khoih-prog/ESPAsync_WiFiManager
#define HTTP_PORT 80
AsyncWebServer webServer(HTTP_PORT);
DNSServer dnsServer;
- Include in your sketch
// Default is 30s, using 20s now
#define TIME_BETWEEN_MODAL_SCANS 20000
// Default is 60s, using 30s now
#define TIME_BETWEEN_MODELESS_SCANS 30000
#ifdef ESP32
#include <esp_wifi.h>
#include <WiFi.h>
#include <WiFiClient.h>
// From v1.1.1
#include <WiFiMulti.h>
WiFiMulti wifiMulti;
// LittleFS has higher priority than SPIFFS
#define USE_LITTLEFS true
#define USE_SPIFFS false
#if USE_LITTLEFS
// Use LittleFS
#include "FS.h"
// The library will be depreciated after being merged to future major Arduino esp32 core release 2.x
// At that time, just remove this library inclusion
#include <LITTLEFS.h> // https://github.com/lorol/LITTLEFS
FS* filesystem = &LITTLEFS;
#define FileFS LITTLEFS
#define FS_Name "LittleFS"
#elif USE_SPIFFS
#include <SPIFFS.h>
FS* filesystem = &SPIFFS;
#define FileFS SPIFFS
#define FS_Name "SPIFFS"
#else
// +Use FFat
#include <FFat.h>
FS* filesystem = &FFat;
#define FileFS FFat
#define FS_Name "FFat"
#endif
//////
#define ESP_getChipId() ((uint32_t)ESP.getEfuseMac())
#define LED_BUILTIN 2
#define LED_ON HIGH
#define LED_OFF LOW
#else
#include <ESP8266WiFi.h> //https://github.com/esp8266/Arduino
//needed for library
#include <DNSServer.h>
// From v1.1.1
#include <ESP8266WiFiMulti.h>
ESP8266WiFiMulti wifiMulti;
#define USE_LITTLEFS true
#if USE_LITTLEFS
#include <LittleFS.h>
FS* filesystem = &LittleFS;
#define FileFS LittleFS
#define FS_Name "LittleFS"
#else
FS* filesystem = &SPIFFS;
#define FileFS SPIFFS
#define FS_Name "SPIFFS"
#endif
//////
#define ESP_getChipId() (ESP.getChipId())
#define LED_ON LOW
#define LED_OFF HIGH
#endif
// SSID and PW for Config Portal
String ssid = "ESP_" + String(ESP_getChipId(), HEX);
const char* password = "your_password";
// SSID and PW for your Router
String Router_SSID;
String Router_Pass;
// From v1.1.0
// You only need to format the filesystem once
//#define FORMAT_FILESYSTEM true
#define FORMAT_FILESYSTEM false
#define MIN_AP_PASSWORD_SIZE 8
#define SSID_MAX_LEN 32
//From v1.0.10, WPA2 passwords can be up to 63 characters long.
#define PASS_MAX_LEN 64
typedef struct
{
char wifi_ssid[SSID_MAX_LEN];
char wifi_pw [PASS_MAX_LEN];
} WiFi_Credentials;
typedef struct
{
String wifi_ssid;
String wifi_pw;
} WiFi_Credentials_String;
#define NUM_WIFI_CREDENTIALS 2
typedef struct
{
WiFi_Credentials WiFi_Creds [NUM_WIFI_CREDENTIALS];
} WM_Config;
WM_Config WM_config;
#define CONFIG_FILENAME F("/wifi_cred.dat")
//////
// Use false if you don't like to display Available Pages in Information Page of Config Portal
// Comment out or use true to display Available Pages in Information Page of Config Portal
// Must be placed before #include <ESPAsync_WiFiManager.h>
#define USE_AVAILABLE_PAGES true
// From v1.0.10 to permit disable/enable StaticIP configuration in Config Portal from sketch. Valid only if DHCP is used.
// You'll loose the feature of dynamically changing from DHCP to static IP, or vice versa
// You have to explicitly specify false to disable the feature.
//#define USE_STATIC_IP_CONFIG_IN_CP false
// Use false to disable NTP config. Advisable when using Cellphone, Tablet to access Config Portal.
// See Issue 23: On Android phone ConfigPortal is unresponsive (https://github.com/khoih-prog/ESP_WiFiManager/issues/23)
#define USE_ESP_WIFIMANAGER_NTP false
// Use true to enable CloudFlare NTP service. System can hang if you don't have Internet access while accessing CloudFlare
// See Issue #21: CloudFlare link in the default portal (https://github.com/khoih-prog/ESP_WiFiManager/issues/21)
#define USE_CLOUDFLARE_NTP false
// New in v1.0.11
#define USING_CORS_FEATURE true
//////
// Use USE_DHCP_IP == true for dynamic DHCP IP, false to use static IP which you have to change accordingly to your network
#if (defined(USE_STATIC_IP_CONFIG_IN_CP) && !USE_STATIC_IP_CONFIG_IN_CP)
// Force DHCP to be true
#if defined(USE_DHCP_IP)
#undef USE_DHCP_IP
#endif
#define USE_DHCP_IP true
#else
// You can select DHCP or Static IP here
//#define USE_DHCP_IP true
#define USE_DHCP_IP false
#endif
#if ( USE_DHCP_IP || ( defined(USE_STATIC_IP_CONFIG_IN_CP) && !USE_STATIC_IP_CONFIG_IN_CP ) )
// Use DHCP
#warning Using DHCP IP
IPAddress stationIP = IPAddress(0, 0, 0, 0);
IPAddress gatewayIP = IPAddress(192, 168, 2, 1);
IPAddress netMask = IPAddress(255, 255, 255, 0);
#else
// Use static IP
#warning Using static IP
#ifdef ESP32
IPAddress stationIP = IPAddress(192, 168, 2, 232);
#else
IPAddress stationIP = IPAddress(192, 168, 2, 186);
#endif
IPAddress gatewayIP = IPAddress(192, 168, 2, 1);
IPAddress netMask = IPAddress(255, 255, 255, 0);
#endif
#define USE_CONFIGURABLE_DNS true
IPAddress dns1IP = gatewayIP;
IPAddress dns2IP = IPAddress(8, 8, 8, 8);
IPAddress APStaticIP = IPAddress(192, 168, 100, 1);
IPAddress APStaticGW = IPAddress(192, 168, 100, 1);
IPAddress APStaticSN = IPAddress(255, 255, 255, 0);
#include <ESPAsync_WiFiManager.h> //https://github.com/khoih-prog/ESPAsync_WiFiManager
#define HTTP_PORT 80
AsyncWebServer webServer(HTTP_PORT);
DNSServer dnsServer;
///////////////////////////////////////////
// New in v1.4.0
/******************************************
* // Defined in ESPAsync_WiFiManager.h
typedef struct
{
IPAddress _ap_static_ip;
IPAddress _ap_static_gw;
IPAddress _ap_static_sn;
} WiFi_AP_IPConfig;
typedef struct
{
IPAddress _sta_static_ip;
IPAddress _sta_static_gw;
IPAddress _sta_static_sn;
#if USE_CONFIGURABLE_DNS
IPAddress _sta_static_dns1;
IPAddress _sta_static_dns2;
#endif
} WiFi_STA_IPConfig;
******************************************/
WiFi_AP_IPConfig WM_AP_IPconfig;
WiFi_STA_IPConfig WM_STA_IPconfig;
// From v1.0.10 to permit disable/enable StaticIP configuration in Config Portal from sketch. Valid only if DHCP is used.
// You'll loose the feature of dynamically changing from DHCP to static IP, or vice versa
// You have to explicitly specify false to disable the feature.
#define USE_STATIC_IP_CONFIG_IN_CP false
// From v1.0.10 to permit disable/enable StaticIP configuration in Config Portal from sketch. Valid only if DHCP is used.
// You'll loose the feature of dynamically changing from DHCP to static IP, or vice versa
// You have to explicitly specify false to disable the feature.
//#define USE_STATIC_IP_CONFIG_IN_CP false
// Use USE_DHCP_IP == true for dynamic DHCP IP, false to use static IP which you have to change accordingly to your network
#if (defined(USE_STATIC_IP_CONFIG_IN_CP) && !USE_STATIC_IP_CONFIG_IN_CP)
// Force DHCP to be true
#if defined(USE_DHCP_IP)
#undef USE_DHCP_IP
#endif
#define USE_DHCP_IP true
#else
// You can select DHCP or Static IP here
#define USE_DHCP_IP true
#endif
// From v1.0.10 to permit disable/enable StaticIP configuration in Config Portal from sketch. Valid only if DHCP is used.
// You'll loose the feature of dynamically changing from DHCP to static IP, or vice versa
// You have to explicitly specify false to disable the feature.
//#define USE_STATIC_IP_CONFIG_IN_CP false
// Use USE_DHCP_IP == true for dynamic DHCP IP, false to use static IP which you have to change accordingly to your network
#if (defined(USE_STATIC_IP_CONFIG_IN_CP) && !USE_STATIC_IP_CONFIG_IN_CP)
// Force DHCP to be true
#if defined(USE_DHCP_IP)
#undef USE_DHCP_IP
#endif
#define USE_DHCP_IP true
#else
// You can select DHCP or Static IP here
#define USE_DHCP_IP false
#endif
6. Using STA-mode StaticIP and configurable DNS, and be able to change to DHCP IP and display in Config Portal
// From v1.0.10 to permit disable/enable StaticIP configuration in Config Portal from sketch. Valid only if DHCP is used.
// You'll loose the feature of dynamically changing from DHCP to static IP, or vice versa
// You have to explicitly specify false to disable the feature.
//#define USE_STATIC_IP_CONFIG_IN_CP false
// Use USE_DHCP_IP == true for dynamic DHCP IP, false to use static IP which you have to change accordingly to your network
#if (defined(USE_STATIC_IP_CONFIG_IN_CP) && !USE_STATIC_IP_CONFIG_IN_CP)
// Force DHCP to be true
#if defined(USE_DHCP_IP)
#undef USE_DHCP_IP
#endif
#define USE_DHCP_IP true
#else
// You can select DHCP or Static IP here
#define USE_DHCP_IP false
#endif
#define USE_CONFIGURABLE_DNS true
IPAddress dns1IP = gatewayIP;
IPAddress dns2IP = IPAddress(8, 8, 8, 8);
7. Using STA-mode StaticIP and auto DNS, and be able to change to DHCP IP and display in Config Portal
// From v1.0.10 to permit disable/enable StaticIP configuration in Config Portal from sketch. Valid only if DHCP is used.
// You'll loose the feature of dynamically changing from DHCP to static IP, or vice versa
// You have to explicitly specify false to disable the feature.
//#define USE_STATIC_IP_CONFIG_IN_CP false
// Use USE_DHCP_IP == true for dynamic DHCP IP, false to use static IP which you have to change accordingly to your network
#if (defined(USE_STATIC_IP_CONFIG_IN_CP) && !USE_STATIC_IP_CONFIG_IN_CP)
// Force DHCP to be true
#if defined(USE_DHCP_IP)
#undef USE_DHCP_IP
#endif
#define USE_DHCP_IP true
#else
// You can select DHCP or Static IP here
#define USE_DHCP_IP false
#endif
#define USE_CONFIGURABLE_DNS false
// Use false to disable NTP config. Advisable when using Cellphone, Tablet to access Config Portal.
// See Issue 23: On Android phone ConfigPortal is unresponsive (https://github.com/khoih-prog/ESP_WiFiManager/issues/23)
#define USE_ESP_WIFIMANAGER_NTP false
9. Using NTP feature with CloudFlare. System can hang until you have Internet access for CloudFlare.
// Use false to disable NTP config. Advisable when using Cellphone, Tablet to access Config Portal.
// See Issue 23: On Android phone ConfigPortal is unresponsive (https://github.com/khoih-prog/ESP_WiFiManager/issues/23)
#define USE_ESP_WIFIMANAGER_NTP true
// Use true to enable CloudFlare NTP service. System can hang if you don't have Internet access while accessing CloudFlare
// See Issue #21: CloudFlare link in the default portal (https://github.com/khoih-prog/ESP_WiFiManager/issues/21)
#define USE_CLOUDFLARE_NTP true
// Use false to disable NTP config. Advisable when using Cellphone, Tablet to access Config Portal.
// See Issue 23: On Android phone ConfigPortal is unresponsive (https://github.com/khoih-prog/ESP_WiFiManager/issues/23)
#define USE_ESP_WIFIMANAGER_NTP true
// Use true to enable CloudFlare NTP service. System can hang if you don't have Internet access while accessing CloudFlare
// See Issue #21: CloudFlare link in the default portal (https://github.com/khoih-prog/ESP_WiFiManager/issues/21)
#define USE_CLOUDFLARE_NTP false
// From v1.0.10 only
// Set config portal channel, default = 1. Use 0 => random channel from 1-13
ESPAsync_wifiManager.setConfigPortalChannel(0);
//////
// From v1.0.10 only
// Set config portal channel, default = 1. Use 0 => random channel from 1-13
ESPAsync_wifiManager.setConfigPortalChannel(3);
//////
// Set static IP, Gateway, Subnetmask, DNS1 and DNS2. New in v1.0.5
//ESPAsync_wifiManager.setSTAStaticIPConfig(stationIP, gatewayIP, netMask, dns1IP, dns2IP);
ESPAsync_wifiManager.setSTAStaticIPConfig(WM_STA_IPconfig);
- Don't invalidate WiFi SSID/PW when calling autoConnect() (default)
#define AUTOCONNECT_NO_INVALIDATE true
- To invalidate WiFi SSID/PW when calling autoConnect()
#define AUTOCONNECT_NO_INVALIDATE false
- To use CORS feature with default CORS Header "". Some WebBrowsers won't accept this allowing-all "" CORS Header.
// Default false for using only whenever necessary to avoid security issue
#define USING_CORS_FEATURE true
- To use CORS feature with specific CORS Header "Your Access-Control-Allow-Origin". To be modified according to your specific Allowed-Origin.
// Default false for using only whenever necessary to avoid security issue
#define USING_CORS_FEATURE true
...
// New from v1.1.1
#if USING_CORS_FEATURE
ESP_wifiManager.setCORSHeader("Your Access-Control-Allow-Origin");
#endif
- Not use CORS feature (default)
// Default false for using only whenever necessary to avoid security issue
#define USING_CORS_FEATURE false
- In loop()
void check_WiFi(void)
{
if ( (WiFi.status() != WL_CONNECTED) )
{
Serial.println("\nWiFi lost. Call connectMultiWiFi in loop");
connectMultiWiFi();
}
}
void check_status(void)
{
static ulong checkwifi_timeout = 0;
static ulong current_millis;
#define WIFICHECK_INTERVAL 1000L
current_millis = millis();
// Check WiFi every WIFICHECK_INTERVAL (1) seconds.
if ((current_millis > checkwifi_timeout) || (checkwifi_timeout == 0))
{
check_WiFi();
checkwifi_timeout = current_millis + WIFICHECK_INTERVAL;
}
}
void loop()
{
// put your main code here, to run repeatedly
check_status();
}
- When you want to open a config portal, with default DHCP hostname
ESP8266-XXXXXX
orESP32-XXXXXX
, just add
#include <ESPAsync_WiFiManager.h> //https://github.com/khoih-prog/ESPAsync_WiFiManager
#define HTTP_PORT 80
AsyncWebServer webServer(HTTP_PORT);
DNSServer dnsServer;
ESPAsync_WiFiManager ESPAsync_wifiManager(&webServer, &dnsServer);
If you'd like to have a personalized hostname
(RFC952-conformed,- 24 chars max,- only a..z A..Z 0..9 '-' and no '-' as last char)
add
ESPAsync_WiFiManager ESPAsync_wifiManager(&webServer, &dnsServer, "Personalized-HostName");
then later call
ESPAsync_wifiManager.startConfigPortal()
While in AP mode, connect to it using its SSID
(ESP_XXXXXX) / Password
("your_password"), then open a browser to the AP IP, default 192.168.4.1
, configure wifi then save. The WiFi connection information will be saved in non volatile memory. It will then reboot and autoconnect.
You can also change the AP IP by:
//set custom ip for portal
//ESPAsync_wifiManager.setAPStaticIPConfig(IPAddress(10,0,1,1), IPAddress(10,0,1,1), IPAddress(255,255,255,0));
ESPAsync_wifiManager.setAPStaticIPConfig(WM_AP_IPconfig);
and use fixed / dynamic / random AP channel by:
// Set config portal channel, default = 1. Use 0 => random channel from 1-13
ESPAsync_wifiManager.setConfigPortalChannel(0);
//////
Once WiFi network information is saved in the ESP32 / ESP8266
, it will try to autoconnect to WiFi every time it is started, without requiring any function calls in the sketch.
These illustrating steps is based on the example Async_ConfigOnSwitchFS
The application will:
- use DHT sensor (either DHT11 or DHT22) and
- need to connect to ThingSpeak with unique user's API Key.
The DHT sensor is connected to the ESP boards using SDA/SCL pins which also need to be configurable.
So this is the list of variables to be dynamically configured using CP
1. `thingspeakApiKey`, type `char array`, max length 17 chars, and just arbitrarily selected default value to be "" or "ThingSpeak-APIKey"
2. `sensorDht22`, type `bool`, default to be `true` (DHT22)
3. `pinSda`, type `int`, default to be `PIN_D2`
4. `pinScl`, type `int`, default to be `PIN_D1`
The Label can be any arbitrary string that help you identify the variable, but must be unique in your application
The initial code will be
#define API_KEY_LEN 17
// Default configuration values
char thingspeakApiKey[API_KEY_LEN] = "";
bool sensorDht22 = true;
int pinSda = PIN_D2; // Pin D2 mapped to pin GPIO4 of ESP8266
int pinScl = PIN_D1; // Pin D1 mapped to pin GPIO5 of ESP8266
// Any unique string helping you identify the vars
#define ThingSpeakAPI_Label "thingspeakApiKey"
#define SensorDht22_Label "SensorDHT22"
#define PinSDA_Label "PinSda"
#define PinSCL_Label "PinScl"
The example Async_ConfigOnSwitchFS will open the CP whenever a SW press is detected in loop(). So the code to add dynamic variables
will be there, just after the CP ESPAsync_WiFiManager
class initialization to create ESPAsync_wifiManager
object.
void loop()
{
// is configuration portal requested?
if ((digitalRead(TRIGGER_PIN) == LOW) || (digitalRead(TRIGGER_PIN2) == LOW))
{
Serial.println("\nConfiguration portal requested.");
digitalWrite(LED_BUILTIN, LED_ON); // turn the LED on by making the voltage LOW to tell us we are in configuration mode.
//Local intialization. Once its business is done, there is no need to keep it around
ESPAsync_WiFiManager ESPAsync_wifiManager(&webServer, &dnsServer, "ConfigOnSwitchFS");
//Check if there is stored WiFi router/password credentials.
//If not found, device will remain in configuration mode until switched off via webserver.
Serial.print("Opening configuration portal. ");
...
// The addition of dynamic vars will be somewhere here
}
The ESPAsync_WMParameter
class constructor will be used to initialize each newly-added parameter object.
2.1 Use the following simple constructor for simple variables such as thingspeakApiKey
, pinSda
and pinScl
:
ESPAsync_WMParameter(const char *id, const char *placeholder, const char *defaultValue, int length);
2.2 For example, to create a new ESPAsync_WMParameter
object p_thingspeakApiKey
for thingspeakApiKey
,
The command to use will be
ESPAsync_WMParameter p_thingspeakApiKey(ThingSpeakAPI_Label, "Thingspeak API Key", thingspeakApiKey, API_KEY_LEN);
where
- p_thingspeakApiKey : ESPAsync_WMParameter class object reference that stores the new Custom Parameter
- id => ThingSpeakAPI_Label : var ref to Json associative name and HTML element ID for the new Custom Paramerter you just defined in step 1
- placeholder => "Thingspeak API Key" : HTML input placeholder and/or label element text the user sees in the configuration interface for this Custom Parameter
- defaultValue => thingspeakApiKey : variable for storing the value of your Custom Parameter in the file system or default value when no data is entered
- length => API_KEY_LEN : max allowed length you want for this Custom Parameter to have
For pinSda
and pinScl
, the command will be similar
// I2C SCL and SDA parameters are integers so we need to convert them to char array but
// no other special considerations
char convertedValue[3];
sprintf(convertedValue, "%d", pinSda);
ESPAsync_WMParameter p_pinSda(PinSDA_Label, "I2C SDA pin", convertedValue, 3);
sprintf(convertedValue, "%d", pinScl);
ESPAsync_WMParameter p_pinScl(PinSCL_Label, "I2C SCL pin", convertedValue, 3);
where
- p_pinSda / p_pinScl : ESPAsync_WMParameter class object reference that stores the new Custom Parameter
- id => PinSDA_Label/PinSCL_Label : var ref to Json associative name and HTML element ID for the new Custom Paramerter you just defined in step 1
- placeholder => "I2C SDA pin"/"I2C SCL pin" : HTML input placeholder and/or label element text the user sees in the configuration interface for this Custom Parameter
- defaultValue => convertedValue : variable for storing the value of your Custom Parameter in the file system or default value when no data is entered
- length => 3 : max allowed length you want for this Custom Parameter to have
ESPAsync_WMParameter(const char *id, const char *placeholder, const char *defaultValue, int length, const char *custom, int labelPlacement);
The command to use will be
ESPAsync_WMParameter p_sensorDht22(SensorDht22_Label, "DHT-22 Sensor", "T", 2, customhtml, WFM_LABEL_AFTER);
where
- p_sensorDht22 : ESPAsync_WMParameter class object reference that stores the new Custom Parameter
- id => SensorDht22_Label : var ref to Json associative name and HTML element ID for the new Custom Paramerter you just defined in step 1
- placeholder => "DHT-22 Sensor" : HTML input placeholder and/or label element text the user sees in the configuration interface for this Custom Parameter
- defaultValue => "T" : variable for storing the value of your Custom Parameter in the file system or default value when no data is entered ("T" means `true`)
- length => 2 : max allowed length you want for this Custom Parameter to have
- custom => customhtml : custom HTML code to add element type, e.g. `checkbox`, and `checked` when `sensorDht22 == true`
- labelPlacement => WFM_LABEL_AFTER : to place label after
and customhtml Code is:
char customhtml[24] = "type=\"checkbox\"";
if (sensorDht22)
{
strcat(customhtml, " checked");
}
Adding those ESPAsync_WMParameter
objects created in Step 2 using the function addParameter()
of object ESPAsync_wifiManager
//adds a custom parameter
bool addParameter(ESPAsync_WMParameter *p);
Add parameter objects, previously created in Step 2, such as : p_thingspeakApiKey
, p_sensorDht22
, p_pinSda
and p_pinScl
//add all parameters here
ESPAsync_wifiManager.addParameter(&p_thingspeakApiKey);
ESPAsync_wifiManager.addParameter(&p_sensorDht22);
ESPAsync_wifiManager.addParameter(&p_pinSda);
ESPAsync_wifiManager.addParameter(&p_pinScl);
When the CP exits, we have to store the parameters' values that users input via CP to use later.
For ESP32, that can be EEPROM
or SPIFFS
. While on ESP8266, LittleFS
can be used besides EEPROM
or deprecated SPIFFS
.
We can write directly to a well-defined structure of our choice, but the current example is using JSON
to be portable but much more complicated and not advised for new users.
After users select Save
, the CP ESPAsync_wifiManager
object will save the user input data into related ESPAsync_WMParameter
objects.
We can now retrieve the data, using getValue()
function, for each ESPAsync_WMParameter
object. Then we can utilize the data for our purpose, such as thingspeakApiKey
to log in, sensorDht22
type to know how to handle the sensor, pinSda
and pinSda
to know which pins to use to communicate with the DHT sensor.
The code is as follows:
// Getting posted form values and overriding local variables parameters
// Config file is written regardless the connection state
strcpy(thingspeakApiKey, p_thingspeakApiKey.getValue());
sensorDht22 = (strncmp(p_sensorDht22.getValue(), "T", 1) == 0);
pinSda = atoi(p_pinSda.getValue());
pinScl = atoi(p_pinScl.getValue());
We can also save to FS file to use later in next boot.
// Writing JSON config file to flash for next boot
writeConfigFile();
First, you have to familiarize yourself with ArduinoJson
library, its functions, the disruptive differences between ArduinoJson version 5.x.x-
and v6.0.0+
. The best documentation can be found at The best JSON library for embedded C++.
This documentation will discuss only ArduinoJson v6.x.x+ (ARDUINOJSON_VERSION_MAJOR >= 6
)
Then have a look at the code snippet of writeConfigFile()
function and the following step-by-step explanations.
bool writeConfigFile()
{
Serial.println("Saving config file");
#if (ARDUINOJSON_VERSION_MAJOR >= 6)
DynamicJsonDocument json(1024);
#else
DynamicJsonBuffer jsonBuffer;
JsonObject& json = jsonBuffer.createObject();
#endif
// JSONify local configuration parameters
json[ThingSpeakAPI_Label] = thingspeakApiKey;
json[SensorDht22_Label] = sensorDht22;
json[PinSDA_Label] = pinSda;
json[PinSCL_Label] = pinScl;
// Open file for writing
File f = FileFS.open(CONFIG_FILE, "w");
if (!f)
{
Serial.println("Failed to open config file for writing");
return false;
}
#if (ARDUINOJSON_VERSION_MAJOR >= 6)
serializeJsonPretty(json, Serial);
// Write data to file and close it
serializeJson(json, f);
#else
json.prettyPrintTo(Serial);
// Write data to file and close it
json.printTo(f);
#endif
f.close();
Serial.println("\nConfig file was successfully saved");
return true;
}
We'll create an object with size 1024 bytes, enough to hold our data:
DynamicJsonDocument json(1024);
Then JSONify
all local parameters we've just received from CP and wish to store into FS by using the function prototype:
json[Unique_Label] = Value_For_Unique_Label;
as follows:
// JSONify local configuration parameters
json[ThingSpeakAPI_Label] = thingspeakApiKey;
json[SensorDht22_Label] = sensorDht22;
json[PinSDA_Label] = pinSda;
json[PinSCL_Label] = pinScl;
This is the CONFIG_FILE
file name we already declared at the beginning of the sketch (for ESP32):
#include <SPIFFS.h>
FS* filesystem = &SPIFFS;
#define FileFS SPIFFS
const char* CONFIG_FILE = "/ConfigSW.json";
Now just open the file for writing, and abort if open-for-writing error:
// Open file for writing
File f = FileFS.open(CONFIG_FILE, "w");
if (!f)
{
Serial.println("Failed to open config file for writing");
return false;
}
As simple as this single command to write the whole json
object we declared then filled with data in steps 5.1 and 5.2
// Write data to file and close it
serializeJson(json, f);
Soooo simple !!! Now everybody can do it.
f.close();
But HOWTO use the saved data in the next startup ???? That's in next step 6.
Now, you have familiarized yourself with ArduinoJson library, its functions. We'll discuss HOWTO read data from the CONFIG_FILE in Jsonified format, then HOWTO parse the to use.
The documentation will discuss only ArduinoJson v6.x.x+ (ARDUINOJSON_VERSION_MAJOR >= 6
)
First, have a look at the code snippet of readConfigFile()
function.
bool readConfigFile()
{
// this opens the config file in read-mode
File f = FileFS.open(CONFIG_FILE, "r");
if (!f)
{
Serial.println("Configuration file not found");
return false;
}
else
{
// we could open the file
size_t size = f.size();
// Allocate a buffer to store contents of the file.
std::unique_ptr<char[]> buf(new char[size + 1]);
// Read and store file contents in buf
f.readBytes(buf.get(), size);
// Closing file
f.close();
// Using dynamic JSON buffer which is not the recommended memory model, but anyway
// See https://github.com/bblanchon/ArduinoJson/wiki/Memory%20model
#if (ARDUINOJSON_VERSION_MAJOR >= 6)
DynamicJsonDocument json(1024);
auto deserializeError = deserializeJson(json, buf.get());
if ( deserializeError )
{
Serial.println("JSON parseObject() failed");
return false;
}
serializeJson(json, Serial);
#else
DynamicJsonBuffer jsonBuffer;
// Parse JSON string
JsonObject& json = jsonBuffer.parseObject(buf.get());
// Test if parsing succeeds.
if (!json.success())
{
Serial.println("JSON parseObject() failed");
return false;
}
json.printTo(Serial);
#endif
// Parse all config file parameters, override
// local config variables with parsed values
if (json.containsKey(ThingSpeakAPI_Label))
{
strcpy(thingspeakApiKey, json[ThingSpeakAPI_Label]);
}
if (json.containsKey(SensorDht22_Label))
{
sensorDht22 = json[SensorDht22_Label];
}
if (json.containsKey(PinSDA_Label))
{
pinSda = json[PinSDA_Label];
}
if (json.containsKey(PinSCL_Label))
{
pinScl = json[PinSCL_Label];
}
}
Serial.println("\nConfig file was successfully parsed");
return true;
}
and the following step-by-step explanations.
As simple as this
// this opens the config file in read-mode
File f = FileFS.open(CONFIG_FILE, "r");
We'll inform and abort if the CONFIG_FILE can't be opened (file not found, can't be opened, etc.)
if (!f)
{
Serial.println("Configuration file not found");
return false;
}
Now we have to determine the file size to create a buffer large enough to store the to-be-read data
// we could open the file
size_t size = f.size();
// Allocate a buffer to store contents of the file.
std::unique_ptr<char[]> buf(new char[size + 1]);
Remember always add 1 to the buffer length to store the terminating 0
.
Then just read the file into the buffer, and close the file to be safe
// Read and store file contents in buf
f.readBytes(buf.get(), size);
// Closing file
f.close();
We again use the same DynamicJsonDocument json
object to store the data we've just read fron CONFIG_FILE
.
Why the same complicated DynamicJsonDocument json
object ?? Because in steps 5, we did store Jsonified data
using the same DynamicJsonDocument json
object. It's much easier we now use it again to facilitate the parsing of Jsonified data
back to the data we can use easily.
We first create the object with enough size
DynamicJsonDocument json(1024);
then populate it with data from buffer we read from CONFIG_FILE in step 6.2, pre-parse and check for error. All is done just by one command deserializeJson()
auto deserializeError = deserializeJson(json, buf.get());
Abort if there is any data error in the process of writing, storing, reading back. If OK, just nicely print out to the Debug Terminal
if ( deserializeError )
{
Serial.println("JSON parseObject() failed");
return false;
}
serializeJson(json, Serial);
6.4 Parse the Jsonified data from the DynamicJsonDocument json object to store into corresponding parameters
This is as simple as in the step 5.2, but in reverse direction.
To be sure there is good corresponding data, not garbage, for each variable, we have to perform sanity checks by
verifying the DynamicJsonDocument json object
still contains the correct keys we passed to it when we wrote into CONFIG_FILE.
For example:
if (json.containsKey(ThingSpeakAPI_Label))
Then proceed to get every parameter we know we stored there from last CP Save
.
// Parse all config file parameters, override
// local config variables with parsed values
if (json.containsKey(ThingSpeakAPI_Label))
{
strcpy(thingspeakApiKey, json[ThingSpeakAPI_Label]);
}
if (json.containsKey(SensorDht22_Label))
{
sensorDht22 = json[SensorDht22_Label];
}
if (json.containsKey(PinSDA_Label))
{
pinSda = json[PinSDA_Label];
}
if (json.containsKey(PinSCL_Label))
{
pinScl = json[PinSCL_Label];
}
Just use those parameters for whatever purpose you designed them for in step 1:
The application will use DHT sensor (either DHT11 or DHT22) and need to connect to ThingSpeak with unique user's API Key. The DHT sensor is connected to the ESP boards using SDA/SCL pins which also need to be configurable.
In ConfigPortal Mode
, it starts an access point called ESP_XXXXXX
. Connect to it using the configurable password
you can define in the code. For example, your_password
(see examples):
// SSID and PW for Config Portal
String ssid = "ESP_" + String(ESP_getChipId(), HEX);
const char* password = "your_password";
After you connected, please, go to http://192.168.4.1, you'll see this Main
page:
Select Information
to enter the Info page where the board info will be shown (long page)
or short page (default)
Select Configuration
to enter this page where you can select an AP and specify its WiFi Credentials
Enter your credentials, then click Save. The WiFi Credentials will be saved and the board reboots to connect to the selected WiFi AP.
If you're already connected to a listed WiFi AP and don't want to change anything, just select Exit Portal from the Main
page to reboot the board and connect to the previously-stored AP. The WiFi Credentials are still intact.
You can password protect the ConfigPortal AP. Simply add an SSID as the first parameter and the password as a second parameter to startConfigPortal
. See the above examples.
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)
ESPAsync_wifiManager.startConfigPortal( SSID , password )
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 Async_ConfigOnSwitchFS Example.
ESPAsync_wifiManager.setSaveConfigCallback(saveConfigCallback);
saveConfigCallback declaration and example
//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;
}
If you need to set a timeout so the ESP32 / ESP8266
doesn't hang waiting to be configured for ever.
ESPAsync_wifiManager.setConfigPortalTimeout(120);
which will wait 2 minutes (120 seconds). When the time passes, the startConfigPortal function will return and continue the sketch,
unless you're accessing the Config Portal. In this case, the startConfigPortal
function will stay until you save config data or exit
the Config Portal.
Example usage
void loop()
{
// is configuration portal requested?
if ((digitalRead(TRIGGER_PIN) == LOW) || (digitalRead(TRIGGER_PIN2) == LOW))
{
Serial.println("\nConfiguration portal requested.");
digitalWrite(LED_BUILTIN, LED_ON); // turn the LED on by making the voltage LOW to tell us we are in configuration mode.
//Local intialization. Once its business is done, there is no need to keep it around
ESPAsync_WiFiManager ESPAsync_wifiManager(&webServer, &dnsServer, "ConfigOnSwitch");
ESPAsync_wifiManager.setMinimumSignalQuality(-1);
// From v1.0.10 only
// Set config portal channel, default = 1. Use 0 => random channel from 1-13
ESPAsync_wifiManager.setConfigPortalChannel(0);
//////
//set custom ip for portal
//ESPAsync_wifiManager.setAPStaticIPConfig(IPAddress(192, 168, 100, 1), IPAddress(192, 168, 100, 1), IPAddress(255, 255, 255, 0));
ESPAsync_wifiManager.setAPStaticIPConfig(WM_AP_IPconfig);
#if !USE_DHCP_IP
// Set (static IP, Gateway, Subnetmask, DNS1 and DNS2) or (IP, Gateway, Subnetmask). New in v1.0.5
// New in v1.4.0
ESPAsync_wifiManager.setSTAStaticIPConfig(WM_STA_IPconfig);
//////
#endif
// New from v1.1.1
#if USING_CORS_FEATURE
ESPAsync_wifiManager.setCORSHeader("Your Access-Control-Allow-Origin");
#endif
//Check if there is stored WiFi router/password credentials.
//If not found, device will remain in configuration mode until switched off via webserver.
Serial.print("Opening configuration portal. ");
Router_SSID = ESPAsync_wifiManager.WiFi_SSID();
Router_Pass = ESPAsync_wifiManager.WiFi_Pass();
// From v1.1.0, Don't permit NULL password
if ( (Router_SSID != "") && (Router_Pass != "") )
{
ESPAsync_wifiManager.setConfigPortalTimeout(120); //If no access point name has been previously entered disable timeout.
Serial.println("Got stored Credentials. Timeout 120s");
}
else
Serial.println("No stored Credentials. No timeout");
//Starts an access point
//and goes into a blocking loop awaiting configuration
if (!ESPAsync_wifiManager.startConfigPortal((const char *) ssid.c_str(), password))
{
Serial.println("Not connected to WiFi but continuing anyway.");
}
else
{
//if you get here you have connected to the WiFi
Serial.println("connected...yeey :)");
Serial.print("Local IP: ");
Serial.println(WiFi.localIP());
}
// Only clear then save data if CP entered and with new valid Credentials
// No CP => stored getSSID() = ""
if ( String(ESPAsync_wifiManager.getSSID(0)) != "" && String(ESPAsync_wifiManager.getSSID(1)) != "" )
{
// Stored for later usage, from v1.1.0, but clear first
memset(&WM_config, 0, sizeof(WM_config));
for (uint8_t i = 0; i < NUM_WIFI_CREDENTIALS; i++)
{
String tempSSID = ESPAsync_wifiManager.getSSID(i);
String tempPW = ESPAsync_wifiManager.getPW(i);
if (strlen(tempSSID.c_str()) < sizeof(WM_config.WiFi_Creds[i].wifi_ssid) - 1)
strcpy(WM_config.WiFi_Creds[i].wifi_ssid, tempSSID.c_str());
else
strncpy(WM_config.WiFi_Creds[i].wifi_ssid, tempSSID.c_str(), sizeof(WM_config.WiFi_Creds[i].wifi_ssid) - 1);
if (strlen(tempPW.c_str()) < sizeof(WM_config.WiFi_Creds[i].wifi_pw) - 1)
strcpy(WM_config.WiFi_Creds[i].wifi_pw, tempPW.c_str());
else
strncpy(WM_config.WiFi_Creds[i].wifi_pw, tempPW.c_str(), sizeof(WM_config.WiFi_Creds[i].wifi_pw) - 1);
// Don't permit NULL SSID and password len < MIN_AP_PASSWORD_SIZE (8)
if ( (String(WM_config.WiFi_Creds[i].wifi_ssid) != "") && (strlen(WM_config.WiFi_Creds[i].wifi_pw) >= MIN_AP_PASSWORD_SIZE) )
{
LOGERROR3(F("* Add SSID = "), WM_config.WiFi_Creds[i].wifi_ssid, F(", PW = "), WM_config.WiFi_Creds[i].wifi_pw );
wifiMulti.addAP(WM_config.WiFi_Creds[i].wifi_ssid, WM_config.WiFi_Creds[i].wifi_pw);
}
}
// New in v1.4.0
ESPAsync_wifiManager.getSTAStaticIPConfig(WM_STA_IPconfig);
displayIPConfigStruct(WM_STA_IPconfig);
//////
saveConfigData();
}
digitalWrite(LED_BUILTIN, LED_OFF); // Turn led off as we are not in configuration mode.
}
// put your main code here, to run repeatedly
check_status();
}
See Async_ConfigOnSwitch example for a more complex version.
Many applications need configuration parameters like MQTT host and port
, Blynk or emoncms tokens, etc. While it is possible to use ESPAsync_WiFiManager
to collect additional parameters, it is better to read these parameters from a web service once ESPAsync_WiFiManager
has been used to connect to the Internet.
To capture other parameters with ESPAsync_WiFiManager
is a little bit more complicated than all the other features. This requires adding custom HTML to your form.
If you want to do it with ESPAsync_WiFiManager
see the example Async_ConfigOnSwitchFS
You can set a custom IP for both AP (access point, config mode) and STA (station mode, client mode, normal project state)
This will set your captive portal to a specific IP should you need/want such a feature. Add the following snippet before startConfigPortal()
//set custom ip for portal
ESPAsync_wifiManager.setAPStaticIPConfig(IPAddress(10,0,1,1), IPAddress(10,0,1,1), IPAddress(255,255,255,0));
This will use the specified IP configuration instead of using DHCP in station mode.
ESPAsync_wifiManager.setSTAStaticIPConfig(IPAddress(192,168,0,99), IPAddress(192,168,0,1), IPAddress(255,255,255,0));
There are various ways in which you can inject custom HTML, CSS or Javascript into the ConfigPortal.
The options are:
- inject custom head element
You can use this to any html bit to the head of the ConfigPortal. If you add a <style>
element, bare in mind it overwrites the included css, not replaces.
ESPAsync_wifiManager.setCustomHeadElement("<style>html{filter: invert(100%); -webkit-filter: invert(100%);}</style>");
- inject a custom bit of html in the configuration form
ESPAsync_WMParameter custom_text("<p>This is just a text paragraph</p>");
ESPAsync_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.
ESPAsync_WMParameter custom_mqtt_server("server", "mqtt server", "iot.eclipse", 40, " readonly");
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 %;
ESPAsync_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.
ESPAsync_wifiManager.setRemoveDuplicateAPs(false);
- Async_ConfigOnSwitch
- Async_ConfigOnSwitchFS
- Async_ConfigOnStartup
- Async_ConfigOnDoubleReset (now support ArduinoJson 6.0.0+ as well as 5.13.5-)
- Async_ConfigPortalParamsOnSwitch (now support ArduinoJson 6.0.0+ as well as 5.13.5-)
- Async_AutoConnect
- Async_AutoConnectWithFeedback
- Async_AutoConnectWithFeedbackLED
- Async_AutoConnectWithFSParameters
- Async_ConfigOnSwitchFS_MQTT_Ptr
- Async_AutoConnectWithFSParametersAndCustomIP
- ModelessConnect
- ModelessWithInterrupts
- Async_ESP32_FSWebServer
- Async_ESP32_FSWebServer_DRD
- Async_ESP_FSWebServer
- Async_ESP_FSWebServer_DRD
- Async_ConfigOnDRD_FS_MQTT_Ptr
- Async_AutoConnect_ESP32_minimal
- Async_AutoConnect_ESP8266_minimal
- Async_ConfigOnDRD_ESP32_minimal
- Async_ConfigOnDRD_ESP8266_minimal
Example Async_ConfigOnDRD_FS_MQTT_Ptr
#if !( defined(ESP8266) || defined(ESP32) )
#error This code is intended to run on the ESP8266 or ESP32 platform! Please check your Tools->Board setting.
#endif
// Use from 0 to 4. Higher number, more debugging messages and memory usage.
#define _ESPASYNC_WIFIMGR_LOGLEVEL_ 3
// Default is 30s, using 20s now
#define TIME_BETWEEN_MODAL_SCANS 20000
// Default is 60s, using 30s now
#define TIME_BETWEEN_MODELESS_SCANS 30000
#include <FS.h>
// Now support ArduinoJson 6.0.0+ ( tested with v6.15.2 to v6.16.1 )
#include <ArduinoJson.h> // get it from https://arduinojson.org/ or install via Arduino library manager
//For ESP32, To use ESP32 Dev Module, QIO, Flash 4MB/80MHz, Upload 921600
//Ported to ESP32
#ifdef ESP32
#include <esp_wifi.h>
#include <WiFi.h>
#include <WiFiClient.h>
// From v1.1.1
#include <WiFiMulti.h>
WiFiMulti wifiMulti;
// LittleFS has higher priority than SPIFFS
#define USE_LITTLEFS true
#define USE_SPIFFS false
#if USE_LITTLEFS
// Use LittleFS
#include "FS.h"
// The library will be depreciated after being merged to future major Arduino esp32 core release 2.x
// At that time, just remove this library inclusion
#include <LITTLEFS.h> // https://github.com/lorol/LITTLEFS
FS* filesystem = &LITTLEFS;
#define FileFS LITTLEFS
#define FS_Name "LittleFS"
#elif USE_SPIFFS
#include <SPIFFS.h>
FS* filesystem = &SPIFFS;
#define FileFS SPIFFS
#define FS_Name "SPIFFS"
#else
// +Use FFat
#include <FFat.h>
FS* filesystem = &FFat;
#define FileFS FFat
#define FS_Name "FFat"
#endif
//////
#define ESP_getChipId() ((uint32_t)ESP.getEfuseMac())
#define LED_BUILTIN 2
#define LED_ON HIGH
#define LED_OFF LOW
#else
#include <ESP8266WiFi.h> //https://github.com/esp8266/Arduino
//needed for library
#include <DNSServer.h>
// From v1.1.1
#include <ESP8266WiFiMulti.h>
ESP8266WiFiMulti wifiMulti;
#define USE_LITTLEFS true
#if USE_LITTLEFS
#include <LittleFS.h>
FS* filesystem = &LittleFS;
#define FileFS LittleFS
#define FS_Name "LittleFS"
#else
FS* filesystem = &SPIFFS;
#define FileFS SPIFFS
#define FS_Name "SPIFFS"
#endif
//////
#define ESP_getChipId() (ESP.getChipId())
#define LED_ON LOW
#define LED_OFF HIGH
#endif
// These defines must be put before #include <ESP_DoubleResetDetector.h>
// to select where to store DoubleResetDetector's variable.
// For ESP32, You must select one to be true (EEPROM or SPIFFS)
// For ESP8266, You must select one to be true (RTC, EEPROM, SPIFFS or LITTLEFS)
// Otherwise, library will use default EEPROM storage
#ifdef ESP32
// These defines must be put before #include <ESP_DoubleResetDetector.h>
// to select where to store DoubleResetDetector's variable.
// For ESP32, You must select one to be true (EEPROM or SPIFFS)
// Otherwise, library will use default EEPROM storage
#if USE_LITTLEFS
#define ESP_DRD_USE_LITTLEFS true
#define ESP_DRD_USE_SPIFFS false
#define ESP_DRD_USE_EEPROM false
#elif USE_SPIFFS
#define ESP_DRD_USE_LITTLEFS false
#define ESP_DRD_USE_SPIFFS true
#define ESP_DRD_USE_EEPROM false
#else
#define ESP_DRD_USE_LITTLEFS false
#define ESP_DRD_USE_SPIFFS false
#define ESP_DRD_USE_EEPROM true
#endif
#else //ESP8266
// For DRD
// These defines must be put before #include <ESP_DoubleResetDetector.h>
// to select where to store DoubleResetDetector's variable.
// For ESP8266, You must select one to be true (RTC, EEPROM, SPIFFS or LITTLEFS)
// Otherwise, library will use default EEPROM storage
#if USE_LITTLEFS
#define ESP_DRD_USE_LITTLEFS true
#define ESP_DRD_USE_SPIFFS false
#else
#define ESP_DRD_USE_LITTLEFS false
#define ESP_DRD_USE_SPIFFS true
#endif
#define ESP_DRD_USE_EEPROM false
#define ESP8266_DRD_USE_RTC false
#endif
#define DOUBLERESETDETECTOR_DEBUG true //false
#include <ESP_DoubleResetDetector.h> //https://github.com/khoih-prog/ESP_DoubleResetDetector
// Number of seconds after reset during which a
// subseqent reset will be considered a double reset.
#define DRD_TIMEOUT 10
// RTC Memory Address for the DoubleResetDetector to use
#define DRD_ADDRESS 0
DoubleResetDetector* drd = NULL;
#include "Adafruit_MQTT.h" //https://github.com/adafruit/Adafruit_MQTT_Library
#include "Adafruit_MQTT_Client.h" //https://github.com/adafruit/Adafruit_MQTT_Library
const char* CONFIG_FILE = "/ConfigMQTT.json";
// Default configuration values for Adafruit IO MQTT
// This actually works
#define AIO_SERVER "io.adafruit.com"
#define AIO_SERVERPORT "1883" //1883, or 8883 for SSL
#define AIO_USERNAME "private" //Adafruit IO
#define AIO_KEY "private"
// Labels for custom parameters in WiFi manager
#define AIO_SERVER_Label "AIO_SERVER_Label"
#define AIO_SERVERPORT_Label "AIO_SERVERPORT_Label"
#define AIO_USERNAME_Label "AIO_USERNAME_Label"
#define AIO_KEY_Label "AIO_KEY_Label"
// Just dummy topics. To be updated later when got valid data from FS or Config Portal
String MQTT_Pub_Topic = "private/feeds/Temperature";
// Variables to save custom parameters to...
// I would like to use these instead of #defines
#define custom_AIO_SERVER_LEN 20
#define custom_AIO_PORT_LEN 5
#define custom_AIO_USERNAME_LEN 20
#define custom_AIO_KEY_LEN 40
char custom_AIO_SERVER[custom_AIO_SERVER_LEN];
char custom_AIO_SERVERPORT[custom_AIO_PORT_LEN];
char custom_AIO_USERNAME[custom_AIO_USERNAME_LEN];
char custom_AIO_KEY[custom_AIO_KEY_LEN];
// Function Prototypes
void MQTT_connect();
bool readConfigFile();
bool writeConfigFile();
// For Config Portal
// SSID and PW for Config Portal
String ssid = "ESP_" + String(ESP_getChipId(), HEX);
const char* password = "your_password";
// SSID and PW for your Router
String Router_SSID;
String Router_Pass;
// From v1.1.1
// You only need to format the filesystem once
//#define FORMAT_FILESYSTEM true
#define FORMAT_FILESYSTEM false
#define MIN_AP_PASSWORD_SIZE 8
#define SSID_MAX_LEN 32
//From v1.0.10, WPA2 passwords can be up to 63 characters long.
#define PASS_MAX_LEN 64
typedef struct
{
char wifi_ssid[SSID_MAX_LEN];
char wifi_pw [PASS_MAX_LEN];
} WiFi_Credentials;
typedef struct
{
String wifi_ssid;
String wifi_pw;
} WiFi_Credentials_String;
#define NUM_WIFI_CREDENTIALS 2
typedef struct
{
WiFi_Credentials WiFi_Creds [NUM_WIFI_CREDENTIALS];
} WM_Config;
WM_Config WM_config;
#define CONFIG_FILENAME F("/wifi_cred.dat")
//////
// Indicates whether ESP has WiFi credentials saved from previous session, or double reset detected
bool initialConfig = false;
// Use false if you don't like to display Available Pages in Information Page of Config Portal
// Comment out or use true to display Available Pages in Information Page of Config Portal
// Must be placed before #include <ESPAsync_WiFiManager.h>
#define USE_AVAILABLE_PAGES false
// From v1.0.10 to permit disable/enable StaticIP configuration in Config Portal from sketch. Valid only if DHCP is used.
// You'll loose the feature of dynamically changing from DHCP to static IP, or vice versa
// You have to explicitly specify false to disable the feature.
//#define USE_STATIC_IP_CONFIG_IN_CP false
// Use false to disable NTP config. Advisable when using Cellphone, Tablet to access Config Portal.
// See Issue 23: On Android phone ConfigPortal is unresponsive (https://github.com/khoih-prog/ESP_WiFiManager/issues/23)
#define USE_ESP_WIFIMANAGER_NTP false
// Use true to enable CloudFlare NTP service. System can hang if you don't have Internet access while accessing CloudFlare
// See Issue #21: CloudFlare link in the default portal (https://github.com/khoih-prog/ESP_WiFiManager/issues/21)
#define USE_CLOUDFLARE_NTP false
// New in v1.0.11
#define USING_CORS_FEATURE true
//////
// Use USE_DHCP_IP == true for dynamic DHCP IP, false to use static IP which you have to change accordingly to your network
#if (defined(USE_STATIC_IP_CONFIG_IN_CP) && !USE_STATIC_IP_CONFIG_IN_CP)
// Force DHCP to be true
#if defined(USE_DHCP_IP)
#undef USE_DHCP_IP
#endif
#define USE_DHCP_IP true
#else
// You can select DHCP or Static IP here
//#define USE_DHCP_IP true
#define USE_DHCP_IP false
#endif
#if ( USE_DHCP_IP || ( defined(USE_STATIC_IP_CONFIG_IN_CP) && !USE_STATIC_IP_CONFIG_IN_CP ) )
// Use DHCP
#warning Using DHCP IP
IPAddress stationIP = IPAddress(0, 0, 0, 0);
IPAddress gatewayIP = IPAddress(192, 168, 2, 1);
IPAddress netMask = IPAddress(255, 255, 255, 0);
#else
// Use static IP
#warning Using static IP
#ifdef ESP32
IPAddress stationIP = IPAddress(192, 168, 2, 232);
#else
IPAddress stationIP = IPAddress(192, 168, 2, 186);
#endif
IPAddress gatewayIP = IPAddress(192, 168, 2, 1);
IPAddress netMask = IPAddress(255, 255, 255, 0);
#endif
#define USE_CONFIGURABLE_DNS true
IPAddress dns1IP = gatewayIP;
IPAddress dns2IP = IPAddress(8, 8, 8, 8);
IPAddress APStaticIP = IPAddress(192, 168, 100, 1);
IPAddress APStaticGW = IPAddress(192, 168, 100, 1);
IPAddress APStaticSN = IPAddress(255, 255, 255, 0);
#include <ESPAsync_WiFiManager.h> //https://github.com/khoih-prog/ESPAsync_WiFiManager
#define HTTP_PORT 80
AsyncWebServer webServer(HTTP_PORT);
DNSServer dnsServer;
// Create an ESP32 WiFiClient class to connect to the MQTT server
WiFiClient *client = NULL;
Adafruit_MQTT_Client *mqtt = NULL;
Adafruit_MQTT_Publish *Temperature = NULL;
// Forward Declaration
///////////////////////////////////////////
// New in v1.4.0
/******************************************
* // Defined in ESPAsync_WiFiManager.h
typedef struct
{
IPAddress _ap_static_ip;
IPAddress _ap_static_gw;
IPAddress _ap_static_sn;
} WiFi_AP_IPConfig;
typedef struct
{
IPAddress _sta_static_ip;
IPAddress _sta_static_gw;
IPAddress _sta_static_sn;
#if USE_CONFIGURABLE_DNS
IPAddress _sta_static_dns1;
IPAddress _sta_static_dns2;
#endif
} WiFi_STA_IPConfig;
******************************************/
WiFi_AP_IPConfig WM_AP_IPconfig;
WiFi_STA_IPConfig WM_STA_IPconfig;
void initAPIPConfigStruct(WiFi_AP_IPConfig &in_WM_AP_IPconfig)
{
in_WM_AP_IPconfig._ap_static_ip = APStaticIP;
in_WM_AP_IPconfig._ap_static_gw = APStaticGW;
in_WM_AP_IPconfig._ap_static_sn = APStaticSN;
}
void initSTAIPConfigStruct(WiFi_STA_IPConfig &in_WM_STA_IPconfig)
{
in_WM_STA_IPconfig._sta_static_ip = stationIP;
in_WM_STA_IPconfig._sta_static_gw = gatewayIP;
in_WM_STA_IPconfig._sta_static_sn = netMask;
#if USE_CONFIGURABLE_DNS
in_WM_STA_IPconfig._sta_static_dns1 = dns1IP;
in_WM_STA_IPconfig._sta_static_dns2 = dns2IP;
#endif
}
void displayIPConfigStruct(WiFi_STA_IPConfig in_WM_STA_IPconfig)
{
LOGERROR3(F("stationIP ="), in_WM_STA_IPconfig._sta_static_ip, ", gatewayIP =", in_WM_STA_IPconfig._sta_static_gw);
LOGERROR1(F("netMask ="), in_WM_STA_IPconfig._sta_static_sn);
#if USE_CONFIGURABLE_DNS
LOGERROR3(F("dns1IP ="), in_WM_STA_IPconfig._sta_static_dns1, ", dns2IP =", in_WM_STA_IPconfig._sta_static_dns2);
#endif
}
void configWiFi(WiFi_STA_IPConfig in_WM_STA_IPconfig)
{
#if USE_CONFIGURABLE_DNS
// Set static IP, Gateway, Subnetmask, DNS1 and DNS2. New in v1.0.5
WiFi.config(in_WM_STA_IPconfig._sta_static_ip, in_WM_STA_IPconfig._sta_static_gw, in_WM_STA_IPconfig._sta_static_sn, in_WM_STA_IPconfig._sta_static_dns1, in_WM_STA_IPconfig._sta_static_dns2);
#else
// Set static IP, Gateway, Subnetmask, Use auto DNS1 and DNS2.
WiFi.config(in_WM_STA_IPconfig._sta_static_ip, in_WM_STA_IPconfig._sta_static_gw, in_WM_STA_IPconfig._sta_static_sn);
#endif
}
///////////////////////////////////////////
uint8_t connectMultiWiFi()
{
#if ESP32
// For ESP32, this better be 0 to shorten the connect time
#define WIFI_MULTI_1ST_CONNECT_WAITING_MS 0
#else
// For ESP8266, this better be 2200 to enable connect the 1st time
#define WIFI_MULTI_1ST_CONNECT_WAITING_MS 2200L
#endif
#define WIFI_MULTI_CONNECT_WAITING_MS 100L
uint8_t status;
LOGERROR(F("ConnectMultiWiFi with :"));
if ( (Router_SSID != "") && (Router_Pass != "") )
{
LOGERROR3(F("* Flash-stored Router_SSID = "), Router_SSID, F(", Router_Pass = "), Router_Pass );
}
for (uint8_t i = 0; i < NUM_WIFI_CREDENTIALS; i++)
{
// Don't permit NULL SSID and password len < MIN_AP_PASSWORD_SIZE (8)
if ( (String(WM_config.WiFi_Creds[i].wifi_ssid) != "") && (strlen(WM_config.WiFi_Creds[i].wifi_pw) >= MIN_AP_PASSWORD_SIZE) )
{
LOGERROR3(F("* Additional SSID = "), WM_config.WiFi_Creds[i].wifi_ssid, F(", PW = "), WM_config.WiFi_Creds[i].wifi_pw );
}
}
LOGERROR(F("Connecting MultiWifi..."));
WiFi.mode(WIFI_STA);
#if !USE_DHCP_IP
// New in v1.4.0
configWiFi(WM_STA_IPconfig);
//////
#endif
int i = 0;
status = wifiMulti.run();
delay(WIFI_MULTI_1ST_CONNECT_WAITING_MS);
while ( ( i++ < 20 ) && ( status != WL_CONNECTED ) )
{
status = wifiMulti.run();
if ( status == WL_CONNECTED )
break;
else
delay(WIFI_MULTI_CONNECT_WAITING_MS);
}
if ( status == WL_CONNECTED )
{
LOGERROR1(F("WiFi connected after time: "), i);
LOGERROR3(F("SSID:"), WiFi.SSID(), F(",RSSI="), WiFi.RSSI());
LOGERROR3(F("Channel:"), WiFi.channel(), F(",IP address:"), WiFi.localIP() );
}
else
LOGERROR(F("WiFi not connected"));
return status;
}
void toggleLED()
{
//toggle state
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
}
void heartBeatPrint()
{
static int num = 1;
if (WiFi.status() == WL_CONNECTED)
Serial.print(F("W")); // W means connected to WiFi
else
Serial.print(F("N")); // N means not connected to WiFi
if (num == 40)
{
Serial.println();
num = 1;
}
else if (num++ % 5 == 0)
{
Serial.print(F(" "));
}
}
void publishMQTT()
{
float some_number = 25.0 + (float) ( millis() % 100 ) / 100;
// For debug only
//Serial.print(F("Published Temp = "));
//Serial.println(some_number);
MQTT_connect();
if (Temperature->publish(some_number))
{
Serial.print(F("T")); // T means publishing OK
}
else
{
Serial.print(F("F")); // F means publishing failure
}
}
void check_WiFi()
{
if ( (WiFi.status() != WL_CONNECTED) )
{
Serial.println(F("\nWiFi lost. Call connectMultiWiFi in loop"));
connectMultiWiFi();
}
}
void check_status()
{
static ulong checkstatus_timeout = 0;
static ulong LEDstatus_timeout = 0;
static ulong checkwifi_timeout = 0;
static ulong mqtt_publish_timeout = 0;
ulong current_millis = millis();
#define WIFICHECK_INTERVAL 1000L
#define LED_INTERVAL 2000L
#define HEARTBEAT_INTERVAL 10000L
#define PUBLISH_INTERVAL 60000L
// Check WiFi every WIFICHECK_INTERVAL (1) seconds.
if ((current_millis > checkwifi_timeout) || (checkwifi_timeout == 0))
{
check_WiFi();
checkwifi_timeout = current_millis + WIFICHECK_INTERVAL;
}
if ((current_millis > LEDstatus_timeout) || (LEDstatus_timeout == 0))
{
// Toggle LED at LED_INTERVAL = 2s
toggleLED();
LEDstatus_timeout = current_millis + LED_INTERVAL;
}
// Print hearbeat every HEARTBEAT_INTERVAL (10) seconds.
if ((current_millis > checkstatus_timeout) || (checkstatus_timeout == 0))
{
heartBeatPrint();
checkstatus_timeout = current_millis + HEARTBEAT_INTERVAL;
}
// Check every PUBLISH_INTERVAL (60) seconds.
if ((current_millis > mqtt_publish_timeout) || (mqtt_publish_timeout == 0))
{
if (WiFi.status() == WL_CONNECTED)
{
publishMQTT();
}
mqtt_publish_timeout = current_millis + PUBLISH_INTERVAL;
}
}
void loadConfigData()
{
File file = FileFS.open(CONFIG_FILENAME, "r");
LOGERROR(F("LoadWiFiCfgFile "));
memset(&WM_config, sizeof(WM_config), 0);
// New in v1.4.0
memset(&WM_STA_IPconfig, sizeof(WM_STA_IPconfig), 0);
//////
if (file)
{
file.readBytes((char *) &WM_config, sizeof(WM_config));
// New in v1.4.0
file.readBytes((char *) &WM_STA_IPconfig, sizeof(WM_STA_IPconfig));
//////
file.close();
LOGERROR(F("OK"));
// New in v1.4.0
displayIPConfigStruct(WM_STA_IPconfig);
//////
}
else
{
LOGERROR(F("failed"));
}
}
void saveConfigData()
{
File file = FileFS.open(CONFIG_FILENAME, "w");
LOGERROR(F("SaveWiFiCfgFile "));
if (file)
{
file.write((uint8_t*) &WM_config, sizeof(WM_config));
// New in v1.4.0
file.write((uint8_t*) &WM_STA_IPconfig, sizeof(WM_STA_IPconfig));
//////
file.close();
LOGERROR(F("OK"));
}
else
{
LOGERROR(F("failed"));
}
}
void deleteOldInstances()
{
// Delete previous instances
if (mqtt)
{
delete mqtt;
mqtt = NULL;
Serial.println(F("Deleting old MQTT object"));
}
if (Temperature)
{
delete Temperature;
Temperature = NULL;
Serial.println(F("Deleting old Temperature object"));
}
}
void createNewInstances()
{
if (!client)
{
client = new WiFiClient;
Serial.print(F("\nCreating new WiFi client object : "));
Serial.println(client? F("OK") : F("failed"));
}
// Create new instances from new data
if (!mqtt)
{
// Setup the MQTT client class by passing in the WiFi client and MQTT server and login details.
mqtt = new Adafruit_MQTT_Client(client, custom_AIO_SERVER, atoi(custom_AIO_SERVERPORT), custom_AIO_USERNAME, custom_AIO_KEY);
Serial.print(F("Creating new MQTT object : "));
if (mqtt)
{
Serial.println(F("OK"));
Serial.println(String("AIO_SERVER = ") + custom_AIO_SERVER + ", AIO_SERVERPORT = " + custom_AIO_SERVERPORT);
Serial.println(String("AIO_USERNAME = ") + custom_AIO_USERNAME + ", AIO_KEY = " + custom_AIO_KEY);
}
else
Serial.println(F("Failed"));
}
if (!Temperature)
{
Serial.print(F("Creating new MQTT_Pub_Topic, Temperature = "));
Serial.println(MQTT_Pub_Topic);
Temperature = new Adafruit_MQTT_Publish(mqtt, MQTT_Pub_Topic.c_str());
Serial.print(F("Creating new Temperature object : "));
if (Temperature)
{
Serial.println(F("OK"));
Serial.println(String("Temperature MQTT_Pub_Topic = ") + MQTT_Pub_Topic);
}
else
Serial.println(F("Failed"));
}
}
void wifi_manager()
{
Serial.println(F("\nConfig Portal requested."));
digitalWrite(LED_BUILTIN, LED_ON); // turn the LED on by making the voltage LOW to tell us we are in configuration mode.
//Local intialization. Once its business is done, there is no need to keep it around
ESPAsync_WiFiManager ESPAsync_wifiManager(&webServer, &dnsServer, "ConfigOnSwichFS-MQTT");
//Check if there is stored WiFi router/password credentials.
//If not found, device will remain in configuration mode until switched off via webserver.
Serial.print(F("Opening Configuration Portal. "));
Router_SSID = ESPAsync_wifiManager.WiFi_SSID();
Router_Pass = ESPAsync_wifiManager.WiFi_Pass();
// From v1.1.1, Don't permit NULL password
if ( !initialConfig && (Router_SSID != "") && (Router_Pass != "") )
{
//If valid AP credential and not DRD, set timeout 120s.
ESPAsync_wifiManager.setConfigPortalTimeout(120);
Serial.println("Got stored Credentials. Timeout 120s");
}
else
{
ESPAsync_wifiManager.setConfigPortalTimeout(0);
Serial.print(F("No timeout : "));
if (initialConfig)
{
Serial.println(F("DRD or No stored Credentials.."));
}
else
{
Serial.println(F("No stored Credentials."));
}
}
//Local intialization. Once its business is done, there is no need to keep it around
// Extra parameters to be configured
// After connecting, parameter.getValue() will get you the configured value
// Format: <ID> <Placeholder text> <default value> <length> <custom HTML> <label placement>
// (*** we are not using <custom HTML> and <label placement> ***)
// AIO_SERVER
ESPAsync_WMParameter AIO_SERVER_FIELD(AIO_SERVER_Label, "AIO SERVER", custom_AIO_SERVER, custom_AIO_SERVER_LEN + 1);
// AIO_SERVERPORT
ESPAsync_WMParameter AIO_SERVERPORT_FIELD(AIO_SERVERPORT_Label, "AIO SERVER PORT", custom_AIO_SERVERPORT, custom_AIO_PORT_LEN + 1);
// AIO_USERNAME
ESPAsync_WMParameter AIO_USERNAME_FIELD(AIO_USERNAME_Label, "AIO USERNAME", custom_AIO_USERNAME, custom_AIO_USERNAME_LEN + 1);
// AIO_KEY
ESPAsync_WMParameter AIO_KEY_FIELD(AIO_KEY_Label, "AIO KEY", custom_AIO_KEY, custom_AIO_KEY_LEN + 1);
// add all parameters here
// order of adding is not important
ESPAsync_wifiManager.addParameter(&AIO_SERVER_FIELD);
ESPAsync_wifiManager.addParameter(&AIO_SERVERPORT_FIELD);
ESPAsync_wifiManager.addParameter(&AIO_USERNAME_FIELD);
ESPAsync_wifiManager.addParameter(&AIO_KEY_FIELD);
// Sets timeout in seconds until configuration portal gets turned off.
// If not specified device will remain in configuration mode until
// switched off via webserver or device is restarted.
//ESPAsync_wifiManager.setConfigPortalTimeout(120);
ESPAsync_wifiManager.setMinimumSignalQuality(-1);
// From v1.0.10 only
// Set config portal channel, default = 1. Use 0 => random channel from 1-13
ESPAsync_wifiManager.setConfigPortalChannel(0);
//////
//set custom ip for portal
// New in v1.4.0
ESPAsync_wifiManager.setAPStaticIPConfig(WM_AP_IPconfig);
//////
#if !USE_DHCP_IP
// Set (static IP, Gateway, Subnetmask, DNS1 and DNS2) or (IP, Gateway, Subnetmask). New in v1.0.5
// New in v1.4.0
ESPAsync_wifiManager.setSTAStaticIPConfig(WM_STA_IPconfig);
//////
#endif
// New from v1.1.1
#if USING_CORS_FEATURE
ESPAsync_wifiManager.setCORSHeader("Your Access-Control-Allow-Origin");
#endif
// Start an access point
// and goes into a blocking loop awaiting configuration.
// Once the user leaves the portal with the exit button
// processing will continue
// SSID to uppercase
ssid.toUpperCase();
if (!ESPAsync_wifiManager.startConfigPortal((const char *) ssid.c_str(), password))
{
Serial.println(F("Not connected to WiFi but continuing anyway."));
}
else
{
// If you get here you have connected to the WiFi
Serial.println(F("Connected...yeey :)"));
Serial.print(F("Local IP: "));
Serial.println(WiFi.localIP());
}
// Only clear then save data if CP entered and with new valid Credentials
// No CP => stored getSSID() = ""
if ( String(ESPAsync_wifiManager.getSSID(0)) != "" && String(ESPAsync_wifiManager.getSSID(1)) != "" )
{
// Stored for later usage, from v1.1.0, but clear first
memset(&WM_config, 0, sizeof(WM_config));
for (uint8_t i = 0; i < NUM_WIFI_CREDENTIALS; i++)
{
String tempSSID = ESPAsync_wifiManager.getSSID(i);
String tempPW = ESPAsync_wifiManager.getPW(i);
if (strlen(tempSSID.c_str()) < sizeof(WM_config.WiFi_Creds[i].wifi_ssid) - 1)
strcpy(WM_config.WiFi_Creds[i].wifi_ssid, tempSSID.c_str());
else
strncpy(WM_config.WiFi_Creds[i].wifi_ssid, tempSSID.c_str(), sizeof(WM_config.WiFi_Creds[i].wifi_ssid) - 1);
if (strlen(tempPW.c_str()) < sizeof(WM_config.WiFi_Creds[i].wifi_pw) - 1)
strcpy(WM_config.WiFi_Creds[i].wifi_pw, tempPW.c_str());
else
strncpy(WM_config.WiFi_Creds[i].wifi_pw, tempPW.c_str(), sizeof(WM_config.WiFi_Creds[i].wifi_pw) - 1);
// Don't permit NULL SSID and password len < MIN_AP_PASSWORD_SIZE (8)
if ( (String(WM_config.WiFi_Creds[i].wifi_ssid) != "") && (strlen(WM_config.WiFi_Creds[i].wifi_pw) >= MIN_AP_PASSWORD_SIZE) )
{
LOGERROR3(F("* Add SSID = "), WM_config.WiFi_Creds[i].wifi_ssid, F(", PW = "), WM_config.WiFi_Creds[i].wifi_pw );
wifiMulti.addAP(WM_config.WiFi_Creds[i].wifi_ssid, WM_config.WiFi_Creds[i].wifi_pw);
}
}
// New in v1.4.0
ESPAsync_wifiManager.getSTAStaticIPConfig(WM_STA_IPconfig);
displayIPConfigStruct(WM_STA_IPconfig);
//////
saveConfigData();
}
// Getting posted form values and overriding local variables parameters
// Config file is written regardless the connection state
strcpy(custom_AIO_SERVER, AIO_SERVER_FIELD.getValue());
strcpy(custom_AIO_SERVERPORT, AIO_SERVERPORT_FIELD.getValue());
strcpy(custom_AIO_USERNAME, AIO_USERNAME_FIELD.getValue());
strcpy(custom_AIO_KEY, AIO_KEY_FIELD.getValue());
// Writing JSON config file to flash for next boot
writeConfigFile();
digitalWrite(LED_BUILTIN, LED_OFF); // Turn LED off as we are not in configuration mode.
deleteOldInstances();
MQTT_Pub_Topic = String(custom_AIO_USERNAME) + "/feeds/Temperature";
createNewInstances();
}
bool readConfigFile()
{
// this opens the config file in read-mode
File f = FileFS.open(CONFIG_FILE, "r");
if (!f)
{
Serial.println(F("Config File not found"));
return false;
}
else
{
// we could open the file
size_t size = f.size();
// Allocate a buffer to store contents of the file.
std::unique_ptr<char[]> buf(new char[size + 1]);
// Read and store file contents in buf
f.readBytes(buf.get(), size);
// Closing file
f.close();
// Using dynamic JSON buffer which is not the recommended memory model, but anyway
// See https://github.com/bblanchon/ArduinoJson/wiki/Memory%20model
#if (ARDUINOJSON_VERSION_MAJOR >= 6)
DynamicJsonDocument json(1024);
auto deserializeError = deserializeJson(json, buf.get());
if ( deserializeError )
{
Serial.println(F("JSON parseObject() failed"));
return false;
}
serializeJson(json, Serial);
#else
DynamicJsonBuffer jsonBuffer;
// Parse JSON string
JsonObject& json = jsonBuffer.parseObject(buf.get());
// Test if parsing succeeds.
if (!json.success())
{
Serial.println(F("JSON parseObject() failed"));
return false;
}
json.printTo(Serial);
#endif
// Parse all config file parameters, override
// local config variables with parsed values
if (json.containsKey(AIO_SERVER_Label))
{
strcpy(custom_AIO_SERVER, json[AIO_SERVER_Label]);
}
if (json.containsKey(AIO_SERVERPORT_Label))
{
strcpy(custom_AIO_SERVERPORT, json[AIO_SERVERPORT_Label]);
}
if (json.containsKey(AIO_USERNAME_Label))
{
strcpy(custom_AIO_USERNAME, json[AIO_USERNAME_Label]);
}
if (json.containsKey(AIO_KEY_Label))
{
strcpy(custom_AIO_KEY, json[AIO_KEY_Label]);
}
}
Serial.println(F("\nConfig File successfully parsed"));
return true;
}
bool writeConfigFile()
{
Serial.println(F("Saving Config File"));
#if (ARDUINOJSON_VERSION_MAJOR >= 6)
DynamicJsonDocument json(1024);
#else
DynamicJsonBuffer jsonBuffer;
JsonObject& json = jsonBuffer.createObject();
#endif
// JSONify local configuration parameters
json[AIO_SERVER_Label] = custom_AIO_SERVER;
json[AIO_SERVERPORT_Label] = custom_AIO_SERVERPORT;
json[AIO_USERNAME_Label] = custom_AIO_USERNAME;
json[AIO_KEY_Label] = custom_AIO_KEY;
// Open file for writing
File f = FileFS.open(CONFIG_FILE, "w");
if (!f)
{
Serial.println(F("Failed to open Config File for writing"));
return false;
}
#if (ARDUINOJSON_VERSION_MAJOR >= 6)
serializeJsonPretty(json, Serial);
// Write data to file and close it
serializeJson(json, f);
#else
json.prettyPrintTo(Serial);
// Write data to file and close it
json.printTo(f);
#endif
f.close();
Serial.println(F("\nConfig File successfully saved"));
return true;
}
// this function is just to display newly saved data,
// it is not necessary though, because data is displayed
// after WiFi manager resets ESP32
void newConfigData()
{
Serial.println();
Serial.print(F("custom_AIO_SERVER: "));
Serial.println(custom_AIO_SERVER);
Serial.print(F("custom_SERVERPORT: "));
Serial.println(custom_AIO_SERVERPORT);
Serial.print(F("custom_USERNAME_KEY: "));
Serial.println(custom_AIO_USERNAME);
Serial.print(F("custom_KEY: "));
Serial.println(custom_AIO_KEY);
Serial.println();
}
void MQTT_connect()
{
int8_t ret;
MQTT_Pub_Topic = String(custom_AIO_USERNAME) + "/feeds/Temperature";
createNewInstances();
// Return if already connected
if (mqtt->connected())
{
return;
}
Serial.println(F("Connecting to MQTT (3 attempts)..."));
uint8_t attempt = 3;
while ((ret = mqtt->connect()) != 0)
{
// connect will return 0 for connected
Serial.println(mqtt->connectErrorString(ret));
Serial.println(F("Another attemtpt to connect to MQTT in 2 seconds..."));
mqtt->disconnect();
delay(2000); // wait 2 seconds
attempt--;
if (attempt == 0)
{
Serial.println(F("MQTT connection failed. Continuing with program..."));
return;
}
}
Serial.println(F("MQTT connection successful!"));
}
// Setup function
void setup()
{
// Initialize the LED digital pin as an output.
pinMode(LED_BUILTIN, OUTPUT);
// Put your setup code here, to run once
Serial.begin(115200);
while (!Serial);
delay(200);
Serial.print("\nStarting Async_ConfigOnDRD_FS_MQTT_Ptr using " + String(FS_Name));
Serial.println(" on " + String(ARDUINO_BOARD));
Serial.println(ESP_ASYNC_WIFIMANAGER_VERSION);
Serial.println("ESP_DoubleResetDetector Version " + String(ESP_DOUBLE_RESET_DETECTOR_VERSION));
Serial.setDebugOutput(false);
// Mount the filesystem
if (FORMAT_FILESYSTEM)
{
Serial.println(F("Forced Formatting."));
FileFS.format();
}
// Format FileFS if not yet
#ifdef ESP32
if (!FileFS.begin(true))
#else
if (!FileFS.begin())
#endif
{
Serial.print(FS_Name);
Serial.println(F(" failed! AutoFormatting."));
#ifdef ESP8266
FileFS.format();
#endif
}
// New in v1.4.0
initAPIPConfigStruct(WM_AP_IPconfig);
initSTAIPConfigStruct(WM_STA_IPconfig);
//////
if (!readConfigFile())
{
Serial.println(F("Can't read Config File, using default values"));
}
drd = new DoubleResetDetector(DRD_TIMEOUT, DRD_ADDRESS);
if (!drd)
{
Serial.println(F("Can't instantiate. Disable DRD feature"));
}
else if (drd->detectDoubleReset())
{
// DRD, disable timeout.
//ESPAsync_wifiManager.setConfigPortalTimeout(0);
Serial.println(F("Open Config Portal without Timeout: Double Reset Detected"));
initialConfig = true;
}
if (initialConfig)
{
wifi_manager();
}
else
{
// Load stored data, the addAP ready for MultiWiFi reconnection
loadConfigData();
// Pretend CP is necessary as we have no AP Credentials
initialConfig = true;
for (uint8_t i = 0; i < NUM_WIFI_CREDENTIALS; i++)
{
// Don't permit NULL SSID and password len < MIN_AP_PASSWORD_SIZE (8)
if ( (String(WM_config.WiFi_Creds[i].wifi_ssid) != "") && (strlen(WM_config.WiFi_Creds[i].wifi_pw) >= MIN_AP_PASSWORD_SIZE) )
{
LOGERROR3(F("* Add SSID = "), WM_config.WiFi_Creds[i].wifi_ssid, F(", PW = "), WM_config.WiFi_Creds[i].wifi_pw );
wifiMulti.addAP(WM_config.WiFi_Creds[i].wifi_ssid, WM_config.WiFi_Creds[i].wifi_pw);
initialConfig = false;
}
}
if (initialConfig)
{
Serial.println(F("Open Config Portal without Timeout: No stored WiFi Credentials"));
wifi_manager();
}
else if ( WiFi.status() != WL_CONNECTED )
{
Serial.println("ConnectMultiWiFi in setup");
connectMultiWiFi();
}
}
digitalWrite(LED_BUILTIN, LED_OFF); // Turn led off as we are not in configuration mode.
}
// Loop function
void loop()
{
// Call the double reset detector loop method every so often,
// so that it can recognise when the timeout expires.
// You can also call drd.stop() when you wish to no longer
// consider the next reset as a double reset.
if (drd)
drd->loop();
// this is just for checking if we are connected to WiFi
check_status();
}
1. Async_ConfigOnDRD_FS_MQTT_Ptr_Medium on ESP32_DEV
This is terminal debug output when running Async_ConfigOnDRD_FS_MQTT_Ptr_Medium on ESP32 ESP32_DEV.. Config Portal was requested by DRD to input and save MQTT Credentials. The boards then connected to Adafruit MQTT Server successfully.
Starting Async_ConfigOnDRD_FS_MQTT_Ptr_Medium using LittleFS on ESP32_DEV
ESPAsync_WiFiManager v1.4.1
ESP_DoubleResetDetector Version v1.1.0
Config File not found
Can't read Config File, using default values
LittleFS Flag read = 0xd0d01234
doubleResetDetected
Saving config file...
Saving config file OK
Open Config Portal without Timeout: Double Reset Detected
Config Portal requested.
Opening Configuration Portal. No timeout : DRD or No stored Credentials..
[WM] Set CORS Header to : Your Access-Control-Allow-Origin
Starting Async_ConfigOnDRD_FS_MQTT_Ptr_Medium using LittleFS on ESP32_DEV
ESPAsync_WiFiManager v1.4.1
ESP_DoubleResetDetector Version v1.1.0
Config File not found
Can't read Config File, using default values
LittleFS Flag read = 0xd0d04321
No doubleResetDetected
Saving config file...
Saving config file OK
[WM] LoadWiFiCfgFile
[WM] failed
Open Config Portal without Timeout: No stored WiFi Credentials
Config Portal requested.
[WM] RFC925 Hostname = ConfigOnSwichFS-MQTT
Opening Configuration Portal. No timeout : DRD or No stored Credentials..
[WM] Adding parameter AIO_SERVER_Label
[WM] Adding parameter AIO_SERVERPORT_Label
[WM] Adding parameter AIO_USERNAME_Label
[WM] Adding parameter AIO_KEY_Label
[WM] setAPStaticIPConfig
[WM] setSTAStaticIPConfig
[WM] Set CORS Header to : Your Access-Control-Allow-Origin
[WM] WiFi.waitForConnectResult Done
[WM] SET AP
[WM]
Configuring AP SSID = ESP_9ABF498
[WM] AP PWD = your_password
[WM] AP Channel = 9
[WM] Custom AP IP/GW/Subnet = 192.168.100.1 192.168.100.1 255.255.255.0
[WM] AP IP address = 192.168.100.1
[WM] HTTP server started
[WM] ESPAsync_WiFiManager::startConfigPortal : Enter loop
[WM] Connecting to new AP
[WM] Previous settings invalidated
[WM] Custom STA IP/GW/Subnet
[WM] DNS1 and DNS2 set
[WM] setWifiStaticIP IP = 192.168.2.235
[WM] Connect to new WiFi using new IP parameters
[WM] Connected after waiting (s) : 0.60
[WM] Local ip = 192.168.2.235
[WM] Connection result: WL_CONNECTED
Connected...yeey :)
Local IP: 192.168.2.235
[WM] * Add SSID = HueNet1 , PW = 12345678
[WM] * Add SSID = HueNet2 , PW = 12345678
[WM] getSTAStaticIPConfig
[WM] stationIP = 192.168.2.235 , gatewayIP = 192.168.2.1
[WM] netMask = 255.255.255.0
[WM] dns1IP = 192.168.2.1 , dns2IP = 8.8.8.8
[WM] SaveWiFiCfgFile
[WM] OK
Saving Config File
{
"AIO_SERVER_Label": "io.adafruit.com",
"AIO_SERVERPORT_Label": "1883",
"AIO_USERNAME_Label": "user_name",
"AIO_KEY_Label": "aio_key"
}
Config File successfully saved
Creating new WiFi client object : OK
Creating new MQTT object : OK
AIO_SERVER = io.adafruit.com, AIO_SERVERPORT = 1883
AIO_USERNAME = user_name, AIO_KEY = aio_key
Creating new MQTT_Pub_Topic, Temperature = user_name/feeds/Temperature
Creating new Temperature object : OK
Temperature MQTT_Pub_Topic = user_name/feeds/Temperature
[WM] freeing allocated params!
Stop doubleResetDetecting
Saving config file...
Saving config file OK
WConnecting to MQTT (3 attempts)...
MQTT connection successful!
TWWWW
2. Async_ConfigOnDRD_FS_MQTT_Ptr_Complex on ESP8266_NODEMCU
This is terminal debug output when running Async_ConfigOnDRD_FS_MQTT_Ptr_Complex on ESP8266_NODEMCU 1.0.. Config Portal was requested to input and save MQTT Credentials. The boards then connected to Adafruit MQTT Server successfully.
Starting Async_ConfigOnDRD_FS_MQTT_Ptr_Complex using LittleFS on ESP8266_NODEMCU
ESPAsync_WiFiManager v1.4.1
ESP_DoubleResetDetector Version v1.1.0
{"AIO_SERVER_Label":"io.adafruit.com","AIO_SERVERPORT_Label":"1883","AIO_USERNAME_Label":"user_name","AIO_KEY_Label":"aio_key"}
Config File successfully parsed
LittleFS Flag read = 0xd0d04321
No doubleResetDetected
Saving config file...
Saving config file OK
[WM] LoadWiFiCfgFile
[WM] OK
[WM] stationIP = 192.168.2.188 , gatewayIP = 192.168.2.1
[WM] netMask = 255.255.255.0
[WM] dns1IP = 192.168.2.1 , dns2IP = 8.8.8.8
[WM] * Add SSID = HueNet1 , PW = 12345678
[WM] * Add SSID = HueNet2 , PW = 12345678
ConnectMultiWiFi in setup
[WM] ConnectMultiWiFi with :
[WM] * Additional SSID = HueNet1 , PW = 12345678
[WM] * Additional SSID = HueNet2 , PW = 12345678
[WM] Connecting MultiWifi...
[WM] WiFi connected after time: 1
[WM] SSID: HueNet1 ,RSSI= -32
[WM] Channel: 2 ,IP address: 192.168.2.188
W
Creating new WiFi client object : OK
Creating new MQTT object : OK
AIO_SERVER = io.adafruit.com, AIO_SERVERPORT = 1883
AIO_USERNAME = user_name, AIO_KEY = aio_key
Creating new MQTT_Pub_Topic, Temperature = user_name/feeds/Temperature
Creating new Temperature object : OK
Temperature MQTT_Pub_Topic = user_name/feeds/Temperature
Connecting to MQTT (3 attempts)...
MQTT connection successful!
TWWWW WTWWW
Starting Async_ConfigOnDRD_FS_MQTT_Ptr_Complex using LittleFS on ESP8266_NODEMCU
ESPAsync_WiFiManager v1.4.1
ESP_DoubleResetDetector Version v1.1.0
{"AIO_SERVER_Label":"io.adafruit.com","AIO_SERVERPORT_Label":"1883","AIO_USERNAME_Label":"user_name","AIO_KEY_Label":"aio_key"}
Config File successfully parsed
LittleFS Flag read = 0xd0d01234
doubleResetDetected
Saving config file...
Saving config file OK
Open Config Portal without Timeout: Double Reset Detected
Config Portal requested.
[WM] RFC925 Hostname = ConfigOnSwichFS-MQTT
Opening Configuration Portal. No timeout : DRD or No stored Credentials..
[WM] Adding parameter AIO_KEY_Label
[WM] Adding parameter AIO_SERVER_Label
[WM] Adding parameter AIO_SERVERPORT_Label
[WM] Adding parameter AIO_USERNAME_Label
[WM] setAPStaticIPConfig
[WM] setSTAStaticIPConfig
[WM] Set CORS Header to : Your Access-Control-Allow-Origin
[WM] WiFi.waitForConnectResult Done
[WM] SET AP
[WM]
Configuring AP SSID = ESP_702FF3
[WM] AP PWD = your_password
[WM] AP Channel = 1
[WM] Custom AP IP/GW/Subnet = 192.168.100.1 192.168.100.1 255.255.255.0
[WM] AP IP address = 192.168.100.1
[WM] HTTP server started
[WM] ESPAsync_WiFiManager::startConfigPortal : Enter loop
[WM] Custom STA IP/GW/Subnet
[WM] DNS1 and DNS2 set
[WM] setWifiStaticIP IP = 192.168.2.188
[WM] Connected after waiting (s) : 0.00
[WM] Local ip = 192.168.2.188
[WM] Timed out connection result: WL_IDLE_STATUS
Not connected to WiFi but continuing anyway.
[WM] * Add SSID = HueNet1 , PW = 12345678
[WM] * Add SSID = HueNet2 , PW = 12345678
[WM] getSTAStaticIPConfig
[WM] stationIP = 192.168.2.188 , gatewayIP = 192.168.2.1
[WM] netMask = 255.255.255.0
[WM] dns1IP = 192.168.2.1 , dns2IP = 8.8.8.8
[WM] SaveWiFiCfgFile
[WM] OK
Saving Config File
{
"AIO_KEY_Label": "aio_key",
"AIO_SERVER_Label": "io.adafruit.com",
"AIO_SERVERPORT_Label": "1883",
"AIO_USERNAME_Label": "user_name"
}
Config File successfully saved
Creating new WiFi client object : OK
Creating new MQTT object : OK
AIO_SERVER = io.adafruit.com, AIO_SERVERPORT = 1883
AIO_USERNAME = user_name, AIO_KEY = aio_key
Creating new MQTT_Pub_Topic, Temperature = user_name/feeds/Temperature
Creating new Temperature object : OK
Temperature MQTT_Pub_Topic = user_name/feeds/Temperature
[WM] freeing allocated params!
WiFi lost. Call connectMultiWiFi in loop
[WM] ConnectMultiWiFi with :
[WM] * Additional SSID = HueNet1 , PW = 12345678
[WM] * Additional SSID = HueNet2 , PW = 12345678
[WM] Connecting MultiWifi...
[WM] WiFi connected after time: 1
[WM] SSID: HueNet1 ,RSSI= -34
[WM] Channel: 2 ,IP address: 192.168.2.188
WConnecting to MQTT (3 attempts)...
MQTT connection successful!
TWW
3. Async_ConfigOnDoubleReset on ESP32_DEV
This is terminal debug output when running Async_ConfigOnDoubleReset on ESP32 ESP32_DEV.. Config Portal was requested by DRD to input and save Credentials. The boards then connected to WiFi using new Static IP successfully. WiFi AP HueNet1 is then lost, and board autoreconnects itself to backup WiFi AP HueNet2.
Starting Async_ConfigOnDoubleReset with DoubleResetDetect using SPIFFS on ESP32_DEV
ESPAsync_WiFiManager v1.4.1
ESP_DoubleResetDetector Version v1.1.0
[WM] RFC925 Hostname = ConfigOnDoubleReset
[WM] setSTAStaticIPConfig for USE_CONFIGURABLE_DNS
[WM] Set CORS Header to : Your Access-Control-Allow-Origin
Stored: SSID = HueNet1, Pass = 12345678
[WM] * Add SSID = HueNet1 , PW = 12345678
Got stored Credentials. Timeout 120s for Config Portal
SPIFFS Flag read = 0xd0d01234
doubleResetDetected
Saving config file...
Saving config file OK
Open Config Portal without Timeout: Double Reset Detected
Starting configuration portal.
[WM] WiFi.waitForConnectResult Done
[WM] SET AP
[WM]
Configuring AP SSID = ESP_E92DE6B4
[WM] AP PWD = your_password
[WM] AP Channel = 3
[WM] AP IP address = 192.168.4.1
[WM] HTTP server started
[WM] ESPAsync_WiFiManager::startConfigPortal : Enter loop
[WM] Connecting to new AP
[WM] Previous settings invalidated
[WM] Custom STA IP/GW/Subnet
[WM] DNS1 and DNS2 set
[WM] setWifiStaticIP IP = 192.168.2.232
[WM] Connect to new WiFi using new IP parameters
[WM] Connected after waiting (s) : 0.60
[WM] Local ip = 192.168.2.232
[WM] Connection result: WL_CONNECTED
WiFi connected...yeey :)
[WM] * Add SSID = HueNet1 , PW = 12345678
[WM] * Add SSID = HueNet2 , PW = 12345678
[WM] SaveWiFiCfgFile
[WM] OK
After waiting 0.00 secs more in setup(), connection result is connected. Local IP: 192.168.2.232
[WM] freeing allocated params!
HH
WiFi lost. Call connectMultiWiFi in loop
[WM] ConnectMultiWiFi with :
[WM] * Flash-stored Router_SSID = HueNet1 , Router_Pass = 12345678
[WM] * Additional SSID = HueNet1 , PW = 12345678
[WM] * Additional SSID = HueNet2 , PW = 12345678
[WM] Connecting MultiWifi...
[WM] WiFi connected after time: 2
[WM] SSID: HueNet2 ,RSSI= -51
[WM] Channel: 4 ,IP address: 192.168.2.232
HHHHHHHHHH HHHHHHHHHH HHHHHHHHHH HHHHHHHHHH
4. Async_ConfigOnDoubleReset on ESP8266_NODEMCU
This is terminal debug output when running Async_ConfigOnDoubleReset on ESP8266_NODEMCU.. Config Portal was requested by DRD to input and save Credentials. The boards then connected to WiFi using new Static IP successfully. WiFi AP HueNet1 is then lost, and board autoreconnects itself to backup WiFi AP HueNet2.
Starting Async_ConfigOnDoubleReset with DoubleResetDetect using LittleFS on ESP8266_NODEMCU
ESPAsync_WiFiManager v1.4.1
ESP_DoubleResetDetector Version v1.1.0
[WM] RFC925 Hostname = ConfigOnDoubleReset
[WM] setSTAStaticIPConfig for USE_CONFIGURABLE_DNS
[WM] Set CORS Header to : Your Access-Control-Allow-Origin
Stored: SSID = HueNet1, Pass = 12345678
[WM] * Add SSID = HueNet1 , PW = 12345678
Got stored Credentials. Timeout 120s for Config Portal
LittleFS Flag read = 0xd0d01234
doubleResetDetected
Saving config file...
Saving config file OK
Open Config Portal without Timeout: Double Reset Detected
Starting configuration portal.
[WM] WiFi.waitForConnectResult Done
[WM] SET AP_STA
[WM]
Configuring AP SSID = ESP_119055
[WM] AP PWD = your_password
[WM] AP Channel = 5
[WM] AP IP address = 192.168.4.1
[WM] HTTP server started
[WM] ESPAsync_WiFiManager::startConfigPortal : Enter loop
[WM] Connecting to new AP
[WM] Previous settings invalidated
[WM] Custom STA IP/GW/Subnet
[WM] DNS1 and DNS2 set
[WM] setWifiStaticIP IP = 192.168.2.186
[WM] Connect to new WiFi using new IP parameters
[WM] Connected after waiting (s) : 3.23
[WM] Local ip = 192.168.2.186
[WM] Connection result: WL_CONNECTED
WiFi connected...yeey :)
[WM] * Add SSID = HueNet1 , PW = 12345678
[WM] * Add SSID = HueNet2 , PW = 12345678
[WM] SaveWiFiCfgFile
[WM] OK
After waiting 0.00 secs more in setup(), connection result is connected. Local IP: 192.168.2.186
[WM] freeing allocated params!
HHH
WiFi lost. Call connectMultiWiFi in loop
[WM] ConnectMultiWiFi with :
[WM] * Flash-stored Router_SSID = HueNet1 , Router_Pass = 12345678
[WM] * Additional SSID = HueNet1 , PW = 12345678
[WM] * Additional SSID = HueNet2 , PW = 12345678
[WM] Connecting MultiWifi...
[WM] WiFi connected after time: 1
[WM] SSID: HueNet2 ,RSSI= -50
[WM] Channel: 4 ,IP address: 192.168.2.186
HHHHHHHHHH HHHHHHHHHH HHH
5. Async_ESP_FSWebServer_DRD on ESP8266_NODEMCU
This is terminal debug output when running Async_ESP_FSWebServer_DRD on ESP8266_NODEMCU.. Config Portal was requested by DRD to input and save Credentials. The boards then connected to WiFi using new Static IP successfully.
Starting Async_ESP_FSWebServer_DRD using LittleFS on ESP8266_NODEMCU
ESPAsync_WiFiManager v1.4.1
ESP_DoubleResetDetector Version v1.1.0
Opening / directory
FS File: CanadaFlag_1.png, size: 40.25KB
FS File: CanadaFlag_2.png, size: 8.12KB
FS File: CanadaFlag_3.jpg, size: 10.89KB
FS File: ConfigMQTT.json, size: 151B
FS File: ConfigSW.json, size: 53B
FS File: drd.dat, size: 4B
FS File: edit.htm.gz, size: 4.02KB
FS File: favicon.ico, size: 1.12KB
FS File: graphs.js.gz, size: 1.92KB
FS File: index.htm, size: 3.63KB
FS File: wifi_cred.dat, size: 192B
[WM] RFC925 Hostname = AsyncESP-FSWebServer
[WM] setAPStaticIPConfig
[WM] setSTAStaticIPConfig for USE_CONFIGURABLE_DNS
[WM] Set CORS Header to : Your Access-Control-Allow-Origin
Stored: SSID = HueNet1, Pass = 12345678
[WM] * Add SSID = HueNet1 , PW = 12345678
Got stored Credentials. Timeout 120s for Config Portal
LittleFS Flag read = 0xd0d01234
doubleResetDetected
Saving config file...
Saving config file OK
Open Config Portal without Timeout: Double Reset Detected
[WM] WiFi.waitForConnectResult Done
[WM] SET AP_STA
[WM]
Configuring AP SSID = ESP_119055
[WM] AP PWD = your_password
[WM] AP Channel = 11
[WM] Custom AP IP/GW/Subnet = 192.168.100.1 192.168.100.1 255.255.255.0
[WM] AP IP address = 192.168.100.1
[WM] HTTP server started
[WM] ESPAsync_WiFiManager::startConfigPortal : Enter loop
[WM] Custom STA IP/GW/Subnet
[WM] DNS1 and DNS2 set
[WM] setWifiStaticIP IP = 192.168.2.186
[WM] Connected after waiting (s) : 0.19
[WM] Local ip = 192.168.2.186
[WM] Timed out connection result: WL_CONNECTED
WiFi connected...yeey :)
[WM] * Add SSID = HueNet1 , PW = 12345678
[WM] * Add SSID = HueNet2 , PW = 12345678
[WM] SaveWiFiCfgFile
[WM] OK
After waiting 0.00 secs more in setup(), connection result is connected. Local IP: 192.168.2.186
HTTP server started @ 192.168.2.186
===============================================================
Open http://async-esp8266fs.local/edit to see the file browser
Using username = admin and password = admin
===============================================================
[WM] freeing allocated params!
HHHHHH
You can access using the HTTP server IP (http://192.168.2.186) or its mDNS hostname (http://async-esp8266fs.local)
By going to http://192.168.2.186/edit or http://async-esp8266fs.local/edit, you can edit / delete / upload / download any file in the folder
6. Async_ESP32_FSWebServer_DRD on ESP32_DEV
This is terminal debug output when running Async_ESP32_FSWebServer_DRD on ESP32_DEV using newly-supported LittleFS.. Config Portal was requested by DRD (also using LittleFS) to input and save Credentials. The boards then connected to WiFi successfully.
Starting Async_ESP32_FSWebServer_DRD using LittleFS on ESP32_DEV
ESPAsync_WiFiManager v1.4.1
ESP_DoubleResetDetector Version v1.1.0
FS File: /CanadaFlag_1.png, size: 40.25KB
FS File: /CanadaFlag_2.png, size: 8.12KB
FS File: /CanadaFlag_3.jpg, size: 10.89KB
FS File: /Credentials.txt, size: 192B
FS File: /drd.dat, size: 4B
FS File: /edit.htm.gz, size: 4.02KB
FS File: /favicon.ico, size: 1.12KB
FS File: /graphs.js.gz, size: 1.92KB
FS File: /index.htm, size: 3.63KB
FS File: /wifi_cred.dat, size: 192B
[WM] RFC925 Hostname = AsyncESP32-FSWebServer
[WM] setAPStaticIPConfig
[WM] Set CORS Header to : Your Access-Control-Allow-Origin
Stored: SSID = HueNet1, Pass = 12345678
[WM] * Add SSID = HueNet1 , PW = 12345678
Got stored Credentials. Timeout 120s for Config Portal
LittleFS Flag read = 0xd0d04321
No doubleResetDetected
Saving config file...
Saving config file OK
[WM] LoadWiFiCfgFile
[WM] OK
[WM] * Add SSID = HueNet1 , PW = 12345678
[WM] * Add SSID = HueNet2 , PW = 12345678
ConnectMultiWiFi in setup
[WM] ConnectMultiWiFi with :
[WM] * Flash-stored Router_SSID = HueNet1 , Router_Pass = 12345678
[WM] * Additional SSID = HueNet1 , PW = 12345678
[WM] * Additional SSID = HueNet2 , PW = 12345678
[WM] Connecting MultiWifi...
[WM] WiFi connected after time: 1
[WM] SSID: HueNet1 ,RSSI= -39
[WM] Channel: 2 ,IP address: 192.168.2.101
After waiting 3.75 secs more in setup(), connection result is connected. Local IP: 192.168.2.101
HTTP server started @ 192.168.2.101
===============================================================
Open http://async-esp32fs.local/edit to see the file browser
Using username = admin and password = admin
===============================================================
[WM] freeing allocated params!
HStop doubleResetDetecting
Saving config file...
Saving config file OK
HHHHHHHHH HHHHHHHHHH HHHHHHHHHH HHHHHHHHHH HHHHHHHHHH HHHHHHHHHH HHHHHHHHHH HHHHHHHHHH
...
Starting ESP32_FSWebServer_DRD using LittleFS on ESP32_DEV
ESPAsync_WiFiManager Version v1.3.0
ESP_DoubleResetDetector Version v1.1.0
FS File: /CanadaFlag_1.png, size: 40.25KB
FS File: /CanadaFlag_2.png, size: 8.12KB
FS File: /CanadaFlag_3.jpg, size: 10.89KB
FS File: /Credentials.txt, size: 192B
FS File: /drd.dat, size: 4B
FS File: /edit.htm.gz, size: 4.02KB
FS File: /favicon.ico, size: 1.12KB
FS File: /graphs.js.gz, size: 1.92KB
FS File: /index.htm, size: 3.63KB
FS File: /wifi_cred.dat, size: 192B
[WM] RFC925 Hostname = AsyncESP32-FSWebServer
[WM] setAPStaticIPConfig
[WM] Set CORS Header to : Your Access-Control-Allow-Origin
Stored: SSID = HueNet1, Pass = 12345678
[WM] * Add SSID = HueNet1 , PW = 87654321
Got stored Credentials. Timeout 120s for Config Portal
LittleFS Flag read = 0xd0d01234
doubleResetDetected
Saving config file...
Saving config file OK
Open Config Portal without Timeout: Double Reset Detected
[WM] WiFi.waitForConnectResult Done
[WM] SET AP
[WM]
Configuring AP SSID = ESP_9ABF498
[WM] AP PWD = your_password
[WM] AP Channel = 8
[WM] Custom AP IP/GW/Subnet = 192.168.100.1 192.168.100.1 255.255.255.0
[WM] AP IP address = 192.168.100.1
[WM] HTTP server started
[WM] ESPAsync_WiFiManager::startConfigPortal : Enter loop
[WM] Can't use Custom STA IP/GW/Subnet
[WM] Connected after waiting (s) : 1.50
[WM] Local ip = 192.168.2.101
[WM] Timed out connection result: WL_CONNECTED
WiFi connected...yeey :)
[WM] * Add SSID = HueNet1 , PW = 12345678
[WM] * Add SSID = HueNet2 , PW = 12345678
[WM] SaveWiFiCfgFile
[WM] OK
After waiting 0.00 secs more in setup(), connection result is connected. Local IP: 192.168.2.101
HTTP server started @ 192.168.2.101
===============================================================
Open http://async-esp32fs.local/edit to see the file browser
Using username = admin and password = admin
===============================================================
[WM] freeing allocated params!
HH
Debug is enabled by default on Serial. To disable, add before startConfigPortal()
ESPAsync_wifiManager.setDebugOutput(false);
You can also change the debugging level from 0 to 4
// Use from 0 to 4. Higher number, more debugging messages and memory usage.
#define _ESPASYNC_WIFIMGR_LOGLEVEL_ 3
If you get compilation errors, more often than not, you may need to install a newer version of the ESP32 / ESP8266
core for Arduino.
Sometimes, the library will only work if you update the ESP32 / 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 ConfigPortal does not show up, just open a browser and type in the IP of the web portal, by default 192.168.4.1
.
Submit issues to: ESPAsync_WiFiManager issues
- Fix bug.
- Fix compiler warnings.
- Fix staticIP not saved in examples. See ESP32 static IP not saved after restarting the device
- Add structures and functions to handle AP and STA IPs.
- Add complex examples
- Async_ConfigOnDRD_FS_MQTT_Ptr_Complex to demo usage of std::map
- Async_ConfigOnDRD_FS_MQTT_Ptr_Medium.
- Add simple minimal examples
- Async_ConfigOnDRD_ESP32_minimal
- Async_ConfigOnDRD_ESP8266_minimal
- Async_AutoConnect_ESP32_minimal
- Async_AutoConnect_ESP8266_minimal
- Modify Version String
- Add Table of Contents
- Add LittleFS support to ESP32-related examples to use LittleFS_esp32 Library
- Add Version String
- Restore cpp code besides Impl.h code to use in case of
multiple definition
linker error. SeeChange Implementation to seperate *.h and *.cpp file instead of *.h and *-Impl.h
andSupport building in PlatformIO PR
. Also have a look at HOWTO Fix Multiple Definitions Linker Error - Fix bug /close does not close the config portal.
- Fix bug in examples.
- Add MultiWiFi feature to auto(Re)connect to the best WiFi at runtime
- Fix bug, typo and minor improvement.
- Completely enhanced examples to use new MultiWiFi feature.
- Add setCORSHeader function to allow configurable CORS Header. See Using CORS feature
- Bump up to v1.1.1 to sync with ESP_WiFiManager v1.0.11.
- Initial coding to use ESPAsyncWebServer instead of (ESP8266)WebServer.
- Add more features and error checking to many examples.
- Add example Async_ConfigOnDRD_FS_MQTT_Ptr
- Bump up to v1.0.11 to sync with ESP_WiFiManager v1.0.11.
This library is based on, modified, bug-fixed and improved from:
Tzapu's WiFiManager
Ken Taylor's WiFiManager
Alan Steremberg's ESPAsyncWiFiManager
Khoi Hoang's ESP_WiFiManager
to use the better asynchronous ESPAsyncWebServer instead of (ESP8266)WebServer.
- Based on and modified from Tzapu, KenTaylor's version,
Alan Steremberg's ESPAsyncWiFiManager
andKhoi Hoang's ESP_WiFiManager
. - Thanks to Hristo Gochkov for great ESPAsyncWebServer Library
- Thanks to good work of Miguel Alexandre Wisintainer for working with, developing, debugging and testing.
- Thanks to cancodr for requesting an enhancement in Issue #29: Is it possible to use AsyncWebServer.h instead of WebServer.h?, leading to this ESPAsync_WiFiManager Library.
- Thanks to Marcel Stör for reporting /close does not close the config portal bug which is fixed in v1.2.0.
- Thanks to Vague Rabbit for requesting, collarborating in creating the HOWTO Add Dynamic Parameters.
- Thanks to krupis for reporting ESP32 static IP not saved after restarting the device bug which is fixed in v1.4.0.
![]() ⭐️⭐️ Hristo Gochkov |
![]() ⭐️ Tzapu |
![]() ⭐️ Ken Taylor |
![]() ⭐️ Alan Steremberg |
![]() Miguel Wisintainer |
![]() cancodr |
![]() Marcel Stör |
![]() Vague Rabbit |
![]() krupis |
If you want to contribute to this project:
- Report bugs and errors
- Ask for enhancements
- Create issues and pull requests
- Tell other people about this library
- The library is licensed under MIT
Copyright 2020- Khoi Hoang