msraynsford/APConfig

If the power supply fails before the flag is cleared, will it go into reset mode?

rtek1000 opened this issue · 1 comments

Hi,

I have noticed that if the power supply fails before the flag is cleared, it will go into reset mode.

To avoid this, we can use a button on a GPIO like the GPIO0

If anyone is interested, see this code:

APConfig.ino:

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>

#define USESERIAL
#define USELED
#define USE_BTN_FACTORY

#include "Config.h"
#include "FirmwareReset.h"
#include "AdminPage.h"

ESP8266WebServer webServer(80);

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);  Serial.printf("\nAP Config Demo\n");

  InitConfig();

  //Start the wifi with the required username and password
  WiFi.mode(WIFI_AP);

  LoadConfig();
  PrintConfig();

  //Check to see if the flag is still set from the previous boot
  if (checkResetFlag()) {
    //Do the firmware reset here
    Serial.printf("Reset Firmware\n");

    //Set the ssid to default value and turn off the password
    WiFi.softAP("APConfig", "", 1, false, 1);
  }
  else {
    WiFi.softAP(config.ssid, config.pass, 1, false, 1);
  }

  // Serve a very basic page on the root url
  webServer.on("/", []() {
    webServer.send(200, "text/plain", "Hello World");
  });

  // Set up the admin page
  webServer.on("/admin", std::bind(serveAdmin, &webServer));
  webServer.begin();
}

void loop() {
  // put your main code here, to run repeatedly:
  webServer.handleClient();
}

FirmwareReset.h:

// The Firmware reset library creates a small window of time, in which the reset button can be pressed to initiate a firmware reset.
// The state of the window is displayed to the user by flashing the built in LED or by Serial updates.

#ifndef FirmwareReset_H
#define FirmwareReset_H

#include <Ticker.h>

#define FLAGSET     0x55555555
#define FLAGCLEAR   0xAAAAAAAA
#define FLAGADDRESS 00
#define SETTIMEOUT 3
#define CLEARTIMEOUT 1

#define btnFactory 0 // Note: GPIO0 must be in the HIGH state to run the program,
                     // otherwise it will enter the Flash mode for reprogramming,
                     // so do not hold down at startup!
Ticker ticker;
bool booted = false;

// Allows shared usage of the built in led for as long as the firmware reset needs it
void BuiltInLED(bool on) {
  #ifdef USELED
    if (booted)
      digitalWrite(LED_BUILTIN, on);
  #endif
}

// End the firmware reset process, at this point the software can run normally without checking for user resets
void finishBoot() {
  booted = true;

  #ifdef USESERIAL
    Serial.printf("Booted\n");
  #endif

  ticker.detach();
}

// Clear the reset flag in memory, this ends the window for a possible reset
void clearFlag() {
  uint32_t value = FLAGCLEAR;
  ESP.rtcUserMemoryWrite(FLAGADDRESS, &value, sizeof(value));

  #ifdef USELED
    digitalWrite(LED_BUILTIN, HIGH);
  #endif

  #ifdef USESERIAL
    Serial.printf("Clear Flag\n");
  #endif

  ticker.attach(CLEARTIMEOUT, finishBoot);
}

void setFlag() {
  uint32_t value = FLAGSET;
  // Set the reset flag in memory, this will indicate if the user has pressed reset while the LED was on
  ESP.rtcUserMemoryWrite(FLAGADDRESS, &value, sizeof(value));

  #ifdef USELED
    digitalWrite(LED_BUILTIN, LOW);
  #endif

  #ifdef USESERIAL
    Serial.printf("Set Flag\n");
  #endif

  ticker.attach(CLEARTIMEOUT, clearFlag);
}

// Checks to see if the user has rebooted the processor within the reset time frame
bool checkResetFlag() {
  uint32_t value = FLAGCLEAR;
  bool result;

  #ifdef USELED
    // Set up the LED as a status indicator
    pinMode(LED_BUILTIN, OUTPUT);
    digitalWrite(LED_BUILTIN, HIGH);
  #endif

#ifdef USE_BTN_FACTORY
  int i = SETTIMEOUT * 10;
  #ifdef USESERIAL
    Serial.printf("Press the Factory button within %d seconds", SETTIMEOUT);
  #endif

  while (i--) {
  #ifdef USESERIAL
      Serial.printf(".");
  #endif
    delay(100);

    if (!digitalRead(btnFactory)) {
  #ifdef USESERIAL
        Serial.printf("\n");
  #endif
      return true;
    }
  } // start window (GPIO0 must be in HIGH state to run program)

  #ifdef USESERIAL
    Serial.printf("\n");
  #endif

  return false;
#else // #ifdef USE_BTN_FACTORY

  // Check to see the state of the flag in memory
  ESP.rtcUserMemoryRead(FLAGADDRESS, &value, sizeof(value));
  result = (value == FLAGSET);

  // If the user previously performed a reset then begin the reset process
  if (result)
    clearFlag();

  // If the flag was not previously set, then set up a new reset window
  else
    ticker.attach(SETTIMEOUT, setFlag);

  return result;
#endif // #ifdef USE_BTN_FACTORY
}
#endif // #ifndef FirmwareReset_H

nice addition, thanks for sharing.