From c93b892ccc71aa6fb2794fbf2f08556339ed7f3b Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Wed, 16 Jul 2025 15:50:42 -0400 Subject: [PATCH 1/2] Bump ESP32 IDF version to 5.4.2 and Arduino version to 3.2.1 (#9305) --- esphome/components/esp32/__init__.py | 16 ++++++++-------- esphome/components/ethernet/esp_eth_phy_jl1101.c | 5 +++++ esphome/components/ethernet/ethernet_component.h | 4 ++++ esphome/core/defines.h | 3 +-- platformio.ini | 8 ++++---- 5 files changed, 22 insertions(+), 14 deletions(-) diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index fdc469e419..97d77431ef 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -309,19 +309,19 @@ def _format_framework_espidf_version( # The default/recommended arduino framework version # - https://github.com/espressif/arduino-esp32/releases -RECOMMENDED_ARDUINO_FRAMEWORK_VERSION = cv.Version(3, 1, 3) +RECOMMENDED_ARDUINO_FRAMEWORK_VERSION = cv.Version(3, 2, 1) # The platform-espressif32 version to use for arduino frameworks # - https://github.com/pioarduino/platform-espressif32/releases -ARDUINO_PLATFORM_VERSION = cv.Version(53, 3, 13) +ARDUINO_PLATFORM_VERSION = cv.Version(54, 3, 21) # The default/recommended esp-idf framework version # - https://github.com/espressif/esp-idf/releases # - https://api.registry.platformio.org/v3/packages/platformio/tool/framework-espidf -RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION = cv.Version(5, 3, 2) +RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION = cv.Version(5, 4, 2) # The platformio/espressif32 version to use for esp-idf frameworks # - https://github.com/platformio/platform-espressif32/releases # - https://api.registry.platformio.org/v3/packages/platformio/platform/espressif32 -ESP_IDF_PLATFORM_VERSION = cv.Version(53, 3, 13) +ESP_IDF_PLATFORM_VERSION = cv.Version(54, 3, 21) # List based on https://registry.platformio.org/tools/platformio/framework-espidf/versions SUPPORTED_PLATFORMIO_ESP_IDF_5X = [ @@ -356,8 +356,8 @@ SUPPORTED_PIOARDUINO_ESP_IDF_5X = [ def _arduino_check_versions(value): value = value.copy() lookups = { - "dev": (cv.Version(3, 1, 3), "https://github.com/espressif/arduino-esp32.git"), - "latest": (cv.Version(3, 1, 3), None), + "dev": (cv.Version(3, 2, 1), "https://github.com/espressif/arduino-esp32.git"), + "latest": (cv.Version(3, 2, 1), None), "recommended": (RECOMMENDED_ARDUINO_FRAMEWORK_VERSION, None), } @@ -395,8 +395,8 @@ def _arduino_check_versions(value): def _esp_idf_check_versions(value): value = value.copy() lookups = { - "dev": (cv.Version(5, 3, 2), "https://github.com/espressif/esp-idf.git"), - "latest": (cv.Version(5, 3, 2), None), + "dev": (cv.Version(5, 4, 2), "https://github.com/espressif/esp-idf.git"), + "latest": (cv.Version(5, 2, 2), None), "recommended": (RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION, None), } diff --git a/esphome/components/ethernet/esp_eth_phy_jl1101.c b/esphome/components/ethernet/esp_eth_phy_jl1101.c index 4f31e0a9fd..5e73e99101 100644 --- a/esphome/components/ethernet/esp_eth_phy_jl1101.c +++ b/esphome/components/ethernet/esp_eth_phy_jl1101.c @@ -25,6 +25,9 @@ #include "driver/gpio.h" #include "esp_rom_gpio.h" #include "esp_rom_sys.h" +#include "esp_idf_version.h" + +#if defined(USE_ARDUINO) || ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 4, 2) static const char *TAG = "jl1101"; #define PHY_CHECK(a, str, goto_tag, ...) \ @@ -336,4 +339,6 @@ esp_eth_phy_t *esp_eth_phy_new_jl1101(const eth_phy_config_t *config) { err: return NULL; } + +#endif /* USE_ARDUINO */ #endif /* USE_ESP32 */ diff --git a/esphome/components/ethernet/ethernet_component.h b/esphome/components/ethernet/ethernet_component.h index 1b347946f5..bdcda6afb4 100644 --- a/esphome/components/ethernet/ethernet_component.h +++ b/esphome/components/ethernet/ethernet_component.h @@ -11,6 +11,7 @@ #include "esp_eth_mac.h" #include "esp_netif.h" #include "esp_mac.h" +#include "esp_idf_version.h" namespace esphome { namespace ethernet { @@ -153,7 +154,10 @@ class EthernetComponent : public Component { // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) extern EthernetComponent *global_eth_component; + +#if defined(USE_ARDUINO) || ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 4, 2) extern "C" esp_eth_phy_t *esp_eth_phy_new_jl1101(const eth_phy_config_t *config); +#endif } // namespace ethernet } // namespace esphome diff --git a/esphome/core/defines.h b/esphome/core/defines.h index 7ddb3436cd..6f100a18d1 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -163,12 +163,11 @@ #define USE_WIFI_11KV_SUPPORT #ifdef USE_ARDUINO -#define USE_ARDUINO_VERSION_CODE VERSION_CODE(3, 1, 3) +#define USE_ARDUINO_VERSION_CODE VERSION_CODE(3, 2, 1) #define USE_ETHERNET #endif #ifdef USE_ESP_IDF -#define USE_ESP_IDF_VERSION_CODE VERSION_CODE(5, 3, 2) #define USE_MICRO_WAKE_WORD #define USE_MICRO_WAKE_WORD_VAD #if defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32H2) diff --git a/platformio.ini b/platformio.ini index 8fcc578103..f731d8b548 100644 --- a/platformio.ini +++ b/platformio.ini @@ -125,9 +125,9 @@ extra_scripts = post:esphome/components/esp8266/post_build.py.script ; This are common settings for the ESP32 (all variants) using Arduino. [common:esp32-arduino] extends = common:arduino -platform = https://github.com/pioarduino/platform-espressif32/releases/download/53.03.13/platform-espressif32.zip +platform = https://github.com/pioarduino/platform-espressif32/releases/download/54.03.21/platform-espressif32.zip platform_packages = - pioarduino/framework-arduinoespressif32@https://github.com/espressif/arduino-esp32/releases/download/3.1.3/esp32-3.1.3.zip + pioarduino/framework-arduinoespressif32@https://github.com/espressif/arduino-esp32/releases/download/3.2.1/esp32-3.2.1.zip framework = arduino lib_deps = @@ -161,9 +161,9 @@ extra_scripts = post:esphome/components/esp32/post_build.py.script ; This are common settings for the ESP32 (all variants) using IDF. [common:esp32-idf] extends = common:idf -platform = https://github.com/pioarduino/platform-espressif32/releases/download/53.03.13/platform-espressif32.zip +platform = https://github.com/pioarduino/platform-espressif32/releases/download/54.03.21/platform-espressif32.zip platform_packages = - pioarduino/framework-espidf@https://github.com/pioarduino/esp-idf/releases/download/v5.3.2/esp-idf-v5.3.2.zip + pioarduino/framework-espidf@https://github.com/pioarduino/esp-idf/releases/download/v5.4.2/esp-idf-v5.4.2.zip framework = espidf lib_deps = From cb0ef0b54afd1424e400c8af0da793c6424314f4 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 16 Jul 2025 10:56:29 -1000 Subject: [PATCH 2/2] Fix lwIP thread safety assertion failures on ESP32 --- esphome/components/e131/e131_packet.cpp | 8 ++++- esphome/components/esp32/helpers.cpp | 23 ++++++++++++++ .../ethernet/ethernet_component.cpp | 11 +++++-- esphome/components/mqtt/mqtt_client.cpp | 12 ++++--- .../wifi/wifi_component_esp32_arduino.cpp | 31 ++++++------------- esphome/core/helpers.h | 18 +++++++++++ 6 files changed, 74 insertions(+), 29 deletions(-) diff --git a/esphome/components/e131/e131_packet.cpp b/esphome/components/e131/e131_packet.cpp index b8fa73b707..e663a3d0fc 100644 --- a/esphome/components/e131/e131_packet.cpp +++ b/esphome/components/e131/e131_packet.cpp @@ -4,6 +4,7 @@ #include "esphome/components/network/ip_address.h" #include "esphome/core/log.h" #include "esphome/core/util.h" +#include "esphome/core/helpers.h" #include #include @@ -71,7 +72,11 @@ bool E131Component::join_igmp_groups_() { ip4_addr_t multicast_addr = network::IPAddress(239, 255, ((universe.first >> 8) & 0xff), ((universe.first >> 0) & 0xff)); - auto err = igmp_joingroup(IP4_ADDR_ANY4, &multicast_addr); + err_t err; + { + LwIPLock lock; + err = igmp_joingroup(IP4_ADDR_ANY4, &multicast_addr); + } if (err) { ESP_LOGW(TAG, "IGMP join for %d universe of E1.31 failed. Multicast might not work.", universe.first); @@ -104,6 +109,7 @@ void E131Component::leave_(int universe) { if (listen_method_ == E131_MULTICAST) { ip4_addr_t multicast_addr = network::IPAddress(239, 255, ((universe >> 8) & 0xff), ((universe >> 0) & 0xff)); + LwIPLock lock; igmp_leavegroup(IP4_ADDR_ANY4, &multicast_addr); } diff --git a/esphome/components/esp32/helpers.cpp b/esphome/components/esp32/helpers.cpp index 310e7bd94a..cfc648e1d1 100644 --- a/esphome/components/esp32/helpers.cpp +++ b/esphome/components/esp32/helpers.cpp @@ -30,6 +30,29 @@ void Mutex::unlock() { xSemaphoreGive(this->handle_); } IRAM_ATTR InterruptLock::InterruptLock() { portDISABLE_INTERRUPTS(); } IRAM_ATTR InterruptLock::~InterruptLock() { portENABLE_INTERRUPTS(); } +#ifdef CONFIG_LWIP_TCPIP_CORE_LOCKING +#include "lwip/priv/tcpip_priv.h" +#endif + +LwIPLock::LwIPLock() { +#ifdef CONFIG_LWIP_TCPIP_CORE_LOCKING + // Only lock if we're not already in the TCPIP thread + if (!sys_thread_tcpip(LWIP_CORE_LOCK_QUERY_HOLDER)) { + LOCK_TCPIP_CORE(); + locked_ = true; + } +#endif +} + +LwIPLock::~LwIPLock() { +#ifdef CONFIG_LWIP_TCPIP_CORE_LOCKING + // Only unlock if we locked it + if (locked_ && sys_thread_tcpip(LWIP_CORE_LOCK_QUERY_HOLDER)) { + UNLOCK_TCPIP_CORE(); + } +#endif +} + void get_mac_address_raw(uint8_t *mac) { // NOLINT(readability-non-const-parameter) #if defined(CONFIG_SOC_IEEE802154_SUPPORTED) // When CONFIG_SOC_IEEE802154_SUPPORTED is defined, esp_efuse_mac_get_default diff --git a/esphome/components/ethernet/ethernet_component.cpp b/esphome/components/ethernet/ethernet_component.cpp index f8c2f3a72e..ff37dcfdd1 100644 --- a/esphome/components/ethernet/ethernet_component.cpp +++ b/esphome/components/ethernet/ethernet_component.cpp @@ -420,6 +420,7 @@ network::IPAddresses EthernetComponent::get_ip_addresses() { } network::IPAddress EthernetComponent::get_dns_address(uint8_t num) { + LwIPLock lock; const ip_addr_t *dns_ip = dns_getserver(num); return dns_ip; } @@ -527,6 +528,7 @@ void EthernetComponent::start_connect_() { ESPHL_ERROR_CHECK(err, "DHCPC set IP info error"); if (this->manual_ip_.has_value()) { + LwIPLock lock; if (this->manual_ip_->dns1.is_set()) { ip_addr_t d; d = this->manual_ip_->dns1; @@ -559,8 +561,13 @@ bool EthernetComponent::is_connected() { return this->state_ == EthernetComponen void EthernetComponent::dump_connect_params_() { esp_netif_ip_info_t ip; esp_netif_get_ip_info(this->eth_netif_, &ip); - const ip_addr_t *dns_ip1 = dns_getserver(0); - const ip_addr_t *dns_ip2 = dns_getserver(1); + const ip_addr_t *dns_ip1; + const ip_addr_t *dns_ip2; + { + LwIPLock lock; + dns_ip1 = dns_getserver(0); + dns_ip2 = dns_getserver(1); + } ESP_LOGCONFIG(TAG, " IP Address: %s\n" diff --git a/esphome/components/mqtt/mqtt_client.cpp b/esphome/components/mqtt/mqtt_client.cpp index 5b93789447..f3e57a66be 100644 --- a/esphome/components/mqtt/mqtt_client.cpp +++ b/esphome/components/mqtt/mqtt_client.cpp @@ -193,13 +193,17 @@ void MQTTClientComponent::start_dnslookup_() { this->dns_resolve_error_ = false; this->dns_resolved_ = false; ip_addr_t addr; + err_t err; + { + LwIPLock lock; #if USE_NETWORK_IPV6 - err_t err = dns_gethostbyname_addrtype(this->credentials_.address.c_str(), &addr, - MQTTClientComponent::dns_found_callback, this, LWIP_DNS_ADDRTYPE_IPV6_IPV4); + err = dns_gethostbyname_addrtype(this->credentials_.address.c_str(), &addr, MQTTClientComponent::dns_found_callback, + this, LWIP_DNS_ADDRTYPE_IPV6_IPV4); #else - err_t err = dns_gethostbyname_addrtype(this->credentials_.address.c_str(), &addr, - MQTTClientComponent::dns_found_callback, this, LWIP_DNS_ADDRTYPE_IPV4); + err = dns_gethostbyname_addrtype(this->credentials_.address.c_str(), &addr, MQTTClientComponent::dns_found_callback, + this, LWIP_DNS_ADDRTYPE_IPV4); #endif /* USE_NETWORK_IPV6 */ + } switch (err) { case ERR_OK: { // Got IP immediately diff --git a/esphome/components/wifi/wifi_component_esp32_arduino.cpp b/esphome/components/wifi/wifi_component_esp32_arduino.cpp index a7877eb90b..b3167c5696 100644 --- a/esphome/components/wifi/wifi_component_esp32_arduino.cpp +++ b/esphome/components/wifi/wifi_component_esp32_arduino.cpp @@ -20,10 +20,6 @@ #include "lwip/dns.h" #include "lwip/err.h" -#ifdef CONFIG_LWIP_TCPIP_CORE_LOCKING -#include "lwip/priv/tcpip_priv.h" -#endif - #include "esphome/core/application.h" #include "esphome/core/hal.h" #include "esphome/core/helpers.h" @@ -295,25 +291,16 @@ bool WiFiComponent::wifi_sta_ip_config_(optional manual_ip) { } if (!manual_ip.has_value()) { -// sntp_servermode_dhcp lwip/sntp.c (Required to lock TCPIP core functionality!) -// https://github.com/esphome/issues/issues/6591 -// https://github.com/espressif/arduino-esp32/issues/10526 -#ifdef CONFIG_LWIP_TCPIP_CORE_LOCKING - if (!sys_thread_tcpip(LWIP_CORE_LOCK_QUERY_HOLDER)) { - LOCK_TCPIP_CORE(); + // sntp_servermode_dhcp lwip/sntp.c (Required to lock TCPIP core functionality!) + // https://github.com/esphome/issues/issues/6591 + // https://github.com/espressif/arduino-esp32/issues/10526 + { + LwIPLock lock; + // lwIP starts the SNTP client if it gets an SNTP server from DHCP. We don't need the time, and more importantly, + // the built-in SNTP client has a memory leak in certain situations. Disable this feature. + // https://github.com/esphome/issues/issues/2299 + sntp_servermode_dhcp(false); } -#endif - - // lwIP starts the SNTP client if it gets an SNTP server from DHCP. We don't need the time, and more importantly, - // the built-in SNTP client has a memory leak in certain situations. Disable this feature. - // https://github.com/esphome/issues/issues/2299 - sntp_servermode_dhcp(false); - -#ifdef CONFIG_LWIP_TCPIP_CORE_LOCKING - if (sys_thread_tcpip(LWIP_CORE_LOCK_QUERY_HOLDER)) { - UNLOCK_TCPIP_CORE(); - } -#endif // No manual IP is set; use DHCP client if (dhcp_status != ESP_NETIF_DHCP_STARTED) { diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index 488ea3cdb3..fd1fbc3d05 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -684,6 +684,24 @@ class InterruptLock { #endif }; +/** Helper class to lock the lwIP TCPIP core when making lwIP API calls from non-TCPIP threads. + * + * This is needed on multi-threaded platforms (ESP32) when CONFIG_LWIP_TCPIP_CORE_LOCKING is enabled. + * It ensures thread-safe access to lwIP APIs. + * + * @note This follows the same pattern as InterruptLock - platform-specific implementations in helpers.cpp + */ +class LwIPLock { + public: + LwIPLock(); + ~LwIPLock(); + + protected: +#if defined(USE_ESP32) + bool locked_; +#endif +}; + /** Helper class to request `loop()` to be called as fast as possible. * * Usually the ESPHome main loop runs at 60 Hz, sleeping in between invocations of `loop()` if necessary. When a higher