feat: Implement Ethernet and Wi-Fi network connectivity with LED status indication and configuration.

This commit is contained in:
2026-03-02 16:18:50 -05:00
parent 056c86644f
commit bad32f33ce
5 changed files with 237 additions and 17 deletions

View File

@@ -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 <assert.h>
#include <string.h>
// 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;
}
}
}
// === 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;
}
}