Fixing ethernet and wifi connection to be more robust

This commit is contained in:
2026-03-03 13:01:56 -05:00
parent bdf4a73cf2
commit 8b24d9ed83
4 changed files with 100 additions and 29 deletions

View File

@@ -21,6 +21,9 @@ internal esp_err_t get_ip_info(esp_netif_ip_info_t *ip_info);
internal void led_blink_number(int n, uint8_t r, uint8_t g, uint8_t b);
internal void blink_last_ip_octet();
// Internal states
internal esp_netif_ip_info_t s_current_ip_info = {};
// === Ethernet ===
internal constexpr char kLogEthernet[] = "ETH";
@@ -31,26 +34,43 @@ 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;
internal SemaphoreHandle_t s_semph_eth_link = nullptr;
internal volatile bool s_eth_link_up = false;
#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) {
ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
bool isEthernetNetif =
strncmp(kNetifDescEth, esp_netif_get_desc(event->esp_netif),
strlen(kNetifDescEth) - 1) == 0;
if (!isEthernetNetif) {
return;
}
ESP_LOGI(kLogEthernet, "Got IPv4 event: Interface \"%s\" address: " IPSTR,
esp_netif_get_desc(event->esp_netif), IP2STR(&event->ip_info.ip));
void ethernet_event_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data) {
if (event_base == IP_EVENT && event_id == IP_EVENT_ETH_GOT_IP) {
ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
if (event->esp_netif != s_eth_netif) {
return;
}
ESP_LOGI(kLogEthernet, "Got IPv4 event: Interface \"%s\" address: " IPSTR,
esp_netif_get_desc(event->esp_netif), IP2STR(&event->ip_info.ip));
xSemaphoreGive(s_semph_get_ip_addrs);
s_current_ip_info = event->ip_info;
xSemaphoreGive(s_semph_get_ip_addrs);
} else if (event_base == ETH_EVENT && event_id == ETHERNET_EVENT_CONNECTED) {
ESP_LOGI(kLogEthernet, "Ethernet Link Up");
s_eth_link_up = true;
if (s_semph_eth_link) {
xSemaphoreGive(s_semph_eth_link);
}
} else if (event_base == ETH_EVENT &&
event_id == ETHERNET_EVENT_DISCONNECTED) {
ESP_LOGI(kLogEthernet, "Ethernet Link Down");
s_eth_link_up = false;
}
}
void teardown_ethernet() {
if (s_semph_eth_link != nullptr) {
vSemaphoreDelete(s_semph_eth_link);
s_semph_eth_link = nullptr;
}
if (s_semph_get_ip_addrs != nullptr) {
vSemaphoreDelete(s_semph_get_ip_addrs);
s_semph_get_ip_addrs = nullptr;
@@ -59,7 +79,9 @@ void teardown_ethernet() {
if (s_eth_netif) {
ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_ETH_GOT_IP,
&eth_on_got_ip));
&ethernet_event_handler));
ESP_ERROR_CHECK(esp_event_handler_unregister(ETH_EVENT, ESP_EVENT_ANY_ID,
&ethernet_event_handler));
ESP_ERROR_CHECK(esp_eth_stop(s_eth_handles[0]));
ESP_ERROR_CHECK(esp_eth_del_netif_glue(s_eth_glue));
esp_netif_destroy(s_eth_netif);
@@ -82,7 +104,8 @@ internal esp_err_t connect_ethernet(bool blockUntilIPAcquired) {
#endif
s_semph_get_ip_addrs = xSemaphoreCreateBinary();
if (s_semph_get_ip_addrs == NULL) {
s_semph_eth_link = xSemaphoreCreateBinary();
if (s_semph_get_ip_addrs == NULL || s_semph_eth_link == NULL) {
return ESP_ERR_NO_MEM;
}
// Connection is split in two steps. First we open the connection and ask for
@@ -107,7 +130,9 @@ internal esp_err_t connect_ethernet(bool blockUntilIPAcquired) {
// Register user defined event handlers
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP,
&eth_on_got_ip, NULL));
&ethernet_event_handler, NULL));
ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID,
&ethernet_event_handler, NULL));
ESP_ERROR_CHECK(esp_eth_start(s_eth_handles[0]));
@@ -135,6 +160,15 @@ void disconnect_ethernet() {
}
internal esp_err_t check_ethernet_connection(uint32_t timeoutSeconds) {
// Wait up to 5000ms for the physical link to negotiate
if (!s_eth_link_up) {
if (!xSemaphoreTake(s_semph_eth_link, pdMS_TO_TICKS(5000))) {
ESP_LOGE(kLogEthernet,
"No physical Ethernet link detected. Skipping DHCP wait.");
return ESP_ERR_INVALID_STATE; // Special error to skip retries
}
}
ESP_LOGI(kLogEthernet, "Waiting for IP address for %d seconds.",
timeoutSeconds);
if (xSemaphoreTake(s_semph_get_ip_addrs,
@@ -151,30 +185,50 @@ internal esp_err_t check_ethernet_connection(uint32_t timeoutSeconds) {
internal constexpr char kLogWifi[] = "WIFI";
internal esp_netif_t *s_wifi_netif = nullptr;
internal SemaphoreHandle_t s_semph_get_wifi_ip_addrs = nullptr;
internal SemaphoreHandle_t s_semph_wifi_link = nullptr;
internal volatile bool s_wifi_link_up = false;
#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();
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_CONNECTED) {
ESP_LOGI(kLogWifi, "WiFi associated with AP.");
s_wifi_link_up = true;
if (s_semph_wifi_link) {
xSemaphoreGive(s_semph_wifi_link);
}
} else if (event_base == WIFI_EVENT &&
event_id == WIFI_EVENT_STA_DISCONNECTED) {
ESP_LOGI(kLogWifi, "WiFi disconnected.");
s_wifi_link_up = false;
} 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;
if (event->esp_netif != s_wifi_netif) {
return;
}
ESP_LOGI(kLogWifi, "Got IPv4 event: Interface \"%s\" address: " IPSTR,
esp_netif_get_desc(event->esp_netif), IP2STR(&event->ip_info.ip));
s_current_ip_info = event->ip_info;
xSemaphoreGive(s_semph_get_wifi_ip_addrs);
}
}
void teardown_wifi() {
if (s_semph_wifi_link != nullptr) {
vSemaphoreDelete(s_semph_wifi_link);
s_semph_wifi_link = nullptr;
}
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_CONNECTED,
&wifi_event_handler);
esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED,
&wifi_event_handler);
esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP,
@@ -197,7 +251,8 @@ internal esp_err_t connect_wifi(const char *ssid, const char *password,
#endif
s_semph_get_wifi_ip_addrs = xSemaphoreCreateBinary();
if (s_semph_get_wifi_ip_addrs == NULL) {
s_semph_wifi_link = xSemaphoreCreateBinary();
if (s_semph_get_wifi_ip_addrs == NULL || s_semph_wifi_link == NULL) {
return ESP_ERR_NO_MEM;
}
@@ -208,6 +263,8 @@ internal esp_err_t connect_wifi(const char *ssid, const char *password,
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_CONNECTED, &wifi_event_handler, NULL));
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,
@@ -248,6 +305,15 @@ void disconnect_wifi() {
}
internal esp_err_t check_wifi_connection(uint32_t timeoutSeconds) {
// Wait up to 10000ms for the physical link to associate with the AP
if (!s_wifi_link_up) {
if (!xSemaphoreTake(s_semph_wifi_link, pdMS_TO_TICKS(10000))) {
ESP_LOGE(kLogWifi,
"Failed to associate with WiFi AP. Skipping DHCP wait.");
return ESP_ERR_INVALID_STATE; // Special error to skip retries
}
}
ESP_LOGI(kLogWifi, "Waiting for IP address for %d seconds.", timeoutSeconds);
if (xSemaphoreTake(s_semph_get_wifi_ip_addrs,
pdMS_TO_TICKS(timeoutSeconds * 1000))) {
@@ -259,12 +325,8 @@ internal esp_err_t check_wifi_connection(uint32_t timeoutSeconds) {
}
internal esp_err_t get_ip_info(esp_netif_ip_info_t *ip_info) {
if (s_eth_netif) {
return esp_netif_get_ip_info(s_eth_netif, ip_info);
} else if (s_wifi_netif) {
return esp_netif_get_ip_info(s_wifi_netif, ip_info);
}
return ESP_FAIL;
*ip_info = s_current_ip_info;
return ESP_OK;
}
internal void led_blink_number(int n, uint8_t r, uint8_t g, uint8_t b);
@@ -280,8 +342,12 @@ internal void blink_last_ip_octet() {
int t = (last_octet / 10) % 10;
int u = last_octet % 10;
led_blink_number(h, 255, 255, 255);
led_blink_number(t, 255, 255, 255);
if (h > 0) {
led_blink_number(h, 255, 255, 255);
}
if (h > 0 || t > 0) {
led_blink_number(t, 255, 255, 255);
}
led_blink_number(u, 255, 255, 255);
}
}

View File

@@ -58,7 +58,7 @@ internal esp_err_t static_file_handler(httpd_req_t *req) {
struct stat file_stat;
if (stat(filepath, &file_stat) == -1) {
// Try gzipped first, then fallback to index.html
char filepath_gz[FILE_PATH_MAX];
char filepath_gz[FILE_PATH_MAX + 16];
snprintf(filepath_gz, sizeof(filepath_gz), "%s.gz", filepath);
if (stat(filepath_gz, &file_stat) == 0) {
strlcpy(filepath, filepath_gz, sizeof(filepath));

View File

@@ -48,6 +48,11 @@ extern "C" void app_main() {
set_led_status(led_status::ConnectingEthernet);
result = check_ethernet_connection(retries);
if (result == ESP_ERR_INVALID_STATE) {
printf("Ethernet cable not plugged in, skipping retries.\n");
break;
}
if (result != ESP_OK) {
set_led_status(led_status::Failed);
vTaskDelay(pdMS_TO_TICKS(1000));