ESP_WiFiManager for ESP32 and ESP8266: Support

I’m creating this new topic for forum members to support one another in using / applying the ESP_WiFiManager

Please share your experience as well as difficulties / bugs.
I’ll personally try my best to help fix the issue and learn something from the experience.

Cheers,

3 Likes

Hello @khoih
Firstly thank you for contributing this library. I was having difficulty successfully running other WiFi manager libraries on my ESP32 wroom dev board and finally found success using and adapting your ConfigOnStartup.ino example, however I am a little stuck now.

On its first ever startup I need the program to run the Wifi portal segment of code and wait for Wifi credentials to be entered before proceeding with the rest of the code. Thereafter, on subsequent bootups the ESP32 must be able to detect that there are stored wifi credentials and simply use the stored Wifi credentials without starting up the Wifi portal. The only time it must run the Wifi portal again is when a hardware pushbutton is pressed. I have adapted the program as follows:


/****************************************************************************************************************************
   ConfigOnStartup.ino
   For ESP8266 / ESP32 boards

   ESP_WiFiManager is a library for the ESP8266/ESP32 platform (https://github.com/esp8266/Arduino) to enable easy
   configuration and reconfiguration of WiFi credentials using a Captive Portal. Inspired by:
   http://www.esp8266.com/viewtopic.php?f=29&t=2520
   https://github.com/chriscook8/esp-arduino-apboot
   https://github.com/esp8266/Arduino/blob/master/libraries/DNSServer/examples/CaptivePortalAdvanced/

   Modified from Tzapu https://github.com/tzapu/WiFiManager
   and from Ken Taylor https://github.com/kentaylor

   Built by Khoi Hoang https://github.com/khoih-prog/ESP_WiFiManager
   Licensed under MIT license
   Version: 1.0.8

   Version Modified By   Date      Comments
   ------- -----------  ---------- -----------
    1.0.0   K Hoang      07/10/2019 Initial coding
    1.0.1   K Hoang      13/12/2019 Fix bug. Add features. Add support for ESP32
    1.0.2   K Hoang      19/12/2019 Fix bug thatkeeps ConfigPortal in endless loop if Portal/Router SSID or Password is NULL.
    1.0.3   K Hoang      05/01/2020 Option not displaying AvailablePages in Info page. Enhance README.md. Modify examples
    1.0.4   K Hoang      07/01/2020 Add RFC952 setHostname feature.
    1.0.5   K Hoang      15/01/2020 Add configurable DNS feature. Thanks to @Amorphous of https://community.blynk.cc
    1.0.6   K Hoang      03/02/2020 Add support for ArduinoJson version 6.0.0+ ( tested with v6.14.1 )
    1.0.7   K Hoang      13/04/2020 Reduce start time, fix SPIFFS bug in examples, update README.md
    1.0.8   K Hoang      10/06/2020 Fix STAstaticIP issue. Restructure code. Add LittleFS support for ESP8266 core 2.7.1+
 *****************************************************************************************************************************/
/****************************************************************************************************************************
   This example will open a configuration portal for 60 seconds when first powered up if the boards has stored WiFi Credentials.
   Otherwise, it'll stay indefinitely in ConfigPortal until getting WiFi Credentials and connecting to WiFi

   ConfigOnSwitch is a a bettter example for most situations but this has the advantage
   that no pins or buttons are required on the ESP32/ESP8266 device at the cost of delaying
   the user sketch for the period that the configuration portal is open.

   Also in this example a password is required to connect to the configuration portal
   network. This is inconvenient but means that only those who know the password or those
   already connected to the target WiFi network can access the configuration portal and
   the WiFi network credentials will be sent from the browser over an encrypted connection and
   can not be read by observers.
 *****************************************************************************************************************************/

#if !( defined(ESP8266) || defined(ESP32) )
  #error This code is intended to run only on the ESP8266 and ESP32 boards ! Please check your Tools->Board setting.
#endif

// Use from 0 to 4. Higher nimber, more debugging messages and memory usage.
#define _WIFIMGR_LOGLEVEL_    4

//For ESP32, To use ESP32 Dev Module, QIO, Flash 4MB/80MHz, Upload 921600

//Ported to ESP32
#include <esp_wifi.h>
#include <WiFi.h>
#include <WiFiClient.h>
  
