[GH-ISSUE #706] background sta connect, reconnect to saved ap on power failure #589

Open
opened 2026-02-28 01:26:01 +03:00 by kerem · 22 comments
Owner

Originally created by @tablatronix on GitHub (Aug 22, 2018).
Original GitHub issue: https://github.com/tzapu/WiFiManager/issues/706

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.

Library

  • leave sta+ap mode and hope for the best...
  • startap with wifi.channel to make the channel static, might increase stability.
  • 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.
  • if no softap clients switch to sta and try to reconnect back, rinse repeat

User

  • use a long connecttimeout and a short configportaltimeout, on exit, if still not connected reboot
  • let user do it themselves, do not use autoconnect, use user code to check and timeout after power failure determined via reset code and handle accordingly start/stop configportal as needed.

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.

Originally created by @tablatronix on GitHub (Aug 22, 2018). Original GitHub issue: https://github.com/tzapu/WiFiManager/issues/706 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. ### Library - leave sta+ap mode and hope for the best... - startap with wifi.channel to make the channel static, might increase stability. - 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. - if no softap clients switch to sta and try to reconnect back, rinse repeat ### User - use a long connecttimeout and a short configportaltimeout, on exit, if still not connected reboot - let user do it themselves, do not use autoconnect, use user code to check and timeout after power failure determined via reset code and handle accordingly start/stop configportal as needed. 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.
Author
Owner

@tablatronix commented on GitHub (Aug 23, 2018):

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
<!-- gh-comment-id:415528390 --> @tablatronix commented on GitHub (Aug 23, 2018): 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 ```
Author
Owner

@aburst42 commented on GitHub (Jan 18, 2019):

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:
0. If (WiFi.status()==WL_CONNECTED), all is good - return true. Otherwise, proceed to 1

  1. Attempt to connect with saved ssid/password, if available (AP is off, STA is on), until _connectTimeout expires or (WiFi.status()==WL_CONNECTED). If (WiFi.status()==WL_CONNECTED), all is good - return true. Otherwise, or if no saved ssid/password pair is available, proceed to 2.
  2. Let user manually configure WiFi (AP is on, STA is off), until _configPortalTimeout expires or WiFi.status() is WL_CONNECTED. Return (WiFi.status()==WL_CONNECTED)

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:

  1. Simulated power cycles of the AP (disabling and re-enabling the wireless network)
  2. SSID changes
  3. Password changes
  4. Security scheme changes.

Thanks!

Cheers,
~alex

<!-- gh-comment-id:455472806 --> @aburst42 commented on GitHub (Jan 18, 2019): 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: 0. If (WiFi.status()==WL_CONNECTED), all is good - return true. Otherwise, proceed to 1 1. Attempt to connect with saved ssid/password, if available (AP is off, STA is on), until _connectTimeout expires or (WiFi.status()==WL_CONNECTED). If (WiFi.status()==WL_CONNECTED), all is good - return true. Otherwise, or if no saved ssid/password pair is available, proceed to 2. 2. Let user manually configure WiFi (AP is on, STA is off), until _configPortalTimeout expires or WiFi.status() is WL_CONNECTED. Return (WiFi.status()==WL_CONNECTED) 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: 1. Simulated power cycles of the AP (disabling and re-enabling the wireless network) 2. SSID changes 3. Password changes 3. Security scheme changes. Thanks! Cheers, ~alex
Author
Owner

@tablatronix commented on GitHub (Jan 18, 2019):

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

<!-- gh-comment-id:455612856 --> @tablatronix commented on GitHub (Jan 18, 2019): 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
Author
Owner

@aburst42 commented on GitHub (Jan 18, 2019):

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...)

#include <ESP8266WiFi.h>

//needed for library
#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h>
#include <ArduinoOTA.h>

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
}
}

<!-- gh-comment-id:455632410 --> @aburst42 commented on GitHub (Jan 18, 2019): 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...) #include <ESP8266WiFi.h> //needed for library #include <DNSServer.h> #include <ESP8266WebServer.h> #include <WiFiManager.h> #include <ArduinoOTA.h> 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 } }
Author
Owner

@tablatronix commented on GitHub (Jan 18, 2019):

what branch are you using ?

<!-- gh-comment-id:455633673 --> @tablatronix commented on GitHub (Jan 18, 2019): what branch are you using ?
Author
Owner

@aburst42 commented on GitHub (Jan 18, 2019):

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?
wifimanagerversion

<!-- gh-comment-id:455634942 --> @aburst42 commented on GitHub (Jan 18, 2019): 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? ![wifimanagerversion](https://user-images.githubusercontent.com/44483527/51404359-148d3e80-1b08-11e9-9cfb-b7ce991f1666.png)
Author
Owner

@aburst42 commented on GitHub (Jan 18, 2019):

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.

<!-- gh-comment-id:455658052 --> @aburst42 commented on GitHub (Jan 18, 2019): 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.
Author
Owner

@tablatronix commented on GitHub (Jan 18, 2019):

Yeah no support or development is being done on anything but development branch

<!-- gh-comment-id:455666526 --> @tablatronix commented on GitHub (Jan 18, 2019): Yeah no support or development is being done on anything but development branch
Author
Owner

@aburst42 commented on GitHub (Jan 18, 2019):

Got it. I'll clone from https://github.com/tzapu/WiFiManager/tree/development and report back with the results. Thanks!

<!-- gh-comment-id:455689526 --> @aburst42 commented on GitHub (Jan 18, 2019): Got it. I'll clone from https://github.com/tzapu/WiFiManager/tree/development and report back with the results. Thanks!
Author
Owner

@tablatronix commented on GitHub (Jan 18, 2019):

hopefully this is a known issue and already fixed, but let me know

<!-- gh-comment-id:455691308 --> @tablatronix commented on GitHub (Jan 18, 2019): hopefully this is a known issue and already fixed, but let me know
Author
Owner

@aburst42 commented on GitHub (Jan 20, 2019):

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!

<!-- gh-comment-id:455840805 --> @aburst42 commented on GitHub (Jan 20, 2019): 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!
Author
Owner

@tablatronix commented on GitHub (Jan 20, 2019):

yeah it still needs to be documented and examples cleaned up, that is mostly why it is not beta yet

<!-- gh-comment-id:455879488 --> @tablatronix commented on GitHub (Jan 20, 2019): yeah it still needs to be documented and examples cleaned up, that is mostly why it is not beta yet
Author
Owner

@alvaroaguero55 commented on GitHub (Aug 11, 2019):

Still same issue with latest versión?

<!-- gh-comment-id:520240454 --> @alvaroaguero55 commented on GitHub (Aug 11, 2019): Still same issue with latest versión?
Author
Owner

@tablatronix commented on GitHub (Aug 11, 2019):

yes there has not been a release since this, use development branch

<!-- gh-comment-id:520244565 --> @tablatronix commented on GitHub (Aug 11, 2019): yes there has not been a release since this, use development branch
Author
Owner

@alvaroaguero55 commented on GitHub (Aug 11, 2019):

Which is the link of the working development branch?

<!-- gh-comment-id:520249142 --> @alvaroaguero55 commented on GitHub (Aug 11, 2019): Which is the link of the working development branch?
Author
Owner

@Bolukan commented on GitHub (Oct 7, 2019):

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.

<!-- gh-comment-id:539126317 --> @Bolukan commented on GitHub (Oct 7, 2019): 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.
Author
Owner

@tablatronix commented on GitHub (Oct 7, 2019):

@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..

<!-- gh-comment-id:539150824 --> @tablatronix commented on GitHub (Oct 7, 2019): @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..
Author
Owner

@Bolukan commented on GitHub (Nov 16, 2019):

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.

<!-- gh-comment-id:554628240 --> @Bolukan commented on GitHub (Nov 16, 2019): 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.
Author
Owner

@tablatronix commented on GitHub (Nov 16, 2019):

This is not something I will look into until next version

<!-- gh-comment-id:554651682 --> @tablatronix commented on GitHub (Nov 16, 2019): This is not something I will look into until next version
Author
Owner

@Jhonathaned commented on GitHub (Jan 22, 2020):

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.

<!-- gh-comment-id:577212325 --> @Jhonathaned commented on GitHub (Jan 22, 2020): 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.
Author
Owner

@ftaibi commented on GitHub (Sep 8, 2020):

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

<!-- gh-comment-id:688588208 --> @ftaibi commented on GitHub (Sep 8, 2020): Hi @aburst42 I have tried your [solution](https://github.com/tzapu/WiFiManager/issues/706#issuecomment-455632410) 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
Author
Owner

@alexburs commented on GitHub (Sep 11, 2020):

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?

#include <ESP8266WiFi.h>

//needed for library
#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h>
#include <ArduinoOTA.h>
#include <Ticker.h>

#define PRINTF_BUF 512 // define the tmp buffer size (change if desired)

// 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 / (3600*24);
  val -= days * (3600*24);
  
  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, "<html>"
                "<b>Dashboard for esp8266 controlled Garage Door Opener.</b><br><br>"
                "MAC Address: %02X%02X%02X%02X%02X%02X<br>"
                "Uptime: %02d:%02d:%02d.%02d<br>"
                "Free heap: %u<br><br>"
                "Web Commands<br>"
                "<a href='/toggle' target='_blank'>Toggle</a><br>"
                "<a href='/toggleOn' target='_blank'>ToggleOn</a><br>"
                "<a href='/toggleOff' target='_blank'>ToggleOff</a><br>"
                "<a href='/restart' target='_blank'>Restart</a><br>"
                "<a href='/debugInfo' target='_blank'>DebugInfo</a><br>"
                "</html>",
                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;
}
<!-- gh-comment-id:691269355 --> @alexburs commented on GitHub (Sep 11, 2020): 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? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #include <ESP8266WiFi.h> //needed for library #include <DNSServer.h> #include <ESP8266WebServer.h> #include <WiFiManager.h> #include <ArduinoOTA.h> #include <Ticker.h> #define PRINTF_BUF 512 // define the tmp buffer size (change if desired) // 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 / (3600*24); val -= days * (3600*24); 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, "<html>" "<b>Dashboard for esp8266 controlled Garage Door Opener.</b><br><br>" "MAC Address: %02X%02X%02X%02X%02X%02X<br>" "Uptime: %02d:%02d:%02d.%02d<br>" "Free heap: %u<br><br>" "Web Commands<br>" "<a href='/toggle' target='_blank'>Toggle</a><br>" "<a href='/toggleOn' target='_blank'>ToggleOn</a><br>" "<a href='/toggleOff' target='_blank'>ToggleOff</a><br>" "<a href='/restart' target='_blank'>Restart</a><br>" "<a href='/debugInfo' target='_blank'>DebugInfo</a><br>" "</html>", 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; }
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/WiFiManager#589
No description provided.