#include "wifi.h" #include "log.h" #include "mqtt.h" #include "console.h" #include #include #include #define NTP_SERVER "pool.ntp.org" #define TIMEZONE_OFFSET 3600 #define DST_OFFSET 3600 #define MIN_EPOCH_SECONDS 1712675973 #define BOOT_DELAY_MS (10 * 1000) #define WIFI_TIMEOUT_MS (15 * 1000) uint8_t otaLastLogStep = 0; bool wifiConnected = false; bool timeSet = false; time_t preTimeOffset = 0; unsigned long wifiLastConnectTry = 0; void otaSetup(); void bootDelay(); void wifiSetup() { WiFi.softAPdisconnect(); WiFi.setAutoReconnect(false); wifiConnect(); otaSetup(); bootDelay(); } void wifiConnect() { wifiConnected = false; wifiLastConnectTry = millis(); sntp_stop(); ArduinoOTA.end(); mqttDisconnect(); WiFi.disconnect(); WiFi.enableAP(false); WiFi.setAutoConnect(false); WiFi.setAutoReconnect(false); yield(); info("WIFI", "Connecting: %s", WIFI_SSID); WiFiClass::hostname(HOSTNAME); yield(); WiFi.begin(WIFI_SSID, WIFI_PKEY); yield(); } void otaSetup() { ArduinoOTA.onStart([] { info("OTA", "OTA start..."); otaLastLogStep = 0; }); ArduinoOTA.onProgress([](unsigned int received, unsigned int total) { uint8_t currentStep = 20 * received / total; if (otaLastLogStep != currentStep) { info("OTA", "OTA Progress: %3d%%", currentStep * 5); otaLastLogStep = currentStep; } }); ArduinoOTA.onEnd([] { info("OTA", "OTA Complete!"); }); ArduinoOTA.onError([](ota_error_t e) { const char *name; switch (e) { case OTA_AUTH_ERROR: name = "AUTH"; break; case OTA_BEGIN_ERROR: name = "BEGIN"; break; case OTA_CONNECT_ERROR: name = "CONNECT"; break; case OTA_RECEIVE_ERROR: name = "RECEIVE"; break; case OTA_END_ERROR: name = "END"; break; default: name = "[???]"; break; } error("OTA", name); }); } void bootDelay() { #if !BOOT_DELAY return; #endif info("BOOT DELAY", "BOOT DELAY: Waiting for WiFi..."); while ((uint32_t) WiFi.localIP() == 0) { consoleLoop(); wifiLoop(); } info("BOOT DELAY", "BOOT DELAY: WiFi connected!"); info("BOOT DELAY", "BOOT DELAY: Waiting 10 seconds..."); unsigned long bootDelayBegin = millis(); while (millis() - bootDelayBegin < BOOT_DELAY_MS) { consoleLoop(); wifiLoop(); } info("BOOT DELAY", "BOOT DELAY: Complete!"); info("BOOT DELAY", "BOOT DELAY: Resuming normal boot!"); } void wifiLoop() { const time_t epochSeconds = getTime(); if (!timeSet) { timeSet = epochSeconds >= MIN_EPOCH_SECONDS; if (!timeSet) { preTimeOffset = epochSeconds; } else { info("NTP", "Time set!"); } } const bool hasIp = static_cast(WiFi.localIP()) != 0; if (wifiConnected) { if (!hasIp) { wifiConnected = false; info("WIFI", "WiFi disconnected!"); wifiConnect(); } else { ArduinoOTA.handle(); } } else { if (hasIp) { wifiConnected = true; info("WIFI", "WiFi connected: ip=%s", WiFi.localIP().toString().c_str()); ArduinoOTA.begin(); configTime(TIMEZONE_OFFSET, DST_OFFSET, WiFi.gatewayIP().toString().c_str(), NTP_SERVER); } else if (millis() - wifiLastConnectTry > WIFI_TIMEOUT_MS) { info("WIFI", "WiFi timeout!"); wifiConnect(); } } } bool isWiFiConnected() { return wifiConnected; } bool isTimeSet() { return timeSet; } time_t getTime() { time_t epochSeconds; time(&epochSeconds); return epochSeconds; } time_t correctTime(const time_t value) { if (!timeSet) { return value; } if (value < MIN_EPOCH_SECONDS) { return getTime() - preTimeOffset + value; } return value; } bool isOlderThan(time_t time, time_t seconds) { return getTime() > correctTime(time) + seconds; }