#define ESP_getChipId()   ((uint32_t)ESP.getEfuseMac())
  
#define LED_ON      HIGH
#define LED_OFF     LOW

// 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="";

IPAddress stationIP   = IPAddress(192, 168, 2, 114);
IPAddress gatewayIP   = IPAddress(192, 168, 2, 1);
IPAddress netMask     = IPAddress(255, 255, 255, 0);
IPAddress dns1IP      = gatewayIP;
IPAddress dns2IP      = IPAddress(8, 8, 8, 8);

// 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 <ESP_WiFiManager.h>
#define USE_AVAILABLE_PAGES     false

//https://github.com/khoih-prog/ESP_WiFiManager 
#include <ESP_WiFiManager.h>           

// Onboard LED I/O pin on NodeMCU board 
const int PIN_LED = 2; // D4 on NodeMCU and WeMos. GPIO2/ADC12 of ESP32. Controls the onboard LED.


//Function responsible for connecting ESP32 to WifiNetwork via webportal
void ConnectToWifiNetwork();
// Use this to default DHCP hostname to ESP8266-XXXXXX or ESP32-XXXXXX 
//ESP_WiFiManager ESP_wifiManager;   //**1 Initialising Wifi Manager here allows program to compile successfully however credentials are erased on every bootup of the ESP32
bool WebServerFlag=0;
bool CheckWifiCredentials=1;

//Checks if project is still connected to a WiFi network 
void heartBeatPrint(void)
{
  if (WiFi.status() == WL_CONNECTED)
    Serial.println("Connected");        //Connected to WiFi
  else{
    Serial.println("Failed");           //Not connected to WiFi
    ConnectToWifiNetwork();
  } 
}

void check_status()
{
  static ulong checkstatus_timeout = 0;

  #define HEARTBEAT_INTERVAL    10000L
  // Print hearbeat every HEARTBEAT_INTERVAL (10) seconds.
  if ((millis() > checkstatus_timeout) || (checkstatus_timeout == 0))
  {
    heartBeatPrint();
    checkstatus_timeout = millis() + HEARTBEAT_INTERVAL;
  }
}

void setup(){
  
  //Initialize the LED digital pin as an output.
  pinMode(PIN_LED, OUTPUT);
  //Initialise pin D18 as input to trigger WifiManager code
  pinMode(18, INPUT_PULLUP);
  Serial.begin(115200);
  
  ConnectToWifiNetwork();
  
}

