ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
ethernet_component.h
Go to the documentation of this file.
1#pragma once
2
5#include "esphome/core/hal.h"
9
10#ifdef USE_ETHERNET
11
12#ifdef USE_ESP32
13#include "esp_eth.h"
14#ifdef USE_ETHERNET_SPI
15#include "hal/spi_types.h"
16#ifdef USE_SPI
18#endif
19#endif
20#include "esp_eth_mac.h"
21#include "esp_eth_mac_esp.h"
22#include "esp_netif.h"
23#include "esp_mac.h"
24#include "esp_idf_version.h"
25
26#if CONFIG_ETH_USE_ESP32_EMAC
27extern "C" eth_esp32_emac_config_t eth_esp32_emac_default_config(void);
28#endif
29#endif // USE_ESP32
30
31#ifdef USE_RP2
32#if defined(USE_ETHERNET_W5500)
33#include <W5500lwIP.h>
34#elif defined(USE_ETHERNET_W5100)
35#include <W5100lwIP.h>
36#elif defined(USE_ETHERNET_W6100)
37#include <W6100lwIP.h>
38#elif defined(USE_ETHERNET_W6300)
39#include <W6300lwIP.h>
40// W6300 uses PIO QSPI, not Arduino SPI. The upstream Wiznet6300 class
41// incorrectly returns needsSPI()=true, causing LwipIntfDev::begin() to
42// call SPI.begin() which claims GPIOs that PIO QSPI needs.
43// This wrapper hides needsSPI() with a version returning false.
44class Wiznet6300NoSPI : public Wiznet6300 {
45 public:
46 using Wiznet6300::Wiznet6300;
47 constexpr bool needsSPI() const { return false; }
48};
49using Wiznet6300lwIPFixed = LwipIntfDev<Wiznet6300NoSPI>;
50#elif defined(USE_ETHERNET_ENC28J60)
51#include <ENC28J60lwIP.h>
52#else
53#error "Unsupported RP2040 SPI Ethernet type"
54#endif
55#endif
56
57namespace esphome::ethernet {
58
59#ifdef USE_ETHERNET_IP_STATE_LISTENERS
69 public:
70 virtual void on_ip_state(const network::IPAddresses &ips, const network::IPAddress &dns1,
71 const network::IPAddress &dns2) = 0;
72};
73#endif // USE_ETHERNET_IP_STATE_LISTENERS
74
97
105
111
112enum class EthernetComponentState : uint8_t {
113 STOPPED,
115 CONNECTED,
116};
117
118// Platform-neutral duplex/speed types
119#ifndef USE_ESP32
120// NOLINTBEGIN(readability-identifier-naming)
123// NOLINTEND(readability-identifier-naming)
124#endif
125
126class EthernetComponent final : public Component {
127 public:
129 void setup() override;
130 void loop() override;
131 void dump_config() override;
132 float get_setup_priority() const override { return setup_priority::ETHERNET; }
133 void on_powerdown() override { powerdown(); }
135
136 // Per-interface lifecycle (parallels WiFiComponent::enable/disable/is_disabled).
137 // enable_on_boot defaults to true; when false, setup() runs all the driver/netif
138 // installation but skips esp_eth_start(), keeping the link cold until enable() is
139 // called. This is the primary lever for memory reclamation in multi-interface
140 // configurations where only one interface should carry traffic at a time.
141 void set_enable_on_boot(bool enable_on_boot) { this->enable_on_boot_ = enable_on_boot; }
142 void enable();
143 void disable();
144 bool is_disabled() { return this->disabled_; }
145 bool is_enabled() { return !this->disabled_; }
146
147#ifdef USE_ESP32
151#endif
152
154#ifdef USE_ETHERNET_MANUAL_IP
155 void set_manual_ip(const ManualIP &manual_ip) { this->manual_ip_ = manual_ip; }
156#endif
157 void set_fixed_mac(const std::array<uint8_t, MAC_ADDRESS_SIZE> &mac) { this->fixed_mac_ = mac; }
158
163 const char *get_use_address() const { return this->use_address_; }
164 void set_use_address(const char *use_address) { this->use_address_ = use_address; }
165 void get_eth_mac_address_raw(uint8_t *mac);
166 const char *get_eth_mac_address_pretty_into_buffer(std::span<char, MAC_ADDRESS_PRETTY_BUFFER_SIZE> buf);
169 bool powerdown();
170
171#ifdef USE_ESP32
172 esp_eth_handle_t get_eth_handle() const { return this->eth_handle_; }
173
174#ifdef USE_ETHERNET_SPI
175 void set_clk_pin(uint8_t clk_pin) { this->clk_pin_ = clk_pin; }
176 void set_miso_pin(uint8_t miso_pin) { this->miso_pin_ = miso_pin; }
177 void set_mosi_pin(uint8_t mosi_pin) { this->mosi_pin_ = mosi_pin; }
178 void set_cs_pin(uint8_t cs_pin) { this->cs_pin_ = cs_pin; }
179 void set_interrupt_pin(uint8_t interrupt_pin) { this->interrupt_pin_ = interrupt_pin; }
180 void set_reset_pin(uint8_t reset_pin) { this->reset_pin_ = reset_pin; }
181 void set_clock_speed(int clock_speed) { this->clock_speed_ = clock_speed; }
182 void set_interface(spi_host_device_t interface) { this->interface_ = interface; }
183#ifdef USE_SPI
184 void set_spi_parent(spi::SPIComponent *parent) { this->spi_parent_ = parent; }
185#endif
186#ifdef USE_ETHERNET_SPI_POLLING_SUPPORT
187 void set_polling_interval(uint32_t polling_interval) { this->polling_interval_ = polling_interval; }
188#endif
189#else
190 void set_phy_addr(uint8_t phy_addr) { this->phy_addr_ = phy_addr; }
191 void set_power_pin(int power_pin) { this->power_pin_ = power_pin; }
192 void set_mdc_pin(uint8_t mdc_pin) { this->mdc_pin_ = mdc_pin; }
193 void set_mdio_pin(uint8_t mdio_pin) { this->mdio_pin_ = mdio_pin; }
194 void set_clk_pin(uint8_t clk_pin) { this->clk_pin_ = clk_pin; }
195 void set_clk_mode(emac_rmii_clock_mode_t clk_mode) { this->clk_mode_ = clk_mode; }
196#ifdef ESPHOME_ETHERNET_PHY_REGISTER_COUNT
197 void add_phy_register(PHYRegister register_value) { this->phy_registers_.push_back(register_value); }
198#endif
199#endif // USE_ETHERNET_SPI
200#endif // USE_ESP32
201
202#ifdef USE_RP2
203 void set_clk_pin(uint8_t clk_pin) { this->clk_pin_ = clk_pin; }
204 void set_miso_pin(uint8_t miso_pin) { this->miso_pin_ = miso_pin; }
205 void set_mosi_pin(uint8_t mosi_pin) { this->mosi_pin_ = mosi_pin; }
206 void set_cs_pin(uint8_t cs_pin) { this->cs_pin_ = cs_pin; }
207 void set_interrupt_pin(int8_t interrupt_pin) { this->interrupt_pin_ = interrupt_pin; }
208 void set_reset_pin(int8_t reset_pin) { this->reset_pin_ = reset_pin; }
209#endif // USE_RP2
210
211#ifdef USE_ETHERNET_IP_STATE_LISTENERS
212 void add_ip_state_listener(EthernetIPStateListener *listener) { this->ip_state_listeners_.push_back(listener); }
213#endif
214
215#ifdef USE_ETHERNET_CONNECT_TRIGGER
217#endif
218#ifdef USE_ETHERNET_DISCONNECT_TRIGGER
220#endif
221
222 protected:
223 void start_connect_();
224 void finish_connect_();
225#if LWIP_IPV6
226 esp_err_t ensure_ip6_linklocal_();
227#endif
229
230#ifdef USE_ESP32
231 // ESP-IDF only: defers the SPI bus init, netif creation, MAC/PHY install, driver
232 // install, netif attach, and event handler registration (which together allocate
233 // ~3-8KB of DMA-capable internal SRAM via SPI driver state + eth driver RX queue)
234 // until ethernet actually needs to come up. Idempotent — guarded by the
235 // ethernet_initialized_ flag. Called from setup() when enable_on_boot_=true, or
236 // from enable() on first runtime enable. Mirrors wifi_lazy_init_() in WiFi.
237 void ethernet_lazy_init_();
238#endif
239
240#ifdef USE_ETHERNET_IP_STATE_LISTENERS
242#endif
243
244#ifdef USE_ESP32
245 static void eth_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data);
246 static void got_ip_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data);
247#if LWIP_IPV6
248 static void got_ip6_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data);
249#endif /* LWIP_IPV6 */
250 void log_error_and_mark_failed_(esp_err_t err, const char *message);
251#ifdef USE_ETHERNET_KSZ8081
253 void ksz8081_set_clock_reference_(esp_eth_mac_t *mac);
254#endif
255#ifdef USE_ETHERNET_YT8531
258 void yt8531_phy_init_();
259#endif
260#ifdef ESPHOME_ETHERNET_PHY_REGISTER_COUNT
262 void write_phy_register_(esp_eth_mac_t *mac, PHYRegister register_data);
263#endif
264
265#ifdef USE_ETHERNET_SPI
266 uint8_t clk_pin_;
267 uint8_t miso_pin_;
268 uint8_t mosi_pin_;
269 uint8_t cs_pin_;
271 int reset_pin_{-1};
274 spi_host_device_t interface_{SPI2_HOST}; // Must match DEFAULT_SPI_INTERFACE in __init__.py
275#ifdef USE_SPI
276 // When set, the SPI bus is owned and initialized by this spi component
277 // and the ethernet chip only adds a device to it.
279#endif
280#ifdef USE_ETHERNET_SPI_POLLING_SUPPORT
282#endif
283#else
284 // Group all 32-bit members first
285 int power_pin_{-1};
286 emac_rmii_clock_mode_t clk_mode_{EMAC_CLK_EXT_IN}; // Must match DEFAULT_CLK_MODE in __init__.py
287#ifdef ESPHOME_ETHERNET_PHY_REGISTER_COUNT
289#endif
290
291 // Group all 8-bit members together
292 uint8_t clk_pin_{0}; // Must match DEFAULT_CLK_PIN in __init__.py
293 uint8_t phy_addr_{0}; // Must match DEFAULT_PHY_ADDR in __init__.py
294 uint8_t mdc_pin_{23}; // Must match DEFAULT_MDC_PIN in __init__.py
295 uint8_t mdio_pin_{18}; // Must match DEFAULT_MDIO_PIN in __init__.py
296#endif // USE_ETHERNET_SPI
297
298 // ESP32 pointers
300 esp_eth_handle_t eth_handle_;
301 esp_eth_phy_t *phy_{nullptr};
302#endif // USE_ESP32
303
304#ifdef USE_RP2
305 static constexpr uint32_t LINK_CHECK_INTERVAL = 500; // ms between link/IP polls
306#if defined(USE_ETHERNET_W5100)
307 static constexpr uint32_t RESET_DELAY_MS = 150; // W5100S PLL lock time
308#elif defined(USE_ETHERNET_W6300)
309 static constexpr uint32_t RESET_DELAY_MS = 100; // W6300 needs 100ms after hardware reset
310#else
311 static constexpr uint32_t RESET_DELAY_MS = 10;
312#endif
313#if defined(USE_ETHERNET_W5500)
314 Wiznet5500lwIP *eth_{nullptr};
315#elif defined(USE_ETHERNET_W5100)
316 Wiznet5100lwIP *eth_{nullptr};
317#elif defined(USE_ETHERNET_W6100)
318 Wiznet6100lwIP *eth_{nullptr};
319#elif defined(USE_ETHERNET_W6300)
321#elif defined(USE_ETHERNET_ENC28J60)
322 ENC28J60lwIP *eth_{nullptr};
323#else
324#error "Unsupported RP2040 SPI Ethernet type"
325#endif
327 uint8_t clk_pin_;
328 uint8_t miso_pin_;
329 uint8_t mosi_pin_;
330 uint8_t cs_pin_;
331 int8_t interrupt_pin_{-1};
332 int8_t reset_pin_{-1};
333#endif // USE_RP2
334
335 // Common members
336#ifdef USE_ETHERNET_MANUAL_IP
337 optional<ManualIP> manual_ip_{};
338#endif
340
341 // Group all uint8_t types together (enums and bools)
344 bool started_{false};
345 bool connected_{false};
346 bool got_ipv4_address_{false};
347 // Codegen-time YAML option. When false, setup() defers esp_eth_start().
348 bool enable_on_boot_{true};
349 // Mirror of "is the link intentionally stopped" — set when setup() honors
350 // enable_on_boot=false, cleared by enable(), set again by disable().
351 bool disabled_{false};
352#ifdef USE_ESP32
353 // Tracks whether ethernet_lazy_init_() has completed successfully. Allows enable()
354 // to be called at runtime after enable_on_boot:false without re-allocating, and
355 // ensures setup() skips the heavy init when enable_on_boot_ is false.
357#endif
358#if LWIP_IPV6
359 uint8_t ipv6_count_{0};
360 bool ipv6_setup_done_{false};
361#endif /* LWIP_IPV6 */
362
363 optional<std::array<uint8_t, MAC_ADDRESS_SIZE>> fixed_mac_;
364
365#ifdef USE_ETHERNET_IP_STATE_LISTENERS
367#endif
368
369#ifdef USE_ETHERNET_CONNECT_TRIGGER
371#endif
372#ifdef USE_ETHERNET_DISCONNECT_TRIGGER
374#endif
375 private:
376 // Stores a pointer to a string literal (static storage duration).
377 // ONLY set from Python-generated code with string literals - never dynamic strings.
378 const char *use_address_{nullptr};
379};
380
381// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
382extern EthernetComponent *global_eth_component;
383
384#ifdef USE_ESP32
385#if defined(USE_ETHERNET_JL1101) && (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(6, 0, 0) || \
386 ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 4, 2) || !defined(PLATFORMIO))
387extern "C" esp_eth_phy_t *esp_eth_phy_new_jl1101(const eth_phy_config_t *config);
388#endif
389#endif // USE_ESP32
390
391} // namespace esphome::ethernet
392
393#endif // USE_ETHERNET
constexpr bool needsSPI() const
Minimal static vector - saves memory by avoiding std::vector overhead.
Definition helpers.h:286
void set_enable_on_boot(bool enable_on_boot)
StaticVector< PHYRegister, ESPHOME_ETHERNET_PHY_REGISTER_COUNT > phy_registers_
void set_interface(spi_host_device_t interface)
static void got_ip_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
void set_polling_interval(uint32_t polling_interval)
void yt8531_phy_init_()
Apply YT8531-specific config: re-enable auto-negotiation (disabled on reset) and set the RGMII Tx/Rx ...
void set_manual_ip(const ManualIP &manual_ip)
static constexpr uint32_t LINK_CHECK_INTERVAL
void write_phy_register_(esp_eth_mac_t *mac, PHYRegister register_data)
Set arbitratry PHY registers from config.
static void got_ip6_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
void log_error_and_mark_failed_(esp_err_t err, const char *message)
void set_spi_parent(spi::SPIComponent *parent)
esp_netif_t * get_esp_netif()
esp_netif handle, used by network for default-route arbitration.
void add_ip_state_listener(EthernetIPStateListener *listener)
void set_interrupt_pin(int8_t interrupt_pin)
network::IPAddress get_dns_address(uint8_t num)
void add_phy_register(PHYRegister register_value)
void ksz8081_set_clock_reference_(esp_eth_mac_t *mac)
Set RMII Reference Clock Select bit for KSZ8081.
StaticVector< EthernetIPStateListener *, ESPHOME_ETHERNET_IP_STATE_LISTENERS > ip_state_listeners_
void set_fixed_mac(const std::array< uint8_t, MAC_ADDRESS_SIZE > &mac)
optional< std::array< uint8_t, MAC_ADDRESS_SIZE > > fixed_mac_
void set_interrupt_pin(uint8_t interrupt_pin)
static constexpr uint32_t RESET_DELAY_MS
static void eth_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
const char * get_use_address() const
Returns nullptr when no explicit use_address is configured and the address is derived at runtime from...
void set_use_address(const char *use_address)
void set_clk_mode(emac_rmii_clock_mode_t clk_mode)
const char * get_eth_mac_address_pretty_into_buffer(std::span< char, MAC_ADDRESS_PRETTY_BUFFER_SIZE > buf)
Listener interface for Ethernet IP state changes.
virtual void on_ip_state(const network::IPAddresses &ips, const network::IPAddress &dns1, const network::IPAddress &dns2)=0
const LogString * message
Definition component.cpp:35
uint8_t type
eth_esp32_emac_config_t eth_esp32_emac_default_config(void)
LwipIntfDev< Wiznet6300NoSPI > Wiznet6300lwIPFixed
esp_eth_phy_t * esp_eth_phy_new_jl1101(const eth_phy_config_t *config)
EthernetComponent * global_eth_component
std::array< IPAddress, 5 > IPAddresses
Definition ip_address.h:369
constexpr float ETHERNET
Definition component.h:51
struct esp_netif_obj esp_netif_t
static void uint32_t
network::IPAddress dns1
The first DNS server. 0.0.0.0 for default.
network::IPAddress dns2
The second DNS server. 0.0.0.0 for default.
uint8_t event_id
Definition tt21100.cpp:3