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 led_blink_number(int n, uint8_t r, uint8_t g, uint8_t b);
internal void blink_last_ip_octet(); internal void blink_last_ip_octet();
// Internal states
internal esp_netif_ip_info_t s_current_ip_info = {};
// === Ethernet === // === Ethernet ===
internal constexpr char kLogEthernet[] = "ETH"; 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_eth_netif_glue_handle_t s_eth_glue = nullptr;
internal esp_netif_t *s_eth_netif = nullptr; internal esp_netif_t *s_eth_netif = nullptr;
internal SemaphoreHandle_t s_semph_get_ip_addrs = 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 #ifndef NDEBUG
internal bool s_ethernet_connected = false; internal bool s_ethernet_connected = false;
#endif #endif
void eth_on_got_ip(void *arg, esp_event_base_t event_base, int32_t event_id, void ethernet_event_handler(void *arg, esp_event_base_t event_base,
void *event_data) { 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; ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
bool isEthernetNetif = if (event->esp_netif != s_eth_netif) {
strncmp(kNetifDescEth, esp_netif_get_desc(event->esp_netif),
strlen(kNetifDescEth) - 1) == 0;
if (!isEthernetNetif) {
return; return;
} }
ESP_LOGI(kLogEthernet, "Got IPv4 event: Interface \"%s\" address: " IPSTR, ESP_LOGI(kLogEthernet, "Got IPv4 event: Interface \"%s\" address: " IPSTR,
esp_netif_get_desc(event->esp_netif), IP2STR(&event->ip_info.ip)); esp_netif_get_desc(event->esp_netif), IP2STR(&event->ip_info.ip));
s_current_ip_info = event->ip_info;
xSemaphoreGive(s_semph_get_ip_addrs); 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() { 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) { if (s_semph_get_ip_addrs != nullptr) {
vSemaphoreDelete(s_semph_get_ip_addrs); vSemaphoreDelete(s_semph_get_ip_addrs);
s_semph_get_ip_addrs = nullptr; s_semph_get_ip_addrs = nullptr;
@@ -59,7 +79,9 @@ void teardown_ethernet() {
if (s_eth_netif) { if (s_eth_netif) {
ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_ETH_GOT_IP, 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_stop(s_eth_handles[0]));
ESP_ERROR_CHECK(esp_eth_del_netif_glue(s_eth_glue)); ESP_ERROR_CHECK(esp_eth_del_netif_glue(s_eth_glue));
esp_netif_destroy(s_eth_netif); esp_netif_destroy(s_eth_netif);
@@ -82,7 +104,8 @@ internal esp_err_t connect_ethernet(bool blockUntilIPAcquired) {
#endif #endif
s_semph_get_ip_addrs = xSemaphoreCreateBinary(); 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; return ESP_ERR_NO_MEM;
} }
// Connection is split in two steps. First we open the connection and ask for // 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 // Register user defined event handlers
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, 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])); 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) { 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.", ESP_LOGI(kLogEthernet, "Waiting for IP address for %d seconds.",
timeoutSeconds); timeoutSeconds);
if (xSemaphoreTake(s_semph_get_ip_addrs, 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 constexpr char kLogWifi[] = "WIFI";
internal esp_netif_t *s_wifi_netif = nullptr; internal esp_netif_t *s_wifi_netif = nullptr;
internal SemaphoreHandle_t s_semph_get_wifi_ip_addrs = 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 #ifndef NDEBUG
internal bool s_wifi_connected = false; internal bool s_wifi_connected = false;
#endif #endif
void wifi_event_handler(void *arg, esp_event_base_t event_base, void wifi_event_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data) { int32_t event_id, void *event_data) {
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_CONNECTED) {
ESP_LOGI(kLogWifi, "WiFi disconnected, retrying..."); ESP_LOGI(kLogWifi, "WiFi associated with AP.");
esp_wifi_connect(); 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) { } 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; 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_LOGI(kLogWifi, "Got IPv4 event: Interface \"%s\" address: " IPSTR,
esp_netif_get_desc(event->esp_netif), IP2STR(&event->ip_info.ip)); 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); xSemaphoreGive(s_semph_get_wifi_ip_addrs);
} }
} }
void teardown_wifi() { 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) { if (s_semph_get_wifi_ip_addrs != nullptr) {
vSemaphoreDelete(s_semph_get_wifi_ip_addrs); vSemaphoreDelete(s_semph_get_wifi_ip_addrs);
s_semph_get_wifi_ip_addrs = nullptr; 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, esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED,
&wifi_event_handler); &wifi_event_handler);
esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, 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 #endif
s_semph_get_wifi_ip_addrs = xSemaphoreCreateBinary(); 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; 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(); wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg)); 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( ESP_ERROR_CHECK(esp_event_handler_register(
WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &wifi_event_handler, NULL)); WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &wifi_event_handler, NULL));
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, 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) { 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); ESP_LOGI(kLogWifi, "Waiting for IP address for %d seconds.", timeoutSeconds);
if (xSemaphoreTake(s_semph_get_wifi_ip_addrs, if (xSemaphoreTake(s_semph_get_wifi_ip_addrs,
pdMS_TO_TICKS(timeoutSeconds * 1000))) { 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) { internal esp_err_t get_ip_info(esp_netif_ip_info_t *ip_info) {
if (s_eth_netif) { *ip_info = s_current_ip_info;
return esp_netif_get_ip_info(s_eth_netif, ip_info); return ESP_OK;
} else if (s_wifi_netif) {
return esp_netif_get_ip_info(s_wifi_netif, ip_info);
}
return ESP_FAIL;
} }
internal void led_blink_number(int n, uint8_t r, uint8_t g, uint8_t b); 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 t = (last_octet / 10) % 10;
int u = last_octet % 10; int u = last_octet % 10;
if (h > 0) {
led_blink_number(h, 255, 255, 255); led_blink_number(h, 255, 255, 255);
}
if (h > 0 || t > 0) {
led_blink_number(t, 255, 255, 255); led_blink_number(t, 255, 255, 255);
}
led_blink_number(u, 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; struct stat file_stat;
if (stat(filepath, &file_stat) == -1) { if (stat(filepath, &file_stat) == -1) {
// Try gzipped first, then fallback to index.html // 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); snprintf(filepath_gz, sizeof(filepath_gz), "%s.gz", filepath);
if (stat(filepath_gz, &file_stat) == 0) { if (stat(filepath_gz, &file_stat) == 0) {
strlcpy(filepath, filepath_gz, sizeof(filepath)); strlcpy(filepath, filepath_gz, sizeof(filepath));

View File

@@ -48,6 +48,11 @@ extern "C" void app_main() {
set_led_status(led_status::ConnectingEthernet); set_led_status(led_status::ConnectingEthernet);
result = check_ethernet_connection(retries); 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) { if (result != ESP_OK) {
set_led_status(led_status::Failed); set_led_status(led_status::Failed);
vTaskDelay(pdMS_TO_TICKS(1000)); vTaskDelay(pdMS_TO_TICKS(1000));

View File

@@ -2,4 +2,4 @@
nvs, data, nvs, 0x9000, 0x6000, nvs, data, nvs, 0x9000, 0x6000,
phy_init, data, phy, 0xf000, 0x1000, phy_init, data, phy, 0xf000, 0x1000,
factory, app, factory, 0x10000, 1M, factory, app, factory, 0x10000, 1M,
www, data, littlefs, , 64K, www, data, littlefs, , 128K,
1 # Name Type SubType Offset Size Flags
2 nvs data nvs 0x9000 0x6000
3 phy_init data phy 0xf000 0x1000
4 factory app factory 0x10000 1M
5 www data littlefs 64K 128K