void ConnectToWifiNetwork(){

  Serial.println("\nConnectToWifiNetwork() executed");
  unsigned long startedAt = millis();
  
//Check if credentials are available. Connect the ESP32 to Wifi network with previously stored credentials
//**2. This segment of code does not work unless "ESP_WiFiManager ESP_wifiManager;" is declared first. However, when this is defined here it opens the wifi portal even before checking if wifi credentials are available
  if(CheckWifiCredentials==1){

    Serial.println("Checking stored wifi credentials");
    Router_SSID = ESP_wifiManager.WiFi_SSID();
    Router_Pass = ESP_wifiManager.WiFi_Pass();
    Serial.println("Stored: SSID=" + Router_SSID + ", Pass=" + Router_Pass);
  
  }

  //Only start the WifiManager web page requesting wifi credentials if credentials are not available
  if(Router_SSID==""){  

    Serial.println("Opening configuration portal.");
    
    //Use this to default DHCP hostname to ESP32-XXXXXX
    //ESP_WiFiManager ESP_wifiManager;
    ESP_wifiManager.setMinimumSignalQuality(-1);
    ESP_wifiManager.setSTAStaticIPConfig(stationIP, gatewayIP, netMask, dns1IP, dns2IP);
    //Device will remain in configuration mode until switched off via webserver.
    digitalWrite(PIN_LED, LED_ON); // turn the LED on by making the voltage LOW to tell us we are in configuration mode.

    // SSID to uppercase
    ssid.toUpperCase();
  
  }

  if (!ESP_wifiManager.startConfigPortal((const char *) ssid.c_str(), password)){
    Serial.println("Failed to connect to WiFi network. Please check credentials and try again.");
    Router_SSID="";     //To bypass the stored Wifi credentials and force the ESP32 to open the Wifi portal so that a new network with new Wifi credentials can be selected if the previous network is not available or incorrect credentials were entered then they can be rectified
    CheckWifiCredentials=0;
    ConnectToWifiNetwork();
  }
  else
    Serial.println("Connection to Wifi network is successful");

  digitalWrite(PIN_LED, LED_OFF); // Turn led off as we are not in configuration mode.

  // For some unknown reason webserver can only be started once per boot up
  // so webserver can not be used again in the sketch. 
  if(WebServerFlag==0){ //Used a flag to make sure it only runs once
    
    #define WIFI_CONNECT_TIMEOUT        30000L
    #define WHILE_LOOP_DELAY            200L
    #define WHILE_LOOP_STEPS            (WIFI_CONNECT_TIMEOUT / ( 3 * WHILE_LOOP_DELAY ))
    WebServerFlag=1;
  
  }

  //This segment of code is responsible for assigning the ESP32 a IP address connecting the ESP32 to the Wifi network selected on the webpage
  startedAt = millis();
  while ( (WiFi.status() != WL_CONNECTED) && (millis() - startedAt < WIFI_CONNECT_TIMEOUT ) ){

    WiFi.mode(WIFI_STA);
    WiFi.persistent (true);
    // We start by connecting to a WiFi network

    Serial.print("Connecting to ");
    Serial.println(Router_SSID);

    WiFi.config(stationIP, gatewayIP, netMask);
    //WiFi.config(stationIP, gatewayIP, netMask, dns1IP, dns2IP);
    
    WiFi.begin(Router_SSID.c_str(), Router_Pass.c_str());

    int i = 0;
    while ((!WiFi.status() || WiFi.status() >= WL_DISCONNECTED) && i++ < WHILE_LOOP_STEPS)
    {
      delay(WHILE_LOOP_DELAY);
    }
  }

  Serial.print("After waiting ");
  Serial.print((millis() - startedAt) / 1000);
  Serial.print(" secs more in setup(), connection result is ");

  if (WiFi.status() == WL_CONNECTED)
  {
    Serial.print("Connected. Local IP: ");
    Serial.println(WiFi.localIP());
  }
  else{

    Serial.println("Failed to connect to network. Please try again");
    Serial.println(ESP_wifiManager.getStatus(WiFi.status()));
    ConnectToWifiNetwork();
    
  } 
}

void loop()
{
  // put your main code here, to run repeatedly
  Serial.println("Void loop executed");
  check_status();
 //External pushbutton used to change Wifi credentials using Wifi portal
  if (digitalRead(18)==LOW) {

    Serial.println("ESP32 has been disconnected. Wifi credentials are still saved");
    WiFi.disconnect(true);
     Router_SSID="";   //To bypass the stored Wifi credentials and force the ESP32 to open the Wifi portal so that a new network with new Wifi credentials can be selected
    //ESP_wifiManager.resetSettings();  //Does not erase wifi credentials
    delay(2000);
    
  }
  delay(1000);
}

I am looking for a way to enable the ESP32 to check if it has stored credentials first before running the portal but am unable to use the commands, mentioned in point 2 ( Router_SSID = ESP_wifiManager.WiFi_SSID(); Router_Pass = ESP_wifiManager.WiFi_Pass();) in above code, because it requires ESP_WiFiManager ESP_wifiManager; to be declared before it. If I declare ESP_WiFiManager ESP_wifiManager; anywhere before point 2 then, although credentials entered are stored and are not erased every time the ESP32 is booted up (which is what I want to happen), it opens up the wifi portal without even checking if stored credentials are available (because it opens up the portal as soon as it executes the ESP_WiFiManager ESP_wifiManager; which I obviously do not want happening) . If I declare ESP_WiFiManager ESP_wifiManager; globally, as i have shown in point 1, then there is no issue of the wifi portal opening when its not suppose to, however wifi credentials are then not being stored permanently (or more accurately speaking, the credentials are being erased everytime the ESP32 is booted). So this means I cannot declare ESP_WiFiManager ESP_wifiManager; globally and neither can I declare it in the program for the reasons mentioned above. I am hoping that there is another function or method of checking if any credentials are stored in ESP32 memory without needing to use Router_SSID = ESP_wifiManager.WiFi_SSID(); Router_Pass = ESP_wifiManager.WiFi_Pass(); because it looks as if this might solve my problem. Please advise.

1 Like

I suggest you try the ConfigOnDoubleReset example.

