From 703b82e36f7f374d5e4be73cc47fad0bb83420c7 Mon Sep 17 00:00:00 2001 From: Khoi Hoang <57012152+khoih-prog@users.noreply.github.com> Date: Fri, 7 Oct 2022 22:43:16 -0400 Subject: [PATCH] v1.15.0 to optionally display Credentials ### Releases v1.15.0 1. Optionally display Credentials (SSIDs, PWDs) in Config Portal. Check [Populate portal wifi with saved credentials #91](https://github.com/khoih-prog/ESP_WiFiManager/discussions/91) and [Prepopulating the configuration with SSID and Password from stored file #115](https://github.com/khoih-prog/ESPAsync_WiFiManager/discussions/115) 2. Display `Credentials` Hint on Config Portal 3. Periodic code clean-up --- CONTRIBUTING.md | 8 +- Images/Configuration.png | Bin 41319 -> 45730 bytes changelog.md | 7 + .../Async_AutoConnect/Async_AutoConnect.ino | 14 +- .../Async_AutoConnectWithFSParameters.ino | 14 +- ...AutoConnectWithFSParametersAndCustomIP.ino | 14 +- .../Async_AutoConnectWithFeedback.ino | 14 +- .../Async_AutoConnectWithFeedbackLED.ino | 14 +- .../Async_ConfigOnDRD_FS_MQTT_Ptr.ino | 38 ++- .../Async_ConfigOnDRD_FS_MQTT_Ptr_Complex.ino | 41 ++-- .../Async_ConfigOnDRD_FS_MQTT_Ptr_Medium.ino | 39 ++- .../Async_ConfigOnDoubleReset.ino | 16 +- .../Async_ConfigOnDoubleReset_Multi.ino | 6 + .../Async_ConfigOnDoubleReset_TZ.ino | 16 +- .../Async_ConfigOnStartup.ino | 14 +- .../Async_ConfigOnSwitch.ino | 20 +- .../Async_ConfigOnSwitchFS.ino | 20 +- .../Async_ConfigOnSwitchFS_MQTT_Ptr.ino | 16 +- .../Async_ConfigPortalParamsOnSwitch.ino | 20 +- .../Async_ESP32_FSWebServer.ino | 14 +- .../Async_ESP32_FSWebServer_DRD.ino | 14 +- .../Async_ESP_FSWebServer.ino | 14 +- .../Async_ESP_FSWebServer_DRD.ino | 14 +- keywords.txt | 1 + library.json | 4 +- library.properties | 4 +- src/ESPAsync_WiFiManager-Impl.h | 77 +++--- src/ESPAsync_WiFiManager.h | 3 +- src/ESPAsync_WiFiManager.hpp | 222 ++++++++++++------ src/ESPAsync_WiFiManager_Debug.h | 3 +- 30 files changed, 514 insertions(+), 187 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0721ba23..1a779837 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -22,10 +22,10 @@ If you don't find anything, please [open a new issue](https://github.com/khoih-p ### How to submit a bug report -Please ensure to specify the following: +Please ensure to specify the following, or your post will be ignored and deleted: * Arduino IDE version (e.g. 1.8.19) or Platform.io version -* `ESP8266` or `ESP32` Core Version (e.g. ESP8266 core v3.0.2 or ESP32 v2.0.4) +* `ESP8266` or `ESP32` Core Version (e.g. ESP8266 core v3.0.2 or ESP32 v2.0.5) * Contextual information (e.g. what you were trying to achieve) * Simplest possible steps to reproduce * Anything that might be relevant in your opinion, such as: @@ -39,10 +39,10 @@ Please ensure to specify the following: Arduino IDE version: 1.8.19 ESP8266 Core Version 3.0.2 OS: Ubuntu 20.04 LTS -Linux xy-Inspiron-3593 5.15.0-46-generic #49~20.04.1-Ubuntu SMP Thu Aug 4 19:15:44 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux +Linux xy-Inspiron-3593 5.15.0-48-generic #54~20.04.1-Ubuntu SMP Thu Sep 1 16:17:26 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux Context: -I encountered an endless loop while trying to connect to Local WiFi. +I encountered a crash when using this library Steps to reproduce: 1. ... diff --git a/Images/Configuration.png b/Images/Configuration.png index 2800497a29e6e75c2fc93e40410114ae29b48ed6..90c9cfae403d4cc73e6529452509136025ddbf8b 100644 GIT binary patch literal 45730 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 M(kjsM7iOXV1xa1~^Z)<= literal 41319 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 diff --git a/changelog.md b/changelog.md index c19cd2b1..8f89de13 100644 --- a/changelog.md +++ b/changelog.md @@ -12,6 +12,7 @@ ## Table of Contents * [Changelog](#changelog) + * [Releases v1.15.0](#releases-v1150) * [Releases v1.14.1](#releases-v1141) * [Releases v1.14.0](#releases-v1140) * [Releases v1.13.0](#releases-v1130) @@ -53,6 +54,12 @@ ## Changelog +### Releases v1.15.0 + +1. Optionally display Credentials (SSIDs, PWDs) in Config Portal. Check [Populate portal wifi with saved credentials #91](https://github.com/khoih-prog/ESP_WiFiManager/discussions/91) and [Prepopulating the configuration with SSID and Password from stored file #115](https://github.com/khoih-prog/ESPAsync_WiFiManager/discussions/115) +2. Display `Credentials` Hint on Config Portal +3. Periodic code clean-up + ### Releases v1.14.1 1. Remove dependency on ESP_AsyncWebServer, ESPAsyncTCP and AsyncTCP in `library.properties`. Check ["no protocol" error #113](https://github.com/khoih-prog/ESPAsync_WiFiManager/issues/113)" diff --git a/examples/Async_AutoConnect/Async_AutoConnect.ino b/examples/Async_AutoConnect/Async_AutoConnect.ino index 20a7090c..be32c2b7 100644 --- a/examples/Async_AutoConnect/Async_AutoConnect.ino +++ b/examples/Async_AutoConnect/Async_AutoConnect.ino @@ -18,12 +18,16 @@ #error This code is intended to run on the ESP8266 or ESP32 platform! Please check your Tools->Board setting. #endif -#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN_TARGET "ESPAsync_WiFiManager v1.14.0" -#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN 1014000 +#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN_TARGET "ESPAsync_WiFiManager v1.15.0" +#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN 1015000 // Use from 0 to 4. Higher number, more debugging messages and memory usage. #define _ESPASYNC_WIFIMGR_LOGLEVEL_ 3 +// To not display stored SSIDs and PWDs on Config Portal, select false. Default is true +// Even the stored Credentials are not display, just leave them all blank to reconnect and reuse the stored Credentials +//#define DISPLAY_STORED_CREDENTIALS_IN_CP false + //Ported to ESP32 #ifdef ESP32 #include @@ -780,6 +784,12 @@ void setup() Serial.print(F(", PWD = ")); Serial.println(AP_PASS); +#if DISPLAY_STORED_CREDENTIALS_IN_CP + // New. Update Credentials, got from loadConfigData(), to display on CP + ESPAsync_wifiManager.setCredentials(WM_config.WiFi_Creds[0].wifi_ssid, WM_config.WiFi_Creds[0].wifi_pw, + WM_config.WiFi_Creds[1].wifi_ssid, WM_config.WiFi_Creds[1].wifi_pw); +#endif + // Starts an access point //if (!ESPAsync_wifiManager.startConfigPortal((const char *) ssid.c_str(), password)) if ( !ESPAsync_wifiManager.startConfigPortal(AP_SSID.c_str(), AP_PASS.c_str()) ) diff --git a/examples/Async_AutoConnectWithFSParameters/Async_AutoConnectWithFSParameters.ino b/examples/Async_AutoConnectWithFSParameters/Async_AutoConnectWithFSParameters.ino index f6200587..bbf49ec8 100644 --- a/examples/Async_AutoConnectWithFSParameters/Async_AutoConnectWithFSParameters.ino +++ b/examples/Async_AutoConnectWithFSParameters/Async_AutoConnectWithFSParameters.ino @@ -19,12 +19,16 @@ #error This code is intended to run on the ESP8266 or ESP32 platform! Please check your Tools->Board setting. #endif -#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN_TARGET "ESPAsync_WiFiManager v1.14.0" -#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN 1014000 +#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN_TARGET "ESPAsync_WiFiManager v1.15.0" +#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN 1015000 // Use from 0 to 4. Higher number, more debugging messages and memory usage. #define _ESPASYNC_WIFIMGR_LOGLEVEL_ 3 +// To not display stored SSIDs and PWDs on Config Portal, select false. Default is true +// Even the stored Credentials are not display, just leave them all blank to reconnect and reuse the stored Credentials +//#define DISPLAY_STORED_CREDENTIALS_IN_CP false + #include //Ported to ESP32 @@ -1015,6 +1019,12 @@ void setup() Serial.print(F(", PWD = ")); Serial.println(AP_PASS); +#if DISPLAY_STORED_CREDENTIALS_IN_CP + // New. Update Credentials, got from loadConfigData(), to display on CP + ESPAsync_wifiManager.setCredentials(WM_config.WiFi_Creds[0].wifi_ssid, WM_config.WiFi_Creds[0].wifi_pw, + WM_config.WiFi_Creds[1].wifi_ssid, WM_config.WiFi_Creds[1].wifi_pw); +#endif + // Starts an access point //if (!ESPAsync_wifiManager.startConfigPortal((const char *) ssid.c_str(), password)) if ( !ESPAsync_wifiManager.startConfigPortal(AP_SSID.c_str(), AP_PASS.c_str()) ) diff --git a/examples/Async_AutoConnectWithFSParametersAndCustomIP/Async_AutoConnectWithFSParametersAndCustomIP.ino b/examples/Async_AutoConnectWithFSParametersAndCustomIP/Async_AutoConnectWithFSParametersAndCustomIP.ino index 84ff7168..74a81412 100644 --- a/examples/Async_AutoConnectWithFSParametersAndCustomIP/Async_AutoConnectWithFSParametersAndCustomIP.ino +++ b/examples/Async_AutoConnectWithFSParametersAndCustomIP/Async_AutoConnectWithFSParametersAndCustomIP.ino @@ -19,12 +19,16 @@ #error This code is intended to run on the ESP8266 or ESP32 platform! Please check your Tools->Board setting. #endif -#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN_TARGET "ESPAsync_WiFiManager v1.14.0" -#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN 1014000 +#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN_TARGET "ESPAsync_WiFiManager v1.15.0" +#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN 1015000 // Use from 0 to 4. Higher number, more debugging messages and memory usage. #define _ESPASYNC_WIFIMGR_LOGLEVEL_ 3 +// To not display stored SSIDs and PWDs on Config Portal, select false. Default is true +// Even the stored Credentials are not display, just leave them all blank to reconnect and reuse the stored Credentials +//#define DISPLAY_STORED_CREDENTIALS_IN_CP false + #include //this needs to be first, or it all crashes and burns... //For ESP32, To use ESP32 Dev Module, QIO, Flash 4MB/80MHz, Upload 921600 @@ -933,6 +937,12 @@ void setup() Serial.print(F(", PWD = ")); Serial.println(AP_PASS); +#if DISPLAY_STORED_CREDENTIALS_IN_CP + // New. Update Credentials, got from loadConfigData(), to display on CP + ESPAsync_wifiManager.setCredentials(WM_config.WiFi_Creds[0].wifi_ssid, WM_config.WiFi_Creds[0].wifi_pw, + WM_config.WiFi_Creds[1].wifi_ssid, WM_config.WiFi_Creds[1].wifi_pw); +#endif + // Starts an access point //if (!ESPAsync_wifiManager.startConfigPortal((const char *) ssid.c_str(), password)) if ( !ESPAsync_wifiManager.startConfigPortal(AP_SSID.c_str(), AP_PASS.c_str()) ) diff --git a/examples/Async_AutoConnectWithFeedback/Async_AutoConnectWithFeedback.ino b/examples/Async_AutoConnectWithFeedback/Async_AutoConnectWithFeedback.ino index e89516a8..941da00a 100644 --- a/examples/Async_AutoConnectWithFeedback/Async_AutoConnectWithFeedback.ino +++ b/examples/Async_AutoConnectWithFeedback/Async_AutoConnectWithFeedback.ino @@ -18,12 +18,16 @@ #error This code is intended to run on the ESP8266 or ESP32 platform! Please check your Tools->Board setting. #endif -#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN_TARGET "ESPAsync_WiFiManager v1.14.0" -#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN 1014000 +#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN_TARGET "ESPAsync_WiFiManager v1.15.0" +#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN 1015000 // Use from 0 to 4. Higher number, more debugging messages and memory usage. #define _ESPASYNC_WIFIMGR_LOGLEVEL_ 3 +// To not display stored SSIDs and PWDs on Config Portal, select false. Default is true +// Even the stored Credentials are not display, just leave them all blank to reconnect and reuse the stored Credentials +//#define DISPLAY_STORED_CREDENTIALS_IN_CP false + //Ported to ESP32 #ifdef ESP32 #include @@ -796,6 +800,12 @@ void setup() Serial.print(F(", PWD = ")); Serial.println(AP_PASS); +#if DISPLAY_STORED_CREDENTIALS_IN_CP + // New. Update Credentials, got from loadConfigData(), to display on CP + ESPAsync_wifiManager.setCredentials(WM_config.WiFi_Creds[0].wifi_ssid, WM_config.WiFi_Creds[0].wifi_pw, + WM_config.WiFi_Creds[1].wifi_ssid, WM_config.WiFi_Creds[1].wifi_pw); +#endif + // Starts an access point //if (!ESPAsync_wifiManager.startConfigPortal((const char *) ssid.c_str(), password)) if ( !ESPAsync_wifiManager.startConfigPortal(AP_SSID.c_str(), AP_PASS.c_str()) ) diff --git a/examples/Async_AutoConnectWithFeedbackLED/Async_AutoConnectWithFeedbackLED.ino b/examples/Async_AutoConnectWithFeedbackLED/Async_AutoConnectWithFeedbackLED.ino index 5500a656..f430ca95 100644 --- a/examples/Async_AutoConnectWithFeedbackLED/Async_AutoConnectWithFeedbackLED.ino +++ b/examples/Async_AutoConnectWithFeedbackLED/Async_AutoConnectWithFeedbackLED.ino @@ -19,12 +19,16 @@ #error This code is intended to run on the ESP8266 or ESP32 platform! Please check your Tools->Board setting. #endif -#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN_TARGET "ESPAsync_WiFiManager v1.14.0" -#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN 1014000 +#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN_TARGET "ESPAsync_WiFiManager v1.15.0" +#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN 1015000 // Use from 0 to 4. Higher number, more debugging messages and memory usage. #define _ESPASYNC_WIFIMGR_LOGLEVEL_ 3 +// To not display stored SSIDs and PWDs on Config Portal, select false. Default is true +// Even the stored Credentials are not display, just leave them all blank to reconnect and reuse the stored Credentials +//#define DISPLAY_STORED_CREDENTIALS_IN_CP false + //Ported to ESP32 #ifdef ESP32 #include @@ -825,6 +829,12 @@ void setup() Serial.print(AP_SSID); Serial.print(F(", PWD = ")); Serial.println(AP_PASS); + +#if DISPLAY_STORED_CREDENTIALS_IN_CP + // New. Update Credentials, got from loadConfigData(), to display on CP + ESPAsync_wifiManager.setCredentials(WM_config.WiFi_Creds[0].wifi_ssid, WM_config.WiFi_Creds[0].wifi_pw, + WM_config.WiFi_Creds[1].wifi_ssid, WM_config.WiFi_Creds[1].wifi_pw); +#endif // Starts an access point //if (!ESPAsync_wifiManager.startConfigPortal((const char *) ssid.c_str(), password)) diff --git a/examples/Async_ConfigOnDRD_FS_MQTT_Ptr/Async_ConfigOnDRD_FS_MQTT_Ptr.ino b/examples/Async_ConfigOnDRD_FS_MQTT_Ptr/Async_ConfigOnDRD_FS_MQTT_Ptr.ino index 12801896..a3931f56 100644 --- a/examples/Async_ConfigOnDRD_FS_MQTT_Ptr/Async_ConfigOnDRD_FS_MQTT_Ptr.ino +++ b/examples/Async_ConfigOnDRD_FS_MQTT_Ptr/Async_ConfigOnDRD_FS_MQTT_Ptr.ino @@ -31,12 +31,16 @@ #error This code is intended to run on the ESP8266 or ESP32 platform! Please check your Tools->Board setting. #endif -#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN_TARGET "ESPAsync_WiFiManager v1.14.0" -#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN 1014000 +#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN_TARGET "ESPAsync_WiFiManager v1.15.0" +#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN 1015000 // Use from 0 to 4. Higher number, more debugging messages and memory usage. #define _ESPASYNC_WIFIMGR_LOGLEVEL_ 3 +// To not display stored SSIDs and PWDs on Config Portal, select false. Default is true +// Even the stored Credentials are not display, just leave them all blank to reconnect and reuse the stored Credentials +//#define DISPLAY_STORED_CREDENTIALS_IN_CP false + #include // Now support ArduinoJson 6.0.0+ ( tested with v6.15.2 to v6.16.1 ) @@ -966,6 +970,12 @@ void wifi_manager() Serial.print(F(", PWD = ")); Serial.println(password); +#if DISPLAY_STORED_CREDENTIALS_IN_CP + // New. Update Credentials, got from loadConfigData(), to display on CP + ESPAsync_wifiManager.setCredentials(WM_config.WiFi_Creds[0].wifi_ssid, WM_config.WiFi_Creds[0].wifi_pw, + WM_config.WiFi_Creds[1].wifi_ssid, WM_config.WiFi_Creds[1].wifi_pw); +#endif + if (!ESPAsync_wifiManager.startConfigPortal((const char *) ssid.c_str(), password.c_str())) { Serial.println(F("Not connected to WiFi but continuing anyway.")); @@ -1336,6 +1346,8 @@ void setup() if (initialConfig) { + loadConfigData(); + wifi_manager(); } else @@ -1347,21 +1359,21 @@ void setup() if (loadConfigData()) { #if USE_ESP_WIFIMANAGER_NTP - if ( strlen(WM_config.TZ_Name) > 0 ) - { - LOGERROR3(F("Current TZ_Name ="), WM_config.TZ_Name, F(", TZ = "), WM_config.TZ); + if ( strlen(WM_config.TZ_Name) > 0 ) + { + LOGERROR3(F("Current TZ_Name ="), WM_config.TZ_Name, F(", TZ = "), WM_config.TZ); #if ESP8266 - configTime(WM_config.TZ, "pool.ntp.org"); + configTime(WM_config.TZ, "pool.ntp.org"); #else - //configTzTime(WM_config.TZ, "pool.ntp.org" ); - configTzTime(WM_config.TZ, "time.nist.gov", "0.pool.ntp.org", "1.pool.ntp.org"); + //configTzTime(WM_config.TZ, "pool.ntp.org" ); + configTzTime(WM_config.TZ, "time.nist.gov", "0.pool.ntp.org", "1.pool.ntp.org"); #endif - } - else - { - Serial.println(F("Current Timezone is not set. Enter Config Portal to set.")); - } + } + else + { + Serial.println(F("Current Timezone is not set. Enter Config Portal to set.")); + } #endif for (uint8_t i = 0; i < NUM_WIFI_CREDENTIALS; i++) diff --git a/examples/Async_ConfigOnDRD_FS_MQTT_Ptr_Complex/Async_ConfigOnDRD_FS_MQTT_Ptr_Complex.ino b/examples/Async_ConfigOnDRD_FS_MQTT_Ptr_Complex/Async_ConfigOnDRD_FS_MQTT_Ptr_Complex.ino index 13bb1399..387d16e4 100644 --- a/examples/Async_ConfigOnDRD_FS_MQTT_Ptr_Complex/Async_ConfigOnDRD_FS_MQTT_Ptr_Complex.ino +++ b/examples/Async_ConfigOnDRD_FS_MQTT_Ptr_Complex/Async_ConfigOnDRD_FS_MQTT_Ptr_Complex.ino @@ -31,11 +31,15 @@ #error This code is intended to run on the ESP8266 or ESP32 platform! Please check your Tools->Board setting. #endif -#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN_TARGET "ESPAsync_WiFiManager v1.14.0" -#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN 1014000 +#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN_TARGET "ESPAsync_WiFiManager v1.15.0" +#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN 1015000 // Use from 0 to 4. Higher number, more debugging messages and memory usage. -#define _ESPASYNC_WIFIMGR_LOGLEVEL_ 2 +#define _ESPASYNC_WIFIMGR_LOGLEVEL_ 3 + +// To not display stored SSIDs and PWDs on Config Portal, select false. Default is true +// Even the stored Credentials are not display, just leave them all blank to reconnect and reuse the stored Credentials +//#define DISPLAY_STORED_CREDENTIALS_IN_CP false // Optional ms delay in ConfigPortal loop, if necessary. Using 0 if not necessary #define TIME_BETWEEN_CONFIG_PORTAL_LOOP 0L @@ -1022,6 +1026,12 @@ void wifi_manager() Serial.print(F(", PWD = ")); Serial.println(password); +#if DISPLAY_STORED_CREDENTIALS_IN_CP + // New. Update Credentials, got from loadConfigData(), to display on CP + ESPAsync_wifiManager.setCredentials(WM_config.WiFi_Creds[0].wifi_ssid, WM_config.WiFi_Creds[0].wifi_pw, + WM_config.WiFi_Creds[1].wifi_ssid, WM_config.WiFi_Creds[1].wifi_pw); +#endif + if (!ESPAsync_wifiManager.startConfigPortal((const char *) ssid.c_str(), password.c_str())) { Serial.println(F("Not connected to WiFi but continuing anyway.")); @@ -1382,8 +1392,11 @@ void setup() if (initialConfig) { + loadConfigData(); + wifi_manager(); } + else { // Pretend CP is necessary as we have no AP Credentials initialConfig = true; @@ -1392,21 +1405,21 @@ void setup() if (loadConfigData()) { #if USE_ESP_WIFIMANAGER_NTP - if ( strlen(WM_config.TZ_Name) > 0 ) - { - LOGERROR3(F("Current TZ_Name ="), WM_config.TZ_Name, F(", TZ = "), WM_config.TZ); + if ( strlen(WM_config.TZ_Name) > 0 ) + { + LOGERROR3(F("Current TZ_Name ="), WM_config.TZ_Name, F(", TZ = "), WM_config.TZ); #if ESP8266 - configTime(WM_config.TZ, "pool.ntp.org"); + configTime(WM_config.TZ, "pool.ntp.org"); #else - //configTzTime(WM_config.TZ, "pool.ntp.org" ); - configTzTime(WM_config.TZ, "time.nist.gov", "0.pool.ntp.org", "1.pool.ntp.org"); + //configTzTime(WM_config.TZ, "pool.ntp.org" ); + configTzTime(WM_config.TZ, "time.nist.gov", "0.pool.ntp.org", "1.pool.ntp.org"); #endif - } - else - { - Serial.println(F("Current Timezone is not set. Enter Config Portal to set.")); - } + } + else + { + Serial.println(F("Current Timezone is not set. Enter Config Portal to set.")); + } #endif for (uint8_t i = 0; i < NUM_WIFI_CREDENTIALS; i++) diff --git a/examples/Async_ConfigOnDRD_FS_MQTT_Ptr_Medium/Async_ConfigOnDRD_FS_MQTT_Ptr_Medium.ino b/examples/Async_ConfigOnDRD_FS_MQTT_Ptr_Medium/Async_ConfigOnDRD_FS_MQTT_Ptr_Medium.ino index 62c63b87..9767b4cf 100644 --- a/examples/Async_ConfigOnDRD_FS_MQTT_Ptr_Medium/Async_ConfigOnDRD_FS_MQTT_Ptr_Medium.ino +++ b/examples/Async_ConfigOnDRD_FS_MQTT_Ptr_Medium/Async_ConfigOnDRD_FS_MQTT_Ptr_Medium.ino @@ -31,11 +31,16 @@ #error This code is intended to run on the ESP8266 or ESP32 platform! Please check your Tools->Board setting. #endif -#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN_TARGET "ESPAsync_WiFiManager v1.14.0" -#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN 1014000 +#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN_TARGET "ESPAsync_WiFiManager v1.15.0" +#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN 1015000 + // Use from 0 to 4. Higher number, more debugging messages and memory usage. #define _ESPASYNC_WIFIMGR_LOGLEVEL_ 3 +// To not display stored SSIDs and PWDs on Config Portal, select false. Default is true +// Even the stored Credentials are not display, just leave them all blank to reconnect and reuse the stored Credentials +//#define DISPLAY_STORED_CREDENTIALS_IN_CP false + #include // Now support ArduinoJson 6.0.0+ ( tested with v6.15.2 to v6.16.1 ) @@ -1005,6 +1010,12 @@ void wifi_manager() Serial.print(ssid); Serial.print(F(", PWD = ")); Serial.println(password); + +#if DISPLAY_STORED_CREDENTIALS_IN_CP + // New. Update Credentials, got from loadConfigData(), to display on CP + ESPAsync_wifiManager.setCredentials(WM_config.WiFi_Creds[0].wifi_ssid, WM_config.WiFi_Creds[0].wifi_pw, + WM_config.WiFi_Creds[1].wifi_ssid, WM_config.WiFi_Creds[1].wifi_pw); +#endif if (!ESPAsync_wifiManager.startConfigPortal((const char *) ssid.c_str(), password.c_str())) { @@ -1365,6 +1376,8 @@ void setup() if (initialConfig) { + loadConfigData(); + wifi_manager(); } else @@ -1376,21 +1389,21 @@ void setup() if (loadConfigData()) { #if USE_ESP_WIFIMANAGER_NTP - if ( strlen(WM_config.TZ_Name) > 0 ) - { - LOGERROR3(F("Current TZ_Name ="), WM_config.TZ_Name, F(", TZ = "), WM_config.TZ); + if ( strlen(WM_config.TZ_Name) > 0 ) + { + LOGERROR3(F("Current TZ_Name ="), WM_config.TZ_Name, F(", TZ = "), WM_config.TZ); #if ESP8266 - configTime(WM_config.TZ, "pool.ntp.org"); + configTime(WM_config.TZ, "pool.ntp.org"); #else - //configTzTime(WM_config.TZ, "pool.ntp.org" ); - configTzTime(WM_config.TZ, "time.nist.gov", "0.pool.ntp.org", "1.pool.ntp.org"); + //configTzTime(WM_config.TZ, "pool.ntp.org" ); + configTzTime(WM_config.TZ, "time.nist.gov", "0.pool.ntp.org", "1.pool.ntp.org"); #endif - } - else - { - Serial.println(F("Current Timezone is not set. Enter Config Portal to set.")); - } + } + else + { + Serial.println(F("Current Timezone is not set. Enter Config Portal to set.")); + } #endif for (uint8_t i = 0; i < NUM_WIFI_CREDENTIALS; i++) diff --git a/examples/Async_ConfigOnDoubleReset/Async_ConfigOnDoubleReset.ino b/examples/Async_ConfigOnDoubleReset/Async_ConfigOnDoubleReset.ino index 15321071..0acb25d1 100644 --- a/examples/Async_ConfigOnDoubleReset/Async_ConfigOnDoubleReset.ino +++ b/examples/Async_ConfigOnDoubleReset/Async_ConfigOnDoubleReset.ino @@ -42,11 +42,15 @@ #error This code is intended to run on the ESP8266 or ESP32 platform! Please check your Tools->Board setting. #endif -#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN_TARGET "ESPAsync_WiFiManager v1.14.0" -#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN 1014000 +#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN_TARGET "ESPAsync_WiFiManager v1.15.0" +#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN 1015000 // Use from 0 to 4. Higher number, more debugging messages and memory usage. -#define _ESPASYNC_WIFIMGR_LOGLEVEL_ 1 +#define _ESPASYNC_WIFIMGR_LOGLEVEL_ 3 + +// To not display stored SSIDs and PWDs on Config Portal, select false. Default is true +// Even the stored Credentials are not display, just leave them all blank to reconnect and reuse the stored Credentials +//#define DISPLAY_STORED_CREDENTIALS_IN_CP false #include @@ -891,6 +895,12 @@ void setup() //switched off via webserver or device is restarted. //ESPAsync_wifiManager.setConfigPortalTimeout(600); +#if DISPLAY_STORED_CREDENTIALS_IN_CP + // New. Update Credentials, got from loadConfigData(), to display on CP + ESPAsync_wifiManager.setCredentials(WM_config.WiFi_Creds[0].wifi_ssid, WM_config.WiFi_Creds[0].wifi_pw, + WM_config.WiFi_Creds[1].wifi_ssid, WM_config.WiFi_Creds[1].wifi_pw); +#endif + // Starts an access point if (!ESPAsync_wifiManager.startConfigPortal((const char *) ssid.c_str(), password.c_str())) Serial.println(F("Not connected to WiFi but continuing anyway.")); diff --git a/examples/Async_ConfigOnDoubleReset_Multi/Async_ConfigOnDoubleReset_Multi.ino b/examples/Async_ConfigOnDoubleReset_Multi/Async_ConfigOnDoubleReset_Multi.ino index 6bb1dc7b..d76cee1c 100644 --- a/examples/Async_ConfigOnDoubleReset_Multi/Async_ConfigOnDoubleReset_Multi.ino +++ b/examples/Async_ConfigOnDoubleReset_Multi/Async_ConfigOnDoubleReset_Multi.ino @@ -247,6 +247,12 @@ void setup() //switched off via webserver or device is restarted. //ESPAsync_wifiManager.setConfigPortalTimeout(600); +#if DISPLAY_STORED_CREDENTIALS_IN_CP + // New. Update Credentials, got from loadConfigData(), to display on CP + ESPAsync_wifiManager.setCredentials(WM_config.WiFi_Creds[0].wifi_ssid, WM_config.WiFi_Creds[0].wifi_pw, + WM_config.WiFi_Creds[1].wifi_ssid, WM_config.WiFi_Creds[1].wifi_pw); +#endif + // Starts an access point if (!ESPAsync_wifiManager.startConfigPortal((const char *) ssid.c_str(), password.c_str())) Serial.println(F("Not connected to WiFi but continuing anyway.")); diff --git a/examples/Async_ConfigOnDoubleReset_TZ/Async_ConfigOnDoubleReset_TZ.ino b/examples/Async_ConfigOnDoubleReset_TZ/Async_ConfigOnDoubleReset_TZ.ino index fac0b209..238913d3 100644 --- a/examples/Async_ConfigOnDoubleReset_TZ/Async_ConfigOnDoubleReset_TZ.ino +++ b/examples/Async_ConfigOnDoubleReset_TZ/Async_ConfigOnDoubleReset_TZ.ino @@ -42,11 +42,15 @@ #error This code is intended to run on the ESP8266 or ESP32 platform! Please check your Tools->Board setting. #endif -#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN_TARGET "ESPAsync_WiFiManager v1.14.0" -#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN 1014000 +#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN_TARGET "ESPAsync_WiFiManager v1.15.0" +#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN 1015000 // Use from 0 to 4. Higher number, more debugging messages and memory usage. -#define _ESPASYNC_WIFIMGR_LOGLEVEL_ 1 +#define _ESPASYNC_WIFIMGR_LOGLEVEL_ 3 + +// To not display stored SSIDs and PWDs on Config Portal, select false. Default is true +// Even the stored Credentials are not display, just leave them all blank to reconnect and reuse the stored Credentials +//#define DISPLAY_STORED_CREDENTIALS_IN_CP false #include @@ -890,6 +894,12 @@ void setup() //switched off via webserver or device is restarted. //ESPAsync_wifiManager.setConfigPortalTimeout(600); +#if DISPLAY_STORED_CREDENTIALS_IN_CP + // New. Update Credentials, got from loadConfigData(), to display on CP + ESPAsync_wifiManager.setCredentials(WM_config.WiFi_Creds[0].wifi_ssid, WM_config.WiFi_Creds[0].wifi_pw, + WM_config.WiFi_Creds[1].wifi_ssid, WM_config.WiFi_Creds[1].wifi_pw); +#endif + // Starts an access point if (!ESPAsync_wifiManager.startConfigPortal((const char *) ssid.c_str(), password.c_str())) Serial.println(F("Not connected to WiFi but continuing anyway.")); diff --git a/examples/Async_ConfigOnStartup/Async_ConfigOnStartup.ino b/examples/Async_ConfigOnStartup/Async_ConfigOnStartup.ino index a643efa4..f4bc8641 100644 --- a/examples/Async_ConfigOnStartup/Async_ConfigOnStartup.ino +++ b/examples/Async_ConfigOnStartup/Async_ConfigOnStartup.ino @@ -33,12 +33,16 @@ #error This code is intended to run on the ESP8266 or ESP32 platform! Please check your Tools->Board setting. #endif -#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN_TARGET "ESPAsync_WiFiManager v1.14.0" -#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN 1014000 +#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN_TARGET "ESPAsync_WiFiManager v1.15.0" +#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN 1015000 // Use from 0 to 4. Higher number, more debugging messages and memory usage. #define _ESPASYNC_WIFIMGR_LOGLEVEL_ 3 +// To not display stored SSIDs and PWDs on Config Portal, select false. Default is true +// Even the stored Credentials are not display, just leave them all blank to reconnect and reuse the stored Credentials +//#define DISPLAY_STORED_CREDENTIALS_IN_CP false + //For ESP32, To use ESP32 Dev Module, QIO, Flash 4MB/80MHz, Upload 921600 //Ported to ESP32 @@ -817,6 +821,12 @@ void setup() digitalWrite(LED_BUILTIN, LED_ON); // turn the LED on by making the voltage LOW to tell us we are in configuration mode. +#if DISPLAY_STORED_CREDENTIALS_IN_CP + // New. Update Credentials, got from loadConfigData(), to display on CP + ESPAsync_wifiManager.setCredentials(WM_config.WiFi_Creds[0].wifi_ssid, WM_config.WiFi_Creds[0].wifi_pw, + WM_config.WiFi_Creds[1].wifi_ssid, WM_config.WiFi_Creds[1].wifi_pw); +#endif + // Starts an access point if (!ESPAsync_wifiManager.startConfigPortal((const char *) ssid.c_str(), password)) Serial.println(F("Not connected to WiFi but continuing anyway.")); diff --git a/examples/Async_ConfigOnSwitch/Async_ConfigOnSwitch.ino b/examples/Async_ConfigOnSwitch/Async_ConfigOnSwitch.ino index 28236b1f..f123e5a5 100644 --- a/examples/Async_ConfigOnSwitch/Async_ConfigOnSwitch.ino +++ b/examples/Async_ConfigOnSwitch/Async_ConfigOnSwitch.ino @@ -30,12 +30,16 @@ #error This code is intended to run on the ESP8266 or ESP32 platform! Please check your Tools->Board setting. #endif -#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN_TARGET "ESPAsync_WiFiManager v1.14.0" -#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN 1014000 +#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN_TARGET "ESPAsync_WiFiManager v1.15.0" +#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN 1015000 // Use from 0 to 4. Higher number, more debugging messages and memory usage. #define _ESPASYNC_WIFIMGR_LOGLEVEL_ 3 +// To not display stored SSIDs and PWDs on Config Portal, select false. Default is true +// Even the stored Credentials are not display, just leave them all blank to reconnect and reuse the stored Credentials +//#define DISPLAY_STORED_CREDENTIALS_IN_CP false + //For ESP32, To use ESP32 Dev Module, QIO, Flash 4MB/80MHz, Upload 921600 //Ported to ESP32 @@ -950,6 +954,12 @@ void setup() //switched off via webserver or device is restarted. //ESPAsync_wifiManager.setConfigPortalTimeout(600); +#if DISPLAY_STORED_CREDENTIALS_IN_CP + // New. Update Credentials, got from loadConfigData(), to display on CP + ESPAsync_wifiManager.setCredentials(WM_config.WiFi_Creds[0].wifi_ssid, WM_config.WiFi_Creds[0].wifi_pw, + WM_config.WiFi_Creds[1].wifi_ssid, WM_config.WiFi_Creds[1].wifi_pw); +#endif + // Starts an access point if (!ESPAsync_wifiManager.startConfigPortal((const char *) ssid.c_str(), password.c_str())) Serial.println(F("Not connected to WiFi but continuing anyway.")); @@ -1142,6 +1152,12 @@ void loop() initialConfig = true; } +#if DISPLAY_STORED_CREDENTIALS_IN_CP + // New. Update Credentials, got from loadConfigData(), to display on CP + ESPAsync_wifiManager.setCredentials(WM_config.WiFi_Creds[0].wifi_ssid, WM_config.WiFi_Creds[0].wifi_pw, + WM_config.WiFi_Creds[1].wifi_ssid, WM_config.WiFi_Creds[1].wifi_pw); +#endif + //Starts an access point //and goes into a blocking loop awaiting configuration if (!ESPAsync_wifiManager.startConfigPortal((const char *) ssid.c_str(), password.c_str())) diff --git a/examples/Async_ConfigOnSwitchFS/Async_ConfigOnSwitchFS.ino b/examples/Async_ConfigOnSwitchFS/Async_ConfigOnSwitchFS.ino index 9cf1ff9f..db5f39ed 100644 --- a/examples/Async_ConfigOnSwitchFS/Async_ConfigOnSwitchFS.ino +++ b/examples/Async_ConfigOnSwitchFS/Async_ConfigOnSwitchFS.ino @@ -53,12 +53,16 @@ #error This code is intended to run on the ESP8266 or ESP32 platform! Please check your Tools->Board setting. #endif -#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN_TARGET "ESPAsync_WiFiManager v1.14.0" -#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN 1014000 +#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN_TARGET "ESPAsync_WiFiManager v1.15.0" +#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN 1015000 // Use from 0 to 4. Higher number, more debugging messages and memory usage. #define _ESPASYNC_WIFIMGR_LOGLEVEL_ 3 +// To not display stored SSIDs and PWDs on Config Portal, select false. Default is true +// Even the stored Credentials are not display, just leave them all blank to reconnect and reuse the stored Credentials +//#define DISPLAY_STORED_CREDENTIALS_IN_CP false + #include // Now support ArduinoJson 6.0.0+ ( tested with v6.14.1 ) #include // get it from https://arduinojson.org/ or install via Arduino library manager @@ -1116,6 +1120,12 @@ void setup() //switched off via webserver or device is restarted. //ESPAsync_wifiManager.setConfigPortalTimeout(600); +#if DISPLAY_STORED_CREDENTIALS_IN_CP + // New. Update Credentials, got from loadConfigData(), to display on CP + ESPAsync_wifiManager.setCredentials(WM_config.WiFi_Creds[0].wifi_ssid, WM_config.WiFi_Creds[0].wifi_pw, + WM_config.WiFi_Creds[1].wifi_ssid, WM_config.WiFi_Creds[1].wifi_pw); +#endif + // Starts an access point if (!ESPAsync_wifiManager.startConfigPortal((const char *) ssid.c_str(), password.c_str())) Serial.println(F("Not connected to WiFi but continuing anyway.")); @@ -1352,6 +1362,12 @@ void loop() ESPAsync_wifiManager.setCORSHeader("Your Access-Control-Allow-Origin"); #endif +#if DISPLAY_STORED_CREDENTIALS_IN_CP + // New. Update Credentials, got from loadConfigData(), to display on CP + ESPAsync_wifiManager.setCredentials(WM_config.WiFi_Creds[0].wifi_ssid, WM_config.WiFi_Creds[0].wifi_pw, + WM_config.WiFi_Creds[1].wifi_ssid, WM_config.WiFi_Creds[1].wifi_pw); +#endif + // Start an access point // and goes into a blocking loop awaiting configuration. // Once the user leaves the portal with the exit button diff --git a/examples/Async_ConfigOnSwitchFS_MQTT_Ptr/Async_ConfigOnSwitchFS_MQTT_Ptr.ino b/examples/Async_ConfigOnSwitchFS_MQTT_Ptr/Async_ConfigOnSwitchFS_MQTT_Ptr.ino index 7457c6db..dba088e5 100644 --- a/examples/Async_ConfigOnSwitchFS_MQTT_Ptr/Async_ConfigOnSwitchFS_MQTT_Ptr.ino +++ b/examples/Async_ConfigOnSwitchFS_MQTT_Ptr/Async_ConfigOnSwitchFS_MQTT_Ptr.ino @@ -35,12 +35,16 @@ #error This code is intended to run on the ESP8266 or ESP32 platform! Please check your Tools->Board setting. #endif -#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN_TARGET "ESPAsync_WiFiManager v1.14.0" -#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN 1014000 +#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN_TARGET "ESPAsync_WiFiManager v1.15.0" +#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN 1015000 // Use from 0 to 4. Higher number, more debugging messages and memory usage. #define _ESPASYNC_WIFIMGR_LOGLEVEL_ 3 +// To not display stored SSIDs and PWDs on Config Portal, select false. Default is true +// Even the stored Credentials are not display, just leave them all blank to reconnect and reuse the stored Credentials +//#define DISPLAY_STORED_CREDENTIALS_IN_CP false + #include // for button #include // for button @@ -1043,6 +1047,12 @@ void wifi_manager() Serial.print(ssid); Serial.print(F(", PWD = ")); Serial.println(password); + +#if DISPLAY_STORED_CREDENTIALS_IN_CP + // New. Update Credentials, got from loadConfigData(), to display on CP + ESPAsync_wifiManager.setCredentials(WM_config.WiFi_Creds[0].wifi_ssid, WM_config.WiFi_Creds[0].wifi_pw, + WM_config.WiFi_Creds[1].wifi_ssid, WM_config.WiFi_Creds[1].wifi_pw); +#endif if (!ESPAsync_wifiManager.startConfigPortal((const char *) ssid.c_str(), password.c_str())) { @@ -1436,7 +1446,7 @@ void setup() if (initialConfig) { Serial.println(F("Open Config Portal without Timeout: No stored WiFi Credentials")); - + wifi_manager(); } else if ( WiFi.status() != WL_CONNECTED ) diff --git a/examples/Async_ConfigPortalParamsOnSwitch/Async_ConfigPortalParamsOnSwitch.ino b/examples/Async_ConfigPortalParamsOnSwitch/Async_ConfigPortalParamsOnSwitch.ino index 994b6de3..cea7bc01 100644 --- a/examples/Async_ConfigPortalParamsOnSwitch/Async_ConfigPortalParamsOnSwitch.ino +++ b/examples/Async_ConfigPortalParamsOnSwitch/Async_ConfigPortalParamsOnSwitch.ino @@ -32,12 +32,16 @@ #error This code is intended to run on the ESP8266 or ESP32 platform! Please check your Tools->Board setting. #endif -#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN_TARGET "ESPAsync_WiFiManager v1.14.0" -#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN 1014000 +#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN_TARGET "ESPAsync_WiFiManager v1.15.0" +#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN 1015000 // Use from 0 to 4. Higher number, more debugging messages and memory usage. #define _ESPASYNC_WIFIMGR_LOGLEVEL_ 3 +// To not display stored SSIDs and PWDs on Config Portal, select false. Default is true +// Even the stored Credentials are not display, just leave them all blank to reconnect and reuse the stored Credentials +//#define DISPLAY_STORED_CREDENTIALS_IN_CP false + #include // Now support ArduinoJson 6.0.0+ ( tested with v6.14.1 ) #include // get it from https://arduinojson.org/ or install via Arduino library manager @@ -1059,6 +1063,12 @@ void setup() digitalWrite(LED_BUILTIN, LED_ON); // Turn led on as we are in configuration mode. +#if DISPLAY_STORED_CREDENTIALS_IN_CP + // New. Update Credentials, got from loadConfigData(), to display on CP + ESPAsync_wifiManager.setCredentials(WM_config.WiFi_Creds[0].wifi_ssid, WM_config.WiFi_Creds[0].wifi_pw, + WM_config.WiFi_Creds[1].wifi_ssid, WM_config.WiFi_Creds[1].wifi_pw); +#endif + //it starts an access point //and goes into a blocking loop awaiting configuration // If Invalid PortalSSID or PortalPassword => use default @@ -1288,6 +1298,12 @@ void loop() ESPAsync_wifiManager.setCORSHeader("Your Access-Control-Allow-Origin"); #endif +#if DISPLAY_STORED_CREDENTIALS_IN_CP + // New. Update Credentials, got from loadConfigData(), to display on CP + ESPAsync_wifiManager.setCredentials(WM_config.WiFi_Creds[0].wifi_ssid, WM_config.WiFi_Creds[0].wifi_pw, + WM_config.WiFi_Creds[1].wifi_ssid, WM_config.WiFi_Creds[1].wifi_pw); +#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 diff --git a/examples/Async_ESP32_FSWebServer/Async_ESP32_FSWebServer.ino b/examples/Async_ESP32_FSWebServer/Async_ESP32_FSWebServer.ino index 3db5b913..05569118 100644 --- a/examples/Async_ESP32_FSWebServer/Async_ESP32_FSWebServer.ino +++ b/examples/Async_ESP32_FSWebServer/Async_ESP32_FSWebServer.ino @@ -34,12 +34,16 @@ #error This code is intended to run only on the ESP32 platform! Please check your Tools->Board setting. #endif -#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN_TARGET "ESPAsync_WiFiManager v1.14.0" -#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN 1014000 +#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN_TARGET "ESPAsync_WiFiManager v1.15.0" +#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN 1015000 // Use from 0 to 4. Higher number, more debugging messages and memory usage. #define _ESPASYNC_WIFIMGR_LOGLEVEL_ 3 +// To not display stored SSIDs and PWDs on Config Portal, select false. Default is true +// Even the stored Credentials are not display, just leave them all blank to reconnect and reuse the stored Credentials +//#define DISPLAY_STORED_CREDENTIALS_IN_CP false + #include #include #include @@ -797,6 +801,12 @@ void setup() Serial.print(F(", PWD = ")); Serial.println(password); +#if DISPLAY_STORED_CREDENTIALS_IN_CP + // New. Update Credentials, got from loadConfigData(), to display on CP + ESPAsync_wifiManager.setCredentials(WM_config.WiFi_Creds[0].wifi_ssid, WM_config.WiFi_Creds[0].wifi_pw, + WM_config.WiFi_Creds[1].wifi_ssid, WM_config.WiFi_Creds[1].wifi_pw); +#endif + // Starts an access point if (!ESPAsync_wifiManager.startConfigPortal((const char *) ssid.c_str(), password.c_str())) Serial.println(F("Not connected to WiFi but continuing anyway.")); diff --git a/examples/Async_ESP32_FSWebServer_DRD/Async_ESP32_FSWebServer_DRD.ino b/examples/Async_ESP32_FSWebServer_DRD/Async_ESP32_FSWebServer_DRD.ino index 03f76ff2..8853c468 100644 --- a/examples/Async_ESP32_FSWebServer_DRD/Async_ESP32_FSWebServer_DRD.ino +++ b/examples/Async_ESP32_FSWebServer_DRD/Async_ESP32_FSWebServer_DRD.ino @@ -34,12 +34,16 @@ #error This code is intended to run only on the ESP32 platform! Please check your Tools->Board setting. #endif -#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN_TARGET "ESPAsync_WiFiManager v1.14.0" -#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN 1014000 +#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN_TARGET "ESPAsync_WiFiManager v1.15.0" +#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN 1015000 // Use from 0 to 4. Higher number, more debugging messages and memory usage. #define _ESPASYNC_WIFIMGR_LOGLEVEL_ 3 +// To not display stored SSIDs and PWDs on Config Portal, select false. Default is true +// Even the stored Credentials are not display, just leave them all blank to reconnect and reuse the stored Credentials +//#define DISPLAY_STORED_CREDENTIALS_IN_CP false + #include #include #include @@ -863,6 +867,12 @@ void setup() Serial.print(F(", PWD = ")); Serial.println(password); +#if DISPLAY_STORED_CREDENTIALS_IN_CP + // New. Update Credentials, got from loadConfigData(), to display on CP + ESPAsync_wifiManager.setCredentials(WM_config.WiFi_Creds[0].wifi_ssid, WM_config.WiFi_Creds[0].wifi_pw, + WM_config.WiFi_Creds[1].wifi_ssid, WM_config.WiFi_Creds[1].wifi_pw); +#endif + // Starts an access point if (!ESPAsync_wifiManager.startConfigPortal((const char *) ssid.c_str(), password.c_str())) Serial.println(F("Not connected to WiFi but continuing anyway.")); diff --git a/examples/Async_ESP_FSWebServer/Async_ESP_FSWebServer.ino b/examples/Async_ESP_FSWebServer/Async_ESP_FSWebServer.ino index 51af98d6..f097f978 100644 --- a/examples/Async_ESP_FSWebServer/Async_ESP_FSWebServer.ino +++ b/examples/Async_ESP_FSWebServer/Async_ESP_FSWebServer.ino @@ -34,12 +34,16 @@ #error This code is intended to run on the ESP8266 platform! Please check your Tools->Board setting. #endif -#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN_TARGET "ESPAsync_WiFiManager v1.12.2" -#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN 1012002 +#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN_TARGET "ESPAsync_WiFiManager v1.15.0" +#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN 1015000 // Use from 0 to 4. Higher number, more debugging messages and memory usage. #define _ESPASYNC_WIFIMGR_LOGLEVEL_ 3 +// To not display stored SSIDs and PWDs on Config Portal, select false. Default is true +// Even the stored Credentials are not display, just leave them all blank to reconnect and reuse the stored Credentials +//#define DISPLAY_STORED_CREDENTIALS_IN_CP false + #include #include #include @@ -758,6 +762,12 @@ void setup() Serial.print(F(", PWD = ")); Serial.println(password); +#if DISPLAY_STORED_CREDENTIALS_IN_CP + // New. Update Credentials, got from loadConfigData(), to display on CP + ESPAsync_wifiManager.setCredentials(WM_config.WiFi_Creds[0].wifi_ssid, WM_config.WiFi_Creds[0].wifi_pw, + WM_config.WiFi_Creds[1].wifi_ssid, WM_config.WiFi_Creds[1].wifi_pw); +#endif + // Starts an access point if (!ESPAsync_wifiManager.startConfigPortal((const char *) ssid.c_str(), password.c_str())) Serial.println(F("Not connected to WiFi but continuing anyway.")); diff --git a/examples/Async_ESP_FSWebServer_DRD/Async_ESP_FSWebServer_DRD.ino b/examples/Async_ESP_FSWebServer_DRD/Async_ESP_FSWebServer_DRD.ino index 2019e43a..87e2cd65 100644 --- a/examples/Async_ESP_FSWebServer_DRD/Async_ESP_FSWebServer_DRD.ino +++ b/examples/Async_ESP_FSWebServer_DRD/Async_ESP_FSWebServer_DRD.ino @@ -34,12 +34,16 @@ #error This code is intended to run on the ESP8266 platform! Please check your Tools->Board setting. #endif -#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN_TARGET "ESPAsync_WiFiManager v1.12.2" -#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN 1012002 +#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN_TARGET "ESPAsync_WiFiManager v1.15.0" +#define ESP_ASYNC_WIFIMANAGER_VERSION_MIN 1015000 // Use from 0 to 4. Higher number, more debugging messages and memory usage. #define _ESPASYNC_WIFIMGR_LOGLEVEL_ 3 +// To not display stored SSIDs and PWDs on Config Portal, select false. Default is true +// Even the stored Credentials are not display, just leave them all blank to reconnect and reuse the stored Credentials +//#define DISPLAY_STORED_CREDENTIALS_IN_CP false + #include #include #include @@ -810,6 +814,12 @@ void setup() Serial.print(F(", PWD = ")); Serial.println(password); +#if DISPLAY_STORED_CREDENTIALS_IN_CP + // New. Update Credentials, got from loadConfigData(), to display on CP + ESPAsync_wifiManager.setCredentials(WM_config.WiFi_Creds[0].wifi_ssid, WM_config.WiFi_Creds[0].wifi_pw, + WM_config.WiFi_Creds[1].wifi_ssid, WM_config.WiFi_Creds[1].wifi_pw); +#endif + // Starts an access point if (!ESPAsync_wifiManager.startConfigPortal((const char *) ssid.c_str(), password.c_str())) Serial.println(F("Not connected to WiFi but continuing anyway.")); diff --git a/keywords.txt b/keywords.txt index 81392fe9..69f07505 100644 --- a/keywords.txt +++ b/keywords.txt @@ -46,6 +46,7 @@ addParameter KEYWORD2 setBreakAfterConfig KEYWORD2 setCustomHeadElement KEYWORD2 setRemoveDuplicateAPs KEYWORD2 +setCredentials KEYWORD2 getSSID KEYWORD2 getPW KEYWORD2 getSSID1 KEYWORD2 diff --git a/library.json b/library.json index eb965fd5..fbe49d0f 100644 --- a/library.json +++ b/library.json @@ -1,8 +1,8 @@ { "name": "ESPAsync_WiFiManager", - "version": "1.14.1", + "version": "1.15.0", "keywords": "wifi, WiFiManager, esp8266, esp32, esp32-s2, esp32-s3, esp32-c3, AsyncWebServer, Async-WiFiManager, MultiWiFi, Async, Communication, Credentials, Config-Portal, DoubleReset, MultiReset, littlefs, spiffs, dns-server, iot, eeprom", - "description": "ESP32 (including ESP32-S2, ESP32-S3 and ESP32-C3), ESP8266 WiFi Connection Manager using AsyncWebServer, with enhanced GUI and fallback Web ConfigPortal. This Library is used for configuring ESP32 (including ESP32-S2, ESP32-S3 and ESP32-C3), ESP8266 modules WiFi Credentials at runtime. You can also specify static DNS servers, personalized HostName, fixed or random AP channel. Now with MultiWiFi auto(Re)connect, configurable CORS Header and auto-Timezone features. Auto detect ESP32 core and use either built-in LittleFS or external LITTLEFS library. Using AsyncDNSServer instead of DNSServer. Now using correct ESP32 chipIP", + "description": "ESP32 (including ESP32-S2, ESP32-S3 and ESP32-C3), ESP8266 WiFi Connection Manager using AsyncWebServer, with enhanced GUI and fallback Web ConfigPortal. This Library is used for configuring ESP32 (including ESP32-S2, ESP32-S3 and ESP32-C3), ESP8266 modules WiFi Credentials at runtime. You can also specify static DNS servers, personalized HostName, fixed or random AP channel. Now with MultiWiFi auto(Re)connect, configurable CORS Header and auto-Timezone features. Auto detect ESP32 core and use either built-in LittleFS or external LITTLEFS library. Using AsyncDNSServer instead of DNSServer. Now using correct ESP32 chipIP and optionally display Credentials on Config Portal", "authors": { "name": "Khoi Hoang", diff --git a/library.properties b/library.properties index 5f7a7f84..2327aead 100644 --- a/library.properties +++ b/library.properties @@ -1,10 +1,10 @@ name=ESPAsync_WiFiManager -version=1.14.1 +version=1.15.0 author=Khoi Hoang maintainer=Khoi Hoang license=MIT sentence=ESP32 (including ESP32-S2, ESP32-S3 and ESP32-C3), ESP8266 WiFi Connection Manager using AsyncWebServer, with enhanced GUI and fallback Web ConfigPortal. -paragraph=This Library is used for configuring ESP32 (including ESP32-S2, ESP32-S3 and ESP32-C3), ESP8266 modules WiFi Credentials at runtime. You can also specify static DNS servers, personalized HostName, fixed or random AP channel. Now with MultiWiFi auto(Re)connect, configurable CORS Header and auto-Timezone features. Auto detect ESP32 core and use either built-in LittleFS or external LITTLEFS library. Using AsyncDNSServer instead of DNSServer now. Now using correct ESP32 chipIP +paragraph=This Library is used for configuring ESP32 (including ESP32-S2, ESP32-S3 and ESP32-C3), ESP8266 modules WiFi Credentials at runtime. You can also specify static DNS servers, personalized HostName, fixed or random AP channel. Now with MultiWiFi auto(Re)connect, configurable CORS Header and auto-Timezone features. Auto detect ESP32 core and use either built-in LittleFS or external LITTLEFS library. Using AsyncDNSServer instead of DNSServer now. Now using correct ESP32 chipIP and optionally display Credentials on Config Portal category=Communication url=https://github.com/khoih-prog/ESPAsync_WiFiManager architectures=esp8266,esp32 diff --git a/src/ESPAsync_WiFiManager-Impl.h b/src/ESPAsync_WiFiManager-Impl.h index 6cd09e4c..8772619f 100644 --- a/src/ESPAsync_WiFiManager-Impl.h +++ b/src/ESPAsync_WiFiManager-Impl.h @@ -14,7 +14,7 @@ Built by Khoi Hoang https://github.com/khoih-prog/ESPAsync_WiFiManager Licensed under MIT license - Version: 1.14.1 + Version: 1.15.0 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -29,6 +29,7 @@ 1.13.0 K Hoang 18/08/2022 Using AsynsDNSServer instead of DNSServer 1.14.0 K Hoang 09/09/2022 Fix ESP32 chipID and add ESP_getChipOUI() 1.14.1 K Hoang 15/09/2022 Remove dependency on ESP_AsyncWebServer, ESPAsyncTCP and AsyncTCP in `library.properties` + 1.15.0 K Hoang 07/10/2022 Optional display Credentials (SSIDs, PWDs) in Config Portal *****************************************************************************************************************************/ #pragma once @@ -36,7 +37,7 @@ #ifndef ESPAsync_WiFiManager_Impl_h #define ESPAsync_WiFiManager_Impl_h -///////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////// ESPAsync_WMParameter::ESPAsync_WMParameter(const char *custom) { @@ -172,7 +173,9 @@ ESPAsync_WMParameter** ESPAsync_WiFiManager::getParameters() { return _params; } -///////////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////// +////////////////////////////////////////// /** [getParametersCount description] @@ -233,7 +236,6 @@ ESPAsync_WiFiManager::ESPAsync_WiFiManager(AsyncWebServer * webserver, AsyncDNSS #endif //WiFi not yet started here, must call WiFi.mode(WIFI_STA) and modify function WiFiGenericClass::mode(wifi_mode_t m) !!! - WiFi.mode(WIFI_STA); if (iHostname[0] == 0) @@ -401,9 +403,9 @@ void ESPAsync_WiFiManager::setupConfigPortal() LOGWARN1(F("AP PWD ="), _apPassword); } - // KH, To enable dynamic/random channel static int channel; + // Use random channel if _WiFiAPChannel == 0 if (_WiFiAPChannel == 0) channel = (_configPortalStart % MAX_WIFI_CHANNEL) + 1; @@ -422,7 +424,6 @@ void ESPAsync_WiFiManager::setupConfigPortal() // Can't use channel here WiFi.softAP(_apName); } - ////// delay(500); // Without delay I've seen the IP address blank @@ -460,6 +461,8 @@ bool ESPAsync_WiFiManager::autoConnect() return autoConnect(ssid.c_str(), NULL); } +////////////////////////////////////////// + /* This is not very useful as there has been an assumption that device has to be told to connect but Wifi already does it's best to connect in background. Calling this method will block until WiFi connects. Sketch can avoid @@ -467,6 +470,11 @@ bool ESPAsync_WiFiManager::autoConnect() See some discussion at https://github.com/tzapu/WiFiManager/issues/68 */ +// To permit autoConnect() to use STA static IP or DHCP IP. +#ifndef AUTOCONNECT_NO_INVALIDATE + #define AUTOCONNECT_NO_INVALIDATE true +#endif + ////////////////////////////////////////// bool ESPAsync_WiFiManager::autoConnect(char const *apName, char const *apPassword) @@ -502,9 +510,7 @@ bool ESPAsync_WiFiManager::autoConnect(char const *apName, char const *apPasswor return startConfigPortal(apName, apPassword); } - -/////////////////////////////////////////////////////////////////// -// NEW +////////////////////////////////////////// String ESPAsync_WiFiManager::networkListAsString() { @@ -580,7 +586,9 @@ void ESPAsync_WiFiManager::scan() if (wifiSSIDscan) { LOGDEBUG(F("Start scan")); + wifi_ssid_count_t n = WiFi.scanNetworks(false, true); + LOGDEBUG(F("Scan done")); if (n == WIFI_SCAN_FAILED) @@ -915,7 +923,10 @@ bool ESPAsync_WiFiManager::startConfigPortal(char const *apName, char const *apP yield(); #if ( defined(TIME_BETWEEN_CONFIG_PORTAL_LOOP) && (TIME_BETWEEN_CONFIG_PORTAL_LOOP > 0) ) -#warning Using delay in startConfigPortal loop + #if (_ESPASYNC_WIFIMGR_LOGLEVEL_ > 3) + #warning Using delay in startConfigPortal loop + #endif + delay(TIME_BETWEEN_CONFIG_PORTAL_LOOP); #endif } @@ -992,7 +1003,6 @@ void ESPAsync_WiFiManager::setWifiStaticIP() ////////////////////////////////////////// -// New from v1.1.1 int ESPAsync_WiFiManager::reconnectWifi() { int connectResult; @@ -1027,7 +1037,7 @@ int ESPAsync_WiFiManager::connectWifi(const String& ssid, const String& pass) // But update the Static/DHCP options if changed. if ( (ssid != "") || ( (ssid == "") && (WiFi_SSID() != "") ) ) { - //fix for auto connect racing issue. Move up from v1.1.0 to avoid resetSettings() + //fix for auto connect racing issue to avoid resetSettings() if (WiFi.status() == WL_CONNECTED) { LOGWARN(F("Already connected. Bailing out.")); @@ -1105,17 +1115,16 @@ wl_status_t ESPAsync_WiFiManager::waitForConnectResult() LOGWARN1(F("Connected after waiting (s) :"), waited / 1000); LOGWARN1(F("Local ip ="), WiFi.localIP()); - // Fix bug from v1.1.0+, connRes is sometimes not correct. - + // Fix bug, connRes is sometimes not correct. //return connRes; return WiFi.status(); } else { LOGERROR(F("Waiting WiFi connection with time out")); + unsigned long start = millis(); bool keepConnecting = true; - wl_status_t status; while (keepConnecting) @@ -1216,10 +1225,10 @@ void ESPAsync_WiFiManager::resetSettings() // Temporary fix for issue of not clearing WiFi SSID/PW from flash of ESP32 // See https://github.com/khoih-prog/ESPAsync_WiFiManager/issues/25 and https://github.com/espressif/arduino-esp32/issues/400 WiFi.begin("0", "0"); - ////// #endif delay(200); + return; } @@ -1278,7 +1287,6 @@ void ESPAsync_WiFiManager::setAPStaticIPConfig(const IPAddress& ip, const IPAddr ////////////////////////////////////////// -// KH, new using struct void ESPAsync_WiFiManager::setAPStaticIPConfig(const WiFi_AP_IPConfig& WM_AP_IPconfig) { LOGINFO(F("setAPStaticIPConfig")); @@ -1332,6 +1340,7 @@ void ESPAsync_WiFiManager::setSTAStaticIPConfig(const IPAddress& ip, const IPAdd const IPAddress& dns_address_1, const IPAddress& dns_address_2) { LOGINFO(F("setSTAStaticIPConfig for USE_CONFIGURABLE_DNS")); + _WiFi_STA_IPconfig._sta_static_ip = ip; _WiFi_STA_IPconfig._sta_static_gw = gw; _WiFi_STA_IPconfig._sta_static_sn = sn; @@ -1394,9 +1403,6 @@ void ESPAsync_WiFiManager::handleRoot(AsyncWebServerRequest *request) // Disable _configPortalTimeout when someone accessing Portal to give some time to config _configPortalTimeout = 0; - //wifiSSIDscan = true; - //scan(); - if (captivePortal(request)) { // If captive portal redirect instead of displaying the error page. @@ -1451,7 +1457,7 @@ void ESPAsync_WiFiManager::handleRoot(AsyncWebServerRequest *request) response->addHeader(FPSTR(WM_HTTP_CACHE_CONTROL), FPSTR(WM_HTTP_NO_STORE)); #if USING_CORS_FEATURE - // New from v1.1.0, for configure CORS Header, default to WM_HTTP_CORS_ALLOW_ALL = "*" + // For configuring CORS Header, default to WM_HTTP_CORS_ALLOW_ALL = "*" response->addHeader(FPSTR(WM_HTTP_CORS), _CORS_Header); #endif @@ -1463,7 +1469,6 @@ void ESPAsync_WiFiManager::handleRoot(AsyncWebServerRequest *request) #endif // ( USING_ESP32_S2 || USING_ESP32_C3 ) } - ////////////////////////////////////////// // Wifi config page handler @@ -1511,7 +1516,18 @@ void ESPAsync_WiFiManager::handleWifi(AsyncWebServerRequest *request) #endif // ( USING_ESP32_S2 || USING_ESP32_C3 ) + page += "*Hint: To reuse the saved WiFi credentials, leave SSID and PWD fields empty"; + page += FPSTR(WM_HTTP_FORM_START); + +#if DISPLAY_STORED_CREDENTIALS_IN_CP + // Populate SSIDs and PWDs if valid + page.replace("[[ssid]]", _ssid ); + page.replace("[[pwd]]", _pass ); + page.replace("[[ssid1]]", _ssid1 ); + page.replace("[[pwd1]]", _pass1 ); +#endif + char parLength[2]; page += FPSTR(WM_FLDSET_START); @@ -1664,7 +1680,6 @@ void ESPAsync_WiFiManager::handleWifi(AsyncWebServerRequest *request) response->addHeader(FPSTR(WM_HTTP_CACHE_CONTROL), FPSTR(WM_HTTP_NO_STORE)); #if USING_CORS_FEATURE - // New from v1.1.0, for configure CORS Header, default to WM_HTTP_CORS_ALLOW_ALL = "*" response->addHeader(FPSTR(WM_HTTP_CORS), _CORS_Header); #endif @@ -1689,7 +1704,6 @@ void ESPAsync_WiFiManager::handleWifiSave(AsyncWebServerRequest *request) _ssid = request->arg("s").c_str(); _pass = request->arg("p").c_str(); - // New from v1.1.0 _ssid1 = request->arg("s1").c_str(); _pass1 = request->arg("p1").c_str(); @@ -1707,6 +1721,7 @@ void ESPAsync_WiFiManager::handleWifiSave(AsyncWebServerRequest *request) LOGDEBUG(F("No TZ arg")); } #endif + /////////////////////// //parameters @@ -1786,8 +1801,6 @@ void ESPAsync_WiFiManager::handleWifiSave(AsyncWebServerRequest *request) page.replace("{v}", _apName); page.replace("{x}", _ssid); - - // KH, update from v1.1.0 page.replace("{x1}", _ssid1); ////// @@ -1806,7 +1819,6 @@ void ESPAsync_WiFiManager::handleWifiSave(AsyncWebServerRequest *request) response->addHeader(FPSTR(WM_HTTP_CACHE_CONTROL), FPSTR(WM_HTTP_NO_STORE)); #if USING_CORS_FEATURE - // New from v1.1.0, for configure CORS Header, default to WM_HTTP_CORS_ALLOW_ALL = "*" response->addHeader(FPSTR(WM_HTTP_CORS), _CORS_Header); #endif @@ -1863,7 +1875,6 @@ void ESPAsync_WiFiManager::handleServerClose(AsyncWebServerRequest *request) response->addHeader(FPSTR(WM_HTTP_CACHE_CONTROL), FPSTR(WM_HTTP_NO_STORE)); #if USING_CORS_FEATURE - // New from v1.1.0, for configure CORS Header, default to WM_HTTP_CORS_ALLOW_ALL = "*" response->addHeader(FPSTR(WM_HTTP_CORS), _CORS_Header); #endif @@ -1925,15 +1936,15 @@ void ESPAsync_WiFiManager::handleInfo(AsyncWebServerRequest *request) page += F("NameValueChip ID"); #ifdef ESP8266 - page += String(ESP.getChipId(), HEX); //ESP.getChipId(); + page += String(ESP.getChipId(), HEX); #else //ESP32 - page += String(ESP_getChipId(), HEX); //ESP.getChipId(); + page += String(ESP_getChipId(), HEX); page += F(""); page += F("Chip OUI"); page += F("0x"); - page += String(getChipOUI(), HEX); //ESP.getChipId(); + page += String(getChipOUI(), HEX); page += F(""); page += F("Chip Model"); @@ -1947,7 +1958,7 @@ void ESPAsync_WiFiManager::handleInfo(AsyncWebServerRequest *request) page += F("Flash Chip ID"); #ifdef ESP8266 - page += String(ESP.getFlashChipId(), HEX); //ESP.getFlashChipId(); + page += String(ESP.getFlashChipId(), HEX); #else //ESP32 // TODO page += F("TODO"); @@ -2015,7 +2026,6 @@ void ESPAsync_WiFiManager::handleInfo(AsyncWebServerRequest *request) response->addHeader(FPSTR(WM_HTTP_CACHE_CONTROL), FPSTR(WM_HTTP_NO_STORE)); #if USING_CORS_FEATURE - // New from v1.1.0, for configure CORS Header, default to WM_HTTP_CORS_ALLOW_ALL = "*" response->addHeader(FPSTR(WM_HTTP_CORS), _CORS_Header); #endif @@ -2071,7 +2081,6 @@ void ESPAsync_WiFiManager::handleState(AsyncWebServerRequest *request) response->addHeader(FPSTR(WM_HTTP_CACHE_CONTROL), FPSTR(WM_HTTP_NO_STORE)); #if USING_CORS_FEATURE - // New from v1.1.0, for configure CORS Header, default to WM_HTTP_CORS_ALLOW_ALL = "*" response->addHeader(FPSTR(WM_HTTP_CORS), _CORS_Header); #endif diff --git a/src/ESPAsync_WiFiManager.h b/src/ESPAsync_WiFiManager.h index 76e291e5..128f20fd 100644 --- a/src/ESPAsync_WiFiManager.h +++ b/src/ESPAsync_WiFiManager.h @@ -14,7 +14,7 @@ Built by Khoi Hoang https://github.com/khoih-prog/ESPAsync_WiFiManager Licensed under MIT license - Version: 1.14.1 + Version: 1.15.0 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -29,6 +29,7 @@ 1.13.0 K Hoang 18/08/2022 Using AsynsDNSServer instead of DNSServer 1.14.0 K Hoang 09/09/2022 Fix ESP32 chipID and add ESP_getChipOUI() 1.14.1 K Hoang 15/09/2022 Remove dependency on ESP_AsyncWebServer, ESPAsyncTCP and AsyncTCP in `library.properties` + 1.15.0 K Hoang 07/10/2022 Optional display Credentials (SSIDs, PWDs) in Config Portal *****************************************************************************************************************************/ #pragma once diff --git a/src/ESPAsync_WiFiManager.hpp b/src/ESPAsync_WiFiManager.hpp index 09d0c1ff..d232d10b 100644 --- a/src/ESPAsync_WiFiManager.hpp +++ b/src/ESPAsync_WiFiManager.hpp @@ -14,7 +14,7 @@ Built by Khoi Hoang https://github.com/khoih-prog/ESPAsync_WiFiManager Licensed under MIT license - Version: 1.14.1 + Version: 1.15.0 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -29,6 +29,7 @@ 1.13.0 K Hoang 18/08/2022 Using AsynsDNSServer instead of DNSServer 1.14.0 K Hoang 09/09/2022 Fix ESP32 chipID and add ESP_getChipOUI() 1.14.1 K Hoang 15/09/2022 Remove dependency on ESP_AsyncWebServer, ESPAsyncTCP and AsyncTCP in `library.properties` + 1.15.0 K Hoang 07/10/2022 Optional display Credentials (SSIDs, PWDs) in Config Portal *****************************************************************************************************************************/ #pragma once @@ -36,12 +37,14 @@ #ifndef ESPAsync_WiFiManager_hpp #define ESPAsync_WiFiManager_hpp +//////////////////////////////////////////////////// + #if !( defined(ESP8266) || defined(ESP32) ) #error This code is intended to run on the ESP8266 or ESP32 platform! Please check your Tools->Board setting. #elif ( ARDUINO_ESP32S2_DEV || ARDUINO_FEATHERS2 || ARDUINO_ESP32S2_THING_PLUS || ARDUINO_MICROS2 || \ ARDUINO_METRO_ESP32S2 || ARDUINO_MAGTAG29_ESP32S2 || ARDUINO_FUNHOUSE_ESP32S2 || \ ARDUINO_ADAFRUIT_FEATHER_ESP32S2_NOPSRAM ) - #if (_ESPASYNC_WIFIMGR_LOGLEVEL_ > 3) + #if (_ESPASYNC_WIFIMGR_LOGLEVEL_ > 3) #warning Using ESP32_S2. To follow library instructions to install esp32-s2 core and WebServer Patch #warning You have to select HUGE APP or 1.9-2.0 MB APP to be able to run Config Portal. Must use PSRAM #endif @@ -49,7 +52,7 @@ #define USING_ESP32_S2 true #elif ( ARDUINO_ESP32C3_DEV ) - #if (_ESPASYNC_WIFIMGR_LOGLEVEL_ > 3) + #if (_ESPASYNC_WIFIMGR_LOGLEVEL_ > 3) #if ( defined(ESP_ARDUINO_VERSION_MAJOR) && (ESP_ARDUINO_VERSION_MAJOR >= 2) ) #warning Using ESP32_C3 using core v2.0.0+. Either LittleFS, SPIFFS or EEPROM OK #else @@ -72,13 +75,17 @@ #define USING_ESP32_S3 true #endif -#define ESP_ASYNC_WIFIMANAGER_VERSION "ESPAsync_WiFiManager v1.14.1" +//////////////////////////////////////////////////// + +#define ESP_ASYNC_WIFIMANAGER_VERSION "ESPAsync_WiFiManager v1.15.0" #define ESP_ASYNC_WIFIMANAGER_VERSION_MAJOR 1 -#define ESP_ASYNC_WIFIMANAGER_VERSION_MINOR 14 -#define ESP_ASYNC_WIFIMANAGER_VERSION_PATCH 1 +#define ESP_ASYNC_WIFIMANAGER_VERSION_MINOR 15 +#define ESP_ASYNC_WIFIMANAGER_VERSION_PATCH 0 + +#define ESP_ASYNC_WIFIMANAGER_VERSION_INT 1015000 -#define ESP_ASYNC_WIFIMANAGER_VERSION_INT 1014001 +//////////////////////////////////////////////////// #if ESP8266 #if (ARDUINO_ESP8266_GIT_VER == 0xcf6ff4c4) @@ -196,8 +203,12 @@ #endif #endif +//////////////////////////////////////////////////// + #include "ESPAsync_WiFiManager_Debug.h" +//////////////////////////////////////////////////// + //KH, for ESP32 #ifdef ESP8266 #include @@ -215,6 +226,8 @@ #include +//////////////////////////////////////////////////// + // fix crash on ESP32 (see https://github.com/alanswx/ESPAsyncWiFiManager/issues/44) #if defined(ESP8266) typedef int wifi_ssid_count_t; @@ -222,6 +235,8 @@ typedef int16_t wifi_ssid_count_t; #endif +//////////////////////////////////////////////////// + //KH, for ESP32 #ifdef ESP8266 extern "C" @@ -245,6 +260,8 @@ #define ESP_getChipOUI() getChipOUI() #endif +//////////////////////////////////////////////////// + typedef struct { IPAddress _ap_static_ip; @@ -253,6 +270,8 @@ typedef struct } WiFi_AP_IPConfig; +//////////////////////////////////////////////////// + // Thanks to @Amorphous for the feature and code // (https://community.blynk.cc/t/esp-wifimanager-for-esp32-and-esp8266/42257/13) // To enable to configure from sketch @@ -260,6 +279,8 @@ typedef struct #define USE_CONFIGURABLE_DNS false #endif +//////////////////////////////////////////////////// + typedef struct { IPAddress _sta_static_ip; @@ -269,9 +290,13 @@ typedef struct IPAddress _sta_static_dns2; } WiFi_STA_IPConfig; -#define WFM_LABEL_BEFORE 1 -#define WFM_LABEL_AFTER 2 -#define WFM_NO_LABEL 0 +//////////////////////////////////////////////////// + +#define WFM_LABEL_BEFORE 1 +#define WFM_LABEL_AFTER 2 +#define WFM_NO_LABEL 0 + +//////////////////////////////////////////////////// /** Handle CORS in pages */ // Default false for using only whenever necessary to avoid security issue when using CORS (Cross-Origin Resource Sharing) @@ -281,6 +306,8 @@ typedef struct #define USING_CORS_FEATURE false #endif +//////////////////////////////////////////////////// + #ifndef TIME_BETWEEN_MODAL_SCANS // Default to 30s #define TIME_BETWEEN_MODAL_SCANS 120000UL @@ -291,17 +318,23 @@ typedef struct #define TIME_BETWEEN_MODELESS_SCANS 120000UL #endif +//////////////////////////////////////////////////// + //KH // Mofidy HTTP_HEAD to WM_HTTP_HEAD_START to avoid conflict in Arduino esp8266 core 2.6.0+ const char WM_HTTP_200[] PROGMEM = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n"; const char WM_HTTP_HEAD_START[] PROGMEM = "{v}"; +//////////////////////////////////////////////////// + const char WM_HTTP_STYLE[] PROGMEM = ""; -// KH, update from v1.1.0 +//////////////////////////////////////////////////// + const char WM_HTTP_SCRIPT[] PROGMEM = ""; -////// +//////////////////////////////////////////////////// +//////////////////////////////////////////////////// // To permit disable or configure NTP from sketch #ifndef USE_ESP_WIFIMANAGER_NTP @@ -309,6 +342,8 @@ const char WM_HTTP_SCRIPT[] PROGMEM = "

"; const char WM_HTTP_SCRIPT_NTP_HIDDEN[] PROGMEM = "

"; +//////////////////////////////////////////////////// + #if ESP8266 #if !(USE_CLOUDFLARE_NTP) #undef USE_CLOUDFLARE_NTP @@ -333,6 +370,8 @@ const char WM_HTTP_SCRIPT_NTP_HIDDEN[] PROGMEM = "

"; #else @@ -345,34 +384,60 @@ const char WM_HTTP_SCRIPT_NTP[] PROGMEM = "