khoih-prog / ESPAsync_WiFiManager

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.
MIT License
290 stars 73 forks source link

Where to put #define when using classes #123

Closed ghost closed 1 year ago

ghost commented 1 year ago

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:

So far I tried:

main.cpp

#include <Arduino.h>

#define _ESPASYNC_WIFIMGR_LOGLEVEL_ 0
#define DISPLAY_STORED_CREDENTIALS_IN_CP true
#define SINGLE_CREDENTIALS true

#define USE_AVAILABLE_PAGES false
#define USE_CLOUDFLARE_NTP false
#define USE_CONFIGURABLE_DNS true
#define USE_ESP_WIFIMANAGER_NTP false

#define USING_CORS_FEATURE true

#define USING_AFRICA false
#define USING_AMERICA false
#define USING_ANTARCTICA false
#define USING_ASIA false
#define USING_ATLANTIC false
#define USING_AUSTRALIA false
#define USING_EUROPE true
#define USING_INDIAN false
#define USING_PACIFIC false
#define USING_ETC_GMT false

#include <ESPAsync_WiFiManager.h> 

#include "provisioning.h"
Provisioning _provisioning;

void setup()
{
  delay(100);

  Serial.begin(115200);
  while(!Serial);

  _provisioning.Begin();
}

void loop()
{
  _provisioning.Fsm();
}

provisioning.h

#ifndef PROVISIONING_H
#define PROVISIONING_H

#include <Arduino.h>

#include <esp_wifi.h>
#include <WiFi.h>
#include <WiFiClient.h>
#include <WiFiMulti.h>

#define USE_LITTLEFS true
#define USE_SPIFFS false

#include "FS.h"
#include <LittleFS.h>

#define FileFS LittleFS
#define FS_Name "LittleFS"

//#define FORMAT_FILESYSTEM true
 #define FORMAT_FILESYSTEM         false

#define MIN_AP_PASSWORD_SIZE 8
#define SSID_MAX_LEN 32
#define PASS_MAX_LEN 64

#define NUM_WIFI_CREDENTIALS 1

#define TZNAME_MAX_LEN 50
#define TIMEZONE_MAX_LEN 50

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

#if (defined(USE_STATIC_IP_CONFIG_IN_CP) && !USE_STATIC_IP_CONFIG_IN_CP)
#if defined(USE_DHCP_IP)
    #undef USE_DHCP_IP
#endif
    #define USE_DHCP_IP true
#else
    #define USE_DHCP_IP true
    // #define USE_DHCP_IP     false
#endif

#define HTTP_PORT 80
#define USE_CUSTOM_AP_IP false

#include <ESPAsync_WiFiManager.hpp> 

class Provisioning
{
public:
    Provisioning();
    void Begin();
    void Fsm();

private:
    typedef struct
    {
        char wifi_ssid[SSID_MAX_LEN];
        char wifi_pw[PASS_MAX_LEN];
    } WiFi_Credentials;

    typedef struct
    {
        String wifi_ssid;
        String wifi_pw;
    } WiFi_Credentials_String;

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

    WM_Config WM_config;

    WiFiMulti wifiMulti;
    FS *filesystem = &LittleFS;

    bool initialConfig = false;

    IPAddress stationIP;
    IPAddress gatewayIP;
    IPAddress netMask;

    IPAddress APStaticIP;
    IPAddress APStaticGW;
    IPAddress APStaticSN;

    String ssid;
    const char *password;

    String Router_SSID;
    String Router_Pass;

    WiFi_AP_IPConfig WM_AP_IPconfig;
    WiFi_STA_IPConfig WM_STA_IPconfig;

    IPAddress dns1IP;
    IPAddress dns2IP;

    void initAPIPConfigStruct(WiFi_AP_IPConfig &in_WM_AP_IPconfig);
    void initSTAIPConfigStruct(WiFi_STA_IPConfig &in_WM_STA_IPconfig);
    void displayIPConfigStruct(WiFi_STA_IPConfig in_WM_STA_IPconfig);
    void configWiFi(WiFi_STA_IPConfig in_WM_STA_IPconfig);
    uint8_t connectMultiWiFi();
    void check_WiFi();
    void check_status();
    int calcChecksum(uint8_t *address, uint16_t sizeToCalc);
    bool loadConfigData();
    void saveConfigData();
};

#endif  /* PROVISIONING_H */
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:

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?