The ConfigOnStartup example has the explanation of the behavior you observed:

This example will open a configuration portal for 60 seconds when first powered up if the boards has stored WiFi Credentials.
Otherwise, it'll stay indefinitely in ConfigPortal until getting WiFi Credentials and connecting to WiFi

Whereas, the ConfigOnDoubleReset example has the following behavior you’re expecting:

This example will open a configuration portal when the reset button is pressed twice.

Thanks khoih. While experimenting with the example you suggested, I realised that I have not figured out how to clear the WiFI credentials from the ESP32’s memory. When I ran the code I adapted, my serial monitor prints no SSID and Password credentials:

ConnectToWifiNetwork() executed
Checking stored wifi credentials
Stored: SSID=, Pass=
Opening configuration portal.
[WM] setSTAStaticIPConfig for USE_CONFIGURABLE_DNS
[WM] WiFi.waitForConnectResult Done
[WM] SET AP
[WM] 
Configuring AP SSID = ESP_A3C40A24
[WM] AP PWD = your_password
[WM] AP IP address = 192.168.4.1
[WM] HTTP server started
[WM] ESP_WiFiManager::startConfigPortal : Enter loop

So as you can see no credentials are detected to be stored on the ESP32. However, when I upload the example code, the credentials appear on the serial monitor as if they were never erased:

Starting
EEPROM size = 512, start = 256
Stored: SSID = HUAWEI-XXXX-XXXX, Pass = XXXXXX
Got stored Credentials. Timeout 60s
EEPROM Flag read = 0xd0d04321
No doubleResetDetected
SetFlag write = 0xd0d01234

The adapted code I showed in the previous message is actually not erasing the credentials from the ESP32’s memory otherwise it would not being shown up on the serial monitor when I run the example code you suggested. In order to fully test both my adapted code and the example code you suggested I need to erase the stored credentials. I tried saving empty credentials on the WiFi portal, although the portal message said the empty credentials were saved the serial monitor still shows the credentials are still stored on the ESP32. I also tried ESP_wifiManager.resetSettings(); but it does not erase the stored WiFi credentials. Is there a way to erase the stored credentials?

1 Like

Thanks for exploring the examples.

I suggest you spend more time to find out a better solution / approach of your code. Have more tests to know the features, then apply into your case.

The ESP32 WiFi Credentials,secretly stored in Flash, can’t be easily overwritten until a new and valid SSID/PW facilitates a good WiFi connection. And this is intentionally designed for a good purpose, and everybody is happy with that.

The example is just an example, whenever you change it for your purpose, you have to be sure.

I’m happy to provide support. But the time is also limited, therefore, the support is only for basic use of examples, bug, and reasonable requests.

Thank you for putting up examples. It is very helpful.

Are you saying that there is no easy way to erase the credentials from memory using software commands?
If the program stores the credentials and is reading it, there must be a way to access it?

Can you please point me towards something that might help. I have spent a considerable amount of time with this library and have found it very helpful for the most part. I am just a little stuck here. I have read here that the code .resetSettings(); is, using the tzapu wifi manager library, is a method to erase stored credentials and since your library is built upon Tzapu, is this a possible bug that it does not erase the credentials? Or is it a feature that was intentionally left out?

1 Like

The Tzapu resetSettings(); only works for ESP8266, and you can try on ESP8266 to verify.

is this a possible bug that it does not erase the credentials?

I concede now that it’s a bug and have the temporary fix for you (and will fix the library later) as follows:

Add this function into your sketch

void resetSettings()
{
  LOGINFO(F("Previous settings invalidated"));
  
#ifdef ESP8266  
  WiFi.disconnect(true);
#else
  WiFi.disconnect(true, true);
#endif

  delay(200);
  return;
}

then call in your setup()

