From bad32f33ce4a75c434d6a877e8c7e05e87f6b1be Mon Sep 17 00:00:00 2001 From: Patedam Date: Mon, 2 Mar 2026 16:18:50 -0500 Subject: [PATCH] feat: Implement Ethernet and Wi-Fi network connectivity with LED status indication and configuration. --- Provider/main/Kconfig.projbuild | 27 +++++++ Provider/main/connect.cpp | 131 +++++++++++++++++++++++++++++++- Provider/main/led_status.cpp | 22 ++++-- Provider/main/main.cpp | 74 +++++++++++++++--- Provider/sdkconfig.defaults | 0 5 files changed, 237 insertions(+), 17 deletions(-) create mode 100644 Provider/main/Kconfig.projbuild create mode 100644 Provider/sdkconfig.defaults diff --git a/Provider/main/Kconfig.projbuild b/Provider/main/Kconfig.projbuild new file mode 100644 index 0000000..10f83d8 --- /dev/null +++ b/Provider/main/Kconfig.projbuild @@ -0,0 +1,27 @@ +menu "CalendarInk Network Configuration" + + config CALENDINK_WIFI_SSID + string "WiFi SSID" + default "" + help + SSID (network name) for the WiFi connection. + + config CALENDINK_WIFI_PASSWORD + string "WiFi Password" + default "" + help + Password for the WiFi connection. + + config CALENDINK_ETH_RETRIES + int "Maximum Ethernet Connection Retries" + default 5 + help + Number of times to retry the Ethernet connection before falling back to WiFi. + + config CALENDINK_WIFI_RETRIES + int "Maximum WiFi Connection Retries" + default 5 + help + Number of times to retry the WiFi connection before failing completely. + +endmenu diff --git a/Provider/main/connect.cpp b/Provider/main/connect.cpp index c50bd20..d443fb8 100644 --- a/Provider/main/connect.cpp +++ b/Provider/main/connect.cpp @@ -6,11 +6,16 @@ #include "esp_log.h" #include "esp_netif.h" #include "esp_system.h" +#include "esp_wifi.h" #include "ethernet_init.h" +#include +#include // Project includes #include "types.hpp" +// === Ethernet === + internal constexpr char kLogEthernet[] = "ETH"; internal constexpr char kNetifDescEth[] = "eth0"; @@ -19,6 +24,9 @@ internal uint8_t s_eth_count = 0; internal esp_eth_netif_glue_handle_t s_eth_glue = nullptr; internal esp_netif_t *s_eth_netif = nullptr; internal SemaphoreHandle_t s_semph_get_ip_addrs = nullptr; +#ifndef NDEBUG +internal bool s_ethernet_connected = false; +#endif void eth_on_got_ip(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { @@ -59,6 +67,12 @@ void teardown_ethernet() { } internal esp_err_t connect_ethernet(bool blockUntilIPAcquired) { +#ifndef NDEBUG + assert(!s_ethernet_connected && + "Ethernet connect called but already connected!"); + s_ethernet_connected = true; +#endif + s_semph_get_ip_addrs = xSemaphoreCreateBinary(); if (s_semph_get_ip_addrs == NULL) { return ESP_ERR_NO_MEM; @@ -101,6 +115,12 @@ internal esp_err_t connect_ethernet(bool blockUntilIPAcquired) { } void disconnect_ethernet() { +#ifndef NDEBUG + assert(s_ethernet_connected && + "Ethernet disconnect called but not connected!"); + s_ethernet_connected = false; +#endif + teardown_ethernet(); ESP_ERROR_CHECK(esp_unregister_shutdown_handler(&teardown_ethernet)); } @@ -114,4 +134,113 @@ internal esp_err_t check_ethernet_connection(uint32_t timeoutSeconds) { } else { return ESP_ERR_TIMEOUT; } -} \ No newline at end of file +} + +// === WiFi === + +internal constexpr char kLogWifi[] = "WIFI"; +internal esp_netif_t *s_wifi_netif = nullptr; +internal SemaphoreHandle_t s_semph_get_wifi_ip_addrs = nullptr; +#ifndef NDEBUG +internal bool s_wifi_connected = false; +#endif + +void wifi_event_handler(void *arg, esp_event_base_t event_base, + int32_t event_id, void *event_data) { + if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { + ESP_LOGI(kLogWifi, "WiFi disconnected, retrying..."); + esp_wifi_connect(); + } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { + ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data; + ESP_LOGI(kLogWifi, "Got IPv4 event: Interface \"%s\" address: " IPSTR, + esp_netif_get_desc(event->esp_netif), IP2STR(&event->ip_info.ip)); + xSemaphoreGive(s_semph_get_wifi_ip_addrs); + } +} + +void teardown_wifi() { + if (s_semph_get_wifi_ip_addrs != nullptr) { + vSemaphoreDelete(s_semph_get_wifi_ip_addrs); + s_semph_get_wifi_ip_addrs = nullptr; + } + + esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, + &wifi_event_handler); + esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, + &wifi_event_handler); + + if (s_wifi_netif) { + esp_wifi_disconnect(); + esp_wifi_stop(); + esp_wifi_deinit(); + esp_netif_destroy(s_wifi_netif); + s_wifi_netif = nullptr; + } +} + +internal esp_err_t connect_wifi(const char *ssid, const char *password, + bool blockUntilIPAcquired) { +#ifndef NDEBUG + assert(!s_wifi_connected && "WiFi connect called but already connected!"); + s_wifi_connected = true; +#endif + + s_semph_get_wifi_ip_addrs = xSemaphoreCreateBinary(); + if (s_semph_get_wifi_ip_addrs == NULL) { + return ESP_ERR_NO_MEM; + } + + // esp_netif_init() is already called by Ethernet, but safe to call multiple + // times or we can assume it's initialized. + s_wifi_netif = esp_netif_create_default_wifi_sta(); + + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); + + ESP_ERROR_CHECK(esp_event_handler_register( + WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &wifi_event_handler, NULL)); + ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, + &wifi_event_handler, NULL)); + + wifi_config_t wifi_config = {}; + strncpy((char *)wifi_config.sta.ssid, ssid, sizeof(wifi_config.sta.ssid) - 1); + strncpy((char *)wifi_config.sta.password, password, + sizeof(wifi_config.sta.password) - 1); + // Setting a default auth mode if password is provided + wifi_config.sta.threshold.authmode = + password[0] != '\0' ? WIFI_AUTH_WPA2_PSK : WIFI_AUTH_OPEN; + + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); + ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config)); + ESP_ERROR_CHECK(esp_wifi_start()); + ESP_ERROR_CHECK(esp_wifi_connect()); + + ESP_ERROR_CHECK(esp_register_shutdown_handler(&teardown_wifi)); + + if (blockUntilIPAcquired) { + ESP_LOGI(kLogWifi, "Waiting for IP address."); + xSemaphoreTake(s_semph_get_wifi_ip_addrs, portMAX_DELAY); + } + + return ESP_OK; +} + +void disconnect_wifi() { +#ifndef NDEBUG + assert(s_wifi_connected && "WiFi disconnect called but not connected!"); + s_wifi_connected = false; +#endif + + teardown_wifi(); + ESP_ERROR_CHECK(esp_unregister_shutdown_handler(&teardown_wifi)); +} + +internal esp_err_t check_wifi_connection(uint32_t timeoutSeconds) { + ESP_LOGI(kLogWifi, "Waiting for IP address for %d seconds.", timeoutSeconds); + if (xSemaphoreTake(s_semph_get_wifi_ip_addrs, + pdMS_TO_TICKS(timeoutSeconds * 1000))) { + return ESP_OK; + } else { + return ESP_ERR_TIMEOUT; + } +} diff --git a/Provider/main/led_status.cpp b/Provider/main/led_status.cpp index 4a4039f..f246756 100644 --- a/Provider/main/led_status.cpp +++ b/Provider/main/led_status.cpp @@ -7,7 +7,13 @@ // Could be a config but its the GPIO on my ESP32-S3-ETH #define LED_GPIO GPIO_NUM_21 -enum class led_status : uint8 { Connecting, Ready, Failed }; +enum class led_status : uint8 { + ConnectingEthernet, + ConnectingWifi, + ReadyEthernet, + ReadyWifi, + Failed +}; internal led_strip_handle_t led_strip; @@ -30,11 +36,17 @@ internal void destroy_led(void) { led_strip_clear(led_strip); } internal void set_led_status(led_status status) { switch (status) { - case led_status::Connecting: - led_strip_set_pixel(led_strip, 0, 255, 255, 0); + case led_status::ConnectingEthernet: + led_strip_set_pixel(led_strip, 0, 255, 165, 0); break; - case led_status::Ready: - led_strip_set_pixel(led_strip, 0, 0, 255, 0); + case led_status::ConnectingWifi: + led_strip_set_pixel(led_strip, 0, 148, 0, 211); + break; + case led_status::ReadyEthernet: + led_strip_set_pixel(led_strip, 0, 0, 255, 0); // Green + break; + case led_status::ReadyWifi: + led_strip_set_pixel(led_strip, 0, 0, 0, 255); // Blue break; case led_status::Failed: led_strip_set_pixel(led_strip, 0, 255, 0, 0); diff --git a/Provider/main/main.cpp b/Provider/main/main.cpp index 0d7ffcd..049bc2f 100644 --- a/Provider/main/main.cpp +++ b/Provider/main/main.cpp @@ -6,6 +6,7 @@ #include "esp_log.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" +#include "nvs_flash.h" #include "sdkconfig.h" #include "soc/gpio_num.h" @@ -15,16 +16,19 @@ // TODO : Make it configurable internal constexpr bool blockUntilEthernetEstablished = false; -internal constexpr uint8_t maxEthernetRetries = 5; +internal bool ethernetInitialized = false; +internal bool wifiInitialized = false; extern "C" void app_main() { printf("Hello, Worldi!\n"); + ESP_ERROR_CHECK(nvs_flash_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); setup_led(); - set_led_status(led_status::Connecting); + set_led_status(led_status::ConnectingEthernet); + ethernetInitialized = true; esp_err_t result = connect_ethernet(blockUntilEthernetEstablished); if (result != ESP_OK) { set_led_status(led_status::Failed); @@ -36,7 +40,7 @@ extern "C" void app_main() { if (!blockUntilEthernetEstablished) { uint8 retries = 1; do { - set_led_status(led_status::Connecting); + set_led_status(led_status::ConnectingEthernet); result = check_ethernet_connection(retries); if (result != ESP_OK) { @@ -45,25 +49,73 @@ extern "C" void app_main() { } retries++; - } while (result == ESP_ERR_TIMEOUT && retries <= maxEthernetRetries); + } while (result == ESP_ERR_TIMEOUT && + retries <= CONFIG_CALENDINK_ETH_RETRIES); } if (result != ESP_OK) { - // TODO : Wifi connection - // Needs to disconnect ethernet at that point if we go wifi - // If wifi failes -> total shutdown - goto shutdown; + printf("Ethernet failed, trying wifi\n"); + disconnect_ethernet(); + ethernetInitialized = false; + + set_led_status(led_status::ConnectingWifi); + wifiInitialized = true; + result = + connect_wifi(CONFIG_CALENDINK_WIFI_SSID, CONFIG_CALENDINK_WIFI_PASSWORD, + blockUntilEthernetEstablished); + if (result != ESP_OK) { + set_led_status(led_status::Failed); + vTaskDelay(pdMS_TO_TICKS(1000)); + goto shutdown; + } + + if (!blockUntilEthernetEstablished) { + uint8 retries = 1; + do { + set_led_status(led_status::ConnectingWifi); + result = check_wifi_connection(retries); + + if (result != ESP_OK) { + set_led_status(led_status::Failed); + vTaskDelay(pdMS_TO_TICKS(1000)); + } + + retries++; + } while (result == ESP_ERR_TIMEOUT && + retries <= CONFIG_CALENDINK_WIFI_RETRIES); + } + + if (result != ESP_OK) { + printf("Wifi failed.\n"); + goto shutdown; + } + + set_led_status(led_status::ReadyWifi); + printf("Will use Wifi!\n"); + } else { + set_led_status(led_status::ReadyEthernet); + printf("Will use Ethernet!\n"); } - set_led_status(led_status::Ready); - vTaskDelay(pdMS_TO_TICKS(1000)); + printf("Connected!\n"); + vTaskDelay(pdMS_TO_TICKS(5000)); // TODO Main loop shutdown: - disconnect_ethernet(); + printf("Shutting down.\n"); + + if (ethernetInitialized) { + disconnect_ethernet(); + ethernetInitialized = false; + } + if (wifiInitialized) { + disconnect_wifi(); + wifiInitialized = false; + } destroy_led(); ESP_ERROR_CHECK(esp_event_loop_delete_default()); + ESP_ERROR_CHECK(nvs_flash_deinit()); } diff --git a/Provider/sdkconfig.defaults b/Provider/sdkconfig.defaults new file mode 100644 index 0000000..e69de29