ESPHome 2026.7.4
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#endif
17#include "esp_eth_mac.h"
18#include "esp_eth_mac_esp.h"
19#include "esp_netif.h"
20#include "esp_mac.h"
21#include "esp_idf_version.h"
22
23#if CONFIG_ETH_USE_ESP32_EMAC
24extern "C" eth_esp32_emac_config_t eth_esp32_emac_default_config(void);
25#endif
26#endif // USE_ESP32
27
28#ifdef USE_RP2
29#if defined(USE_ETHERNET_W5500)
30#include <W5500lwIP.h>
31#elif defined(USE_ETHERNET_W5100)
32#include <W5100lwIP.h>
33#elif defined(USE_ETHERNET_W6100)
34#include <W6100lwIP.h>
35#elif defined(USE_ETHERNET_W6300)
36#include <W6300lwIP.h>
37// W6300 uses PIO QSPI, not Arduino SPI. The upstream Wiznet6300 class
38// incorrectly returns needsSPI()=true, causing LwipIntfDev::begin() to
39// call SPI.begin() which claims GPIOs that PIO QSPI needs.
40// This wrapper hides needsSPI() with a version returning false.
41class Wiznet6300NoSPI : public Wiznet6300 {
42 public:
43 using Wiznet6300::Wiznet6300;
44 constexpr bool needsSPI() const { return false; }
45};
46using Wiznet6300lwIPFixed = LwipIntfDev<Wiznet6300NoSPI>;
47#elif defined(USE_ETHERNET_ENC28J60)
48#include <ENC28J60lwIP.h>
49#else
50#error "Unsupported RP2040 SPI Ethernet type"
51#endif
52#endif
53
54namespace esphome::ethernet {
55
56#ifdef USE_ETHERNET_IP_STATE_LISTENERS
66 public:
67 virtual void on_ip_state(const network::IPAddresses &ips, const network::IPAddress &dns1,
68 const network::IPAddress &dns2) = 0;
69};
70#endif // USE_ETHERNET_IP_STATE_LISTENERS
71
92
100
106
107enum class EthernetComponentState : uint8_t {
108 STOPPED,
110 CONNECTED,
111};
112
113// Platform-neutral duplex/speed types
114#ifndef USE_ESP32
117#endif
118
119class EthernetComponent final : public Component {
120 public:
122 void setup() override;
123 void loop() override;
124 void dump_config() override;
125 float get_setup_priority() const override;
126 void on_powerdown() override { powerdown(); }
128
129 // Per-interface lifecycle (parallels WiFiComponent::enable/disable/is_disabled).
130 // enable_on_boot defaults to true; when false, setup() runs all the driver/netif
131 // installation but skips esp_eth_start(), keeping the link cold until enable() is
132 // called. This is the primary lever for memory reclamation in multi-interface
133 // configurations where only one interface should carry traffic at a time.
134 void set_enable_on_boot(bool enable_on_boot) { this->enable_on_boot_ = enable_on_boot; }
135 void enable();
136 void disable();
137 bool is_disabled() { return this->disabled_; }
138 bool is_enabled() { return !this->disabled_; }
139
141#ifdef USE_ETHERNET_MANUAL_IP
142 void set_manual_ip(const ManualIP &manual_ip);
143#endif
144 void set_fixed_mac(const std::array<uint8_t, 6> &mac) { this->fixed_mac_ = mac; }
145
150 const char *get_use_address() const { return this->use_address_; }
151 void set_use_address(const char *use_address) { this->use_address_ = use_address; }
152 void get_eth_mac_address_raw(uint8_t *mac);
153 // Remove before 2026.9.0
154 ESPDEPRECATED("Use get_eth_mac_address_pretty_into_buffer() instead. Removed in 2026.9.0", "2026.3.0")
155 std::string get_eth_mac_address_pretty();
156 const char *get_eth_mac_address_pretty_into_buffer(std::span<char, MAC_ADDRESS_PRETTY_BUFFER_SIZE> buf);
159 bool powerdown();
160
161#ifdef USE_ESP32
162 esp_eth_handle_t get_eth_handle() const { return this->eth_handle_; }
163
164#ifdef USE_ETHERNET_SPI
165 void set_clk_pin(uint8_t clk_pin);
166 void set_miso_pin(uint8_t miso_pin);
167 void set_mosi_pin(uint8_t mosi_pin);
168 void set_cs_pin(uint8_t cs_pin);
169 void set_interrupt_pin(uint8_t interrupt_pin);
170 void set_reset_pin(uint8_t reset_pin);
171 void set_clock_speed(int clock_speed);
172 void set_interface(spi_host_device_t interface);
173#ifdef USE_ETHERNET_SPI_POLLING_SUPPORT
174 void set_polling_interval(uint32_t polling_interval);
175#endif
176#else
177 void set_phy_addr(uint8_t phy_addr);
178 void set_power_pin(int power_pin);
179 void set_mdc_pin(uint8_t mdc_pin);
180 void set_mdio_pin(uint8_t mdio_pin);
181 void set_clk_pin(uint8_t clk_pin);
182 void set_clk_mode(emac_rmii_clock_mode_t clk_mode);
183 void add_phy_register(PHYRegister register_value);
184#endif // USE_ETHERNET_SPI
185#endif // USE_ESP32
186
187#ifdef USE_RP2
188 void set_clk_pin(uint8_t clk_pin);
189 void set_miso_pin(uint8_t miso_pin);
190 void set_mosi_pin(uint8_t mosi_pin);
191 void set_cs_pin(uint8_t cs_pin);
192 void set_interrupt_pin(int8_t interrupt_pin);
193 void set_reset_pin(int8_t reset_pin);
194#endif // USE_RP2
195
196#ifdef USE_ETHERNET_IP_STATE_LISTENERS
197 void add_ip_state_listener(EthernetIPStateListener *listener) { this->ip_state_listeners_.push_back(listener); }
198#endif
199
200#ifdef USE_ETHERNET_CONNECT_TRIGGER
202#endif
203#ifdef USE_ETHERNET_DISCONNECT_TRIGGER
205#endif
206
207 protected:
208 void start_connect_();
209 void finish_connect_();
211
212#ifdef USE_ESP32
213 // ESP-IDF only: defers the SPI bus init, netif creation, MAC/PHY install, driver
214 // install, netif attach, and event handler registration (which together allocate
215 // ~3-8KB of DMA-capable internal SRAM via SPI driver state + eth driver RX queue)
216 // until ethernet actually needs to come up. Idempotent — guarded by the
217 // ethernet_initialized_ flag. Called from setup() when enable_on_boot_=true, or
218 // from enable() on first runtime enable. Mirrors wifi_lazy_init_() in WiFi.
219 void ethernet_lazy_init_();
220#endif
221
222#ifdef USE_ETHERNET_IP_STATE_LISTENERS
224#endif
225
226#ifdef USE_ESP32
227 static void eth_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data);
228 static void got_ip_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data);
229#if LWIP_IPV6
230 static void got_ip6_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data);
231#endif /* LWIP_IPV6 */
232 void log_error_and_mark_failed_(esp_err_t err, const char *message);
233#ifdef USE_ETHERNET_KSZ8081
235 void ksz8081_set_clock_reference_(esp_eth_mac_t *mac);
236#endif
237#ifdef USE_ETHERNET_YT8531
240 void yt8531_phy_init_();
241#endif
243 void write_phy_register_(esp_eth_mac_t *mac, PHYRegister register_data);
244
245#ifdef USE_ETHERNET_SPI
246 uint8_t clk_pin_;
247 uint8_t miso_pin_;
248 uint8_t mosi_pin_;
249 uint8_t cs_pin_;
251 int reset_pin_{-1};
254 spi_host_device_t interface_{SPI2_HOST};
255#ifdef USE_ETHERNET_SPI_POLLING_SUPPORT
257#endif
258#else
259 // Group all 32-bit members first
260 int power_pin_{-1};
261 emac_rmii_clock_mode_t clk_mode_{EMAC_CLK_EXT_IN};
262 std::vector<PHYRegister> phy_registers_{};
263
264 // Group all 8-bit members together
265 uint8_t clk_pin_{0};
266 uint8_t phy_addr_{0};
267 uint8_t mdc_pin_{23};
268 uint8_t mdio_pin_{18};
269#endif // USE_ETHERNET_SPI
270
271 // ESP32 pointers
272 esp_netif_t *eth_netif_{nullptr};
273 esp_eth_handle_t eth_handle_;
274 esp_eth_phy_t *phy_{nullptr};
275#endif // USE_ESP32
276
277#ifdef USE_RP2
278 static constexpr uint32_t LINK_CHECK_INTERVAL = 500; // ms between link/IP polls
279#if defined(USE_ETHERNET_W5100)
280 static constexpr uint32_t RESET_DELAY_MS = 150; // W5100S PLL lock time
281#elif defined(USE_ETHERNET_W6300)
282 static constexpr uint32_t RESET_DELAY_MS = 100; // W6300 needs 100ms after hardware reset
283#else
284 static constexpr uint32_t RESET_DELAY_MS = 10;
285#endif
286#if defined(USE_ETHERNET_W5500)
287 Wiznet5500lwIP *eth_{nullptr};
288#elif defined(USE_ETHERNET_W5100)
289 Wiznet5100lwIP *eth_{nullptr};
290#elif defined(USE_ETHERNET_W6100)
291 Wiznet6100lwIP *eth_{nullptr};
292#elif defined(USE_ETHERNET_W6300)
294#elif defined(USE_ETHERNET_ENC28J60)
295 ENC28J60lwIP *eth_{nullptr};
296#else
297#error "Unsupported RP2040 SPI Ethernet type"
298#endif
300 uint8_t clk_pin_;
301 uint8_t miso_pin_;
302 uint8_t mosi_pin_;
303 uint8_t cs_pin_;
304 int8_t interrupt_pin_{-1};
305 int8_t reset_pin_{-1};
306#endif // USE_RP2
307
308 // Common members
309#ifdef USE_ETHERNET_MANUAL_IP
310 optional<ManualIP> manual_ip_{};
311#endif
313
314 // Group all uint8_t types together (enums and bools)
317 bool started_{false};
318 bool connected_{false};
319 bool got_ipv4_address_{false};
320 // Codegen-time YAML option. When false, setup() defers esp_eth_start().
321 bool enable_on_boot_{true};
322 // Mirror of "is the link intentionally stopped" — set when setup() honors
323 // enable_on_boot=false, cleared by enable(), set again by disable().
324 bool disabled_{false};
325#ifdef USE_ESP32
326 // Tracks whether ethernet_lazy_init_() has completed successfully. Allows enable()
327 // to be called at runtime after enable_on_boot:false without re-allocating, and
328 // ensures setup() skips the heavy init when enable_on_boot_ is false.
330#endif
331#if LWIP_IPV6
332 uint8_t ipv6_count_{0};
333 bool ipv6_setup_done_{false};
334#endif /* LWIP_IPV6 */
335
336 optional<std::array<uint8_t, 6>> fixed_mac_;
337
338#ifdef USE_ETHERNET_IP_STATE_LISTENERS
340#endif
341
342#ifdef USE_ETHERNET_CONNECT_TRIGGER
344#endif
345#ifdef USE_ETHERNET_DISCONNECT_TRIGGER
347#endif
348 private:
349 // Stores a pointer to a string literal (static storage duration).
350 // ONLY set from Python-generated code with string literals - never dynamic strings.
351 const char *use_address_{nullptr};
352};
353
354// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
355extern EthernetComponent *global_eth_component;
356
357#ifdef USE_ESP32
358#if defined(USE_ETHERNET_JL1101) && (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(6, 0, 0) || \
359 ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 4, 2) || !defined(PLATFORMIO))
360extern "C" esp_eth_phy_t *esp_eth_phy_new_jl1101(const eth_phy_config_t *config);
361#endif
362#endif // USE_ESP32
363
364} // namespace esphome::ethernet
365
366#endif // USE_ETHERNET
constexpr bool needsSPI() const
ESPDEPRECATED("set_retry is deprecated and will be removed in 2026.8.0. Use set_timeout or set_interval instead.", "2026.2.0") void set_retry(const std ESPDEPRECATED("set_retry is deprecated and will be removed in 2026.8.0. Use set_timeout or set_interval instead.", "2026.2.0") void set_retry(const char *name
Minimal static vector - saves memory by avoiding std::vector overhead.
Definition helpers.h:222
void set_enable_on_boot(bool enable_on_boot)
void set_interface(spi_host_device_t interface)
std::vector< PHYRegister > phy_registers_
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 add_ip_state_listener(EthernetIPStateListener *listener)
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, 6 > &mac)
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...
optional< std::array< uint8_t, 6 > > fixed_mac_
void set_use_address(const char *use_address)
void set_clk_mode(emac_rmii_clock_mode_t clk_mode)
ESPDEPRECATED("Use get_eth_mac_address_pretty_into_buffer() instead. Removed in 2026.9.0", "2026.3.0") std 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
uint16_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:223
STL namespace.
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