Skip to content
This repository has been archived by the owner on Feb 4, 2023. It is now read-only.

Latest commit

 

History

History
2072 lines (1532 loc) · 73.1 KB

README.md

File metadata and controls

2072 lines (1532 loc) · 73.1 KB

AsyncESP8266_W5500_Manager

arduino-library-badge GitHub release GitHub contributions welcome GitHub issues

Donate to my libraries using BuyMeACoffee



Table of Contents



Why do we need this Async AsyncESP8266_W5500_Manager library

Features

This is an ESP8266 + LwIP W5500 Credentials and Connection Manager with fallback Web ConfigPortal. This Library is used for configuring ESP8266 Ethernet Static / DHCP and Credentials at runtime. You can specify static DNS servers, personalized HostName and CORS feature.

This library is based on, modified, bug-fixed and improved from:

  1. Khoi Hoang's ESPAsync_WiFiManager

to use the better and faster asynchronous ESPAsyncWebServer

Thanks to this AsyncESP8266_W5500_Manager library is based on and sync'ed with ESPAsync_WiFiManager, all the features currently supported by ESPAsync_WiFiManager will be available. Please have a look at ESPAsync_WiFiManager for those too-many-to-list features.

Why Async is better

  • Using asynchronous network means that you can handle more than one connection at the same time
  • You are called once the request is ready and parsed
  • When you send the response, you are immediately ready to handle other connections while the server is taking care of sending the response in the background
  • Speed is OMG
  • Easy to use API, HTTP Basic and Digest MD5 Authentication (default), ChunkedResponse
  • Easily extensible to handle any type of content
  • Supports Continue 100
  • Async WebSocket plugin offering different locations without extra servers or ports
  • Async EventSource (Server-Sent Events) plugin to send events to the browser
  • URL Rewrite plugin for conditional and permanent url rewrites
  • ServeStatic plugin that supports cache, Last-Modified, default index and more
  • Simple template processing engine to handle templates

To appreciate the power of the ESPAsyncWebServer and underlying Async libraries, please compare the more efficient Async_ESP_FSWebServer example example with the complicated twin ESP32_FSWebServer.

Currently supported Boards

This AsyncESP8266_W5500_Manager library currently supports these following boards:

  1. ESP8266_DEV boards using LwIP W5500 Ethernet



Prerequisites

  1. Arduino IDE 1.8.19+ for Arduino. GitHub release
  2. ESP8266 Core 3.0.2+ for ESP8266-based boards. Latest release
  3. ESPAsyncWebServer v1.2.3+. You have to use the latest forked ESPAsyncWebServer if the PR Fix compiler error for ESP32-C3 and mbed TLS v2.7.0+ #970 hasn't been merged. To install manually for Arduino IDE
  4. ESPAsyncTCP v1.2.2+ for ESP8266-based boards. To install manually for Arduino IDE
  5. ESP_DoubleResetDetector v1.3.2+ if using DRD feature. To install, check arduino-library-badge


Installation

Use Arduino Library Manager

The best and easiest way is to use Arduino Library Manager. Search for AsyncESP8266_W5500_Manager, then select / install the latest version. You can also use this link arduino-library-badge for more detailed instructions.

Manual Install

  1. Navigate to AsyncESP8266_W5500_Manager page.
  2. Download the latest release AsyncESP8266_W5500_Manager-main.zip.
  3. Extract the zip file to AsyncESP8266_W5500_Manager-main directory
  4. Copy the whole AsyncESP8266_W5500_Manager-main folder to Arduino libraries' directory such as ~/Arduino/libraries/.

VS Code & PlatformIO:

  1. Install VS Code
  2. Install PlatformIO
  3. Install AsyncESP8266_W5500_Manager library by using Library Manager. Search for AsyncESP8266_W5500_Manager in Platform.io Author's Libraries
  4. Use included platformio.ini file from examples to ensure that all dependent libraries will installed automatically. Please visit documentation for the other options and examples at Project Configuration File


HOWTO Fix Multiple Definitions Linker Error

The current library implementation, using xyz-Impl.h instead of standard xyz.cpp, possibly creates certain Multiple Definitions Linker error in certain use cases.

You can use

#include <AsyncESP8266_W5500_Manager.hpp>        //https://github.com/khoih-prog/AsyncESP8266_W5500_Manager

in many files. But be sure to use the following #include <AsyncESP8266_W5500_Manager.h> in just 1 .h, .cpp or .ino file, which must not be included in any other file, to avoid Multiple Definitions Linker Error

// To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error
#include <AsyncESP8266_W5500_Manager.h>          //https://github.com/khoih-prog/AsyncESP8266_W5500_Manager

Have a look at the discussion in Different behaviour using the src_cpp or src_h lib #80



How It Works

  • The Async_ConfigOnSwitch example shows how it works and should be used as the basis for a sketch that uses this library.
  • The concept of Async_ConfigOnSwitch is that a new ESP8266 will start a ConfigPortal when powered up and save the configuration data in non volatile memory. Thereafter, the ConfigPortal will only be started again if a button is pushed on the ESP8266 module.
  • Using any network-enabled device with a browser (computer, phone, tablet) connect to the newly created Access Point (AP) (Dynamic or Static IP specified in sketch)

then connect WebBrowser to configurable ConfigPortal IP address, e.g. 192.168.186



HOWTO Basic configurations

1. Using default for every configurable parameter

  • Include in your sketch
//////////////////////////////////////////////////////////

#include <FS.h>

#include <ESP8266WiFi.h>          //https://github.com/esp8266/Arduino
//needed for library
#include <ESPAsyncDNSServer.h>

#define USE_LITTLEFS      true

#if USE_LITTLEFS
  #include <LittleFS.h>
  FS* filesystem =      &LittleFS;
  #define FileFS        LittleFS
  #define FS_Name       "LittleFS"
#else
  FS* filesystem =      &SPIFFS;
  #define FileFS        SPIFFS
  #define FS_Name       "SPIFFS"
#endif

//////////////////////////////////////////////////////////

// You only need to format the filesystem once
//#define FORMAT_FILESYSTEM       true
#define FORMAT_FILESYSTEM         false

////////////////////////////////////////////////////

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

#define HTTP_PORT     80

2. Using many configurable parameters

  • Include in your sketch
//////////////////////////////////////////////////////////

#include <FS.h>

#include <ESP8266WiFi.h>          //https://github.com/esp8266/Arduino
//needed for library
#include <ESPAsyncDNSServer.h>

#define USE_LITTLEFS      true

#if USE_LITTLEFS
  #include <LittleFS.h>
  FS* filesystem =      &LittleFS;
  #define FileFS        LittleFS
  #define FS_Name       "LittleFS"
#else
  FS* filesystem =      &SPIFFS;
  #define FileFS        SPIFFS
  #define FS_Name       "SPIFFS"
#endif

//////////////////////////////////////////////////////////

#define ESP_getChipId()   (ESP.getChipId())

#define LED_ON      LOW
#define LED_OFF     HIGH

//////////////////////////////////////////////////////////

// These defines must be put before #include <ESP_DoubleResetDetector.h>
// to select where to store DoubleResetDetector's variable.

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

#define DOUBLERESETDETECTOR_DEBUG       true  //false

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

// Number of seconds after reset during which a
// subsequent 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);
DoubleResetDetector* drd;

//////////////////////////////////////////////////////////

// Onboard LED I/O pin on NodeMCU board
const int PIN_LED = 2; // D4 on NodeMCU and WeMos

// You only need to format the filesystem once
//#define FORMAT_FILESYSTEM       true
#define FORMAT_FILESYSTEM         false

//////////////////////////////////////////////////////////

// Assuming max 49 chars
#define TZNAME_MAX_LEN            50
#define TIMEZONE_MAX_LEN          50

typedef struct
{
  char TZ_Name[TZNAME_MAX_LEN];     // "America/Toronto"
  char TZ[TIMEZONE_MAX_LEN];        // "EST5EDT,M3.2.0,M11.1.0"
  uint16_t checksum;
} EthConfig;

EthConfig         Ethconfig;

#define  CONFIG_FILENAME              F("/eth_cred.dat")

//////////////////////////////////////////////////////////

// Indicates whether ESP has credentials saved from previous session, or double reset detected
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 <AsyncESP8266_W5500_Manager.h>
#define USE_AVAILABLE_PAGES     true  //false

// 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_ETH_MANAGER_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

#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

  #if (_ESPASYNC_ETH_MGR_LOGLEVEL_ > 3)
    #warning Using DHCP IP
  #endif

  IPAddress stationIP   = IPAddress(0, 0, 0, 0);
  IPAddress gatewayIP   = IPAddress(192, 168, 2, 1);
  IPAddress netMask     = IPAddress(255, 255, 255, 0);

#else
  // Use static IP

  #if (_ESPASYNC_ETH_MGR_LOGLEVEL_ > 3)
    #warning Using static IP
  #endif

  IPAddress stationIP   = IPAddress(192, 168, 2, 186);
  IPAddress gatewayIP   = IPAddress(192, 168, 2, 1);
  IPAddress netMask     = IPAddress(255, 255, 255, 0);
#endif

//////////////////////////////////////////////////////////

#define USE_CONFIGURABLE_DNS      true

IPAddress dns1IP      = gatewayIP;
IPAddress dns2IP      = IPAddress(8, 8, 8, 8);

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

#define HTTP_PORT     80

//////////////////////////////////////////////////////////

/******************************************
   // Defined in AsyncESP8266_W5500_Manager.hpp
  typedef struct
  {
    IPAddress _sta_static_ip;
    IPAddress _sta_static_gw;
    IPAddress _sta_static_sn;
    #if USE_CONFIGURABLE_DNS
    IPAddress _sta_static_dns1;
    IPAddress _sta_static_dns2;
    #endif
  }  ETH_STA_IPConfig;
******************************************/

ETH_STA_IPConfig EthSTA_IPconfig;

3. Using STA-mode DHCP, but don't like to change to static IP or display in Config Portal

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

4. Using STA-mode DHCP, but permit to change to static IP and display in Config Portal

