#include "wifi.h" #include "base.h" #include "log.h" #include "mqtt.h" #include "console.h" #include "config.h" #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 (5 * 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; bool otaInitialized = false; void otaSetup(); void bootDelay(); void wifiSetup() { WiFi.softAPdisconnect(); WiFi.setAutoReconnect(false); wifiConnect(); otaSetup(); bootDelay(); } void wifiConnect() { wifiConnected = false; wifiLastConnectTry = millis(); sntp_stop(); if (otaInitialized) { ArduinoOTA.end(); otaInitialized = false; } mqttDisconnect(); yield(); WiFi.disconnect(); WiFi.enableAP(false); WiFi.setAutoConnect(false); WiFi.setAutoReconnect(false); yield(); String ssid = configGetString("WIFI_SSID", WIFI_SSID, false); String pkey = configGetString("WIFI_PKEY", WIFI_PKEY, false); info("WIFI connecting: %s", ssid.c_str()); #ifdef ESP32 WiFiClass::hostname(HOSTNAME); #else WiFi.hostname(HOSTNAME); #endif yield(); WiFi.begin(ssid, pkey); yield(); } void otaSetup() { ArduinoOTA.setPassword(OTA_PASSWORD); ArduinoOTA.onStart([] { info("OTA start..."); otaLastLogStep = 0; }); ArduinoOTA.onProgress([](unsigned int received, unsigned int total) { uint8_t currentStep = 20 * received / total; if (otaLastLogStep != currentStep) { info("OTA Progress: %3d%%", currentStep * 5); otaLastLogStep = currentStep; } }); ArduinoOTA.onEnd([] { info("OTA Complete!"); mqttDisconnect(); }); 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 error: %s", name); }); } void bootDelayLoop() { // TODO merge this into Arduino:loop consoleLoop(); wifiLoop(); yield(); wdt_reset(); } void bootDelay() { #if !BOOT_DELAY return; #endif info("BOOT DELAY: Waiting for WiFi..."); while ((uint32_t) WiFi.localIP() == 0) { bootDelayLoop(); } info("BOOT DELAY: WiFi connected!"); info("BOOT DELAY: Waiting %d milliseconds...", BOOT_DELAY_MS); unsigned long bootDelayBegin = millis(); while (millis() - bootDelayBegin < BOOT_DELAY_MS) { bootDelayLoop(); } info("BOOT DELAY: Complete!"); info("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("Got time via NTP"); } } const bool hasIp = static_cast(WiFi.localIP()) != 0; if (wifiConnected) { if (!hasIp) { wifiConnected = false; info("WiFi disconnected!"); wifiConnect(); } else { ArduinoOTA.handle(); } } else { if (hasIp) { wifiConnected = true; info("WiFi connected: ip=%s", WiFi.localIP().toString().c_str()); ArduinoOTA.begin(); otaInitialized = true; configTime(TIMEZONE_OFFSET, DST_OFFSET, WiFi.gatewayIP().toString().c_str(), NTP_SERVER); } else if (millis() - wifiLastConnectTry > WIFI_TIMEOUT_MS) { info("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; } void getDateTime(char *buffer, size_t size) { time_t now; time(&now); tm time{}; localtime_r(&now, &time); strftime(buffer, size, "%Y-%m-%d %H:%M:%S %z", &time); } uint64_t uptimeMillis = 0; unsigned long uptimeLastMillis = 0; uint64_t getUptimeMillis() { unsigned long now = millis(); uptimeMillis += now - uptimeLastMillis; uptimeLastMillis = now; return uptimeMillis; }