void setup()
{
...

  //Local intialization. Once its business is done, there is no need to keep it around
  // Use this to default DHCP hostname to ESP8266-XXXXXX or ESP32-XXXXXX
  //ESP_WiFiManager ESP_wifiManager;
  // Use this to personalize DHCP hostname (RFC952 conformed)
  ESP_WiFiManager ESP_wifiManager("ConfigOnSwitch");

  ESP_wifiManager.setDebugOutput(true);

  // Use only to erase stored WiFi Credentials. Remember to comment out when done
  resetSettings();                         <==== this is the new function
  //ESP_wifiManager.resetSettings();       <==== this is the new function use later with updated lib
 

  ESP_wifiManager.setMinimumSignalQuality(-1);
  // Set static IP, Gateway, Subnetmask, DNS1 and DNS2. New in v1.0.5
  ESP_wifiManager.setSTAStaticIPConfig(stationIP, gatewayIP, netMask, dns1IP, dns2IP);                             

  // We can't use WiFi.SSID() in ESP32as it's only valid after connected.
  // SSID and Password stored in ESP32 wifi_ap_record_t and wifi_config_t are also cleared in reboot
  // Have to create a new function to store in EEPROM/SPIFFS for this purpose
  Router_SSID = ESP_wifiManager.WiFi_SSID();
  Router_Pass = ESP_wifiManager.WiFi_Pass();

  //Remove this line if you do not want to see WiFi password printed
  Serial.println("Stored: SSID = " + Router_SSID + ", Pass = " + Router_Pass);

  // SSID to uppercase
  ssid.toUpperCase();

  if (Router_SSID == "")
  {
    Serial.println("We haven't got any access point credentials, so get them now");

    digitalWrite(PIN_LED, LED_ON); // Turn led on as we are in configuration mode.

    //it starts an access point
    //and goes into a blocking loop awaiting configuration
    if (!ESP_wifiManager.startConfigPortal((const char *) ssid.c_str(), password))
      Serial.println("Not connected to WiFi but continuing anyway.");
    else
      Serial.println("WiFi connected...yeey :)");
  }

  digitalWrite(PIN_LED, LED_OFF); // Turn led off as we are not in configuration mode.

#define WIFI_CONNECT_TIMEOUT        30000L
#define WHILE_LOOP_DELAY            200L
#define WHILE_LOOP_STEPS            (WIFI_CONNECT_TIMEOUT / ( 3 * WHILE_LOOP_DELAY ))

  startedAt = millis();

  while ( (WiFi.status() != WL_CONNECTED) && (millis() - startedAt < WIFI_CONNECT_TIMEOUT ) )
  {
    WiFi.mode(WIFI_STA);
    WiFi.persistent (true);

    // We start by connecting to a WiFi network

    Serial.print("Connecting to ");
    Serial.println(Router_SSID);

    WiFi.config(stationIP, gatewayIP, netMask);
    //WiFi.config(stationIP, gatewayIP, netMask, dns1IP, dns2IP);

    WiFi.begin(Router_SSID.c_str(), Router_Pass.c_str());

    int i = 0;
    while ((!WiFi.status() || WiFi.status() >= WL_DISCONNECTED) && i++ < WHILE_LOOP_STEPS)
    {
      delay(WHILE_LOOP_DELAY);
    }
  }

  Serial.print("After waiting ");
  Serial.print((millis() - startedAt) / 1000);
  Serial.print(" secs more in setup(), connection result is ");

  if (WiFi.status() == WL_CONNECTED)
  {
    Serial.print("connected. Local IP: ");
    Serial.println(WiFi.localIP());
  }
  else
    Serial.println(ESP_wifiManager.getStatus(WiFi.status()));
}

You can also use the ESP_WiFiManager Master version just being updated.

1 Like

Hello @khoih.
I have managed to construct a program using your sketch as a guide and its working almost exactly the way I want. There is one problem that I am having:
When the ESP32 is in access point mode and I put in incorrect Wifi credentials on the web portal then I loop the program to request Wifi credentials again until correct Wifi credentials are entered. So this will continue to occur until the ESP32 has legitimate Wifi credentials and it is able to connect to my house network. Now here is my problem, since the ESP32 is already in access point mode when I call my function “ConnectToWifiNetwork()”(which contains similar Wifi manager code to what you have put in the setup() function above), the web portal does not launch again when I connect to the ESP32’s Wifi network. The work around I found for this involves restarting the ESP32 which is not ideal for obvious reasons. What I need to fix my problem is to stop the ESP32 from being in access point mode before calling"ConnectToWifiNetwork()". Is there a function I can call or what code can I implement to take the ESP32 out of access point mode so that when I call “ConnectToWifiNetwork()” then the web portal will launch again?

A post was merged into an existing topic: Need help converting WiFiManager code from ESP8266 to ESP32