This is an ESP32 (including ESP32-S2 and ESP32-C3) / ESP8266 WiFi Connection Manager, using ESPAsyncWebServer, with fallback web configuration portal. Use this library for configuring ESP32, ESP8266 modules' WiFi, etc. Credentials at runtime. You can also specify static DNS servers, personalized HostName, fixed or random AP WiFi channel. With examples supporting ArduinoJson 6.0.0+ as well as 5.13.5- . Using AsyncDNSServer instead of DNSServer now.
provisioning.cpp
#include "provisioning.h"
#include "common.h"
Provisioning::Provisioning()
{
stationIP = IPAddress(0, 0, 0, 0);
gatewayIP = IPAddress(192, 168, 2, 1);
netMask = IPAddress(255, 255, 255, 0);
APStaticIP = IPAddress(192, 168, 100, 1);
APStaticGW = IPAddress(192, 168, 100, 1);
APStaticSN = IPAddress(255, 255, 255, 0);
ssid = "ESP_" + String(ESP_getChipId(), HEX);
password = NULL;
dns1IP = gatewayIP;
dns2IP = IPAddress(8, 8, 8, 8);
}
void Provisioning::Begin()
{
if (!FileFS.begin(true))
{
Serial.println(F("SPIFFS/LittleFS failed! Already tried formatting."));
if (!FileFS.begin())
{
delay(100);
Serial.println(F("LittleFS failed!. Please use SPIFFS or EEPROM. Stay forever"));
while (true)
{
delay(1);
}
}
}
unsigned long startedAt = millis();
initAPIPConfigStruct(WM_AP_IPconfig);
initSTAIPConfigStruct(WM_STA_IPconfig);
AsyncWebServer webServer(HTTP_PORT);
AsyncDNSServer dnsServer;
ESPAsync_WiFiManager ESPAsync_wifiManager(&webServer, &dnsServer, "AsyncConfigOnStartup");
#if USE_CUSTOM_AP_IP
ESPAsync_wifiManager.setAPStaticIPConfig(WM_AP_IPconfig);
#endif
ESPAsync_wifiManager.setMinimumSignalQuality(-1);
ESPAsync_wifiManager.setConfigPortalChannel(0);
#if !USE_DHCP_IP
// Set (static IP, Gateway, Subnetmask, DNS1 and DNS2) or (IP, Gateway, Subnetmask). New in v1.0.5
// New in v1.4.0
ESPAsync_wifiManager.setSTAStaticIPConfig(WM_STA_IPconfig);
//////
#endif
#if USING_CORS_FEATURE
ESPAsync_wifiManager.setCORSHeader("Your Access-Control-Allow-Origin");
#endif
Router_SSID = ESPAsync_wifiManager.WiFi_SSID();
Router_Pass = ESPAsync_wifiManager.WiFi_Pass();
Serial.println("ESP Self-Stored: SSID = " + Router_SSID + ", Pass = " + Router_Pass);
Serial.println(F("Opening configuration portal."));
bool configDataLoaded = false;
if ((Router_SSID != "") && (Router_Pass != ""))
{
LOGERROR3(F("* Add SSID = "), Router_SSID, F(", PW = "), Router_Pass);
wifiMulti.addAP(Router_SSID.c_str(), Router_Pass.c_str());
ESPAsync_wifiManager.setConfigPortalTimeout(120);
Serial.println(F("Got ESP Self-Stored Credentials. Timeout 120s for Config Portal"));
}
if (loadConfigData())
{
configDataLoaded = true;
ESPAsync_wifiManager.setConfigPortalTimeout(120);
Serial.println(F("Got stored Credentials. Timeout 120s for Config Portal"));
#if USE_ESP_WIFIMANAGER_NTP
if (strlen(WM_config.TZ_Name) > 0)
{
LOGERROR3(F("Current TZ_Name ="), WM_config.TZ_Name, F(", TZ = "), WM_config.TZ);
#if ESP8266
configTime(WM_config.TZ, "pool.ntp.org");
#else
// configTzTime(WM_config.TZ, "pool.ntp.org" );
configTzTime(WM_config.TZ, "time.nist.gov", "0.pool.ntp.org", "1.pool.ntp.org");
#endif
}
else
{
Serial.println(F("Current Timezone is not set. Enter Config Portal to set."));
}
#endif
}
else
{
Serial.println(F("Open Config Portal without Timeout: No stored Credentials."));
initialConfig = true;
}
ssid.toUpperCase();
Serial.print(F("Starting configuration portal @ "));
#if USE_CUSTOM_AP_IP
Serial.print(APStaticIP);
#else
Serial.print(F("192.168.4.1"));
#endif
Serial.print(F(", SSID = "));
Serial.print(ssid);
Serial.print(F(", PWD = "));
Serial.println(password);
// digitalWrite(LED_BUILTIN, LED_ON); // turn the LED on by making the voltage LOW to tell us we are in configuration mode.
#if DISPLAY_STORED_CREDENTIALS_IN_CP
ESPAsync_wifiManager.setCredentials(WM_config.WiFi_Creds[0].wifi_ssid, WM_config.WiFi_Creds[0].wifi_pw,
WM_config.WiFi_Creds[1].wifi_ssid, WM_config.WiFi_Creds[1].wifi_pw);
#endif
if (!ESPAsync_wifiManager.startConfigPortal((const char *)ssid.c_str(), password))
Serial.println(F("Not connected to WiFi but continuing anyway."));
else
{
Serial.println(F("WiFi connected...yeey :)"));
}
if (String(ESPAsync_wifiManager.getSSID(0)) != "" && String(ESPAsync_wifiManager.getSSID(1)) != "")
{
memset(&WM_config, 0, sizeof(WM_config));
for (uint8_t i = 0; i < NUM_WIFI_CREDENTIALS; i++)
{
String tempSSID = ESPAsync_wifiManager.getSSID(i);
String tempPW = ESPAsync_wifiManager.getPW(i);
if (strlen(tempSSID.c_str()) < sizeof(WM_config.WiFi_Creds[i].wifi_ssid) - 1)
strcpy(WM_config.WiFi_Creds[i].wifi_ssid, tempSSID.c_str());
else
strncpy(WM_config.WiFi_Creds[i].wifi_ssid, tempSSID.c_str(), sizeof(WM_config.WiFi_Creds[i].wifi_ssid) - 1);
if (strlen(tempPW.c_str()) < sizeof(WM_config.WiFi_Creds[i].wifi_pw) - 1)
strcpy(WM_config.WiFi_Creds[i].wifi_pw, tempPW.c_str());
else
strncpy(WM_config.WiFi_Creds[i].wifi_pw, tempPW.c_str(), sizeof(WM_config.WiFi_Creds[i].wifi_pw) - 1);
if ((String(WM_config.WiFi_Creds[i].wifi_ssid) != "") && (strlen(WM_config.WiFi_Creds[i].wifi_pw) >= MIN_AP_PASSWORD_SIZE))
{
LOGERROR3(F("* Add SSID = "), WM_config.WiFi_Creds[i].wifi_ssid, F(", PW = "), WM_config.WiFi_Creds[i].wifi_pw);
wifiMulti.addAP(WM_config.WiFi_Creds[i].wifi_ssid, WM_config.WiFi_Creds[i].wifi_pw);
}
}
#if USE_ESP_WIFIMANAGER_NTP
String tempTZ = ESPAsync_wifiManager.getTimezoneName();
if (strlen(tempTZ.c_str()) < sizeof(WM_config.TZ_Name) - 1)
strcpy(WM_config.TZ_Name, tempTZ.c_str());
else
strncpy(WM_config.TZ_Name, tempTZ.c_str(), sizeof(WM_config.TZ_Name) - 1);
const char *TZ_Result = ESPAsync_wifiManager.getTZ(WM_config.TZ_Name);
if (strlen(TZ_Result) < sizeof(WM_config.TZ) - 1)
strcpy(WM_config.TZ, TZ_Result);
else
strncpy(WM_config.TZ, TZ_Result, sizeof(WM_config.TZ_Name) - 1);
if (strlen(WM_config.TZ_Name) > 0)
{
LOGERROR3(F("Saving current TZ_Name ="), WM_config.TZ_Name, F(", TZ = "), WM_config.TZ);
#if ESP8266
configTime(WM_config.TZ, "pool.ntp.org");
#else
// configTzTime(WM_config.TZ, "pool.ntp.org" );
configTzTime(WM_config.TZ, "time.nist.gov", "0.pool.ntp.org", "1.pool.ntp.org");
#endif
}
else
{
LOGERROR(F("Current Timezone Name is not set. Enter Config Portal to set."));
}
#endif
// New in v1.4.0
ESPAsync_wifiManager.getSTAStaticIPConfig(WM_STA_IPconfig);
//////
saveConfigData();
initialConfig = true;
}
// digitalWrite(LED_BUILTIN, LED_OFF); // Turn led off as we are not in configuration mode.
startedAt = millis();
if (!initialConfig)
{
if (!configDataLoaded)
loadConfigData();
for (uint8_t i = 0; i < NUM_WIFI_CREDENTIALS; i++)
{
if ((String(WM_config.WiFi_Creds[i].wifi_ssid) != "") && (strlen(WM_config.WiFi_Creds[i].wifi_pw) >= MIN_AP_PASSWORD_SIZE))
{
LOGERROR3(F("* Add SSID = "), WM_config.WiFi_Creds[i].wifi_ssid, F(", PW = "), WM_config.WiFi_Creds[i].wifi_pw);
wifiMulti.addAP(WM_config.WiFi_Creds[i].wifi_ssid, WM_config.WiFi_Creds[i].wifi_pw);
}
}
if (WiFi.status() != WL_CONNECTED)
{
Serial.println(F("ConnectMultiWiFi in setup"));
connectMultiWiFi();
}
}
Serial.print(F("After waiting "));
Serial.print((float)(millis() - startedAt) / 1000L);
Serial.print(F(" secs more in setup(), connection result is "));
if (WiFi.status() == WL_CONNECTED)
{
Serial.print(F("connected. Local IP: "));
Serial.println(WiFi.localIP());
}
else
Serial.println(ESPAsync_wifiManager.getStatus(WiFi.status()));
}
void Provisioning::Fsm()
{
check_status();
}
void Provisioning::initAPIPConfigStruct(WiFi_AP_IPConfig &in_WM_AP_IPconfig)
{
in_WM_AP_IPconfig._ap_static_ip = APStaticIP;
in_WM_AP_IPconfig._ap_static_gw = APStaticGW;
in_WM_AP_IPconfig._ap_static_sn = APStaticSN;
}
void Provisioning::initSTAIPConfigStruct(WiFi_STA_IPConfig &in_WM_STA_IPconfig)
{
in_WM_STA_IPconfig._sta_static_ip = stationIP;
in_WM_STA_IPconfig._sta_static_gw = gatewayIP;
in_WM_STA_IPconfig._sta_static_sn = netMask;
#if USE_CONFIGURABLE_DNS
in_WM_STA_IPconfig._sta_static_dns1 = dns1IP;
in_WM_STA_IPconfig._sta_static_dns2 = dns2IP;
#endif
}
void Provisioning::displayIPConfigStruct(WiFi_STA_IPConfig in_WM_STA_IPconfig)
{
LOGERROR3(F("stationIP ="), in_WM_STA_IPconfig._sta_static_ip, ", gatewayIP =", in_WM_STA_IPconfig._sta_static_gw);
LOGERROR1(F("netMask ="), in_WM_STA_IPconfig._sta_static_sn);
#if USE_CONFIGURABLE_DNS
LOGERROR3(F("dns1IP ="), in_WM_STA_IPconfig._sta_static_dns1, ", dns2IP =", in_WM_STA_IPconfig._sta_static_dns2);
#endif
}
void Provisioning::configWiFi(WiFi_STA_IPConfig in_WM_STA_IPconfig)
{
#if USE_CONFIGURABLE_DNS
// Set static IP, Gateway, Subnetmask, DNS1 and DNS2. New in v1.0.5
WiFi.config(in_WM_STA_IPconfig._sta_static_ip, in_WM_STA_IPconfig._sta_static_gw, in_WM_STA_IPconfig._sta_static_sn, in_WM_STA_IPconfig._sta_static_dns1, in_WM_STA_IPconfig._sta_static_dns2);
#else
// Set static IP, Gateway, Subnetmask, Use auto DNS1 and DNS2.
WiFi.config(in_WM_STA_IPconfig._sta_static_ip, in_WM_STA_IPconfig._sta_static_gw, in_WM_STA_IPconfig._sta_static_sn);
#endif
}
uint8_t Provisioning::connectMultiWiFi()
{
#define WIFI_MULTI_1ST_CONNECT_WAITING_MS 800L
#define WIFI_MULTI_CONNECT_WAITING_MS 500L
uint8_t status;
// WiFi.mode(WIFI_STA);
LOGERROR(F("ConnectMultiWiFi with :"));
if ((Router_SSID != "") && (Router_Pass != ""))
{
LOGERROR3(F("* Flash-stored Router_SSID = "), Router_SSID, F(", Router_Pass = "), Router_Pass);
LOGERROR3(F("* Add SSID = "), Router_SSID, F(", PW = "), Router_Pass);
wifiMulti.addAP(Router_SSID.c_str(), Router_Pass.c_str());
}
for (uint8_t i = 0; i < NUM_WIFI_CREDENTIALS; i++)
{
// Don't permit NULL SSID and password len < MIN_AP_PASSWORD_SIZE (8)
if ((String(WM_config.WiFi_Creds[i].wifi_ssid) != "") && (strlen(WM_config.WiFi_Creds[i].wifi_pw) >= MIN_AP_PASSWORD_SIZE))
{
LOGERROR3(F("* Additional SSID = "), WM_config.WiFi_Creds[i].wifi_ssid, F(", PW = "), WM_config.WiFi_Creds[i].wifi_pw);
}
}
LOGERROR(F("Connecting MultiWifi..."));
// WiFi.mode(WIFI_STA);
#if !USE_DHCP_IP
// New in v1.4.0
configWiFi(WM_STA_IPconfig);
//////
#endif
int i = 0;
status = wifiMulti.run();
delay(WIFI_MULTI_1ST_CONNECT_WAITING_MS);
while ((i++ < 20) && (status != WL_CONNECTED))
{
status = WiFi.status();
if (status == WL_CONNECTED)
break;
else
delay(WIFI_MULTI_CONNECT_WAITING_MS);
}
if (status == WL_CONNECTED)
{
LOGERROR1(F("WiFi connected after time: "), i);
LOGERROR3(F("SSID:"), WiFi.SSID(), F(",RSSI="), WiFi.RSSI());
LOGERROR3(F("Channel:"), WiFi.channel(), F(",IP address:"), WiFi.localIP());
}
else
{
LOGERROR(F("WiFi not connected"));
ESP.restart();
}
return status;
}
#if USE_ESP_WIFIMANAGER_NTP
void printLocalTime()
{
#if ESP8266
static time_t now;
now = time(nullptr);
if (now > 1451602800)
{
Serial.print("Local Date/Time: ");
Serial.print(ctime(&now));
}
#else
struct tm timeinfo;
getLocalTime(&timeinfo);
// Valid only if year > 2000.
// You can get from timeinfo : tm_year, tm_mon, tm_mday, tm_hour, tm_min, tm_sec
if (timeinfo.tm_year > 100)
{
Serial.print("Local Date/Time: ");
Serial.print(asctime(&timeinfo));
}
#endif
}
#endif
void Provisioning::check_WiFi()
{
if ((WiFi.status() != WL_CONNECTED))
{
Serial.println(F("\nWiFi lost. Call connectMultiWiFi in loop"));
connectMultiWiFi();
}
}
void Provisioning::check_status()
{
static ulong LEDstatus_timeout = 0;
static ulong checkwifi_timeout = 0;
static ulong current_millis;
#define WIFICHECK_INTERVAL 1000L
#if USE_ESP_WIFIMANAGER_NTP
#define HEARTBEAT_INTERVAL 60000L
#else
#define HEARTBEAT_INTERVAL 10000L
#endif
#define LED_INTERVAL 2000L
current_millis = millis();
// Check WiFi every WIFICHECK_INTERVAL (1) seconds.
if ((current_millis > checkwifi_timeout) || (checkwifi_timeout == 0))
{
check_WiFi();
checkwifi_timeout = current_millis + WIFICHECK_INTERVAL;
}
if ((current_millis > LEDstatus_timeout) || (LEDstatus_timeout == 0))
{
// Toggle LED at LED_INTERVAL = 2s
// toggleLED();
LEDstatus_timeout = current_millis + LED_INTERVAL;
}
}
int Provisioning::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 Provisioning::loadConfigData()
{
File file = FileFS.open(CONFIG_FILENAME, "r");
LOGERROR(F("LoadWiFiCfgFile "));
memset((void *)&WM_config, 0, sizeof(WM_config));
memset((void *)&WM_STA_IPconfig, 0, sizeof(WM_STA_IPconfig));
if (file)
{
file.readBytes((char *)&WM_config, sizeof(WM_config));
file.readBytes((char *)&WM_STA_IPconfig, sizeof(WM_STA_IPconfig));
file.close();
LOGERROR(F("OK"));
if (WM_config.checksum != calcChecksum((uint8_t *)&WM_config, sizeof(WM_config) - sizeof(WM_config.checksum)))
{
LOGERROR(F("WM_config checksum wrong"));
return false;
}
displayIPConfigStruct(WM_STA_IPconfig);
return true;
}
else
{
LOGERROR(F("failed"));
return false;
}
}
void Provisioning::saveConfigData()
{
File file = FileFS.open(CONFIG_FILENAME, "w");
LOGERROR(F("SaveWiFiCfgFile "));
if (file)
{
WM_config.checksum = calcChecksum((uint8_t *)&WM_config, sizeof(WM_config) - sizeof(WM_config.checksum));
file.write((uint8_t *)&WM_config, sizeof(WM_config));
displayIPConfigStruct(WM_STA_IPconfig);
file.write((uint8_t *)&WM_STA_IPconfig, sizeof(WM_STA_IPconfig));
file.close();
LOGERROR(F("OK"));
}
else
{
LOGERROR(F("failed"));
}
}
As expected I have no problem with the multiple include issue, but instead I'm not sure how to handle the configuration defines.
Let's talk for example about #define USE_ESP_WIFIMANAGER_NTP false.
In my code is placed in main.cpp before #include <ESPAsync_WiFiManager.h>.
But of course this define is not found inside provisioning.cpp.
Moving it inside:
provisioning.h, or
provisioning.cpp, or
common.h
leads to:
In file included from src/main.cpp:38:
include/provisioning.h:13: warning: "USE_ESP_WIFIMANAGER_NTP" redefined
define USE_ESP_WIFIMANAGER_NTP false
In file included from .pio/libdeps/myproject/ESPAsync_WiFiManager/src/ESPAsync_WiFiManager.h:41,
from src/main.cpp:37:
.pio/libdeps/myproject/ESPAsync_WiFiManager/src/ESPAsync_WiFiManager.hpp:343: note: this is the location of the previous definition
define USE_ESP_WIFIMANAGER_NTP true
What is the correct handling of these defines when using a class approach?
I'm aware about the multiple include issue, I read some questions like this but still I'm not sure how to address this use-case.
I'm using PlatformIO so I have:
setup()
andloop()
live)So far I tried:
main.cpp
provisioning.h
As expected I have no problem with the multiple include issue, but instead I'm not sure how to handle the configuration defines. Let's talk for example about
#define USE_ESP_WIFIMANAGER_NTP false
.In my code is placed in main.cpp before
#include <ESPAsync_WiFiManager.h>
. But of course this define is not found inside provisioning.cpp.Moving it inside:
leads to:
What is the correct handling of these defines when using a class approach?