There is an issue with short lapses in connectivity and especially power failures that need a special handling to deal with either by the user or the library.
Ideally we would fire up the softap and leave sta on to connect, if status is connected we can abort the configportal automatically or after a timeout.
The problem with this is having a non connected sta makes softap unstable if not completely unusable. The theory is that the channel scanning autoconnect does makes the softap drop clients.
With this in mind there are a few options to resolve this.
It depends on user case, but If there is a saved credential, the odds of a power failure is higher than reconfigure of credentials. So waiting a minute for the router before starting configportal is a practical solution, also using an ondemand configportal removes this issue entirely.
Another options is to use some state tracking, if the last reboot was pwr, and last status was connected and time since last reboot is x, odds are router is rebooting...
A good option might be to fire a callout if saved ap found while cp is running.. and let user do whatever, stopconfigportal or restart esp.
added several private options for testing this
bool _disableSTA = false; // disable sta when starting ap, always
bool _disableSTAConn = true; // disable sta when starting ap, if sta is not connected ( stability )
bool _channelSync = false; // use wifi channel when starting ap
I am encountering the same issue.
The current code seems to do something very close to the desired functionality, except it leaves the STA up while also serving the soft AP, which seems to interfere with the next attempt to run the chip in STA mode. Would love to see a solution to this that follows the description above:
"[when attempting manual resolution] stop sta, start ap, do background scans for ap, if default ap is found in scan, restart sta snd try to connect, abort softap on result."
In a nutshell, the desire is for an ReconnectWiFi function on the WifiManager, which attempts three steps, in sequence:
The user could then write this in their loop() function:
void loop() {
if (wifiManager.ReconnectWiFi(AP_SSID, AP_PWD)) {
// WiFi connection is ready for use.
}
}
This would allow very robust self-healing (by repeating step 1) as well as manual healing (by repeating step 2) any time an interruption occurs, be it transient (power outage, AP reboot, etc.), or permanent (SSID change, password change, security mode change, etc.)
Happy to help test a solution through:
Thanks!
Cheers,
~alex
that is literally what I posted above...
all those features can be done now by user code, or autoconnect without cp
Except the background scan for home ap, this might be in the next version, but there are ways to do it now already
Thank you for the quick reply, Shawn!
I feel like I'm missing something... I wrote the trivially simple sketch below... I let the board connect to WiFi (with saved credentials), then I turned off the wireless network. Predictably, after a few seconds, the board lost connectivity and started serving the captive portal. I then turned the wireless network back on and, although the board continued to cycle between trying to connect to WiFi and serving the captive portal, it was never able to reconnect to WiFi. When I physically reset the board, it connected to WiFi in a couple of seconds.
What is your recommendation for changing the sketch below without: 1. Losing most of the benefits of using the WiFiManager class and 2. Repeatedly powercycling the board (can't be good for it over the long term...)
//needed for library
void setup() {
}
const unsigned long CONNECT_TIMEOUT = 180; // Wait 3 minutes to connect to the real AP before trying to boot the local AP
const unsigned long AP_TIMEOUT = 180; // Wait 3 minutes in the config portal before trying again the original WiFi creds
const char* AP_SSID = "ARDUINO"; // Create an access point with this SSID
const char* AP_PWD = "CONFIGURE"; // Protected the access point with this password
bool EnsureWiFi() {
if (WiFi.status() != WL_CONNECTED) {
WiFiManager wifiManager;
wifiManager.setConnectTimeout(CONNECT_TIMEOUT);
wifiManager.setTimeout(AP_TIMEOUT);
wifiManager.autoConnect(AP_SSID, AP_PWD);
}
return (WiFi.status() == WL_CONNECTED);
}
void loop() {
if (EnsureWiFi()) {
// WiFi connection is ready
}
}
what branch are you using ?
The one that the Arduino Studio library manager installs automatically (version appears to be 0.14.0) (see attached screenshot). Should I be using a different, manually installed one?

Also... If scanning for APs is what's causing the issues, I would gladly trade that (i.e. type the ssid/password in a text box, rather than selecting it) in order to have rock solid auto-recovery when possible.
Yeah no support or development is being done on anything but development branch
Got it. I'll clone from https://github.com/tzapu/WiFiManager/tree/development and report back with the results. Thanks!
hopefully this is a known issue and already fixed, but let me know
I am very happy to report that it worked exactly as intended. Also, I noticed that the behavior that enables this library to work as expected is much more sophisticated than it was on the master branch - thank you for improving the code and making it work for my scenario!
yeah it still needs to be documented and examples cleaned up, that is mostly why it is not beta yet
Still same issue with latest versi贸n?
yes there has not been a release since this, use development branch
Which is the link of the working development branch?
Suggestion to the opening post: If the server gets a client request via the AP, disconnect STA for some time (so AP-only mode). Maybe like a watchdog counter (each server-request reset time counter). Maybe even a client connection (before the page-request) may activate this behaviour.
@Bolukan it is worth testing, But the main problem is the ap can be entirely unstable when sta is trying to connect, but it would be nice to find a stable solution to background connecting, ideally users can just use non blocking and make their own logic, this can probably be POC in user code atm using events or WiFi_softap_num_stations,
I am not even sure you can enablesta when in ap atm without it breaking something..
I am aware of the lack of progress. It was quiet timeconsuming (wiping, downloading and analysuing flashes) but bottom-line too difficult for me to create reproducable testcases.
This is not something I will look into until next version
Any progress on this? I have the very same issue reported here #738.
I want to use "AutoConnect" on setup, so It will run once on boot, so lets suppose that It failed to connect to the saved credentials, and that the ConfigPortalTimeout was hit: Now I need It trying to reconnect to saved credentials forever, but It doesn't do that, 'cause when I start up my router/AP, it simply doesn't connect.
When disconnected, I cannot call the "AutoConnect" function again in the loop because it will enter on AP mode, and the really problem of it is that it stucks the code, as it is using an "while(1)" inside library. The code being stuck is a real problem for me, as I am using the NRF24Mesh library, which needs to have its "mesh.update()" function cycled in loop, otherwise it will not keep my mesh network up.
I'm using the latest library version provided by the Arduino IDE's library manager.
I would really appreciate any kind of help.
Hi @aburst42
I have tried your solution and does not work for me. Any idea why?
After losing the wifi connection switch to AP but I turn the wifi on again and does not reconnect.
Cheers
Hi @ftaibi,
Unfortunately, I don't know why the solution wouldn't work for you. I included below the full code of my garage controller. Beyond staying connected to WiFi through power outages and such, it doesn't do much - it just sets a pin to activate the garage door. Maybe give it a go on your Arduino with a copy of WifiManager from the development branch?
~~~~~~~~
//needed for library
// For control
ESP8266WebServer server(80);
//DEBUG
// Change this to true to collects logs
const bool DEBUG_MODE = true;
const int MAX_DEBUG_ENTRIES = 100;
long debugCodes[MAX_DEBUG_ENTRIES][2];
long crtDebugIndex = 0;
void logCode(long code)
{
if (DEBUG_MODE)
{
debugCodes[crtDebugIndex][0] = code;
debugCodes[crtDebugIndex][1] = os_getCurrentTimeSec();
crtDebugIndex++;
if (crtDebugIndex >= MAX_DEBUG_ENTRIES)
{
crtDebugIndex = 0;
}
}
}
void handleDebugInfo() {
char szTmp[MAX_DEBUG_ENTRIES * 10];
if (DEBUG_MODE)
{
unsigned long days = 0, hours = 0, minutes = 0;
int length = 0;
for (int crtDebugInfoIndex = 0;crtDebugInfoIndex < crtDebugIndex; crtDebugInfoIndex++)
{
long code = debugCodes[crtDebugInfoIndex][0];
long val = debugCodes[crtDebugInfoIndex][1];
days = val / (3600*24);
val -= days * (3600*24);
hours = val / 3600;
val -= hours * 3600;
minutes = val / 60;
val -= minutes*60;
length += sprintf(
szTmp + length,
"%02d %02d:%02d:%02d.%02d<br>",
(int)code,
(int)days, (int)hours, (int)minutes, (int)val);
}
}
else // !DEBUG_MODE
{
sprintf(szTmp, "Debug Info Not Available - enable DEBUG_MODE in the code");
}
server.send(200, "text/html", szTmp);
}
const uint8_t DOOR_CONTROL_PIN = D7;
// Duration to close the switch on the door opener. This should be long
// enough for the mechanism to start; typically it doesn't have to remain
// activated for the door to complete its motion. It is the same as the
// time you'd hold down the button to start the door moving.
const float DOOR_ACTIVATION_PERIOD = 0.3; // [s]
// For LED status
Ticker ticker;
void tick()
{
//toggle state
int state = digitalRead(LED_BUILTIN); // get the current state of GPIO1 pin
digitalWrite(LED_BUILTIN, !state); // set pin to the opposite state
}
void ledOn() {
ticker.detach();
//keep LED on
digitalWrite(LED_BUILTIN, LOW);
}
void ledBlink(float interval /in seconds/) {
ticker.attach(interval, tick);
}
void configModeCallback (WiFiManager *myWiFiManager) {
ledBlink(0.2);
}
void InitializeHardware() {
// set led pin as output
pinMode(LED_BUILTIN, OUTPUT);
ledBlink(0.5);
// initialize relay pin
pinMode(DOOR_CONTROL_PIN, OUTPUT);
digitalWrite(DOOR_CONTROL_PIN, LOW);
}
void ToggleOn() {
digitalWrite(DOOR_CONTROL_PIN, HIGH);
}
void ToggleOff() {
digitalWrite(DOOR_CONTROL_PIN, LOW);
}
void handleRoot() {
// This returns the 'homepage' with links to each other main page
unsigned long days = 0, hours = 0, minutes = 0;
unsigned long val = os_getCurrentTimeSec();
days = val / (360024);
val -= days * (360024);
hours = val / 3600;
val -= hours * 3600;
minutes = val / 60;
val -= minutes*60;
byte mac[6];
WiFi.macAddress(mac);
char szTmp[PRINTF_BUF*2];
sprintf(szTmp, ""
"Dashboard for esp8266 controlled Garage Door Opener.
"
"MAC Address: %02X%02X%02X%02X%02X%02X
"
"Uptime: %02d:%02d:%02d.%02d
"
"Free heap: %u
"
"Web Commands
"
"Toggle
"
"ToggleOn
"
"ToggleOff
"
"Restart
"
"DebugInfo
"
"",
mac[0],mac[1],mac[2],mac[3],mac[4],mac[5],
(int)days, (int)hours, (int)minutes, (int)val,
ESP.getFreeHeap());
server.send(200, "text/html", szTmp);
}
Ticker toggleTicker;
void handleToggle() {
ToggleOn();
toggleTicker.attach(DOOR_ACTIVATION_PERIOD, handleToggleOff);
}
void handleToggleOn() {
ToggleOn();
}
void handleToggleOff() {
toggleTicker.detach();
ToggleOff();
}
void handleRestart() {
server.send(200, "text/plain", "ESP is restarting (can also reset with changed URL)");
server.stop();
delay(2000);
ESP.restart();
}
const unsigned long CONNECT_TIMEOUT = 30; // Wait 30 seconds to connect to the real AP before trying to boot the local AP
const unsigned long AP_TIMEOUT = 180; // Wait 3 minutes in the config portal before trying again the original WiFi creds
const char* AP_SSID = "ARDUINO2"; // Create an access point with this SSID
const char* AP_PWD = "CONFIGURE"; // Protect the access point with this password
// Returns true if connection is successful
// false if connection failed (timeout occurred, or wrong ssid/password)
// Returning false typically means we should loop again to give the original
// ssid/password a chance, after which we try the AP again
bool EnsureWiFi() {
// If we're already connected, do nothing
if (WiFi.status() != WL_CONNECTED) {
logCode(0);
WiFiManager wifiManager;
wifiManager.setAPCallback(configModeCallback);
wifiManager.setDebugOutput(true);
wifiManager.setConnectTimeout(CONNECT_TIMEOUT);
// Exit after the assigned amount of time, regardless of whether we're
// connected or not. This will give the caller a chance to try calling
// EnsureWiFi again, which in turn will try again with the original
// ssid/password (auto-recovery from transient failure), followed by
// the next access point cycle.
wifiManager.setTimeout(AP_TIMEOUT);
// This will block until a WiFi is connected, or the timeout has elapsed
wifiManager.autoConnect(AP_SSID, AP_PWD);
logCode(1);
}
return (WiFi.status() == WL_CONNECTED);
}
void EnsureSerial() {
Serial.begin(115200);
}
bool serverStarted = false;
void EnsureServer() {
if ((WiFi.status() == WL_CONNECTED)
&& (!serverStarted)) {
logCode(2);
server.begin();
serverStarted = true;
logCode(3);
}
}
void EnsureConnections() {
EnsureWiFi();
EnsureSerial();
EnsureServer();
}
void PrepareWebServerHandlers() {
server.on("/", handleRoot);
server.on("/toggle", handleToggle);
server.on("/toggleOn", handleToggleOn);
server.on("/toggleOff", handleToggleOff);
server.on("/restart", handleRestart);
server.on("/debugInfo", handleDebugInfo);
}
void setup() {
InitializeHardware();
PrepareWebServerHandlers();
EnsureConnections();
ArduinoOTA.begin();
Serial.println("setup complete");
}
void loop() {
ArduinoOTA.handle();
ledBlink(0.2);
EnsureConnections();
server.handleClient();
if (WiFi.status() == WL_CONNECTED) {
ledOn();
}
}
unsigned long os_getCurrentTimeSec()
{
static unsigned int wrapCnt = 0;
static unsigned long lastVal = 0;
unsigned long currentVal = millis();
if(currentVal < lastVal)
{
wrapCnt++;
}
unsigned long seconds = currentVal/1000;
//millis will wrap each 50 days, as we are interested only in seconds, let's keep the wrap counter
return (wrapCnt*4294967) + seconds;
}