From 425d1387692268440d90f8e4597648106d0c2fd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=B3r=C3=A1nt=20Pint=C3=A9r?= Date: Sun, 1 Dec 2024 19:06:49 +0100 Subject: [PATCH] Use ESP-IDF WiFi implementation only --- main/devices/Device.hpp | 52 +++--- main/kernel/Kernel.hpp | 5 +- main/kernel/Strings.hpp | 28 --- main/kernel/drivers/WiFiDriver.hpp | 266 +++++++++++++++++------------ 4 files changed, 195 insertions(+), 156 deletions(-) delete mode 100644 main/kernel/Strings.hpp diff --git a/main/devices/Device.hpp b/main/devices/Device.hpp index 581a46e5..2f90ada4 100644 --- a/main/devices/Device.hpp +++ b/main/devices/Device.hpp @@ -5,6 +5,8 @@ #include +#include "esp_netif.h" +#include "esp_wifi.h" #include #include @@ -194,26 +196,34 @@ class ConsolePrinter { private: static String wifiStatus() { - switch (WiFi.status()) { - case WL_NO_SHIELD: - return "\033[0;31moff\033[0m"; - case WL_IDLE_STATUS: - return "\033[0;31midle\033[0m"; - case WL_NO_SSID_AVAIL: - return "\033[0;31mno SSID\033[0m"; - case WL_SCAN_COMPLETED: - return "\033[0;33mscan completed\033[0m"; - case WL_CONNECTED: - return "\033[0;33m" + WiFi.localIP().toString() + "\033[0m"; - case WL_CONNECT_FAILED: - return "\033[0;31mfailed\033[0m"; - case WL_CONNECTION_LOST: - return "\033[0;31mconnection lost\033[0m"; - case WL_DISCONNECTED: - return "\033[0;33mdisconnected\033[0m"; - default: - return "\033[0;31munknown\033[0m"; + esp_netif_t* netif = esp_netif_get_handle_from_ifkey("WIFI_AP_DEF"); + if (!netif) { + return "\033[0;31moff\033[0m"; + } + + // Retrieve the current Wi-Fi station connection status + wifi_ap_record_t ap_info; + esp_err_t err = esp_wifi_sta_get_ap_info(&ap_info); + + // TODO Handle ESP_ERR_WIFI_CONN, or better yet, use `WiFiDriver` directly + if (err == ESP_ERR_WIFI_NOT_CONNECT) { + return "\033[0;33mdisconnected\033[0m"; + } else if (err == ESP_ERR_WIFI_NOT_STARTED) { + return "\033[0;31mWi-Fi not started\033[0m"; + } else if (err != ESP_OK) { + return "\033[0;31m(" + String(esp_err_to_name(err)) + ")\033[0m"; } + + // Check IP address + esp_netif_ip_info_t ip_info; + err = esp_netif_get_ip_info(netif, &ip_info); + if (err == ESP_OK && ip_info.ip.addr != 0) { + static char ip_str[32]; + snprintf(ip_str, sizeof(ip_str), "\033[0;33m" IPSTR "\033[0m", IP2STR(&ip_info.ip)); + return ip_str; + } + + return "\033[0;31midle\033[0m"; } int counter; @@ -522,8 +532,8 @@ class Device { deviceConfig.model.get().c_str(), deviceConfig.instance.get().c_str(), deviceConfig.getHostname().c_str(), - WiFi.localIP().toString().c_str(), - WiFi.SSID().c_str(), + kernel.wifi.getIp().value_or("").c_str(), + kernel.wifi.getSsid().value_or("").c_str(), duration_cast(system_clock::now().time_since_epoch()).count()); } diff --git a/main/kernel/Kernel.hpp b/main/kernel/Kernel.hpp index 28be256c..6f1e5400 100644 --- a/main/kernel/Kernel.hpp +++ b/main/kernel/Kernel.hpp @@ -238,8 +238,6 @@ class Kernel { return "Network not ready, aborting update"; } - Log.info("WiFi connected: %s", WiFi.localIP().toString().c_str()); - // TODO Disable power save mode for WiFi esp_http_client_config_t httpConfig = { @@ -324,7 +322,10 @@ class Kernel { StateSource mqttReadyState = stateManager.createStateSource("mqtt-ready"); StateSource kernelReadyState = stateManager.createStateSource("kernel-ready"); +public: WiFiDriver wifi { networkRequestedState, networkReadyState, configPortalRunningState, deviceConfig.getHostname(), deviceConfig.sleepWhenIdle.get() }; + +private: MdnsDriver mdns { wifi, deviceConfig.getHostname(), "ugly-duckling", version, mdnsReadyState }; RtcDriver rtc { wifi, mdns, deviceConfig.ntp.get(), rtcInSyncState }; diff --git a/main/kernel/Strings.hpp b/main/kernel/Strings.hpp deleted file mode 100644 index 30bccb90..00000000 --- a/main/kernel/Strings.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include - -namespace farmhub::kernel { - -class StringPrint : public Print { -public: - String buffer; - - virtual size_t write(uint8_t byte) { - buffer += (char) byte; - return 1; - } - - virtual size_t write(const uint8_t* buffer, size_t size) { - for (size_t i = 0; i < size; i++) { - write(buffer[i]); - } - return size; - } - - void clear() { - buffer = ""; - } -}; - -} // namespace farmhub::kernel diff --git a/main/kernel/drivers/WiFiDriver.hpp b/main/kernel/drivers/WiFiDriver.hpp index d2e7f2e3..08a17857 100644 --- a/main/kernel/drivers/WiFiDriver.hpp +++ b/main/kernel/drivers/WiFiDriver.hpp @@ -3,15 +3,12 @@ #include #include -#include -#include - +#include #include #include #include #include -#include #include using namespace std::chrono_literals; @@ -29,95 +26,143 @@ class WiFiDriver { , powerSaveMode(powerSaveMode) { Log.debug("WiFi: initializing"); - WiFi.begin(); - if (powerSaveMode) { - auto listenInterval = 50; - Log.debug("WiFi enabling power save mode, listen interval: %d", - listenInterval); - esp_wifi_set_ps(WIFI_PS_MAX_MODEM); - wifi_config_t conf; - ESP_ERROR_CHECK(esp_wifi_get_config(WIFI_IF_STA, &conf)); - conf.sta.listen_interval = listenInterval; - ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &conf)); - } - - WiFi.onEvent( - [](WiFiEvent_t event, WiFiEventInfo_t info) { - Log.debug("WiFi: connected to %s", - String(info.wifi_sta_connected.ssid, info.wifi_sta_connected.ssid_len).c_str()); - }, - ARDUINO_EVENT_WIFI_STA_CONNECTED); - WiFi.onEvent( - [this, &networkReady](WiFiEvent_t event, WiFiEventInfo_t info) { - Log.debug("WiFi: got IP %s, netmask %s, gateway %s", - IPAddress(info.got_ip.ip_info.ip.addr).toString().c_str(), - IPAddress(info.got_ip.ip_info.netmask.addr).toString().c_str(), - IPAddress(info.got_ip.ip_info.gw.addr).toString().c_str()); - networkReady.set(); - eventQueue.offer(WiFiEvent::CONNECTED); - }, - ARDUINO_EVENT_WIFI_STA_GOT_IP); - WiFi.onEvent( - [this, &networkReady](WiFiEvent_t event, WiFiEventInfo_t info) { - Log.debug("WiFi: lost IP address"); - networkReady.clear(); - eventQueue.offer(WiFiEvent::DISCONNECTED); - }, - ARDUINO_EVENT_WIFI_STA_LOST_IP); - WiFi.onEvent( - [this, &networkReady](WiFiEvent_t event, WiFiEventInfo_t info) { - Log.debug("WiFi: disconnected from %s, reason: %s", - String(info.wifi_sta_disconnected.ssid, info.wifi_sta_disconnected.ssid_len).c_str(), - WiFi.disconnectReasonName(static_cast(info.wifi_sta_disconnected.reason))); - networkReady.clear(); - eventQueue.offer(WiFiEvent::DISCONNECTED); - }, - ARDUINO_EVENT_WIFI_STA_DISCONNECTED); - WiFi.onEvent( - [&configPortalRunning](WiFiEvent_t event, WiFiEventInfo_t info) { - Log.debug("WiFi: provisioning started"); - configPortalRunning.set(); - }, - ARDUINO_EVENT_PROV_START); - WiFi.onEvent( - [](WiFiEvent_t event, WiFiEventInfo_t info) { - Log.debug("Received Wi-Fi credentials for SSID '%s'", - (const char*) info.prov_cred_recv.ssid); - }, - ARDUINO_EVENT_PROV_CRED_RECV); - WiFi.onEvent( - [](WiFiEvent_t event, WiFiEventInfo_t info) { - Log.debug("WiFi: provisioning failed because %s", - info.prov_fail_reason == NETWORK_PROV_WIFI_STA_AUTH_ERROR ? "authentication failed" : "AP not found"); - }, - ARDUINO_EVENT_PROV_CRED_FAIL); - WiFi.onEvent( - [](WiFiEvent_t event, WiFiEventInfo_t info) { - Log.debug("WiFi: provisioning successful"); - }, - ARDUINO_EVENT_PROV_CRED_SUCCESS); - WiFi.onEvent( - [&configPortalRunning](WiFiEvent_t event, WiFiEventInfo_t info) { - Log.debug("WiFi: provisioning finished"); - configPortalRunning.clear(); - }, - ARDUINO_EVENT_PROV_END); + // Initialize TCP/IP adapter and event loop + ESP_ERROR_CHECK(esp_netif_init()); + ESP_ERROR_CHECK(esp_event_loop_create_default()); + + // Create default WiFi station interface + esp_netif_create_default_wifi_sta(); + esp_netif_create_default_wifi_ap(); + + // Initialize WiFi + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); + ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); + + // Register event handlers + ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &WiFiDriver::onWiFiEvent, this)); + ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID, &WiFiDriver::onIpEvent, this)); + + // TODO Rewrite provisioning + // WiFi.onEvent( + // [&configPortalRunning](WiFiEvent_t event, WiFiEventInfo_t info) { + // Log.debug("WiFi: provisioning started"); + // configPortalRunning.set(); + // }, + // ARDUINO_EVENT_PROV_START); + // WiFi.onEvent( + // [](WiFiEvent_t event, WiFiEventInfo_t info) { + // Log.debug("Received Wi-Fi credentials for SSID '%s'", + // (const char*) info.prov_cred_recv.ssid); + // }, + // ARDUINO_EVENT_PROV_CRED_RECV); + // WiFi.onEvent( + // [](WiFiEvent_t event, WiFiEventInfo_t info) { + // Log.debug("WiFi: provisioning failed because %s", + // info.prov_fail_reason == NETWORK_PROV_WIFI_STA_AUTH_ERROR ? "authentication failed" : "AP not found"); + // }, + // ARDUINO_EVENT_PROV_CRED_FAIL); + // WiFi.onEvent( + // [](WiFiEvent_t event, WiFiEventInfo_t info) { + // Log.debug("WiFi: provisioning successful"); + // }, + // ARDUINO_EVENT_PROV_CRED_SUCCESS); + // WiFi.onEvent( + // [&configPortalRunning](WiFiEvent_t event, WiFiEventInfo_t info) { + // Log.debug("WiFi: provisioning finished"); + // configPortalRunning.clear(); + // }, + // ARDUINO_EVENT_PROV_END); Task::run("wifi", 3072, [this](Task&) { runLoop(); }); } + std::optional getSsid() { + Lock lock(metadataMutex); + return ssid; + } + + std::optional getIp() { + Lock lock(metadataMutex); + return ip.transform([](const esp_ip4_addr_t& ip) { + char ipString[16]; + esp_ip4addr_ntoa(&ip, ipString, sizeof(ipString)); + return String(ipString); + }); + } + private: + // Event handler for WiFi events + static void onWiFiEvent(void* arg, esp_event_base_t eventBase, int32_t eventId, void* eventData) { + auto* driver = static_cast(arg); + switch (eventId) { + case WIFI_EVENT_STA_CONNECTED: { + auto event = static_cast(eventData); + String ssid(event->ssid, event->ssid_len); + { + Lock lock(driver->metadataMutex); + driver->ssid = ssid; + } + Log.debug("WiFi: Connected to the AP %s", + ssid.c_str()); + break; + } + case WIFI_EVENT_STA_DISCONNECTED: { + auto event = static_cast(eventData); + driver->networkReady.clear(); + driver->eventQueue.offer(WiFiEvent::DISCONNECTED); + { + Lock lock(driver->metadataMutex); + driver->ssid.reset(); + } + Log.debug("WiFi: Disconnected from the AP %s, reason: %d", + String(event->ssid, event->ssid_len).c_str(), event->reason); + break; + } + } + } + + // Event handler for IP events + static void onIpEvent(void* arg, esp_event_base_t eventBase, int32_t eventId, void* eventData) { + auto* driver = static_cast(arg); + switch (eventId) { + case IP_EVENT_STA_GOT_IP: { + auto* event = static_cast(eventData); + driver->networkReady.set(); + driver->eventQueue.offer(WiFiEvent::CONNECTED); + { + Lock lock(driver->metadataMutex); + driver->ip = event->ip_info.ip; + } + Log.debug("WiFi: Got IP - " IPSTR, IP2STR(&event->ip_info.ip)); + break; + } + case IP_EVENT_STA_LOST_IP: { + driver->networkReady.clear(); + driver->eventQueue.offer(WiFiEvent::DISCONNECTED); + { + Lock lock(driver->metadataMutex); + driver->ip.reset(); + } + Log.debug("WiFi: Lost IP"); + break; + } + } + } + inline void runLoop() { int clients = 0; + bool connected = false; while (true) { - auto event = eventQueue.pollIn(WIFI_CHECK_INTERVAL); - if (event.has_value()) { + for (auto event = eventQueue.pollIn(WIFI_CHECK_INTERVAL); event.has_value(); event = eventQueue.poll()) { switch (event.value()) { case WiFiEvent::CONNECTED: + connected = true; break; case WiFiEvent::DISCONNECTED: + connected = false; break; case WiFiEvent::WANTS_CONNECT: clients++; @@ -128,7 +173,6 @@ class WiFiDriver { } } - bool connected = WiFi.isConnected(); if (clients > 0) { networkRequested.set(); if (!connected && !configPortalRunning.isSet()) { @@ -146,43 +190,51 @@ class WiFiDriver { } void connect() { - // Print wifi status - Log.debug("WiFi status: %d", - WiFi.status()); #ifdef WOKWI Log.debug("Skipping WiFi provisioning on Wokwi"); - // Use Wokwi WiFi network - WiFi.begin("Wokwi-GUEST", "", 6); -#else - StringPrint qr; -// BLE Provisioning using the ESP SoftAP Prov works fine for any BLE SoC, including ESP32, ESP32S3 and ESP32C3. -#if CONFIG_BLUEDROID_ENABLED && !defined(USE_SOFT_AP) - Log.debug("Begin Provisioning using BLE"); - // Sample uuid that user can pass during provisioning using BLE - uint8_t uuid[16] = { 0xb4, 0xdf, 0x5a, 0x1c, 0x3f, 0x6b, 0xf4, 0xbf, 0xea, 0x4a, 0x82, 0x03, 0x04, 0x90, 0x1a, 0x02 }; - WiFiProv.beginProvision( - NETWORK_PROV_SCHEME_BLE, NETWORK_PROV_SCHEME_HANDLER_FREE_BLE, NETWORK_PROV_SECURITY_1, pop, hostname.c_str(), serviceKey, uuid, resetProvisioned); - WiFiProv.printQR(hostname.c_str(), pop, "ble", qr); + wifi_config_t wifi_config = { + .sta = { + .ssid = "Wokwi-GUEST", + .password = "", + .channel = 6 } + }; + + setWiFiMode(WIFI_MODE_STA, wifi_config); + ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config)); + ESP_ERROR_CHECK(esp_wifi_start()); + ESP_ERROR_CHECK(esp_wifi_connect()); #else - Log.debug("Begin Provisioning using Soft AP"); - WiFiProv.beginProvision( - NETWORK_PROV_SCHEME_SOFTAP, NETWORK_PROV_SCHEME_HANDLER_NONE, NETWORK_PROV_SECURITY_1, pop, hostname.c_str(), serviceKey, nullptr, resetProvisioned); - WiFiProv.printQR(hostname.c_str(), pop, "softap", qr); -#endif - Log.debug("%s", - qr.buffer.c_str()); + // TODO Rewrite provisioning + // WiFiProv.beginProvision( + // NETWORK_PROV_SCHEME_SOFTAP, NETWORK_PROV_SCHEME_HANDLER_NONE, NETWORK_PROV_SECURITY_1, pop, hostname.c_str(), serviceKey, nullptr, resetProvisioned); + // WiFiProv.printQR(hostname.c_str(), pop, "softap", qr); + // Log.debug("%s", + // qr.buffer.c_str()); #endif } + // TODO This should probably be about setting STA only + void setWiFiMode(wifi_mode_t mode, wifi_config_t& config) { + ESP_ERROR_CHECK(esp_wifi_set_mode(mode)); + + if (powerSaveMode) { + auto listenInterval = 50; + Log.debug("WiFi enabling power save mode, listen interval: %d", + listenInterval); + ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_MAX_MODEM)); + config.sta.listen_interval = listenInterval; + } + } + static constexpr const char* pop = "abcd1234"; static constexpr const char* serviceKey = nullptr; static constexpr const bool resetProvisioned = false; void disconnect() { networkReady.clear(); - if (!WiFi.disconnect(true)) { - Log.error("WiFi: failed to shut down"); - } + Log.debug("WiFi: Disconnecting"); + ESP_ERROR_CHECK(esp_wifi_disconnect()); + ESP_ERROR_CHECK(esp_wifi_stop()); } StateSource& acquire() { @@ -213,6 +265,10 @@ class WiFiDriver { static constexpr milliseconds WIFI_QUEUE_TIMEOUT = 1s; static constexpr milliseconds WIFI_CHECK_INTERVAL = 5s; + Mutex metadataMutex; + std::optional ssid; + std::optional ip; + friend class WiFiConnection; };