// 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 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
#endif

5. Using STA-mode StaticIP, and be able to change to DHCP IP and display in Config Portal

// 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 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     false
#endif

6. Using STA-mode StaticIP and configurable DNS, and be able to change to DHCP IP and display in Config Portal

// 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 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     false
#endif

#define USE_CONFIGURABLE_DNS      true

IPAddress dns1IP      = gatewayIP;
IPAddress dns2IP      = IPAddress(8, 8, 8, 8);

7. Using STA-mode StaticIP and auto DNS, and be able to change to DHCP IP and display in Config Portal

// 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 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     false
#endif

#define USE_CONFIGURABLE_DNS      false

8. Not using NTP to avoid issue with some WebBrowsers, especially in CellPhone or Tablets.

// 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_ETH_MANAGER_NTP     false

9. Using NTP feature with CloudFlare. System can hang until you have Internet access for CloudFlare.

// 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_ETH_MANAGER_NTP     true

// 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          true

10. Using NTP feature without CloudFlare to avoid system hang if no Internet access for CloudFlare.

// 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_ETH_MANAGER_NTP     true

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

11. Setting STA-mode static IP

//AsyncESP8266_W5500_Manager.setSTAStaticIPConfig(stationIP, gatewayIP, netMask, dns1IP, dns2IP);
AsyncESP8266_W5500_Manager.setSTAStaticIPConfig(WM_STA_IPconfig);

12. Using CORS (Cross-Origin Resource Sharing) feature

  1. To use CORS feature with default CORS Header "". Some WebBrowsers won't accept this allowing-all "" CORS Header.
// Default false for using only whenever necessary to avoid security issue
#define USING_CORS_FEATURE     true
  1. To use CORS feature with specific CORS Header "Your Access-Control-Allow-Origin". To be modified according to your specific Allowed-Origin.
// Default false for using only whenever necessary to avoid security issue
#define USING_CORS_FEATURE     true

...

#if USING_CORS_FEATURE
  ESP_wifiManager.setCORSHeader("Your Access-Control-Allow-Origin");
#endif
  1. Not use CORS feature (default)
// Default false for using only whenever necessary to avoid security issue
#define USING_CORS_FEATURE     false

13. How to auto getting _timezoneName

  1. Turn on auto NTP configuration by
// 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_ETH_MANAGER_NTP     true
  1. The _timezoneName, in the format similar to America/New_York, America/Toronto, Europe/London, etc., can be retrieved by using
String tempTZ = AsyncESP8266_W5500_Manager.getTimezoneName();

14. How to get TZ variable to configure Timezone

  1. ESP8266 TZ can be configured, using the similar to EST5EDT,M3.2.0,M11.1.0 (for America/New_York) , as follows:
// EST5EDT,M3.2.0,M11.1.0 (for America/New_York)
// EST5EDT is the name of the time zone
// EST is the abbreviation used when DST is off
// 6 hours is the time difference from GMT
// EDT is the abbreviation used when DST is on
// ,M3 is the third month
// .2 is the second occurrence of the day in the month
// .0 is Sunday
// ,M11 is the eleventh month
// .1 is the first occurrence of the day in the month
// .0 is Sunday

configTime(Ethconfig.TZ, "pool.ntp.org");
  1. To convert from _timezoneName to TZ, use the function getTZ() as follows:
const char * TZ_Result = AsyncESP8266_W5500_Manager.getTZ(_timezoneName);

The conversion depends on the stored TZs, which is using some memory, and can cause issue for ESP8266 in certain cases. Therefore, enable just the region you're interested.

For example, your application is used in America continent, you need just

#define USING_AMERICA       true

Hereafter is the regions' list

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

15. How to use the TZ variable to configure Timezone

configTime(Ethconfig.TZ, "pool.ntp.org");

then to print local time

void printLocalTime()
{
  static time_t now;

  now = time(nullptr);

  if ( now > 1451602800 )
  {
    Serial.print("Local Date/Time: ");
    Serial.print(ctime(&now));
  }
}


HOWTO Open Config Portal

  • When you want to open a config portal, with default DHCP hostname ESP8266-XXXXXX, just add
#include <AsyncESP8266_W5500_Manager.h>              //https://github.com/khoih-prog/AsyncESP8266_W5500_Manager

#define HTTP_PORT           80

AsyncWebServer webServer(HTTP_PORT);
AsyncDNSServer dnsServer;

AsyncESP8266_W5500_Manager AsyncESP8266_W5500_Manager(&webServer, &dnsServer);

If you'd like to have a personalized hostname (RFC952-conformed,- 24 chars max,- only a..z A..Z 0..9 '-' and no '-' as last char)

add

AsyncESP8266_W5500_Manager AsyncESP8266_W5500_Manager(&webServer, &dnsServer, "Personalized-HostName");

then later call

AsyncESP8266_W5500_Manager.startConfigPortal()

While in Config Portal, connect to it using its AP IP, e.g. 192.168.2.186, configure Credentials, then save. The settings will be saved in non volatile memory. It will then reboot and autoconnect.



HOWTO Add Dynamic Parameters

These illustrating steps is based on the example Async_ConfigOnSwitchFS

1. Determine the variables to be configured via Config Portal (CP)

The application will:

  • use DHT sensor (either DHT11 or DHT22) and
  • need to connect to ThingSpeak with unique user's API Key.

The DHT sensor is connected to the ESP boards using SDA/SCL pins which also need to be configurable.

So this is the list of variables to be dynamically configured using CP

1. `thingspeakApiKey`,  type `char array`, max length 17 chars, and just arbitrarily selected default value to be "" or "ThingSpeak-APIKey"
2. `sensorDht22`,       type `bool`, default to be `true` (DHT22)
3. `pinSda`,            type `int`,  default to be `PIN_D2`
4. `pinScl`,            type `int`,  default to be `PIN_D1`

The Label can be any arbitrary string that help you identify the variable, but must be unique in your application

The initial code will be

#define API_KEY_LEN                 17

// Default configuration values
char thingspeakApiKey[API_KEY_LEN]  = "";
bool sensorDht22                    = true;
int pinSda                          = PIN_D2;     // Pin D2 mapped to pin GPIO4 of ESP8266
int pinScl                          = PIN_D1;     // Pin D1 mapped to pin GPIO5 of ESP8266

// Any unique string helping you identify the vars
#define ThingSpeakAPI_Label         "thingspeakApiKey"
#define SensorDht22_Label           "SensorDHT22"
#define PinSDA_Label                "PinSda"
#define PinSCL_Label                "PinScl"

2. Initialize the variables to prepare for Config Portal (CP)

The example Async_ConfigOnSwitchFS will open the CP whenever a SW press is detected in loop(). So the code to add dynamic variables will be there, just after the CP AsyncESP8266_W5500_Manager class initialization to create AsyncESP8266_W5500_Manager object.

