From b636f2613df87025c4afc9b320f17321b5dc3753 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 15 Mar 2026 16:56:08 -1000 Subject: [PATCH 1/9] [ethernet] Add IDF 6.0 registry component dependencies ESP-IDF 6.0 moved individual ethernet PHY and SPI MAC/PHY drivers out of the builtin esp_eth component into the Espressif Component Registry. Changes: - Add USE_ETHERNET_W5500/DM9051 defines, replacing CONFIG_ETH_SPI_ETHERNET_* Kconfig options that were removed in IDF 6.0 - Conditionally pull registry components (lan87xx, rtl8201, dp83848, ip101, ksz80xx, w5500, dm9051) via add_idf_component() on IDF >= 6.0 - Skip CONFIG_ETH_SPI_ETHERNET_{TYPE} sdkconfig on IDF >= 6.0 - Fix emac_rmii_clock_gpio_t cast using decltype (enum removed in IDF 6.0) - Use esp_eth_phy_new_generic() for OPENETH and JL1101 on IDF >= 6.0 - Fix JL1101 to work on IDF 5.4.2+ with PlatformIO (was previously falling through to mark_failed due to overly restrictive guard) - Exclude custom JL1101 driver on IDF >= 6.0 - Add registry components to idf_component.yml for static analysis - Update defines.h for static analysis All changes maintain backward compatibility with IDF 5.x. --- esphome/components/ethernet/__init__.py | 37 ++++++++++++++++++- .../components/ethernet/esp_eth_phy_jl1101.c | 3 +- .../ethernet/ethernet_component_esp32.cpp | 34 +++++++++++------ esphome/core/defines.h | 4 +- esphome/idf_component.yml | 28 ++++++++++++++ 5 files changed, 91 insertions(+), 15 deletions(-) diff --git a/esphome/components/ethernet/__init__.py b/esphome/components/ethernet/__init__.py index 83bef4d91c..ba8830948a 100644 --- a/esphome/components/ethernet/__init__.py +++ b/esphome/components/ethernet/__init__.py @@ -1,3 +1,4 @@ +from dataclasses import dataclass import logging from esphome import automation, pins @@ -126,9 +127,32 @@ _PHY_TYPE_TO_DEFINE = { "JL1101": "USE_ETHERNET_JL1101", "KSZ8081": "USE_ETHERNET_KSZ8081", "KSZ8081RNA": "USE_ETHERNET_KSZ8081", + "W5500": "USE_ETHERNET_W5500", + "DM9051": "USE_ETHERNET_DM9051", "LAN8670": "USE_ETHERNET_LAN8670", } + +@dataclass(frozen=True) +class IDFRegistryComponent: + """An ESP-IDF component from the Espressif Component Registry.""" + + name: str + version: str + + +# IDF 6.0 moved per-chip PHY/MAC drivers to the Espressif Component Registry. +_IDF6_ETHERNET_COMPONENTS: dict[str, IDFRegistryComponent] = { + "LAN8720": IDFRegistryComponent("espressif/lan87xx", "1.0.0"), + "RTL8201": IDFRegistryComponent("espressif/rtl8201", "1.0.1"), + "DP83848": IDFRegistryComponent("espressif/dp83848", "1.0.0"), + "IP101": IDFRegistryComponent("espressif/ip101", "1.0.0"), + "KSZ8081": IDFRegistryComponent("espressif/ksz80xx", "1.0.0"), + "KSZ8081RNA": IDFRegistryComponent("espressif/ksz80xx", "1.0.0"), + "W5500": IDFRegistryComponent("espressif/w5500", "1.0.1"), + "DM9051": IDFRegistryComponent("espressif/dm9051", "1.0.0"), +} + SPI_ETHERNET_TYPES = ["W5500", "DM9051"] SPI_ETHERNET_DEFAULT_POLLING_INTERVAL = TimePeriodMilliseconds(milliseconds=10) @@ -439,6 +463,7 @@ async def _to_code_esp32(var, config): from esphome.components.esp32 import ( add_idf_component, add_idf_sdkconfig_option, + idf_version, include_builtin_idf_component, ) @@ -459,7 +484,11 @@ async def _to_code_esp32(var, config): cg.add_define("USE_ETHERNET_SPI") add_idf_sdkconfig_option("CONFIG_ETH_USE_SPI_ETHERNET", True) - add_idf_sdkconfig_option(f"CONFIG_ETH_SPI_ETHERNET_{config[CONF_TYPE]}", True) + # CONFIG_ETH_SPI_ETHERNET_{TYPE} Kconfig options were removed in IDF 6.0 + if idf_version() < cv.Version(6, 0, 0): + add_idf_sdkconfig_option( + f"CONFIG_ETH_SPI_ETHERNET_{config[CONF_TYPE]}", True + ) elif config[CONF_TYPE] == "OPENETH": cg.add_define("USE_ETHERNET_OPENETH") add_idf_sdkconfig_option("CONFIG_ETH_USE_OPENETH", True) @@ -491,6 +520,12 @@ async def _to_code_esp32(var, config): # Add LAN867x 10BASE-T1S PHY support component add_idf_component(name="espressif/lan867x", ref="2.0.0") + # IDF 6.0 moved per-chip PHY/MAC drivers to the Espressif Component Registry + if idf_version() >= cv.Version(6, 0, 0) and ( + component := _IDF6_ETHERNET_COMPONENTS.get(config[CONF_TYPE]) + ): + add_idf_component(name=component.name, ref=component.version) + def _final_validate_rmii_pins(config: ConfigType) -> None: """Validate that RMII pins are not used by other components.""" diff --git a/esphome/components/ethernet/esp_eth_phy_jl1101.c b/esphome/components/ethernet/esp_eth_phy_jl1101.c index b81d8227d4..692f759eed 100644 --- a/esphome/components/ethernet/esp_eth_phy_jl1101.c +++ b/esphome/components/ethernet/esp_eth_phy_jl1101.c @@ -29,7 +29,8 @@ #include "esp_rom_sys.h" #include "esp_idf_version.h" -#if defined(USE_ETHERNET_JL1101) && (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 4, 2) || !defined(PLATFORMIO)) +#if defined(USE_ETHERNET_JL1101) && ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(6, 0, 0) && \ + (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 4, 2) || !defined(PLATFORMIO)) static const char *TAG = "jl1101"; #define PHY_CHECK(a, str, goto_tag, ...) \ diff --git a/esphome/components/ethernet/ethernet_component_esp32.cpp b/esphome/components/ethernet/ethernet_component_esp32.cpp index ac8680f3e1..82ac69f4b9 100644 --- a/esphome/components/ethernet/ethernet_component_esp32.cpp +++ b/esphome/components/ethernet/ethernet_component_esp32.cpp @@ -164,21 +164,21 @@ void EthernetComponent::setup() { .post_cb = nullptr, }; -#if CONFIG_ETH_SPI_ETHERNET_W5500 +#ifdef USE_ETHERNET_W5500 eth_w5500_config_t w5500_config = ETH_W5500_DEFAULT_CONFIG(host, &devcfg); #endif -#if CONFIG_ETH_SPI_ETHERNET_DM9051 +#ifdef USE_ETHERNET_DM9051 eth_dm9051_config_t dm9051_config = ETH_DM9051_DEFAULT_CONFIG(host, &devcfg); #endif -#if CONFIG_ETH_SPI_ETHERNET_W5500 +#ifdef USE_ETHERNET_W5500 w5500_config.int_gpio_num = this->interrupt_pin_; #ifdef USE_ETHERNET_SPI_POLLING_SUPPORT w5500_config.poll_period_ms = this->polling_interval_; #endif #endif -#if CONFIG_ETH_SPI_ETHERNET_DM9051 +#ifdef USE_ETHERNET_DM9051 dm9051_config.int_gpio_num = this->interrupt_pin_; #ifdef USE_ETHERNET_SPI_POLLING_SUPPORT dm9051_config.poll_period_ms = this->polling_interval_; @@ -204,7 +204,8 @@ void EthernetComponent::setup() { esp32_emac_config.smi_mdio_gpio_num = this->mdio_pin_; #endif esp32_emac_config.clock_config.rmii.clock_mode = this->clk_mode_; - esp32_emac_config.clock_config.rmii.clock_gpio = (emac_rmii_clock_gpio_t) this->clk_pin_; + esp32_emac_config.clock_config.rmii.clock_gpio = + static_cast(this->clk_pin_); esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&esp32_emac_config, &mac_config); #endif @@ -213,7 +214,11 @@ void EthernetComponent::setup() { #ifdef USE_ETHERNET_OPENETH case ETHERNET_TYPE_OPENETH: { phy_config.autonego_timeout_ms = 1000; +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(6, 0, 0) + this->phy_ = esp_eth_phy_new_generic(&phy_config); +#else this->phy_ = esp_eth_phy_new_dp83848(&phy_config); +#endif break; } #endif @@ -242,9 +247,16 @@ void EthernetComponent::setup() { break; } #endif -#if defined(USE_ETHERNET_JL1101) && (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 4, 2) || !defined(PLATFORMIO)) +#ifdef USE_ETHERNET_JL1101 case ETHERNET_TYPE_JL1101: { +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(6, 0, 0) + // No registry component exists; generic PHY driver works for JL1101 + this->phy_ = esp_eth_phy_new_generic(&phy_config); +#else + // IDF < 5.4.2 or non-PlatformIO: uses custom ESPHome driver (esp_eth_phy_jl1101.c) + // IDF 5.4.2+ with PlatformIO: uses builtin esp_eth_phy_new_jl1101() this->phy_ = esp_eth_phy_new_jl1101(&phy_config); +#endif break; } #endif @@ -263,14 +275,14 @@ void EthernetComponent::setup() { #endif #endif #ifdef USE_ETHERNET_SPI -#if CONFIG_ETH_SPI_ETHERNET_W5500 +#ifdef USE_ETHERNET_W5500 case ETHERNET_TYPE_W5500: { mac = esp_eth_mac_new_w5500(&w5500_config, &mac_config); this->phy_ = esp_eth_phy_new_w5500(&phy_config); break; } #endif -#if CONFIG_ETH_SPI_ETHERNET_DM9051 +#ifdef USE_ETHERNET_DM9051 case ETHERNET_TYPE_DM9051: { mac = esp_eth_mac_new_dm9051(&dm9051_config, &mac_config); this->phy_ = esp_eth_phy_new_dm9051(&phy_config); @@ -354,7 +366,7 @@ void EthernetComponent::dump_config() { eth_type = "IP101"; break; #endif -#if defined(USE_ETHERNET_JL1101) && (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 4, 2) || !defined(PLATFORMIO)) +#ifdef USE_ETHERNET_JL1101 case ETHERNET_TYPE_JL1101: eth_type = "JL1101"; break; @@ -368,12 +380,12 @@ void EthernetComponent::dump_config() { eth_type = "KSZ8081RNA"; break; #endif -#if CONFIG_ETH_SPI_ETHERNET_W5500 +#ifdef USE_ETHERNET_W5500 case ETHERNET_TYPE_W5500: eth_type = "W5500"; break; #endif -#if CONFIG_ETH_SPI_ETHERNET_DM9051 +#ifdef USE_ETHERNET_DM9051 case ETHERNET_TYPE_DM9051: eth_type = "DM9051"; break; diff --git a/esphome/core/defines.h b/esphome/core/defines.h index 75e63b1462..e3d443dda4 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -281,8 +281,8 @@ #define USE_ETHERNET_SPI #define USE_ETHERNET_SPI_POLLING_SUPPORT #define USE_ETHERNET_OPENETH -#define CONFIG_ETH_SPI_ETHERNET_W5500 1 -#define CONFIG_ETH_SPI_ETHERNET_DM9051 1 +#define USE_ETHERNET_W5500 +#define USE_ETHERNET_DM9051 #define CONFIG_ETH_USE_ESP32_EMAC 1 #define USE_ETHERNET_MANUAL_IP #define USE_ETHERNET_IP_STATE_LISTENERS diff --git a/esphome/idf_component.yml b/esphome/idf_component.yml index 1e2d452919..ef33ccfc04 100644 --- a/esphome/idf_component.yml +++ b/esphome/idf_component.yml @@ -29,6 +29,34 @@ dependencies: version: "2.0.0" rules: - if: "target in [esp32, esp32p4]" + espressif/lan87xx: + version: "1.0.0" + rules: + - if: "idf_version >=6.0.0 && target in [esp32, esp32p4]" + espressif/rtl8201: + version: "1.0.1" + rules: + - if: "idf_version >=6.0.0 && target in [esp32, esp32p4]" + espressif/dp83848: + version: "1.0.0" + rules: + - if: "idf_version >=6.0.0 && target in [esp32, esp32p4]" + espressif/ip101: + version: "1.0.0" + rules: + - if: "idf_version >=6.0.0 && target in [esp32, esp32p4]" + espressif/ksz80xx: + version: "1.0.0" + rules: + - if: "idf_version >=6.0.0 && target in [esp32, esp32p4]" + espressif/w5500: + version: "1.0.1" + rules: + - if: "idf_version >=6.0.0" + espressif/dm9051: + version: "1.0.0" + rules: + - if: "idf_version >=6.0.0" espressif/esp_tinyusb: version: "2.1.1" rules: From d27b3e8c0f386a11596df3e3fe4ee2a1dd304a1f Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 15 Mar 2026 17:01:17 -1000 Subject: [PATCH 2/9] [ethernet] Add explicit PHY/MAC includes and exclude JL1101 driver when unused - Add explicit includes for per-chip PHY/MAC headers (required on IDF 6.0 where they are no longer pulled in via esp_eth.h, also works on IDF 5.x) - Exclude esp_eth_phy_jl1101.c from compilation on IDF >= 6.0 (uses generic PHY) and IDF >= 5.4.2 with PlatformIO (uses builtin driver) --- esphome/components/ethernet/__init__.py | 24 ++++++++++++++++++- .../ethernet/ethernet_component_esp32.cpp | 23 ++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/esphome/components/ethernet/__init__.py b/esphome/components/ethernet/__init__.py index ba8830948a..20e79279ff 100644 --- a/esphome/components/ethernet/__init__.py +++ b/esphome/components/ethernet/__init__.py @@ -600,11 +600,33 @@ async def final_step(): cg.add_define("ESPHOME_ETHERNET_IP_STATE_LISTENERS", ip_state_count) -FILTER_SOURCE_FILES = filter_source_files_from_platform( +_platform_filter = filter_source_files_from_platform( { "ethernet_component_esp32.cpp": { PlatformFramework.ESP32_IDF, PlatformFramework.ESP32_ARDUINO, }, + "esp_eth_phy_jl1101.c": { + PlatformFramework.ESP32_IDF, + PlatformFramework.ESP32_ARDUINO, + }, } ) + + +def _filter_source_files() -> list[str]: + excluded = _platform_filter() + if not CORE.is_esp32: + return excluded + from esphome.components.esp32 import idf_version + + # Custom JL1101 driver not needed on IDF >= 6.0 (uses generic PHY) + # or IDF >= 5.4.2 with PlatformIO (uses builtin driver) + if idf_version() >= cv.Version(6, 0, 0) or ( + idf_version() >= cv.Version(5, 4, 2) and CORE.using_platformio + ): + excluded.append("esp_eth_phy_jl1101.c") + return excluded + + +FILTER_SOURCE_FILES = _filter_source_files diff --git a/esphome/components/ethernet/ethernet_component_esp32.cpp b/esphome/components/ethernet/ethernet_component_esp32.cpp index 82ac69f4b9..212a60356e 100644 --- a/esphome/components/ethernet/ethernet_component_esp32.cpp +++ b/esphome/components/ethernet/ethernet_component_esp32.cpp @@ -10,9 +10,32 @@ #include #include "esp_event.h" +#ifdef USE_ETHERNET_LAN8720 +#include "esp_eth_phy_lan87xx.h" +#endif +#ifdef USE_ETHERNET_RTL8201 +#include "esp_eth_phy_rtl8201.h" +#endif +#ifdef USE_ETHERNET_DP83848 +#include "esp_eth_phy_dp83848.h" +#endif +#ifdef USE_ETHERNET_IP101 +#include "esp_eth_phy_ip101.h" +#endif +#ifdef USE_ETHERNET_KSZ8081 +#include "esp_eth_phy_ksz80xx.h" +#endif #ifdef USE_ETHERNET_LAN8670 #include "esp_eth_phy_lan867x.h" #endif +#ifdef USE_ETHERNET_W5500 +#include "esp_eth_mac_w5500.h" +#include "esp_eth_phy_w5500.h" +#endif +#ifdef USE_ETHERNET_DM9051 +#include "esp_eth_mac_dm9051.h" +#include "esp_eth_phy_dm9051.h" +#endif #ifdef USE_ETHERNET_SPI #include From 6e8ef553818e41d3c70a7cc0bd5cb617798bb369 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 15 Mar 2026 17:03:21 -1000 Subject: [PATCH 3/9] [ethernet] Fix AttributeError: CORE has no using_platformio Only exclude esp_eth_phy_jl1101.c at the Python level on IDF >= 6.0. On IDF 5.4.2+ with PlatformIO, the file's own preprocessor guard (checking the C-level PLATFORMIO define) compiles it to empty. --- esphome/components/ethernet/__init__.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/esphome/components/ethernet/__init__.py b/esphome/components/ethernet/__init__.py index 20e79279ff..3cc39c6e9e 100644 --- a/esphome/components/ethernet/__init__.py +++ b/esphome/components/ethernet/__init__.py @@ -621,10 +621,9 @@ def _filter_source_files() -> list[str]: from esphome.components.esp32 import idf_version # Custom JL1101 driver not needed on IDF >= 6.0 (uses generic PHY) - # or IDF >= 5.4.2 with PlatformIO (uses builtin driver) - if idf_version() >= cv.Version(6, 0, 0) or ( - idf_version() >= cv.Version(5, 4, 2) and CORE.using_platformio - ): + # On IDF 5.4.2+ with PlatformIO, the .c file compiles to empty via + # its own preprocessor guard (PLATFORMIO is a C-level define) + if idf_version() >= cv.Version(6, 0, 0): excluded.append("esp_eth_phy_jl1101.c") return excluded From 0183d2ca615c8297e398111c5cfb072d44f9ab6f Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 15 Mar 2026 17:09:59 -1000 Subject: [PATCH 4/9] [ethernet] Only compile JL1101 driver when needed - Exclude esp_eth_phy_jl1101.c when JL1101 is not configured - Exclude on IDF 5.4.2-5.x where pioarduino has it builtin - Keep custom driver for IDF < 5.4.2 and IDF 6.0+ (pioarduino may not have patched JL1101 into their IDF 6.0 fork yet) - Always use esp_eth_phy_new_jl1101() in C++ (no generic fallback) - Namespace ethernet type in CORE.data[KEY_ETHERNET] --- esphome/components/ethernet/__init__.py | 23 ++++++++++++------- .../components/ethernet/esp_eth_phy_jl1101.c | 3 +-- .../ethernet/ethernet_component_esp32.cpp | 9 ++------ 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/esphome/components/ethernet/__init__.py b/esphome/components/ethernet/__init__.py index 3cc39c6e9e..b534df8985 100644 --- a/esphome/components/ethernet/__init__.py +++ b/esphome/components/ethernet/__init__.py @@ -54,6 +54,9 @@ LOGGER = logging.getLogger(__name__) # Key for tracking IP state listener count in CORE.data ETHERNET_IP_STATE_LISTENERS_KEY = "ethernet_ip_state_listeners" +# Key for tracking configured ethernet type +ETHERNET_TYPE_KEY = "ethernet_type" +KEY_ETHERNET = "ethernet" def request_ethernet_ip_state_listener() -> None: @@ -430,6 +433,7 @@ async def to_code(config): cg.add(var.set_type(ETHERNET_TYPES[config[CONF_TYPE]])) cg.add(var.set_use_address(config[CONF_USE_ADDRESS])) + CORE.data.setdefault(KEY_ETHERNET, {})[ETHERNET_TYPE_KEY] = config[CONF_TYPE] if CONF_MANUAL_IP in config: cg.add_define("USE_ETHERNET_MANUAL_IP") @@ -616,15 +620,18 @@ _platform_filter = filter_source_files_from_platform( def _filter_source_files() -> list[str]: excluded = _platform_filter() - if not CORE.is_esp32: - return excluded - from esphome.components.esp32 import idf_version - - # Custom JL1101 driver not needed on IDF >= 6.0 (uses generic PHY) - # On IDF 5.4.2+ with PlatformIO, the .c file compiles to empty via - # its own preprocessor guard (PLATFORMIO is a C-level define) - if idf_version() >= cv.Version(6, 0, 0): + eth_data = CORE.data.get(KEY_ETHERNET, {}) + eth_type = eth_data.get(ETHERNET_TYPE_KEY) + # Only compile the custom JL1101 driver when JL1101 is configured + # and pioarduino doesn't have it builtin (IDF 5.4.2 to 5.x) + if eth_type != "JL1101": excluded.append("esp_eth_phy_jl1101.c") + elif CORE.is_esp32: + from esphome.components.esp32 import idf_version + + ver = idf_version() + if cv.Version(5, 4, 2) <= ver < cv.Version(6, 0, 0): + excluded.append("esp_eth_phy_jl1101.c") return excluded diff --git a/esphome/components/ethernet/esp_eth_phy_jl1101.c b/esphome/components/ethernet/esp_eth_phy_jl1101.c index 692f759eed..b81d8227d4 100644 --- a/esphome/components/ethernet/esp_eth_phy_jl1101.c +++ b/esphome/components/ethernet/esp_eth_phy_jl1101.c @@ -29,8 +29,7 @@ #include "esp_rom_sys.h" #include "esp_idf_version.h" -#if defined(USE_ETHERNET_JL1101) && ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(6, 0, 0) && \ - (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 4, 2) || !defined(PLATFORMIO)) +#if defined(USE_ETHERNET_JL1101) && (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 4, 2) || !defined(PLATFORMIO)) static const char *TAG = "jl1101"; #define PHY_CHECK(a, str, goto_tag, ...) \ diff --git a/esphome/components/ethernet/ethernet_component_esp32.cpp b/esphome/components/ethernet/ethernet_component_esp32.cpp index 212a60356e..9afa46f310 100644 --- a/esphome/components/ethernet/ethernet_component_esp32.cpp +++ b/esphome/components/ethernet/ethernet_component_esp32.cpp @@ -272,14 +272,9 @@ void EthernetComponent::setup() { #endif #ifdef USE_ETHERNET_JL1101 case ETHERNET_TYPE_JL1101: { -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(6, 0, 0) - // No registry component exists; generic PHY driver works for JL1101 - this->phy_ = esp_eth_phy_new_generic(&phy_config); -#else - // IDF < 5.4.2 or non-PlatformIO: uses custom ESPHome driver (esp_eth_phy_jl1101.c) - // IDF 5.4.2+ with PlatformIO: uses builtin esp_eth_phy_new_jl1101() + // PlatformIO (pioarduino): builtin esp_eth_phy_new_jl1101() on all IDF versions + // Non-PlatformIO: custom ESPHome driver (esp_eth_phy_jl1101.c) this->phy_ = esp_eth_phy_new_jl1101(&phy_config); -#endif break; } #endif From 63a2abf19c07831354c689f874f5da61fc94c22d Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 15 Mar 2026 17:13:00 -1000 Subject: [PATCH 5/9] [ethernet] Fix JL1101 on IDF 6.0: pioarduino doesn't have it patched pioarduino only patches esp_eth_phy_new_jl1101() into IDF 5.4.2-5.x, not IDF 6.0. Update guards in both the .c driver and .h declaration to also compile when IDF >= 6.0. Guard logic: compile custom driver when JL1101 configured AND (IDF >= 6.0 OR IDF < 5.4.2 OR not PlatformIO) --- esphome/components/ethernet/esp_eth_phy_jl1101.c | 3 ++- esphome/components/ethernet/ethernet_component.h | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/esphome/components/ethernet/esp_eth_phy_jl1101.c b/esphome/components/ethernet/esp_eth_phy_jl1101.c index b81d8227d4..46671a2dd0 100644 --- a/esphome/components/ethernet/esp_eth_phy_jl1101.c +++ b/esphome/components/ethernet/esp_eth_phy_jl1101.c @@ -29,7 +29,8 @@ #include "esp_rom_sys.h" #include "esp_idf_version.h" -#if defined(USE_ETHERNET_JL1101) && (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 4, 2) || !defined(PLATFORMIO)) +#if defined(USE_ETHERNET_JL1101) && (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(6, 0, 0) || \ + ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 4, 2) || !defined(PLATFORMIO)) static const char *TAG = "jl1101"; #define PHY_CHECK(a, str, goto_tag, ...) \ diff --git a/esphome/components/ethernet/ethernet_component.h b/esphome/components/ethernet/ethernet_component.h index 80038d50ec..cc73c01df4 100644 --- a/esphome/components/ethernet/ethernet_component.h +++ b/esphome/components/ethernet/ethernet_component.h @@ -239,7 +239,8 @@ class EthernetComponent : public Component { extern EthernetComponent *global_eth_component; #ifdef USE_ESP32 -#if defined(USE_ETHERNET_JL1101) && (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 4, 2) || !defined(PLATFORMIO)) +#if defined(USE_ETHERNET_JL1101) && (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(6, 0, 0) || \ + ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 4, 2) || !defined(PLATFORMIO)) extern "C" esp_eth_phy_t *esp_eth_phy_new_jl1101(const eth_phy_config_t *config); #endif #endif // USE_ESP32 From d4bce7f042c4a8e11bd7bdc4994518e7f24fbf59 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 15 Mar 2026 17:16:21 -1000 Subject: [PATCH 6/9] [ethernet] Guard per-chip PHY/MAC includes with IDF >= 6.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On IDF 5.x these headers don't exist as standalone files — they are included internally via esp_eth.h. On IDF 6.0 they moved to registry components and need explicit includes. LAN867x is unchanged (external component since IDF 5.3). --- .../ethernet/ethernet_component_esp32.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/esphome/components/ethernet/ethernet_component_esp32.cpp b/esphome/components/ethernet/ethernet_component_esp32.cpp index 9afa46f310..fb69a901aa 100644 --- a/esphome/components/ethernet/ethernet_component_esp32.cpp +++ b/esphome/components/ethernet/ethernet_component_esp32.cpp @@ -10,6 +10,10 @@ #include #include "esp_event.h" +// IDF 6.0 moved per-chip PHY/MAC drivers to the Espressif Component Registry; +// they are no longer included via esp_eth.h and need explicit includes. +// On IDF 5.x these headers don't exist as standalone files. +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(6, 0, 0) #ifdef USE_ETHERNET_LAN8720 #include "esp_eth_phy_lan87xx.h" #endif @@ -25,9 +29,6 @@ #ifdef USE_ETHERNET_KSZ8081 #include "esp_eth_phy_ksz80xx.h" #endif -#ifdef USE_ETHERNET_LAN8670 -#include "esp_eth_phy_lan867x.h" -#endif #ifdef USE_ETHERNET_W5500 #include "esp_eth_mac_w5500.h" #include "esp_eth_phy_w5500.h" @@ -36,6 +37,12 @@ #include "esp_eth_mac_dm9051.h" #include "esp_eth_phy_dm9051.h" #endif +#endif // ESP_IDF_VERSION >= 6.0.0 + +// LAN867x header exists on all IDF versions (external component since IDF 5.3) +#ifdef USE_ETHERNET_LAN8670 +#include "esp_eth_phy_lan867x.h" +#endif #ifdef USE_ETHERNET_SPI #include From b7960ee19b255abad8dbb6353fe1addb17553f9b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 15 Mar 2026 17:57:52 -1000 Subject: [PATCH 7/9] [ethernet] Keep CONFIG_ETH_SPI_ETHERNET_* in defines.h for static analysis On IDF 5.x, the ESP-IDF headers use CONFIG_ETH_SPI_ETHERNET_W5500 and CONFIG_ETH_SPI_ETHERNET_DM9051 to conditionally expose the W5500/DM9051 types. Static analysis (clang-tidy) needs both the new USE_ETHERNET_* defines and the old CONFIG_* defines to resolve all symbols. --- esphome/core/defines.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/esphome/core/defines.h b/esphome/core/defines.h index e3d443dda4..513c70e17e 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -283,6 +283,8 @@ #define USE_ETHERNET_OPENETH #define USE_ETHERNET_W5500 #define USE_ETHERNET_DM9051 +#define CONFIG_ETH_SPI_ETHERNET_W5500 1 +#define CONFIG_ETH_SPI_ETHERNET_DM9051 1 #define CONFIG_ETH_USE_ESP32_EMAC 1 #define USE_ETHERNET_MANUAL_IP #define USE_ETHERNET_IP_STATE_LISTENERS From 968c0c245208e0d8d99c0f153f03169960e58c5d Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 15 Mar 2026 20:27:07 -1000 Subject: [PATCH 8/9] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- esphome/components/ethernet/__init__.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/esphome/components/ethernet/__init__.py b/esphome/components/ethernet/__init__.py index b534df8985..cbc50c6bf6 100644 --- a/esphome/components/ethernet/__init__.py +++ b/esphome/components/ethernet/__init__.py @@ -36,9 +36,9 @@ from esphome.const import ( CONF_VALUE, KEY_CORE, KEY_FRAMEWORK_VERSION, + KEY_NATIVE_IDF, Platform, PlatformFramework, -) from esphome.core import ( CORE, CoroPriority, @@ -629,9 +629,12 @@ def _filter_source_files() -> list[str]: elif CORE.is_esp32: from esphome.components.esp32 import idf_version - ver = idf_version() - if cv.Version(5, 4, 2) <= ver < cv.Version(6, 0, 0): - excluded.append("esp_eth_phy_jl1101.c") + # Mirror C preprocessor logic: only exclude for PlatformIO (non-native IDF) + is_native_idf = CORE.data.get(KEY_NATIVE_IDF, False) + if not is_native_idf: + ver = idf_version() + if cv.Version(5, 4, 2) <= ver < cv.Version(6, 0, 0): + excluded.append("esp_eth_phy_jl1101.c") return excluded From 67386f0dd17d45d19af4e7e7b200329e2a4477b6 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 15 Mar 2026 20:34:25 -1000 Subject: [PATCH 9/9] fix copilots manged suggestion --- esphome/components/ethernet/__init__.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/esphome/components/ethernet/__init__.py b/esphome/components/ethernet/__init__.py index cbc50c6bf6..e1ceefeacd 100644 --- a/esphome/components/ethernet/__init__.py +++ b/esphome/components/ethernet/__init__.py @@ -39,6 +39,7 @@ from esphome.const import ( KEY_NATIVE_IDF, Platform, PlatformFramework, +) from esphome.core import ( CORE, CoroPriority, @@ -626,15 +627,13 @@ def _filter_source_files() -> list[str]: # and pioarduino doesn't have it builtin (IDF 5.4.2 to 5.x) if eth_type != "JL1101": excluded.append("esp_eth_phy_jl1101.c") - elif CORE.is_esp32: + elif CORE.is_esp32 and not CORE.data.get(KEY_NATIVE_IDF, False): from esphome.components.esp32 import idf_version - # Mirror C preprocessor logic: only exclude for PlatformIO (non-native IDF) - is_native_idf = CORE.data.get(KEY_NATIVE_IDF, False) - if not is_native_idf: - ver = idf_version() - if cv.Version(5, 4, 2) <= ver < cv.Version(6, 0, 0): - excluded.append("esp_eth_phy_jl1101.c") + # pioarduino has JL1101 builtin on IDF 5.4.2-5.x; exclude custom driver + # to avoid shadowing. Native IDF builds always need the custom driver. + if cv.Version(5, 4, 2) <= idf_version() < cv.Version(6, 0, 0): + excluded.append("esp_eth_phy_jl1101.c") return excluded