text: /****************************************************************************************************************************
    Async_ConfigOnDoubleReset_Multi.h
    For ESP8266 / ESP32 boards
  
    ESPAsync_WiFiManager is a library for the ESP8266/Arduino platform, using (ESP)AsyncWebServer to enable easy
    configuration and reconfiguration of WiFi credentials using a Captive Portal.
  
    Modified from
    1. Tzapu               (https://github.com/tzapu/WiFiManager)
    2. Ken Taylor          (https://github.com/kentaylor)
    3. Alan Steremberg     (https://github.com/alanswx/ESPAsyncWiFiManager)
    4. Khoi Hoang          (https://github.com/khoih-prog/ESP_WiFiManager)
  
    Built by Khoi Hoang https://github.com/khoih-prog/ESPAsync_WiFiManager
    Licensed under MIT license
   *****************************************************************************************************************************/
  /****************************************************************************************************************************
     This example will open a configuration portal when the reset button is pressed twice.
     This method works well on Wemos boards which have a single reset button on board. It avoids using a pin for launching the configuration portal.
  
     How It Works
     1) ESP8266
     Save data in RTC memory
     2) ESP32
     Save data in EEPROM from address 256, size 512 bytes (both configurable)
  
     So when the device starts up it checks this region of ram for a flag to see if it has been recently reset.
     If so it launches a configuration portal, if not it sets the reset flag. After running for a while this flag is cleared so that
     it will only launch the configuration portal in response to closely spaced resets.
  
     Settings
     There are two values to be set in the sketch.
  
     DRD_TIMEOUT - Number of seconds to wait for the second reset. Set to 10 in the example.
     DRD_ADDRESS - The address in ESP8266 RTC RAM to store the flag. This memory must not be used for other purposes in the same sketch. Set to 0 in the example.
  
     This example, originally relied on the Double Reset Detector library from https://github.com/datacute/DoubleResetDetector
     To support ESP32, use ESP_DoubleResetDetector library from //https://github.com/khoih-prog/ESP_DoubleResetDetector
   *****************************************************************************************************************************/
  
  #if !( defined(ESP8266) ||  defined(ESP32) )
    #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.11.0"
  #define ESP_ASYNC_WIFIMANAGER_VERSION_MIN             1011000
  
  // Use from 0 to 4. Higher number, more debugging messages and memory usage.
  #define _ESPASYNC_WIFIMGR_LOGLEVEL_    1
  
  #include <FS.h>
  
  //Ported to ESP32
  #ifdef ESP32
    #include <esp_wifi.h>
    #include <WiFi.h>
    #include <WiFiClient.h>
  
    // From v1.1.1
    #include <WiFiMulti.h>
    extern WiFiMulti wifiMulti;
  
    // LittleFS has higher priority than SPIFFS
    #if ( ARDUINO_ESP32C3_DEV )
      // Currently, ESP32-C3 only supporting SPIFFS and EEPROM. Will fix to support LittleFS
      #define USE_LITTLEFS          false
      #define USE_SPIFFS            true
    #else
      #define USE_LITTLEFS    true
      #define USE_SPIFFS      false
    #endif
  
    #if USE_LITTLEFS
      // Use LittleFS
      #include "FS.h"
  
      // Check cores/esp32/esp_arduino_version.h and cores/esp32/core_version.h
      //#if ( ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(2, 0, 0) )  //(ESP_ARDUINO_VERSION_MAJOR >= 2)
      #if ( defined(ESP_ARDUINO_VERSION_MAJOR) && (ESP_ARDUINO_VERSION_MAJOR >= 2) )
        #warning Using ESP32 Core 1.0.6 or 2.0.0+
        // The library has been merged into esp32 core from release 1.0.6
        #include <LittleFS.h>
        
        extern FS* filesystem;    // =      &LittleFS;
        #define FileFS        LittleFS
        #define FS_Name       "LittleFS"
      #else
        #warning Using ESP32 Core 1.0.5-. You must install LITTLEFS library
        // The library has been merged into esp32 core from release 1.0.6
        #include <LITTLEFS.h>             // https://github.com/lorol/LITTLEFS
        
        exern FS* filesystem;   // =      &LITTLEFS;
        #define FileFS        LITTLEFS
        #define FS_Name       "LittleFS"
      #endif
      
    #elif USE_SPIFFS
      #include <SPIFFS.h>
      extern FS* filesystem;    // =      &SPIFFS;
      #define FileFS        SPIFFS
      #define FS_Name       "SPIFFS"
    #else
      // +Use FFat
      #include <FFat.h>
      extern FS* filesystem;    // =      &FFat;
      #define FileFS        FFat
      #define FS_Name       "FFat"
    #endif
    //////
  
    #define ESP_getChipId()   ((uint32_t)ESP.getEfuseMac())
  
    #define LED_BUILTIN       2
    #define LED_ON            HIGH
    #define LED_OFF           LOW
  
  #else
  
    #include <ESP8266WiFi.h>          //https://github.com/esp8266/Arduino
    //needed for library
    #include <DNSServer.h>
  
    // From v1.1.1
    #include <ESP8266WiFiMulti.h>
    extern ESP8266WiFiMulti wifiMulti;
  
    #define USE_LITTLEFS      true
    
    #if USE_LITTLEFS
      #include <LittleFS.h>
      extern FS* filesystem;    // =      &LittleFS;
      #define FileFS        LittleFS
      #define FS_Name       "LittleFS"
    #else
      extern FS* filesystem;    // =      &SPIFFS;
      #define FileFS        SPIFFS
      #define FS_Name       "SPIFFS"
    #endif
    //////
    
    #define ESP_getChipId()   (ESP.getChipId())
    
    #define LED_ON      LOW
    #define LED_OFF     HIGH
  #endif
  
  // These defines must be put before #include <ESP_DoubleResetDetector.h>
  // to select where to store DoubleResetDetector's variable.
  // For ESP32, You must select one to be true (EEPROM or SPIFFS)
  // For ESP8266, You must select one to be true (RTC, EEPROM, SPIFFS or LITTLEFS)
  // Otherwise, library will use default EEPROM storage
  #ifdef ESP32
  
    // These defines must be put before #include <ESP_DoubleResetDetector.h>
    // to select where to store DoubleResetDetector's variable.
    // For ESP32, You must select one to be true (EEPROM or SPIFFS)
    // Otherwise, library will use default EEPROM storage
    #if USE_LITTLEFS
      #define ESP_DRD_USE_LITTLEFS    true
      #define ESP_DRD_USE_SPIFFS      false
      #define ESP_DRD_USE_EEPROM      false
    #elif USE_SPIFFS
      #define ESP_DRD_USE_LITTLEFS    false
      #define ESP_DRD_USE_SPIFFS      true
      #define ESP_DRD_USE_EEPROM      false
    #else
      #define ESP_DRD_USE_LITTLEFS    false
      #define ESP_DRD_USE_SPIFFS      false
      #define ESP_DRD_USE_EEPROM      true
    #endif
  
  #else //ESP8266
  
    // For DRD
    // These defines must be put before #include <ESP_DoubleResetDetector.h>
    // to select where to store DoubleResetDetector's variable.
    // For ESP8266, You must select one to be true (RTC, EEPROM, SPIFFS or LITTLEFS)
    // Otherwise, library will use default EEPROM storage
    #if USE_LITTLEFS
      #define ESP_DRD_USE_LITTLEFS    true
      #define ESP_DRD_USE_SPIFFS      false
    #else
      #define ESP_DRD_USE_LITTLEFS    false
      #define ESP_DRD_USE_SPIFFS      true
    #endif
    
    #define ESP_DRD_USE_EEPROM      false
    #define ESP8266_DRD_USE_RTC     false
  #endif
  
  #define DOUBLERESETDETECTOR_DEBUG       true  //false
  
  #include <ESP_DoubleResetDetector.h>      //https://github.com/khoih-prog/ESP_DoubleResetDetector
  
  // Number of seconds after reset during which a
  // subseqent reset will be considered a double reset.
  #define DRD_TIMEOUT 10
  
  // RTC Memory Address for the DoubleResetDetector to use
  #define DRD_ADDRESS 0
  
  //DoubleResetDetector drd(DRD_TIMEOUT, DRD_ADDRESS);
  extern DoubleResetDetector* drd;//////
  
  // Onboard LED I/O pin on NodeMCU board
  extern const int PIN_LED;   // = 2; // D4 on NodeMCU and WeMos. GPIO2/ADC12 of ESP32. Controls the onboard LED.
  
  // SSID and PW for Config Portal
  //String ssid = "ESP_" + String(ESP_getChipId(), HEX);
  extern String ssid;
  extern String password;
  //extern const char* password;    // = "your_password";
  
  // SSID and PW for your Router
  extern String Router_SSID;
  extern String Router_Pass;
  
  // From v1.1.1
  // You only need to format the filesystem once
  //#define FORMAT_FILESYSTEM       true
  #define FORMAT_FILESYSTEM         false
  
  #define MIN_AP_PASSWORD_SIZE    8
  
  #define SSID_MAX_LEN            32
  //From v1.0.10, WPA2 passwords can be up to 63 characters long.
  #define PASS_MAX_LEN            64
  
  typedef struct
  {
    char wifi_ssid[SSID_MAX_LEN];
    char wifi_pw  [PASS_MAX_LEN];
  }  WiFi_Credentials;
  
  typedef struct
  {
    String wifi_ssid;
    String wifi_pw;
  }  WiFi_Credentials_String;
  
  #define NUM_WIFI_CREDENTIALS      2
  
  // Assuming max 49 chars
  #define TZNAME_MAX_LEN            50
  #define TIMEZONE_MAX_LEN          50
  
  typedef struct
  {
    WiFi_Credentials  WiFi_Creds [NUM_WIFI_CREDENTIALS];
    char TZ_Name[TZNAME_MAX_LEN];     // "America/Toronto"
    char TZ[TIMEZONE_MAX_LEN];        // "EST5EDT,M3.2.0,M11.1.0"
    uint16_t checksum;
  } WM_Config;
  
  extern WM_Config WM_config;
  
  #define  CONFIG_FILENAME              F("/wifi_cred.dat")
  //////
  
  // Indicates whether ESP has WiFi credentials saved from previous session, or double reset detected
  extern bool initialConfig;    // = false;
  
  // 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 <ESPAsync_WiFiManager.h>
  #define USE_AVAILABLE_PAGES     true  //false
  
  // From v1.0.10 to permit disable/enable StaticIP configuration in Config Portal from sketch. Valid only if DHCP is used.
  // You'll loose the feature of dynamically changing from DHCP to static IP, or vice versa
  // You have to explicitly specify false to disable the feature.
  //#define USE_STATIC_IP_CONFIG_IN_CP          false
  
  // Use false to disable NTP config. Advisable when using Cellphone, Tablet to access Config Portal.
  // See Issue 23: On Android phone ConfigPortal is unresponsive (https://github.com/khoih-prog/ESP_WiFiManager/issues/23)
  #define USE_ESP_WIFIMANAGER_NTP     false
  
  // Just use enough to save memory. On ESP8266, can cause blank ConfigPortal screen
  // if using too much memory
  #define USING_AFRICA        false
  #define USING_AMERICA       true
  #define USING_ANTARCTICA    false
  #define USING_ASIA          false
  #define USING_ATLANTIC      false
  #define USING_AUSTRALIA     false
  #define USING_EUROPE        false
  #define USING_INDIAN        false
  #define USING_PACIFIC       false
  #define USING_ETC_GMT       false
  
  // Use true to enable CloudFlare NTP service. System can hang if you don't have Internet access while accessing CloudFlare
  // See Issue #21: CloudFlare link in the default portal (https://github.com/khoih-prog/ESP_WiFiManager/issues/21)
  #define USE_CLOUDFLARE_NTP          false
  
  // New in v1.0.11
  #define USING_CORS_FEATURE          true
  //////
  
  // Use USE_DHCP_IP == true for dynamic DHCP IP, false to use static IP which you have to change accordingly to your network
  #if (defined(USE_STATIC_IP_CONFIG_IN_CP) && !USE_STATIC_IP_CONFIG_IN_CP)
    // Force DHCP to be true
    #if defined(USE_DHCP_IP)
      #undef USE_DHCP_IP
    #endif
    #define USE_DHCP_IP     true
  #else
    // You can select DHCP or Static IP here
    //#define USE_DHCP_IP     true
    #define USE_DHCP_IP     false
  #endif
  
  #if ( USE_DHCP_IP )
    // Use DHCP
    #warning Using DHCP IP
    extern IPAddress stationIP;   //   = IPAddress(0, 0, 0, 0);
    extern IPAddress gatewayIP;   //   = IPAddress(192, 168, 1, 1);
    extern IPAddress netMask;     //     = IPAddress(255, 255, 255, 0);
  #else
    // Use static IP
    #warning Using static IP
    
    #ifdef ESP32
      extern IPAddress stationIP;   //   = IPAddress(192, 168, 2, 232);
    #else
      extern IPAddress stationIP;   //   = IPAddress(192, 168, 2, 186);
    #endif
    
    extern IPAddress gatewayIP;   //   = IPAddress(192, 168, 2, 1);
    extern IPAddress netMask;     //     = IPAddress(255, 255, 255, 0);
  #endif  
  
  #define USE_CONFIGURABLE_DNS      true
  
  extern IPAddress dns1IP;    //      = gatewayIP;
  extern IPAddress dns2IP;    //      = IPAddress(8, 8, 8, 8);
  
  #define USE_CUSTOM_AP_IP          false
  
  extern IPAddress APStaticIP;    //  = IPAddress(192, 168, 100, 1);
  extern IPAddress APStaticGW;    //  = IPAddress(192, 168, 100, 1);
  extern IPAddress APStaticSN;    //  = IPAddress(255, 255, 255, 0);
  
  #include <ESPAsync_WiFiManager.hpp>               //https://github.com/khoih-prog/ESPAsync_WiFiManager
  
  #define HTTP_PORT     80
  
  extern WiFi_AP_IPConfig  WM_AP_IPconfig;
  extern WiFi_STA_IPConfig WM_STA_IPconfig;
  
  void initAPIPConfigStruct(WiFi_AP_IPConfig &in_WM_AP_IPconfig);
  void initSTAIPConfigStruct(WiFi_STA_IPConfig &in_WM_STA_IPconfig);
  void displayIPConfigStruct(WiFi_STA_IPConfig in_WM_STA_IPconfig);
  void configWiFi(WiFi_STA_IPConfig in_WM_STA_IPconfig);
  
  uint8_t connectMultiWiFi();
  
  
  #if USE_ESP_WIFIMANAGER_NTP
  void printLocalTime();
  #endif
  
  void heartBeatPrint();
  
  void check_WiFi();
  void check_status();
  int calcChecksum(uint8_t* address, uint16_t sizeToCalc);
  bool loadConfigData();
  void saveConfigData();
  