void loop()
{
// is configuration portal requested?
  if ((digitalRead(TRIGGER_PIN) == LOW) || (digitalRead(TRIGGER_PIN2) == LOW))
  {
    Serial.println("\nConfiguration portal requested.");
    digitalWrite(LED_BUILTIN, LED_ON); // turn the LED on by making the voltage LOW to tell us we are in configuration mode.

    //Local initialization. Once its business is done, there is no need to keep it around
    AsyncESP8266_W5500_Manager AsyncESP8266_W5500_Manager(&webServer, &dnsServer, "ConfigOnSwitchFS");

    //Check if there is stored WiFi router/password credentials.
    //If not found, device will remain in configuration mode until switched off via webserver.
    Serial.print("Opening configuration portal. ");
    
    ...
    
    // The addition of dynamic vars will be somewhere here
    
}    

The ESPAsync_EMParameter class constructor will be used to initialize each newly-added parameter object.

2.1 Use the following simple constructor for simple variables such as thingspeakApiKey, pinSda and pinScl :

ESPAsync_EMParameter(const char *id, const char *placeholder, const char *defaultValue, int length);

2.2 For example, to create a new ESPAsync_EMParameter object p_thingspeakApiKey for thingspeakApiKey,

The command to use will be

ESPAsync_EMParameter p_thingspeakApiKey(ThingSpeakAPI_Label, "Thingspeak API Key", thingspeakApiKey, API_KEY_LEN);

where

- p_thingspeakApiKey                  : ESPAsync_EMParameter class object reference that stores the new Custom Parameter
- id => ThingSpeakAPI_Label           : var ref to Json associative name and HTML element ID for the new Custom Paramerter you just defined in step 1
- placeholder => "Thingspeak API Key" : HTML input placeholder and/or label element text the user sees in the configuration interface for this Custom Parameter
- defaultValue => thingspeakApiKey    : variable for storing the value of your Custom Parameter in the file system or default value when no data is entered
- length  => API_KEY_LEN              : max allowed length you want for this Custom Parameter to have

For pinSda and pinScl, the command will be similar

// I2C SCL and SDA parameters are integers so we need to convert them to char array but
// no other special considerations
char convertedValue[3];
sprintf(convertedValue, "%d", pinSda);
ESPAsync_EMParameter p_pinSda(PinSDA_Label, "I2C SDA pin", convertedValue, 3);

sprintf(convertedValue, "%d", pinScl);
ESPAsync_EMParameter p_pinScl(PinSCL_Label, "I2C SCL pin", convertedValue, 3);

where

- p_pinSda / p_pinScl                         : ESPAsync_EMParameter class object reference that stores the new Custom Parameter
- id => PinSDA_Label/PinSCL_Label             : var ref to Json associative name and HTML element ID for the new Custom Paramerter you just defined in step 1
- placeholder => "I2C SDA pin"/"I2C SCL pin"  : HTML input placeholder and/or label element text the user sees in the configuration interface for this Custom Parameter
- defaultValue => convertedValue              : variable for storing the value of your Custom Parameter in the file system or default value when no data is entered
- length  => 3                                : max allowed length you want for this Custom Parameter to have

2.3 Use the more complex following constructor for variables such as sensorDht22:

ESPAsync_EMParameter(const char *id, const char *placeholder, const char *defaultValue, int length, const char *custom, int labelPlacement);

2.4 For example, to create a new ESPAsync_EMParameter object p_sensorDht22 for sensorDht22,

The command to use will be

ESPAsync_EMParameter p_sensorDht22(SensorDht22_Label, "DHT-22 Sensor", "T", 2, customhtml, WFM_LABEL_AFTER);

where

- p_sensorDht22                       : ESPAsync_EMParameter class object reference that stores the new Custom Parameter
- id => SensorDht22_Label             : var ref to Json associative name and HTML element ID for the new Custom Paramerter you just defined in step 1
- placeholder => "DHT-22 Sensor"      : HTML input placeholder and/or label element text the user sees in the configuration interface for this Custom Parameter
- defaultValue => "T"                 : variable for storing the value of your Custom Parameter in the file system or default value when no data is entered ("T" means `true`)
- length  => 2                        : max allowed length you want for this Custom Parameter to have
- custom => customhtml                : custom HTML code to add element type, e.g. `checkbox`, and `checked` when `sensorDht22 == true`
- labelPlacement => WFM_LABEL_AFTER   : to place label after

and customhtml Code is:

char customhtml[24] = "type=\"checkbox\"";

if (sensorDht22)
{
  strcat(customhtml, " checked");
}

3. Add the variables to Config Portal (CP)

Adding those ESPAsync_EMParameter objects created in Step 2 using the function addParameter() of object AsyncESP8266_W5500_Manager

3.1 addParameter() function Prototype:

//adds a custom parameter
bool addParameter(ESPAsync_EMParameter *p);

3.2 Code to add variables to CP

Add parameter objects, previously created in Step 2, such as : p_thingspeakApiKey, p_sensorDht22, p_pinSda and p_pinScl

//add all parameters here

AsyncESP8266_W5500_Manager.addParameter(&p_thingspeakApiKey);
AsyncESP8266_W5500_Manager.addParameter(&p_sensorDht22);
AsyncESP8266_W5500_Manager.addParameter(&p_pinSda);
AsyncESP8266_W5500_Manager.addParameter(&p_pinScl);

4. Save the variables configured in Config Portal (CP)

When the CP exits, we have to store the parameters' values that users input via CP to use later.

For ESP8266, LittleFS can be used besides EEPROM or deprecated SPIFFS.

We can write directly to a well-defined structure of our choice, but the current example is using JSON to be portable but much more complicated and not advised for new users.

4.1 Getting variables' data from CP

After users select Save, the CP AsyncESP8266_W5500_Manager object will save the user input data into related ESPAsync_EMParameter objects.

We can now retrieve the data, using getValue() function, for each ESPAsync_EMParameter object. Then we can utilize the data for our purpose, such as thingspeakApiKey to log in, sensorDht22 type to know how to handle the sensor, pinSda and pinSda to know which pins to use to communicate with the DHT sensor.

The code is as follows:

// Getting posted form values and overriding local variables parameters
// Config file is written regardless the connection state
strcpy(thingspeakApiKey, p_thingspeakApiKey.getValue());
sensorDht22 = (strncmp(p_sensorDht22.getValue(), "T", 1) == 0);
pinSda = atoi(p_pinSda.getValue());
pinScl = atoi(p_pinScl.getValue());

We can also save to FS file to use later in next boot.

// Writing JSON config file to flash for next boot
writeConfigFile();

5. Write to FS (SPIFFS, LittleFS, etc.) using JSON format

First, you have to familiarize yourself with ArduinoJson library, its functions, the disruptive differences between ArduinoJson version 5.x.x- and v6.0.0+. The best documentation can be found at The best JSON library for embedded C++.

This documentation will discuss only ArduinoJson v6.x.x+ (ARDUINOJSON_VERSION_MAJOR >= 6)

Then have a look at the code snippet of writeConfigFile() function and the following step-by-step explanations.

bool writeConfigFile()
{
  Serial.println("Saving config file");

#if (ARDUINOJSON_VERSION_MAJOR >= 6)
  DynamicJsonDocument json(1024);
#else
  DynamicJsonBuffer jsonBuffer;
  JsonObject& json = jsonBuffer.createObject();
#endif

  // JSONify local configuration parameters
  json[ThingSpeakAPI_Label] = thingspeakApiKey;
  json[SensorDht22_Label] = sensorDht22;
  json[PinSDA_Label] = pinSda;
  json[PinSCL_Label] = pinScl;

  // Open file for writing
  File f = FileFS.open(CONFIG_FILE, "w");

  if (!f)
  {
    Serial.println("Failed to open config file for writing");
    return false;
  }

#if (ARDUINOJSON_VERSION_MAJOR >= 6)
  serializeJsonPretty(json, Serial);
  // Write data to file and close it
  serializeJson(json, f);
#else
  json.prettyPrintTo(Serial);
  // Write data to file and close it
  json.printTo(f);
#endif

  f.close();

  Serial.println("\nConfig file was successfully saved");
  return true;
}

5.1 Create a DynamicJsonDocument Object

We'll create an object with size 1024 bytes, enough to hold our data:

DynamicJsonDocument json(1024);

5.2 Fill the DynamicJsonDocument Object with data got from Config Portal

Then JSONify all local parameters we've just received from CP and wish to store into FS by using the function prototype:

json[Unique_Label] = Value_For_Unique_Label;

as follows:

// JSONify local configuration parameters
json[ThingSpeakAPI_Label] = thingspeakApiKey;
json[SensorDht22_Label]   = sensorDht22;
json[PinSDA_Label]        = pinSda;
json[PinSCL_Label]        = pinScl;

5.3 Open file to write the Jsonified data

This is the CONFIG_FILE file name we already declared at the beginning of the sketch (for ESP8266):

#include <SPIFFS.h>
FS* filesystem =      &SPIFFS;
#define FileFS        SPIFFS
    
const char* JSON_CONFIG_FILE = "/ConfigSW.json";

Now just open the file for writing, and abort if open-for-writing error:

// Open file for writing
File f = FileFS.open(CONFIG_FILE, "w");

if (!f)
{
  Serial.println("Failed to open config file for writing");
  return false;
}

5.4 Write the Jsonified data to CONFIG_FILE

As simple as this single command to write the whole json object we declared then filled with data in steps 5.1 and 5.2

// Write data to file and close it
serializeJson(json, f);

5.5 Close CONFIG_FILE to flush and save the data

Soooo simple !!! Now everybody can do it.

f.close();

But HOWTO use the saved data in the next startup ???? That's in next step 6.

6. Read from FS using JSON format

Now, you have familiarized yourself with ArduinoJson library, its functions. We'll discuss HOWTO read data from the CONFIG_FILE in Jsonified format, then HOWTO parse the to use.

The documentation will discuss only ArduinoJson v6.x.x+ (ARDUINOJSON_VERSION_MAJOR >= 6)

First, have a look at the code snippet of readConfigFile() function.

bool readConfigFile()
{
  // this opens the config file in read-mode
  File f = FileFS.open(CONFIG_FILE, "r");

  if (!f)
  {
    Serial.println("Configuration file not found");
    return false;
  }
  else
  {
    // we could open the file
    size_t size = f.size();
    // Allocate a buffer to store contents of the file.
    std::unique_ptr<char[]> buf(new char[size + 1]);

    // Read and store file contents in buf
    f.readBytes(buf.get(), size);
    // Closing file
    f.close();
    // Using dynamic JSON buffer which is not the recommended memory model, but anyway
    // See https://github.com/bblanchon/ArduinoJson/wiki/Memory%20model

#if (ARDUINOJSON_VERSION_MAJOR >= 6)
    DynamicJsonDocument json(1024);
    auto deserializeError = deserializeJson(json, buf.get());
    if ( deserializeError )
    {
      Serial.println("JSON parseObject() failed");
      return false;
    }
    serializeJson(json, Serial);
#else
    DynamicJsonBuffer jsonBuffer;
    // Parse JSON string
    JsonObject& json = jsonBuffer.parseObject(buf.get());
    // Test if parsing succeeds.
    if (!json.success())
    {
      Serial.println("JSON parseObject() failed");
      return false;
    }
    json.printTo(Serial);
#endif

    // Parse all config file parameters, override
    // local config variables with parsed values
    if (json.containsKey(ThingSpeakAPI_Label))
    {
      strcpy(thingspeakApiKey, json[ThingSpeakAPI_Label]);
    }

    if (json.containsKey(SensorDht22_Label))
    {
      sensorDht22 = json[SensorDht22_Label];
    }

    if (json.containsKey(PinSDA_Label))
    {
      pinSda = json[PinSDA_Label];
    }

    if (json.containsKey(PinSCL_Label))
    {
      pinScl = json[PinSCL_Label];
    }
  }
  Serial.println("\nConfig file was successfully parsed");
  return true;
}

and the following step-by-step explanations.

6.1 Open CONFIG_FILE to read

As simple as this

// this opens the config file in read-mode
File f = FileFS.open(CONFIG_FILE, "r");

We'll inform and abort if the CONFIG_FILE can't be opened (file not found, can't be opened, etc.)

if (!f)
{
  Serial.println("Configuration file not found");
  return false;
}

6.2 Open CONFIG_FILE to read

Now we have to determine the file size to create a buffer large enough to store the to-be-read data

// we could open the file
size_t size = f.size();
// Allocate a buffer to store contents of the file.
std::unique_ptr<char[]> buf(new char[size + 1]);

Remember always add 1 to the buffer length to store the terminating 0.

Then just read the file into the buffer, and close the file to be safe

// Read and store file contents in buf
f.readBytes(buf.get(), size);
// Closing file
f.close();

6.3 Populate the just-read Jsonified data into the DynamicJsonDocument json object

We again use the same DynamicJsonDocument json object to store the data we've just read from CONFIG_FILE.

Why the same complicated DynamicJsonDocument json object ?? Because in steps 5, we did store Jsonified data using the same DynamicJsonDocument json object. It's much easier we now use it again to facilitate the parsing of Jsonified data back to the data we can use easily.

We first create the object with enough size

DynamicJsonDocument json(1024);

then populate it with data from buffer we read from CONFIG_FILE in step 6.2, pre-parse and check for error. All is done just by one command deserializeJson()

auto deserializeError = deserializeJson(json, buf.get());

Abort if there is any data error in the process of writing, storing, reading back. If OK, just nicely print out to the Debug Terminal

if ( deserializeError )
{
  Serial.println("JSON parseObject() failed");
  return false;
}

serializeJson(json, Serial);

6.4 Parse the Jsonified data from the DynamicJsonDocument json object to store into corresponding parameters

This is as simple as in the step 5.2, but in reverse direction.

To be sure there is good corresponding data, not garbage, for each variable, we have to perform sanity checks by verifying the DynamicJsonDocument json object still contains the correct keys we passed to it when we wrote into CONFIG_FILE.

For example:

if (json.containsKey(ThingSpeakAPI_Label))

Then proceed to get every parameter we know we stored there from last CP Save.

// Parse all config file parameters, override
// local config variables with parsed values
if (json.containsKey(ThingSpeakAPI_Label))
{
  strcpy(thingspeakApiKey, json[ThingSpeakAPI_Label]);
}

if (json.containsKey(SensorDht22_Label))
{
  sensorDht22 = json[SensorDht22_Label];
}

if (json.containsKey(PinSDA_Label))
{
  pinSda = json[PinSDA_Label];
}

if (json.containsKey(PinSCL_Label))
{
  pinScl = json[PinSCL_Label];
}

6.5 Then what to do now

Just use those parameters for whatever purpose you designed them for in step 1:

The application will use DHT sensor (either DHT11 or DHT22) and need to connect to ThingSpeak with unique user's API Key. The DHT sensor is connected to the ESP boards using SDA/SCL pins which also need to be configurable.


So, how it works?

In ConfigPortal Mode, it starts an access point @ the current Static or DHCP IP.

Connect to it by going to, e.g. http://192.168.2.186, you'll see this Main page:

Select Information to enter the Info page where the board info will be shown (long page)

Select Configuration to enter this page where you can modify its Credentials

Enter your credentials, then click Save. The Credentials will be saved and the board continues, or reboots if necessary, to connect to the selected Static IP or DHCP.

If you're already satisfied with the current settings and don't want to change anything, just select Exit Portal from the Main page to continue or reboot the board and connect using the previously-stored Credentials.



Documentation

Password protect the configuration Access Point

You can password protect the ConfigPortal AP. Check Async_ESP_FSWebServer example


Callbacks

Save settings

This gets called when custom parameters have been set AND a connection has been established. Use it to set a flag, so when all the configuration finishes, you can save the extra parameters somewhere.

See Async_ConfigOnSwitchFS Example.

AsyncESP8266_W5500_Manager.setSaveConfigCallback(saveConfigCallback);

saveConfigCallback declaration and example

//flag for saving data
bool shouldSaveConfig = false;

//callback notifying us of the need to save config
void saveConfigCallback () 
{
  Serial.println("Should save config");
  shouldSaveConfig = true;
}

ConfigPortal Timeout

If you need to set a timeout so the ESP8266 doesn't hang waiting to be configured for ever.

AsyncESP8266_W5500_Manager.setConfigPortalTimeout(120);

which will wait 2 minutes (120 seconds). When the time passes, the startConfigPortal() function will return and continue the sketch, unless you're accessing the Config Portal. In this case, the startConfigPortal() function will stay until you save config data or exit the Config Portal.


On Demand ConfigPortal

Example usage

void loop()
{
  // is configuration portal requested?
  if ((digitalRead(TRIGGER_PIN) == LOW) || (digitalRead(TRIGGER_PIN2) == LOW))
  {
    Serial.println(F("\nConfiguration portal requested."));
    digitalWrite(LED_BUILTIN, LED_ON); // turn the LED on by making the voltage LOW to tell us we are in configuration mode.

    //Local initialization. Once its business is done, there is no need to keep it around
    // Use this to default DHCP hostname to ESP8266-XXXXXX
    //AsyncESP8266_W5500_Manager AsyncESP8266_W5500_manager(&webServer, &dnsServer);
    // Use this to personalize DHCP hostname (RFC952 conformed)
    AsyncWebServer webServer(HTTP_PORT);

    AsyncDNSServer dnsServer;

    AsyncESP8266_W5500_Manager AsyncESP8266_W5500_manager(&webServer, &dnsServer, "ConfigOnSwitch");

#if !USE_DHCP_IP
#if USE_CONFIGURABLE_DNS
    // Set static IP, Gateway, Subnetmask, DNS1 and DNS2
    AsyncESP8266_W5500_manager.setSTAStaticIPConfig(stationIP, gatewayIP, netMask, dns1IP, dns2IP);
#else
    // Set static IP, Gateway, Subnetmask, Use auto DNS1 and DNS2.
    AsyncESP8266_W5500_manager.setSTAStaticIPConfig(stationIP, gatewayIP, netMask);
#endif
#endif

#if USING_CORS_FEATURE
    AsyncESP8266_W5500_manager.setCORSHeader("Your Access-Control-Allow-Origin");
#endif

    //Check if there is stored credentials.
    //If not found, device will remain in configuration mode until switched off via webserver.
    Serial.println(F("Opening configuration portal. "));

    if (loadConfigData())
    {
      AsyncESP8266_W5500_manager.setConfigPortalTimeout(
        120); //If no access point name has been previously entered disable timeout.
      Serial.println(F("Got stored Credentials. Timeout 120s for Config Portal"));
    }
    else
    {
      // Enter CP only if no stored SSID on flash and file
      AsyncESP8266_W5500_manager.setConfigPortalTimeout(0);
      Serial.println(F("Open Config Portal without Timeout: No stored Credentials."));
      initialConfig = true;
    }

    //Starts an access point
    //and goes into a blocking loop awaiting configuration
    if (!AsyncESP8266_W5500_manager.startConfigPortal())
      Serial.println(F("Not connected to ETH network but continuing anyway."));
    else
    {
      Serial.println(F("ETH network connected...yeey :)"));
      Serial.print(F("Local IP: "));
      Serial.println(eth.localIP());
    }

#if USE_ESP_ETH_MANAGER_NTP
    String tempTZ = AsyncESP8266_W5500_manager.getTimezoneName();

    if (strlen(tempTZ.c_str()) < sizeof(Ethconfig.TZ_Name) - 1)
      strcpy(Ethconfig.TZ_Name, tempTZ.c_str());
    else
      strncpy(Ethconfig.TZ_Name, tempTZ.c_str(), sizeof(Ethconfig.TZ_Name) - 1);

    const char * TZ_Result = AsyncESP8266_W5500_manager.getTZ(Ethconfig.TZ_Name);

    if (strlen(TZ_Result) < sizeof(Ethconfig.TZ) - 1)
      strcpy(Ethconfig.TZ, TZ_Result);
    else
      strncpy(Ethconfig.TZ, TZ_Result, sizeof(Ethconfig.TZ_Name) - 1);

    if ( strlen(Ethconfig.TZ_Name) > 0 )
    {
      LOGERROR3(F("Saving current TZ_Name ="), Ethconfig.TZ_Name, F(", TZ = "), Ethconfig.TZ);

      configTime(Ethconfig.TZ, "pool.ntp.org");
    }
    else
    {
      LOGERROR(F("Current Timezone Name is not set. Enter Config Portal to set."));
    }

#endif

    AsyncESP8266_W5500_manager.getSTAStaticIPConfig(EthSTA_IPconfig);

    saveConfigData();

#if !USE_DHCP_IP

    // Reset to use new Static IP, if different from current eth.localIP()
    if (eth.localIP() != EthSTA_IPconfig._sta_static_ip)
    {
      Serial.print(F("Current IP = "));
      Serial.print(eth.localIP());
      Serial.print(F(". Reset to take new IP = "));
      Serial.println(EthSTA_IPconfig._sta_static_ip);

      ESP.reset();
      delay(2000);
    }

#endif

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

  // put your main code here, to run repeatedly
  check_status();
}

See Async_ConfigOnSwitch example for a more complex version.



Custom Parameters

Many applications need configuration parameters like MQTT host and port, Blynk or emoncms tokens, etc. While it is possible to use AsyncESP8266_W5500_Manager to collect additional parameters, it is better to read these parameters from a web service once AsyncESP8266_W5500_Manager has been used to connect to the Internet.

To capture other parameters with AsyncESP8266_W5500_Manager is a little bit more complicated than all the other features. This requires adding custom HTML to your form.

If you want to do it with AsyncESP8266_W5500_Manager see the example Async_ConfigOnSwitchFS


Custom IP Configuration

You can set a custom IP for both AP (access point, config mode) and STA (station mode, client mode, normal project state)

Custom Station (client) Static IP Configuration

This will use the specified IP configuration instead of using DHCP in station mode.

AsyncESP8266_W5500_Manager.setSTAStaticIPConfig(IPAddress(192,168,2,186), IPAddress(192,168,2,1), IPAddress(255,255,255,0));

Custom HTML, CSS, Javascript

There are various ways in which you can inject custom HTML, CSS or Javascript into the ConfigPortal.

The options are:

  • inject custom head element

You can use this to any html bit to the head of the ConfigPortal. If you add a <style> element, bare in mind it overwrites the included css, not replaces.

AsyncESP8266_W5500_Manager.setCustomHeadElement("<style>html{filter: invert(100%); -webkit-filter: invert(100%);}</style>");
  • inject a custom bit of html in the configuration form
ESPAsync_EMParameter custom_text("<p>This is just a text paragraph</p>");
AsyncESP8266_W5500_Manager.addParameter(&custom_text);
  • inject a custom bit of html in a configuration form element Just add the bit you want added as the last parameter to the custom parameter constructor.
ESPAsync_EMParameter custom_mqtt_server("server", "mqtt server", "iot.eclipse", 40, " readonly");


How to connect W5x00 to ESP8266

You can change the CS/SS pin to another one. Default is GPIO16

Connecting CS/SS to TX0/GPIO15 interferes with uploading firmware to ESP8266. If absolutely necessary to use TX0/GPIO15, remove the wire to TX0/GPIO15 before uploading firmware. Then reconnect after done uploading.

// Using GPIO4, GPIO16, or GPIO5
#define CSPIN             16

W5x00 <---> ESP8266
MOSI <---> GPIO13
MISO <---> GPIO12
SCK <---> GPIO14
SS <---> GPIO16
GND <---> GND
3.3V <---> 3.3V


Examples

  1. Async_ConfigOnSwitch
  2. Async_ConfigOnSwitchFS
  3. Async_ConfigOnDoubleReset_TZ (now support ArduinoJson 6.0.0+ as well as 5.13.5-)
  4. Async_ConfigOnDoubleReset (now support ArduinoJson 6.0.0+ as well as 5.13.5-)
  5. Async_ConfigPortalParamsOnSwitch (now support ArduinoJson 6.0.0+ as well as 5.13.5-)
  6. Async_ESP_FSWebServer
  7. Async_ESP_FSWebServer_DRD


#if !( defined(ESP8266) )
#error This code is intended to run on the (ESP8266 + W5500) platform! Please check your Tools->Board setting.
#endif
//////////////////////////////////////////////////////////////
// Use from 0 to 4. Higher number, more debugging messages and memory usage.
#define _ESPASYNC_ETH_MGR_LOGLEVEL_ 4
// To not display stored SSIDs and PWDs on Config Portal, select false. Default is true
// Even the stored Credentials are not display, just leave them all blank to reconnect and reuse the stored Credentials
//#define DISPLAY_STORED_CREDENTIALS_IN_CP false
//////////////////////////////////////////////////////////////
// Using GPIO4, GPIO16, or GPIO5
#define CSPIN 16
//////////////////////////////////////////////////////////
#include <FS.h>
#include <ESP8266WiFi.h> //https://github.com/esp8266/Arduino
//needed for library
#include <ESPAsyncDNSServer.h>
#define USE_LITTLEFS true
#if USE_LITTLEFS
#include <LittleFS.h>
FS* filesystem = &LittleFS;
#define FileFS LittleFS
#define FS_Name "LittleFS"
#else
FS* filesystem = &SPIFFS;
#define FileFS SPIFFS
#define FS_Name "SPIFFS"
#endif
//////////////////////////////////////////////////////////
#define ESP_getChipId() (ESP.getChipId())
#define LED_ON LOW
#define LED_OFF HIGH
//////////////////////////////////////////////////////////
// Onboard LED I/O pin on NodeMCU board
#define LED_BUILTIN 2 // Pin D4 mapped to pin GPIO2/TXD1 of ESP8266, NodeMCU and WeMoS, control on-board LED
//PIN_D0 can't be used for PWM/I2C
#define PIN_D0 16 // Pin D0 mapped to pin GPIO16/USER/WAKE of ESP8266. This pin is also used for Onboard-Blue LED. PIN_D0 = 0 => LED ON
#define PIN_D1 5 // Pin D1 mapped to pin GPIO5 of ESP8266
#define PIN_D2 4 // Pin D2 mapped to pin GPIO4 of ESP8266
#define PIN_D3 0 // Pin D3 mapped to pin GPIO0/FLASH of ESP8266
#define PIN_D4 2 // Pin D4 mapped to pin GPIO2/TXD1 of ESP8266
#define PIN_D5 14 // Pin D5 mapped to pin GPIO14/HSCLK of ESP8266
#define PIN_D6 12 // Pin D6 mapped to pin GPIO12/HMISO of ESP8266
#define PIN_D7 13 // Pin D7 mapped to pin GPIO13/RXD2/HMOSI of ESP8266
#define PIN_D8 15 // Pin D8 mapped to pin GPIO15/TXD2/HCS of ESP8266
//Don't use pins GPIO6 to GPIO11 as already connected to flash, etc. Use them can crash the program
//GPIO9(D11/SD2) and GPIO11 can be used only if flash in DIO mode ( not the default QIO mode)
#define PIN_D11 9 // Pin D11/SD2 mapped to pin GPIO9/SDD2 of ESP8266
#define PIN_D12 10 // Pin D12/SD3 mapped to pin GPIO10/SDD3 of ESP8266
#define PIN_SD2 9 // Pin SD2 mapped to pin GPIO9/SDD2 of ESP8266
#define PIN_SD3 10 // Pin SD3 mapped to pin GPIO10/SDD3 of ESP8266
#define PIN_D9 3 // Pin D9 /RX mapped to pin GPIO3/RXD0 of ESP8266
#define PIN_D10 1 // Pin D10/TX mapped to pin GPIO1/TXD0 of ESP8266
#define PIN_RX 3 // Pin RX mapped to pin GPIO3/RXD0 of ESP8266
#define PIN_TX 1 // Pin RX mapped to pin GPIO1/TXD0 of ESP8266
#define LED_PIN 16 // Pin D0 mapped to pin GPIO16 of ESP8266. This pin is also used for Onboard-Blue LED. PIN_D0 = 0 => LED ON
/* Trigger for inititating config mode is Pin D1 and also flash button on NodeMCU
Flash button is convenient to use but if it is pressed it will stuff up the serial port device driver
until the computer is rebooted on windows machines.
*/
const int TRIGGER_PIN = PIN_D1; // D1 on NodeMCU and WeMos.
/*
Alternative trigger pin. Needs to be connected to a button to use this pin. It must be a momentary connection
not connected permanently to ground. Either trigger pin will work.
*/
const int TRIGGER_PIN2 = PIN_D2; // D2 on NodeMCU and WeMos.
//////////////////////////////////////////////////////////
// You only need to format the filesystem once
//#define FORMAT_FILESYSTEM true
#define FORMAT_FILESYSTEM false
//////////////////////////////////////////////////////////
// Assuming max 49 chars
#define TZNAME_MAX_LEN 50
#define TIMEZONE_MAX_LEN 50
typedef struct
{
char TZ_Name[TZNAME_MAX_LEN]; // "America/Toronto"
char TZ[TIMEZONE_MAX_LEN]; // "EST5EDT,M3.2.0,M11.1.0"
uint16_t checksum;
} EthConfig;
EthConfig Ethconfig;
#define CONFIG_FILENAME F("/eth_cred.dat")
//////////////////////////////////////////////////////////
// Indicates whether ESP has credentials saved from previous session, or double reset detected
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 <AsyncESP8266_W5500_Manager.h>
#define USE_AVAILABLE_PAGES true //false
// 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_ETH_MANAGER_NTP true //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
#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
#if (_ESPASYNC_ETH_MGR_LOGLEVEL_ > 3)
#warning Using DHCP IP
#endif
IPAddress stationIP = IPAddress(0, 0, 0, 0);
IPAddress gatewayIP = IPAddress(192, 168, 2, 1);
IPAddress netMask = IPAddress(255, 255, 255, 0);
#else
// Use static IP
#if (_ESPASYNC_ETH_MGR_LOGLEVEL_ > 3)
#warning Using static IP
#endif
IPAddress stationIP = IPAddress(192, 168, 2, 186);
IPAddress gatewayIP = IPAddress(192, 168, 2, 1);
IPAddress netMask = IPAddress(255, 255, 255, 0);
#endif
//////////////////////////////////////////////////////////
#define USE_CONFIGURABLE_DNS true
IPAddress dns1IP = gatewayIP;
IPAddress dns2IP = IPAddress(8, 8, 8, 8);
#include <AsyncESP8266_W5500_Manager.h> //https://github.com/khoih-prog/AsyncESP8266_W5500_Manager
#define HTTP_PORT 80
//////////////////////////////////////////////////////////////
/******************************************
// Defined in AsyncESP8266_W5500_Manager.hpp
typedef struct
{
IPAddress _sta_static_ip;
IPAddress _sta_static_gw;
IPAddress _sta_static_sn;
#if USE_CONFIGURABLE_DNS
IPAddress _sta_static_dns1;
IPAddress _sta_static_dns2;
#endif
} ETH_STA_IPConfig;
******************************************/
ETH_STA_IPConfig EthSTA_IPconfig;
//////////////////////////////////////////////////////////////
void initSTAIPConfigStruct(ETH_STA_IPConfig &in_EthSTA_IPconfig)
{
in_EthSTA_IPconfig._sta_static_ip = stationIP;
in_EthSTA_IPconfig._sta_static_gw = gatewayIP;
in_EthSTA_IPconfig._sta_static_sn = netMask;
#if USE_CONFIGURABLE_DNS
in_EthSTA_IPconfig._sta_static_dns1 = dns1IP;
in_EthSTA_IPconfig._sta_static_dns2 = dns2IP;
#endif
}
//////////////////////////////////////////////////////////////
void displayIPConfigStruct(ETH_STA_IPConfig in_EthSTA_IPconfig)
{
LOGERROR3(F("stationIP ="), in_EthSTA_IPconfig._sta_static_ip, ", gatewayIP =", in_EthSTA_IPconfig._sta_static_gw);
LOGERROR1(F("netMask ="), in_EthSTA_IPconfig._sta_static_sn);
#if USE_CONFIGURABLE_DNS
LOGERROR3(F("dns1IP ="), in_EthSTA_IPconfig._sta_static_dns1, ", dns2IP =", in_EthSTA_IPconfig._sta_static_dns2);
#endif
}
//////////////////////////////////////////////////////////////
void toggleLED()
{
//toggle state
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
}
//////////////////////////////////////////////////////////////
#if USE_ESP_ETH_MANAGER_NTP
void printLocalTime()
{
static time_t now;
now = time(nullptr);
if ( now > 1451602800 )
{
Serial.print("Local Date/Time: ");
Serial.print(ctime(&now));
}
}
#endif
void heartBeatPrint()
{
#if USE_ESP_ETH_MANAGER_NTP
printLocalTime();
#else
static int num = 1;
if (eth.connected())
Serial.print(F("H")); // H means connected to Ethernet
else
Serial.print(F("F")); // F means not connected to Ethernet
if (num == 80)
{
Serial.println();
num = 1;
}
else if (num++ % 10 == 0)
{
Serial.print(F(" "));
}
#endif
}
//////////////////////////////////////////////////////////////
void check_status()
{
static ulong checkstatus_timeout = 0;
static ulong LEDstatus_timeout = 0;
static ulong current_millis;
#if USE_ESP_ETH_MANAGER_NTP
#define HEARTBEAT_INTERVAL 60000L
#else
#define HEARTBEAT_INTERVAL 10000L
#endif
#define LED_INTERVAL 2000L
current_millis = millis();
if ((current_millis > LEDstatus_timeout) || (LEDstatus_timeout == 0))
{
// Toggle LED at LED_INTERVAL = 2s
toggleLED();
LEDstatus_timeout = current_millis + LED_INTERVAL;
}
// Print hearbeat every HEARTBEAT_INTERVAL (10) seconds.
if ((current_millis > checkstatus_timeout) || (checkstatus_timeout == 0))
{
heartBeatPrint();
checkstatus_timeout = current_millis + HEARTBEAT_INTERVAL;
}
}
//////////////////////////////////////////////////////////////
int calcChecksum(uint8_t* address, uint16_t sizeToCalc)
{
uint16_t checkSum = 0;
for (uint16_t index = 0; index < sizeToCalc; index++)
{
checkSum += * ( ( (byte*) address ) + index);
}
return checkSum;
}
//////////////////////////////////////////////////////////////
bool loadConfigData()
{
File file = FileFS.open(CONFIG_FILENAME, "r");
LOGERROR(F("LoadCfgFile "));
memset((void *) &Ethconfig, 0, sizeof(Ethconfig));
memset((void *) &EthSTA_IPconfig, 0, sizeof(EthSTA_IPconfig));
if (file)
{
file.readBytes((char *) &Ethconfig, sizeof(Ethconfig));
file.readBytes((char *) &EthSTA_IPconfig, sizeof(EthSTA_IPconfig));
file.close();
LOGERROR(F("OK"));
if ( Ethconfig.checksum != calcChecksum( (uint8_t*) &Ethconfig, sizeof(Ethconfig) - sizeof(Ethconfig.checksum) ) )
{
LOGERROR(F("Ethconfig checksum wrong"));
return false;
}
displayIPConfigStruct(EthSTA_IPconfig);
return true;
}
else
{
LOGERROR(F("failed"));
return false;
}
}
//////////////////////////////////////////////////////////////
void saveConfigData()
{
File file = FileFS.open(CONFIG_FILENAME, "w");
LOGERROR(F("SaveCfgFile "));
if (file)
{
Ethconfig.checksum = calcChecksum( (uint8_t*) &Ethconfig, sizeof(Ethconfig) - sizeof(Ethconfig.checksum) );
file.write((uint8_t*) &Ethconfig, sizeof(Ethconfig));
displayIPConfigStruct(EthSTA_IPconfig);
file.write((uint8_t*) &EthSTA_IPconfig, sizeof(EthSTA_IPconfig));
file.close();
LOGERROR(F("OK"));
}
else
{
LOGERROR(F("failed"));
}
}
//////////////////////////////////////////////////////////////
void initEthernet()
{
SPI.begin();
SPI.setClockDivider(SPI_CLOCK_DIV4);
SPI.setBitOrder(MSBFIRST);
SPI.setDataMode(SPI_MODE0);
LOGWARN(F("Default SPI pinout:"));
LOGWARN1(F("MOSI:"), MOSI);
LOGWARN1(F("MISO:"), MISO);
LOGWARN1(F("SCK:"), SCK);
LOGWARN1(F("CS:"), CSPIN);
LOGWARN(F("========================="));
#if !USING_DHCP
//eth.config(localIP, gateway, netMask, gateway);
eth.config(EthSTA_IPconfig._sta_static_ip, EthSTA_IPconfig._sta_static_gw, EthSTA_IPconfig._sta_static_sn,
EthSTA_IPconfig._sta_static_dns1);
#endif
eth.setDefault();
if (!eth.begin())
{
Serial.println("No Ethernet hardware ... Stop here");
while (true)
{
delay(1000);
}
}
else
{
Serial.print("Connecting to network : ");
while (!eth.connected())
{
Serial.print(".");
delay(1000);
}
}
Serial.println();
#if USING_DHCP
Serial.print("Ethernet DHCP IP address: ");
#else
Serial.print("Ethernet Static IP address: ");
#endif
Serial.println(eth.localIP());
}
//////////////////////////////////////////////////////////////
void setup()
{
//set led pin as output
pinMode(LED_BUILTIN, OUTPUT);
pinMode(TRIGGER_PIN, INPUT_PULLUP);
pinMode(TRIGGER_PIN2, INPUT_PULLUP);
Serial.begin(115200);
while (!Serial && millis() < 5000);
delay(200);
Serial.print(F("\nStarting Async_ConfigOnSwitch using "));
Serial.print(FS_Name);
Serial.print(F(" on "));
Serial.print(ARDUINO_BOARD);
Serial.print(F(" with "));
Serial.println(SHIELD_TYPE);
Serial.println(ASYNC_ESP8266_W5500_MANAGER_VERSION);
Serial.setDebugOutput(false);
#if FORMAT_FILESYSTEM
Serial.println(F("Forced Formatting."));
FileFS.format();
#endif
// Format FileFS if not yet
if (!FileFS.begin())
{
FileFS.format();
Serial.println(F("SPIFFS/LittleFS failed! Already tried formatting."));
if (!FileFS.begin())
{
// prevents debug info from the library to hide err message.
delay(100);
#if USE_LITTLEFS
Serial.println(F("LittleFS failed!. Please use SPIFFS or EEPROM. Stay forever"));
#else
Serial.println(F("SPIFFS failed!. Please use LittleFS or EEPROM. Stay forever"));
#endif
while (true)
{
delay(1);
}
}
}
unsigned long startedAt = millis();
initSTAIPConfigStruct(EthSTA_IPconfig);
digitalWrite(LED_BUILTIN, LED_ON); // turn the LED on by making the voltage LOW to tell us we are in configuration mode.
//Local intialization. Once its business is done, there is no need to keep it around
// Use this to default DHCP hostname to ESP32-XXXXXX
//AsyncESP8266_W5500_Manager AsyncESP8266_W5500_manager(&webServer, &dnsServer);
// Use this to personalize DHCP hostname (RFC952 conformed)
AsyncWebServer webServer(HTTP_PORT);
#if ( USING_ESP32_S2 || USING_ESP32_C3 )
AsyncESP8266_W5500_Manager AsyncESP8266_W5500_manager(&webServer, NULL, "AsyncConfigOnSwitch");
#else
AsyncDNSServer dnsServer;
AsyncESP8266_W5500_Manager AsyncESP8266_W5500_manager(&webServer, &dnsServer, "AsyncConfigOnSwitch");
#endif
AsyncESP8266_W5500_manager.setDebugOutput(true);
#if !USE_DHCP_IP
// Set (static IP, Gateway, Subnetmask, DNS1 and DNS2) or (IP, Gateway, Subnetmask)
AsyncESP8266_W5500_manager.setSTAStaticIPConfig(EthSTA_IPconfig);
#endif
#if USING_CORS_FEATURE
AsyncESP8266_W5500_manager.setCORSHeader("Your Access-Control-Allow-Origin");
#endif
bool configDataLoaded = false;
if (loadConfigData())
{
configDataLoaded = true;
//If no access point name has been previously entered disable timeout
AsyncESP8266_W5500_manager.setConfigPortalTimeout(120);
Serial.println(F("Got stored Credentials. Timeout 120s for Config Portal"));
#if USE_ESP_ETH_MANAGER_NTP
if ( strlen(Ethconfig.TZ_Name) > 0 )
{
LOGERROR3(F("Current TZ_Name ="), Ethconfig.TZ_Name, F(", TZ = "), Ethconfig.TZ);
configTime(Ethconfig.TZ, "pool.ntp.org");
}
else
{
Serial.println(F("Current Timezone is not set. Enter Config Portal to set."));
}
#endif
}
else
{
// Enter CP only if no stored SSID on flash and file
Serial.println(F("Open Config Portal without Timeout: No stored Credentials."));
initialConfig = true;
}
//////////////////////////////////
// Connect ETH now if using STA
initEthernet();
//////////////////////////////////
if (initialConfig)
{
Serial.print(F("Starting configuration portal @ "));
Serial.println(eth.localIP());
digitalWrite(LED_BUILTIN, LED_ON); // Turn led on as we are in configuration mode.
//sets timeout in seconds until configuration portal gets turned off.
//If not specified device will remain in configuration mode until
//switched off via webserver or device is restarted.
//AsyncESP8266_W5500_manager.setConfigPortalTimeout(600);
// Starts an access point
if (!AsyncESP8266_W5500_manager.startConfigPortal())
Serial.println(F("Not connected to ETH network but continuing anyway."));
else
{
Serial.println(F("ETH network connected...yeey :)"));
}
#if USE_ESP_ETH_MANAGER_NTP
String tempTZ = AsyncESP8266_W5500_manager.getTimezoneName();
if (strlen(tempTZ.c_str()) < sizeof(Ethconfig.TZ_Name) - 1)
strcpy(Ethconfig.TZ_Name, tempTZ.c_str());
else
strncpy(Ethconfig.TZ_Name, tempTZ.c_str(), sizeof(Ethconfig.TZ_Name) - 1);
const char * TZ_Result = AsyncESP8266_W5500_manager.getTZ(Ethconfig.TZ_Name);
if (strlen(TZ_Result) < sizeof(Ethconfig.TZ) - 1)
strcpy(Ethconfig.TZ, TZ_Result);
else
strncpy(Ethconfig.TZ, TZ_Result, sizeof(Ethconfig.TZ_Name) - 1);
if ( strlen(Ethconfig.TZ_Name) > 0 )
{
LOGERROR3(F("Saving current TZ_Name ="), Ethconfig.TZ_Name, F(", TZ = "), Ethconfig.TZ);
configTime(Ethconfig.TZ, "pool.ntp.org");
}
else
{
LOGERROR(F("Current Timezone Name is not set. Enter Config Portal to set."));
}
#endif
AsyncESP8266_W5500_manager.getSTAStaticIPConfig(EthSTA_IPconfig);
saveConfigData();
}
digitalWrite(LED_BUILTIN, LED_OFF); // Turn led off as we are not in configuration mode.
startedAt = millis();
Serial.print(F("After waiting "));
Serial.print((float) (millis() - startedAt) / 1000);
Serial.print(F(" secs more in setup(), connection result is "));
if (eth.connected())
{
Serial.print(F("connected. Local IP: "));
Serial.println(eth.localIP());
}
}
//////////////////////////////////////////////////////////////
void loop()
{
// is configuration portal requested?
if ((digitalRead(TRIGGER_PIN) == LOW) || (digitalRead(TRIGGER_PIN2) == LOW))
{
Serial.println(F("\nConfiguration portal requested."));
digitalWrite(LED_BUILTIN, LED_ON); // turn the LED on by making the voltage LOW to tell us we are in configuration mode.
//Local intialization. Once its business is done, there is no need to keep it around
// Use this to default DHCP hostname to ESP32-XXXXXX
//AsyncESP8266_W5500_Manager AsyncESP8266_W5500_manager(&webServer, &dnsServer);
// Use this to personalize DHCP hostname (RFC952 conformed)
AsyncWebServer webServer(HTTP_PORT);
#if ( USING_ESP32_S2 || USING_ESP32_C3 )
AsyncESP8266_W5500_Manager AsyncESP8266_W5500_manager(&webServer, NULL, "ConfigOnSwitch");
#else
AsyncDNSServer dnsServer;
AsyncESP8266_W5500_Manager AsyncESP8266_W5500_manager(&webServer, &dnsServer, "ConfigOnSwitch");
#endif
#if !USE_DHCP_IP
#if USE_CONFIGURABLE_DNS
// Set static IP, Gateway, Subnetmask, DNS1 and DNS2
AsyncESP8266_W5500_manager.setSTAStaticIPConfig(stationIP, gatewayIP, netMask, dns1IP, dns2IP);
#else
// Set static IP, Gateway, Subnetmask, Use auto DNS1 and DNS2.
AsyncESP8266_W5500_manager.setSTAStaticIPConfig(stationIP, gatewayIP, netMask);
#endif
#endif
#if USING_CORS_FEATURE
AsyncESP8266_W5500_manager.setCORSHeader("Your Access-Control-Allow-Origin");
#endif
//Check if there is stored credentials.
//If not found, device will remain in configuration mode until switched off via webserver.
Serial.println(F("Opening configuration portal. "));
if (loadConfigData())
{
AsyncESP8266_W5500_manager.setConfigPortalTimeout(
120); //If no access point name has been previously entered disable timeout.
Serial.println(F("Got stored Credentials. Timeout 120s for Config Portal"));
}
else
{
// Enter CP only if no stored SSID on flash and file
AsyncESP8266_W5500_manager.setConfigPortalTimeout(0);
Serial.println(F("Open Config Portal without Timeout: No stored Credentials."));
initialConfig = true;
}
//Starts an access point
//and goes into a blocking loop awaiting configuration
if (!AsyncESP8266_W5500_manager.startConfigPortal())
Serial.println(F("Not connected to ETH network but continuing anyway."));
else
{
Serial.println(F("ETH network connected...yeey :)"));
Serial.print(F("Local IP: "));
Serial.println(eth.localIP());
}
#if USE_ESP_ETH_MANAGER_NTP
String tempTZ = AsyncESP8266_W5500_manager.getTimezoneName();
if (strlen(tempTZ.c_str()) < sizeof(Ethconfig.TZ_Name) - 1)
strcpy(Ethconfig.TZ_Name, tempTZ.c_str());
else
strncpy(Ethconfig.TZ_Name, tempTZ.c_str(), sizeof(Ethconfig.TZ_Name) - 1);
const char * TZ_Result = AsyncESP8266_W5500_manager.getTZ(Ethconfig.TZ_Name);
if (strlen(TZ_Result) < sizeof(Ethconfig.TZ) - 1)
strcpy(Ethconfig.TZ, TZ_Result);
else
strncpy(Ethconfig.TZ, TZ_Result, sizeof(Ethconfig.TZ_Name) - 1);
if ( strlen(Ethconfig.TZ_Name) > 0 )
{
LOGERROR3(F("Saving current TZ_Name ="), Ethconfig.TZ_Name, F(", TZ = "), Ethconfig.TZ);
configTime(Ethconfig.TZ, "pool.ntp.org");
}
else
{
LOGERROR(F("Current Timezone Name is not set. Enter Config Portal to set."));
}
#endif
AsyncESP8266_W5500_manager.getSTAStaticIPConfig(EthSTA_IPconfig);
saveConfigData();
#if !USE_DHCP_IP
// Reset to use new Static IP, if different from current eth.localIP()
if (eth.localIP() != EthSTA_IPconfig._sta_static_ip)
{
Serial.print(F("Current IP = "));
Serial.print(eth.localIP());
Serial.print(F(". Reset to take new IP = "));
Serial.println(EthSTA_IPconfig._sta_static_ip);
ESP.reset();
delay(2000);
}
#endif
digitalWrite(LED_BUILTIN, LED_OFF); // Turn led off as we are not in configuration mode.
}
// put your main code here, to run repeatedly
check_status();
}



Debug Terminal Output Samples

1. Async_ConfigOnDoubleReset_TZ using LittleFS on ESP8266_NODEMCU_ESP12E with ESP8266_W5500

1.1 DRD => Config Portal

This is terminal debug output when running Async_ConfigOnDoubleReset on ESP8266_W5500. Config Portal was requested by DRD to input and save Credentials, such as Static IP address.

Starting Async_ConfigOnDoubleReset_TZ using LittleFS on ESP8266_NODEMCU_ESP12E with ESP8266_W5500 Ethernet
AsyncESP8266_W5500_Manager v1.0.0
ESP_DoubleResetDetector v1.3.2
[EM] Default SPI pinout:
[EM] MOSI: 13
[EM] MISO: 12
[EM] SCK: 14
[EM] CS: 16
[EM] =========================
Connecting to network : ...
Ethernet Static IP address: 192.168.2.119
[EM] RFC925 Hostname = AsyncConfigOnDoubleReset
[EM] setSTAStaticIPConfig
[EM] Set CORS Header to :  Your Access-Control-Allow-Origin
[EM] LoadCfgFile 
[EM] failed
LittleFS Flag read = 0xD0D01234
doubleResetDetected
Saving config file...
Saving config file OK
Open Config Portal without Timeout: Double Reset Detected
Starting configuration portal @ 192.168.2.119
[EM] _configPortalStart millis() = 4135
[EM] Config Portal IP address = 192.168.2.119
[EM] HTTP server started
[EM] startConfigPortal : Enter loop
[EM] handleRoot
[EM] request host IP = 192.168.2.119
[EM] Info
[EM] Info page sent
[EM] handleRoot
[EM] request host IP = 192.168.2.119
[EM] Handle ETH
[EM] Static IP = 192.168.2.186
[EM] Sent config page
[EM] ETH save
[EM] TZ = America/Toronto
[EM] New Static IP = 192.168.2.186
[EM] New Static Gateway = 192.168.2.1
[EM] New Static Netmask = 255.255.255.0
[EM] New Static DNS1 = 192.168.2.1
[EM] New Static DNS2 = 8.8.8.8
[EM] Sent eth save page
[EM] stopConfigPortal
ETH network connected...yeey :)
[EM] Saving current TZ_Name = America/Toronto , TZ =  EST5EDT,M3.2.0,M11.1.0
[EM] getSTAStaticIPConfig
[EM] SaveCfgFile 
[EM] stationIP = 192.168.2.186 , gatewayIP = 192.168.2.1
[EM] netMask = 255.255.255.0
[EM] dns1IP = 192.168.2.1 , dns2IP = 8.8.8.8
[EM] OK
Current IP = 192.168.2.119. Reset to take new IP = 192.168.2.186
ets Jan  8 2013,rst cause:2, boot mode:(3,6)
1.2. Get new Static IP after reset
Starting Async_ConfigOnDoubleReset_TZ using LittleFS on ESP8266_NODEMCU_ESP12E with ESP8266_W5500 Ethernet
AsyncESP8266_W5500_Manager v1.0.0
ESP_DoubleResetDetector v1.3.2
[EM] RFC925 Hostname = AsyncConfigOnDoubleReset
[EM] setSTAStaticIPConfig
[EM] Set CORS Header to :  Your Access-Control-Allow-Origin
[EM] LoadCfgFile 
[EM] OK
[EM] stationIP = 192.168.2.187 , gatewayIP = 192.168.2.1
[EM] netMask = 255.255.255.0
[EM] dns1IP = 192.168.2.1 , dns2IP = 8.8.8.8
Got stored Credentials. Timeout 120s for Config Portal
[EM] Current TZ_Name = America/Toronto , TZ =  EST5EDT,M3.2.0,M11.1.0
[EM] Default SPI pinout:
[EM] MOSI: 13
[EM] MISO: 12
[EM] SCK: 14
[EM] CS: 16
[EM] =========================
Connecting to network : 
Ethernet Static IP address: 192.168.2.187
LittleFS Flag read = 0xD0D04321
No doubleResetDetected
Saving config file...
Saving config file OK
After waiting 0.00 secs more in setup(), connection result is connected. Local IP: 192.168.2.187
[EM] freeing allocated params!
Stop doubleResetDetecting
Saving config file...
Saving config file OK
Local Date/Time: Fri Dec  9 20:39:37 2022
Local Date/Time: Fri Dec  9 20:40:37 2022
Local Date/Time: Fri Dec  9 20:41:37 2022
Local Date/Time: Fri Dec  9 20:42:37 2022
Local Date/Time: Fri Dec  9 20:43:37 2022
Local Date/Time: Fri Dec  9 20:44:37 2022

2. Async_ConfigOnSwichFS using LittleFS on ESP8266_NODEMCU_ESP12E with ESP8266_W5500

This is terminal debug output when running Async_ConfigOnSwichFS on ESP8266_W5500. Config Portal was requested to input and save Credentials.

Starting Async_ConfigOnSwichFS using LittleFS on ESP8266_NODEMCU_ESP12E with ESP8266_W5500 Ethernet
AsyncESP8266_W5500_Manager v1.0.0
{"thingspeakApiKey":"","SensorDHT22":true,"PinSda":4,"PinScl":5}
Config file was successfully parsed
[EM] RFC925 Hostname = ConfigOnSwitchFS
[EM] setSTAStaticIPConfig
[EM] Set CORS Header to :  Your Access-Control-Allow-Origin
[EM] LoadCfgFile 
[EM] OK
[EM] stationIP = 192.168.2.232 , gatewayIP = 192.168.2.1
[EM] netMask = 255.255.255.0
[EM] dns1IP = 192.168.2.1 , dns2IP = 8.8.8.8
Got stored Credentials. Timeout 120s for Config Portal
Current Timezone is not set. Enter Config Portal to set.
[EM] Default SPI pinout:
[EM] MOSI: 13
[EM] MISO: 12
[EM] SCK: 14
[EM] CS: 16
[EM] =========================
Connecting to network : 
Ethernet Static IP address: 192.168.2.232
After waiting 0.00 secs more in setup(), connection result is connected. Local IP: 192.168.2.232
[EM] freeing allocated params!

Configuration portal requested.
[EM] RFC925 Hostname = ConfigOnSwitchFS
Opening configuration portal. 
[EM] LoadCfgFile 
[EM] OK
[EM] stationIP = 192.168.2.232 , gatewayIP = 192.168.2.1
[EM] netMask = 255.255.255.0
[EM] dns1IP = 192.168.2.1 , dns2IP = 8.8.8.8
Got stored Credentials. Timeout 120s for Config Portal
[EM] Adding parameter thingspeakApiKey
[EM] Adding parameter SensorDHT22
[EM] Adding parameter PinSda
[EM] Adding parameter PinScl
[EM] setSTAStaticIPConfig for USE_CONFIGURABLE_DNS
[EM] Set CORS Header to :  Your Access-Control-Allow-Origin
[EM] _configPortalStart millis() = 26788
[EM] Config Portal IP address = 192.168.2.232
[EM] HTTP server started
[EM] startConfigPortal : Enter loop
[EM] handleRoot
[EM] request host IP = 192.168.2.232
[EM] request host IP = 192.168.2.232
[EM] Info
[EM] Info page sent
[EM] handleRoot
[EM] request host IP = 192.168.2.232
[EM] Handle ETH
[EM] Static IP = 192.168.2.232
[EM] Sent config page
[EM] ETH save
[EM] TZ = America/Toronto
[EM] Parameter and value : thingspeakApiKey 
[EM] Parameter and value : SensorDHT22 T
[EM] Parameter and value : PinSda 4
[EM] Parameter and value : PinScl 5
[EM] New Static IP = 192.168.2.186
[EM] New Static Gateway = 192.168.2.1
[EM] New Static Netmask = 255.255.255.0
[EM] New Static DNS1 = 192.168.2.1
[EM] New Static DNS2 = 8.8.8.8
[EM] Sent eth save page
[EM] stopConfigPortal
ETH network connected...yeey :)
Local IP: 192.168.2.232
[EM] Saving current TZ_Name = America/Toronto , TZ =  EST5EDT,M3.2.0,M11.1.0
[EM] getSTAStaticIPConfig
[EM] SaveCfgFile 
[EM] stationIP = 192.168.2.186 , gatewayIP = 192.168.2.1
[EM] netMask = 255.255.255.0
[EM] dns1IP = 192.168.2.1 , dns2IP = 8.8.8.8
[EM] OK
Saving config file
{
  "thingspeakApiKey": "",
  "SensorDHT22": true,
  "PinSda": 4,
  "PinScl": 5
}
Config file was successfully saved
Current IP = 192.168.2.232. Reset to take new IP = 192.168.2.186
 ets Jan  8 2013,rst cause:2, boot mode:(3,6)
  ...
 
Starting Async_ConfigOnSwichFS using LittleFS on ESP8266_NODEMCU_ESP12E with ESP8266_W5500 Ethernet
AsyncESP8266_W5500_Manager v1.0.0
{"thingspeakApiKey":"","SensorDHT22":true,"PinSda":4,"PinScl":5}
Config file was successfully parsed
[EM] RFC925 Hostname = ConfigOnSwitchFS
[EM] setSTAStaticIPConfig
[EM] Set CORS Header to :  Your Access-Control-Allow-Origin
[EM] LoadCfgFile 
[EM] OK
[EM] stationIP = 192.168.2.186 , gatewayIP = 192.168.2.1
[EM] netMask = 255.255.255.0
[EM] dns1IP = 192.168.2.1 , dns2IP = 8.8.8.8
Got stored Credentials. Timeout 120s for Config Portal
[EM] Current TZ_Name = America/Toronto , TZ =  EST5EDT,M3.2.0,M11.1.0
[EM] Default SPI pinout:
[EM] MOSI: 13
[EM] MISO: 12
[EM] SCK: 14
[EM] CS: 16
[EM] =========================
Connecting to network : 
Ethernet Static IP address: 192.168.2.186
After waiting 0.00 secs more in setup(), connection result is connected. Local IP: 192.168.2.186
[EM] freeing allocated params!
Local Date/Time: Fri Dec  9 23:05:29 2022
Local Date/Time: Fri Dec  9 23:06:29 2022

3. Async_ESP_FSWebServer_DRD using LittleFS on ESP8266_NODEMCU_ESP12E with ESP8266_W5500

This is terminal debug output when running Async_ESP_FSWebServer_DRD on ESP8266_W5500. Config Portal was requested by DRD (also using LittleFS) to input and save Credentials.

Starting Async_ESP_FSWebServer_DRD using LittleFS on ESP8266_NODEMCU_ESP12E with ESP8266_W5500 Ethernet
AsyncESP8266_W5500_Manager v1.0.0
ESP_DoubleResetDetector v1.3.2
Opening / directory
FS File: CanadaFlag_1.png, size: 40.25KB
FS File: CanadaFlag_2.png, size: 8.12KB
FS File: CanadaFlag_3.jpg, size: 10.89KB
FS File: ConfigMQTT.json, size: 153B
FS File: drd.dat, size: 4B
FS File: edit.htm.gz, size: 4.02KB
FS File: eth_cred.dat, size: 142B
FS File: favicon.ico, size: 1.12KB
FS File: graphs.js.gz, size: 1.92KB
FS File: gsm_config.bak, size: 556B
FS File: gsm_config.dat, size: 556B
FS File: gsm_cp.bak, size: 4B
FS File: gsm_cp.dat, size: 4B
FS File: gsm_cred.bak, size: 180B
FS File: gsm_cred.dat, size: 180B
FS File: index.htm, size: 3.63KB
FS File: mrd.dat, size: 4B
FS File: timezone.dat, size: 32B
FS File: wifi_cred.dat, size: 334B
FS File: wm_config.bak, size: 236B
FS File: wm_config.dat, size: 236B
FS File: wm_cp.bak, size: 4B
FS File: wm_cp.dat, size: 4B
FS File: wm_cred.bak, size: 170B
FS File: wm_cred.dat, size: 170B
FS File: wmssl_conf.bak, size: 376B
FS File: wmssl_conf.dat, size: 376B
FS File: wmssl_cred.bak, size: 180B
FS File: wmssl_cred.dat, size: 180B

[EM] RFC925 Hostname = AsyncESP-FSWebServer
[EM] setSTAStaticIPConfig
[EM] Set CORS Header to :  Your Access-Control-Allow-Origin
[EM] LoadCfgFile 
[EM] OK
[EM] stationIP = 192.168.2.187 , gatewayIP = 192.168.2.1
[EM] netMask = 255.255.255.0
[EM] dns1IP = 192.168.2.1 , dns2IP = 8.8.8.8
[EM] Default SPI pinout:
[EM] MOSI: 13
[EM] MISO: 12
[EM] SCK: 14
[EM] CS: 16
[EM] =========================
Connecting to network : 
Ethernet Static IP address: 192.168.2.187
LittleFS Flag read = 0xD0D04321
No doubleResetDetected
Saving config file...
Saving config file OK
After waiting 0.00 secs more in setup(), connection result is connected. Local IP: 192.168.2.187
HTTP server started @ 192.168.2.187
===============================================================
Open http://192.168.2.187/edit.htm to see the file browser
Using username = admin and password = admin
===============================================================
[EM] freeing allocated params!
Stop doubleResetDetecting
Saving config file...
Saving config file OK
HHHHH HHHHHHHHHH HHHHHHHHHH HHHHHHHHHH


Debug

Debug is enabled by default on Serial. To disable, add before startConfigPortal()

AsyncESP8266_W5500_Manager.setDebugOutput(false);

You can also change the debugging level from 0 to 4

// Use from 0 to 4. Higher number, more debugging messages and memory usage.
#define _ESPASYNC_ETH_MGR_LOGLEVEL_    3

Troubleshooting

If you get compilation errors, more often than not, you may need to install a newer version of the ESP8266 core for Arduino.

Sometimes, the library will only work if you update the ESP8266 core to the latest version because I am using some newly added function.

If you connect to the created configuration Access Point but the ConfigPortal does not show up, just open a browser and type in the IP of the web portal, by default 192.168.2.186.


Issues

Submit issues to: AsyncESP8266_W5500_Manager issues



Contributions and Thanks

  1. Thanks to Hristo Gochkov for great ESPAsyncWebServer Library
me-no-dev
⭐️⭐️ Hristo Gochkov


Contributing

If you want to contribute to this project:

  • Report bugs and errors
  • Ask for enhancements
  • Create issues and pull requests
  • Tell other people about this library

License and credits

  • The library is licensed under MIT

Copyright

Copyright 2022- Khoi Hoang