From 797330d6ab411eda34a4639b625c64e0c5306f03 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 16 Jun 2025 17:28:04 +0200 Subject: [PATCH 1/3] Disable Ethernet loop polling when connected and stable --- esphome/components/ethernet/ethernet_component.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/esphome/components/ethernet/ethernet_component.cpp b/esphome/components/ethernet/ethernet_component.cpp index fe96973924..f2e465c144 100644 --- a/esphome/components/ethernet/ethernet_component.cpp +++ b/esphome/components/ethernet/ethernet_component.cpp @@ -274,6 +274,9 @@ void EthernetComponent::loop() { ESP_LOGW(TAG, "Connection lost; reconnecting"); this->state_ = EthernetComponentState::CONNECTING; this->start_connect_(); + } else { + // When connected and stable, disable the loop to save CPU cycles + this->disable_loop(); } break; } @@ -397,11 +400,13 @@ void EthernetComponent::eth_event_handler(void *arg, esp_event_base_t event_base case ETHERNET_EVENT_START: event_name = "ETH started"; global_eth_component->started_ = true; + global_eth_component->enable_loop(); break; case ETHERNET_EVENT_STOP: event_name = "ETH stopped"; global_eth_component->started_ = false; global_eth_component->connected_ = false; + global_eth_component->enable_loop(); break; case ETHERNET_EVENT_CONNECTED: event_name = "ETH connected"; @@ -409,6 +414,7 @@ void EthernetComponent::eth_event_handler(void *arg, esp_event_base_t event_base case ETHERNET_EVENT_DISCONNECTED: event_name = "ETH disconnected"; global_eth_component->connected_ = false; + global_eth_component->enable_loop(); break; default: return; @@ -452,6 +458,8 @@ void EthernetComponent::start_connect_() { #endif /* USE_NETWORK_IPV6 */ this->connect_begin_ = millis(); this->status_set_warning("waiting for IP configuration"); + // Enable loop during connection phase + this->enable_loop(); esp_err_t err; err = esp_netif_set_hostname(this->eth_netif_, App.get_name().c_str()); @@ -620,6 +628,7 @@ bool EthernetComponent::powerdown() { } this->connected_ = false; this->started_ = false; + // No need to enable_loop() here as this is only called during shutdown/reboot if (this->phy_->pwrctl(this->phy_, false) != ESP_OK) { ESP_LOGE(TAG, "Error powering down ethernet PHY"); return false; From 1179ab33f2f5dbf067e6876d334ed6e003c29d8c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 18 Jun 2025 12:51:57 +0200 Subject: [PATCH 2/3] tweaks --- .../ethernet/ethernet_component.cpp | 27 ++++++++++++------- .../components/ethernet/ethernet_component.h | 2 ++ 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/esphome/components/ethernet/ethernet_component.cpp b/esphome/components/ethernet/ethernet_component.cpp index f2e465c144..8ae15250c4 100644 --- a/esphome/components/ethernet/ethernet_component.cpp +++ b/esphome/components/ethernet/ethernet_component.cpp @@ -405,16 +405,14 @@ void EthernetComponent::eth_event_handler(void *arg, esp_event_base_t event_base case ETHERNET_EVENT_STOP: event_name = "ETH stopped"; global_eth_component->started_ = false; - global_eth_component->connected_ = false; - global_eth_component->enable_loop(); + global_eth_component->set_connected_(false); // This will enable the loop break; case ETHERNET_EVENT_CONNECTED: event_name = "ETH connected"; break; case ETHERNET_EVENT_DISCONNECTED: event_name = "ETH disconnected"; - global_eth_component->connected_ = false; - global_eth_component->enable_loop(); + global_eth_component->set_connected_(false); // This will enable the loop break; default: return; @@ -430,9 +428,9 @@ void EthernetComponent::got_ip_event_handler(void *arg, esp_event_base_t event_b ESP_LOGV(TAG, "[Ethernet event] ETH Got IP " IPSTR, IP2STR(&ip_info->ip)); global_eth_component->got_ipv4_address_ = true; #if USE_NETWORK_IPV6 && (USE_NETWORK_MIN_IPV6_ADDR_COUNT > 0) - global_eth_component->connected_ = global_eth_component->ipv6_count_ >= USE_NETWORK_MIN_IPV6_ADDR_COUNT; + global_eth_component->set_connected_(global_eth_component->ipv6_count_ >= USE_NETWORK_MIN_IPV6_ADDR_COUNT); #else - global_eth_component->connected_ = true; + global_eth_component->set_connected_(true); #endif /* USE_NETWORK_IPV6 */ } @@ -443,10 +441,10 @@ void EthernetComponent::got_ip6_event_handler(void *arg, esp_event_base_t event_ ESP_LOGV(TAG, "[Ethernet event] ETH Got IPv6: " IPV6STR, IPV62STR(event->ip6_info.ip)); global_eth_component->ipv6_count_ += 1; #if (USE_NETWORK_MIN_IPV6_ADDR_COUNT > 0) - global_eth_component->connected_ = - global_eth_component->got_ipv4_address_ && (global_eth_component->ipv6_count_ >= USE_NETWORK_MIN_IPV6_ADDR_COUNT); + global_eth_component->set_connected_(global_eth_component->got_ipv4_address_ && + (global_eth_component->ipv6_count_ >= USE_NETWORK_MIN_IPV6_ADDR_COUNT)); #else - global_eth_component->connected_ = global_eth_component->got_ipv4_address_; + global_eth_component->set_connected_(global_eth_component->got_ipv4_address_); #endif } #endif /* USE_NETWORK_IPV6 */ @@ -523,6 +521,15 @@ void EthernetComponent::start_connect_() { bool EthernetComponent::is_connected() { return this->state_ == EthernetComponentState::CONNECTED; } +void EthernetComponent::set_connected_(bool connected) { + if (this->connected_ != connected) { + this->connected_ = connected; + // Always enable loop when connection state changes + // so the state machine can process the state change + this->enable_loop(); + } +} + void EthernetComponent::dump_connect_params_() { esp_netif_ip_info_t ip; esp_netif_get_ip_info(this->eth_netif_, &ip); @@ -626,7 +633,7 @@ bool EthernetComponent::powerdown() { ESP_LOGE(TAG, "Ethernet PHY not assigned"); return false; } - this->connected_ = false; + this->set_connected_(false); this->started_ = false; // No need to enable_loop() here as this is only called during shutdown/reboot if (this->phy_->pwrctl(this->phy_, false) != ESP_OK) { diff --git a/esphome/components/ethernet/ethernet_component.h b/esphome/components/ethernet/ethernet_component.h index 7a205d89f0..ebcd4ded81 100644 --- a/esphome/components/ethernet/ethernet_component.h +++ b/esphome/components/ethernet/ethernet_component.h @@ -104,6 +104,8 @@ class EthernetComponent : public Component { void ksz8081_set_clock_reference_(esp_eth_mac_t *mac); /// @brief Set arbitratry PHY registers from config. void write_phy_register_(esp_eth_mac_t *mac, PHYRegister register_data); + /// @brief Safely set connected state and ensure loop is enabled for state machine processing + void set_connected_(bool connected); std::string use_address_; #ifdef USE_ETHERNET_SPI From bd50a7f1ab42b80a27a58a9c63dc787171b2eb52 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 18 Jun 2025 14:33:58 +0200 Subject: [PATCH 3/3] cleanup --- .../ethernet/ethernet_component.cpp | 33 +++++++++---------- .../components/ethernet/ethernet_component.h | 2 -- 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/esphome/components/ethernet/ethernet_component.cpp b/esphome/components/ethernet/ethernet_component.cpp index 8ae15250c4..47db61eea5 100644 --- a/esphome/components/ethernet/ethernet_component.cpp +++ b/esphome/components/ethernet/ethernet_component.cpp @@ -400,19 +400,21 @@ void EthernetComponent::eth_event_handler(void *arg, esp_event_base_t event_base case ETHERNET_EVENT_START: event_name = "ETH started"; global_eth_component->started_ = true; - global_eth_component->enable_loop(); + global_eth_component->enable_loop_soon_any_context(); break; case ETHERNET_EVENT_STOP: event_name = "ETH stopped"; global_eth_component->started_ = false; - global_eth_component->set_connected_(false); // This will enable the loop + global_eth_component->connected_ = false; + global_eth_component->enable_loop_soon_any_context(); // Enable loop when connection state changes break; case ETHERNET_EVENT_CONNECTED: event_name = "ETH connected"; break; case ETHERNET_EVENT_DISCONNECTED: event_name = "ETH disconnected"; - global_eth_component->set_connected_(false); // This will enable the loop + global_eth_component->connected_ = false; + global_eth_component->enable_loop_soon_any_context(); // Enable loop when connection state changes break; default: return; @@ -428,9 +430,11 @@ void EthernetComponent::got_ip_event_handler(void *arg, esp_event_base_t event_b ESP_LOGV(TAG, "[Ethernet event] ETH Got IP " IPSTR, IP2STR(&ip_info->ip)); global_eth_component->got_ipv4_address_ = true; #if USE_NETWORK_IPV6 && (USE_NETWORK_MIN_IPV6_ADDR_COUNT > 0) - global_eth_component->set_connected_(global_eth_component->ipv6_count_ >= USE_NETWORK_MIN_IPV6_ADDR_COUNT); + global_eth_component->connected_ = global_eth_component->ipv6_count_ >= USE_NETWORK_MIN_IPV6_ADDR_COUNT; + global_eth_component->enable_loop_soon_any_context(); // Enable loop when connection state changes #else - global_eth_component->set_connected_(true); + global_eth_component->connected_ = true; + global_eth_component->enable_loop_soon_any_context(); // Enable loop when connection state changes #endif /* USE_NETWORK_IPV6 */ } @@ -441,10 +445,12 @@ void EthernetComponent::got_ip6_event_handler(void *arg, esp_event_base_t event_ ESP_LOGV(TAG, "[Ethernet event] ETH Got IPv6: " IPV6STR, IPV62STR(event->ip6_info.ip)); global_eth_component->ipv6_count_ += 1; #if (USE_NETWORK_MIN_IPV6_ADDR_COUNT > 0) - global_eth_component->set_connected_(global_eth_component->got_ipv4_address_ && - (global_eth_component->ipv6_count_ >= USE_NETWORK_MIN_IPV6_ADDR_COUNT)); + global_eth_component->connected_ = + global_eth_component->got_ipv4_address_ && (global_eth_component->ipv6_count_ >= USE_NETWORK_MIN_IPV6_ADDR_COUNT); + global_eth_component->enable_loop_soon_any_context(); // Enable loop when connection state changes #else - global_eth_component->set_connected_(global_eth_component->got_ipv4_address_); + global_eth_component->connected_ = global_eth_component->got_ipv4_address_; + global_eth_component->enable_loop_soon_any_context(); // Enable loop when connection state changes #endif } #endif /* USE_NETWORK_IPV6 */ @@ -521,15 +527,6 @@ void EthernetComponent::start_connect_() { bool EthernetComponent::is_connected() { return this->state_ == EthernetComponentState::CONNECTED; } -void EthernetComponent::set_connected_(bool connected) { - if (this->connected_ != connected) { - this->connected_ = connected; - // Always enable loop when connection state changes - // so the state machine can process the state change - this->enable_loop(); - } -} - void EthernetComponent::dump_connect_params_() { esp_netif_ip_info_t ip; esp_netif_get_ip_info(this->eth_netif_, &ip); @@ -633,7 +630,7 @@ bool EthernetComponent::powerdown() { ESP_LOGE(TAG, "Ethernet PHY not assigned"); return false; } - this->set_connected_(false); + this->connected_ = false; this->started_ = false; // No need to enable_loop() here as this is only called during shutdown/reboot if (this->phy_->pwrctl(this->phy_, false) != ESP_OK) { diff --git a/esphome/components/ethernet/ethernet_component.h b/esphome/components/ethernet/ethernet_component.h index ebcd4ded81..7a205d89f0 100644 --- a/esphome/components/ethernet/ethernet_component.h +++ b/esphome/components/ethernet/ethernet_component.h @@ -104,8 +104,6 @@ class EthernetComponent : public Component { void ksz8081_set_clock_reference_(esp_eth_mac_t *mac); /// @brief Set arbitratry PHY registers from config. void write_phy_register_(esp_eth_mac_t *mac, PHYRegister register_data); - /// @brief Safely set connected state and ensure loop is enabled for state machine processing - void set_connected_(bool connected); std::string use_address_; #ifdef USE_ETHERNET_SPI