ESPHome  2023.11.6
ethernet_component.cpp
Go to the documentation of this file.
1 #include "ethernet_component.h"
2 #include "esphome/core/log.h"
3 #include "esphome/core/util.h"
5 
6 #ifdef USE_ESP32
7 
8 #include <cinttypes>
9 #include <lwip/dns.h>
10 #include "esp_event.h"
11 
12 namespace esphome {
13 namespace ethernet {
14 
15 static const char *const TAG = "ethernet";
16 
17 EthernetComponent *global_eth_component; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
18 
19 #define ESPHL_ERROR_CHECK(err, message) \
20  if ((err) != ESP_OK) { \
21  ESP_LOGE(TAG, message ": (%d) %s", err, esp_err_to_name(err)); \
22  this->mark_failed(); \
23  return; \
24  }
25 
26 EthernetComponent::EthernetComponent() { global_eth_component = this; }
27 
29  ESP_LOGCONFIG(TAG, "Setting up Ethernet...");
30  if (esp_reset_reason() != ESP_RST_DEEPSLEEP) {
31  // Delay here to allow power to stabilise before Ethernet is initialized.
32  delay(300); // NOLINT
33  }
34 
35  esp_err_t err;
36  err = esp_netif_init();
37  ESPHL_ERROR_CHECK(err, "ETH netif init error");
38  err = esp_event_loop_create_default();
39  ESPHL_ERROR_CHECK(err, "ETH event loop error");
40 
41  esp_netif_config_t cfg = ESP_NETIF_DEFAULT_ETH();
42  this->eth_netif_ = esp_netif_new(&cfg);
43 
44  // Init MAC and PHY configs to default
45  eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
46  phy_config.phy_addr = this->phy_addr_;
47  phy_config.reset_gpio_num = this->power_pin_;
48 
49  eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
50 #if ESP_IDF_VERSION_MAJOR >= 5
51  eth_esp32_emac_config_t esp32_emac_config = ETH_ESP32_EMAC_DEFAULT_CONFIG();
52  esp32_emac_config.smi_mdc_gpio_num = this->mdc_pin_;
53  esp32_emac_config.smi_mdio_gpio_num = this->mdio_pin_;
54  esp32_emac_config.clock_config.rmii.clock_mode = this->clk_mode_;
55  esp32_emac_config.clock_config.rmii.clock_gpio = this->clk_gpio_;
56 
57  esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&esp32_emac_config, &mac_config);
58 #else
59  mac_config.smi_mdc_gpio_num = this->mdc_pin_;
60  mac_config.smi_mdio_gpio_num = this->mdio_pin_;
61  mac_config.clock_config.rmii.clock_mode = this->clk_mode_;
62  mac_config.clock_config.rmii.clock_gpio = this->clk_gpio_;
63 
64  esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&mac_config);
65 #endif
66 
67  switch (this->type_) {
68  case ETHERNET_TYPE_LAN8720: {
69  this->phy_ = esp_eth_phy_new_lan87xx(&phy_config);
70  break;
71  }
72  case ETHERNET_TYPE_RTL8201: {
73  this->phy_ = esp_eth_phy_new_rtl8201(&phy_config);
74  break;
75  }
76  case ETHERNET_TYPE_DP83848: {
77  this->phy_ = esp_eth_phy_new_dp83848(&phy_config);
78  break;
79  }
80  case ETHERNET_TYPE_IP101: {
81  this->phy_ = esp_eth_phy_new_ip101(&phy_config);
82  break;
83  }
84  case ETHERNET_TYPE_JL1101: {
85  this->phy_ = esp_eth_phy_new_jl1101(&phy_config);
86  break;
87  }
90 #if ESP_IDF_VERSION_MAJOR >= 5
91  this->phy_ = esp_eth_phy_new_ksz80xx(&phy_config);
92 #else
93  this->phy_ = esp_eth_phy_new_ksz8081(&phy_config);
94 #endif
95  break;
96  }
97  default: {
98  this->mark_failed();
99  return;
100  }
101  }
102 
103  esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(mac, this->phy_);
104  this->eth_handle_ = nullptr;
105  err = esp_eth_driver_install(&eth_config, &this->eth_handle_);
106  ESPHL_ERROR_CHECK(err, "ETH driver install error");
107 
108  if (this->type_ == ETHERNET_TYPE_KSZ8081RNA && this->clk_mode_ == EMAC_CLK_OUT) {
109  // KSZ8081RNA default is incorrect. It expects a 25MHz clock instead of the 50MHz we provide.
110  this->ksz8081_set_clock_reference_(mac);
111  }
112 
113  /* attach Ethernet driver to TCP/IP stack */
114  err = esp_netif_attach(this->eth_netif_, esp_eth_new_netif_glue(this->eth_handle_));
115  ESPHL_ERROR_CHECK(err, "ETH netif attach error");
116 
117  // Register user defined event handers
118  err = esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, &EthernetComponent::eth_event_handler, nullptr);
119  ESPHL_ERROR_CHECK(err, "ETH event handler register error");
120  err = esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &EthernetComponent::got_ip_event_handler, nullptr);
121  ESPHL_ERROR_CHECK(err, "GOT IP event handler register error");
122 #if ENABLE_IPV6
123  err = esp_event_handler_register(IP_EVENT, IP_EVENT_GOT_IP6, &EthernetComponent::got_ip6_event_handler, nullptr);
124  ESPHL_ERROR_CHECK(err, "GOT IP6 event handler register error");
125 #endif /* ENABLE_IPV6 */
126 
127  /* start Ethernet driver state machine */
128  err = esp_eth_start(this->eth_handle_);
129  ESPHL_ERROR_CHECK(err, "ETH start error");
130 }
131 
133  const uint32_t now = millis();
134 
135  switch (this->state_) {
137  if (this->started_) {
138  ESP_LOGI(TAG, "Starting ethernet connection");
140  this->start_connect_();
141  }
142  break;
144  if (!this->started_) {
145  ESP_LOGI(TAG, "Stopped ethernet connection");
147  } else if (this->connected_) {
148  // connection established
149  ESP_LOGI(TAG, "Connected via Ethernet!");
151 
152  this->dump_connect_params_();
153  this->status_clear_warning();
154  } else if (now - this->connect_begin_ > 15000) {
155  ESP_LOGW(TAG, "Connecting via ethernet failed! Re-connecting...");
156  this->start_connect_();
157  }
158  break;
160  if (!this->started_) {
161  ESP_LOGI(TAG, "Stopped ethernet connection");
163  } else if (!this->connected_) {
164  ESP_LOGW(TAG, "Connection via Ethernet lost! Re-connecting...");
166  this->start_connect_();
167  }
168 #if ENABLE_IPV6
169  else if (this->got_ipv6_) {
170  esp_ip6_addr_t ip6_addr;
171  if (esp_netif_get_ip6_global(this->eth_netif_, &ip6_addr) == 0 &&
172  esp_netif_ip6_get_addr_type(&ip6_addr) == ESP_IP6_ADDR_IS_GLOBAL) {
173  ESP_LOGCONFIG(TAG, "IPv6 Addr (Global): " IPV6STR, IPV62STR(ip6_addr));
174  } else {
175  esp_netif_get_ip6_linklocal(this->eth_netif_, &ip6_addr);
176  ESP_LOGCONFIG(TAG, " IPv6: " IPV6STR, IPV62STR(ip6_addr));
177  }
178 
179  this->got_ipv6_ = false;
180  }
181 #endif /* ENABLE_IPV6 */
182  break;
183  }
184 }
185 
187  const char *eth_type;
188  switch (this->type_) {
190  eth_type = "LAN8720";
191  break;
192 
194  eth_type = "RTL8201";
195  break;
196 
198  eth_type = "DP83848";
199  break;
200 
201  case ETHERNET_TYPE_IP101:
202  eth_type = "IP101";
203  break;
204 
206  eth_type = "JL1101";
207  break;
208 
210  eth_type = "KSZ8081";
211  break;
212 
214  eth_type = "KSZ8081RNA";
215  break;
216 
217  default:
218  eth_type = "Unknown";
219  break;
220  }
221 
222  ESP_LOGCONFIG(TAG, "Ethernet:");
223  this->dump_connect_params_();
224  if (this->power_pin_ != -1) {
225  ESP_LOGCONFIG(TAG, " Power Pin: %u", this->power_pin_);
226  }
227  ESP_LOGCONFIG(TAG, " MDC Pin: %u", this->mdc_pin_);
228  ESP_LOGCONFIG(TAG, " MDIO Pin: %u", this->mdio_pin_);
229  ESP_LOGCONFIG(TAG, " Type: %s", eth_type);
230  ESP_LOGCONFIG(TAG, " PHY addr: %u", this->phy_addr_);
231 }
232 
234 
236 
238  esp_netif_ip_info_t ip;
239  esp_netif_get_ip_info(this->eth_netif_, &ip);
240  return network::IPAddress(&ip.ip);
241 }
242 
243 void EthernetComponent::eth_event_handler(void *arg, esp_event_base_t event_base, int32_t event, void *event_data) {
244  const char *event_name;
245 
246  switch (event) {
247  case ETHERNET_EVENT_START:
248  event_name = "ETH started";
249  global_eth_component->started_ = true;
250  break;
251  case ETHERNET_EVENT_STOP:
252  event_name = "ETH stopped";
253  global_eth_component->started_ = false;
254  global_eth_component->connected_ = false;
255  break;
256  case ETHERNET_EVENT_CONNECTED:
257  event_name = "ETH connected";
258  break;
259  case ETHERNET_EVENT_DISCONNECTED:
260  event_name = "ETH disconnected";
261  global_eth_component->connected_ = false;
262  break;
263  default:
264  return;
265  }
266 
267  ESP_LOGV(TAG, "[Ethernet event] %s (num=%" PRId32 ")", event_name, event);
268 }
269 
270 void EthernetComponent::got_ip_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id,
271  void *event_data) {
272  global_eth_component->connected_ = true;
273  ESP_LOGV(TAG, "[Ethernet event] ETH Got IP (num=%" PRId32 ")", event_id);
274 }
275 
276 #if ENABLE_IPV6
277 void EthernetComponent::got_ip6_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id,
278  void *event_data) {
279  ESP_LOGV(TAG, "[Ethernet event] ETH Got IP6 (num=%" PRId32 ")", event_id);
280  global_eth_component->got_ipv6_ = true;
281  global_eth_component->ipv6_count_ += 1;
282 }
283 #endif /* ENABLE_IPV6 */
284 
286  this->connect_begin_ = millis();
287  this->status_set_warning();
288 
289  esp_err_t err;
290  err = esp_netif_set_hostname(this->eth_netif_, App.get_name().c_str());
291  if (err != ERR_OK) {
292  ESP_LOGW(TAG, "esp_netif_set_hostname failed: %s", esp_err_to_name(err));
293  }
294 
295  esp_netif_ip_info_t info;
296  if (this->manual_ip_.has_value()) {
297  info.ip = this->manual_ip_->static_ip;
298  info.gw = this->manual_ip_->gateway;
299  info.netmask = this->manual_ip_->subnet;
300  } else {
301  info.ip.addr = 0;
302  info.gw.addr = 0;
303  info.netmask.addr = 0;
304  }
305 
306  esp_netif_dhcp_status_t status = ESP_NETIF_DHCP_INIT;
307 
308  err = esp_netif_dhcpc_get_status(this->eth_netif_, &status);
309  ESPHL_ERROR_CHECK(err, "DHCPC Get Status Failed!");
310 
311  ESP_LOGV(TAG, "DHCP Client Status: %d", status);
312 
313  err = esp_netif_dhcpc_stop(this->eth_netif_);
314  if (err != ESP_ERR_ESP_NETIF_DHCP_ALREADY_STOPPED) {
315  ESPHL_ERROR_CHECK(err, "DHCPC stop error");
316  }
317 
318  err = esp_netif_set_ip_info(this->eth_netif_, &info);
319  ESPHL_ERROR_CHECK(err, "DHCPC set IP info error");
320 
321  if (this->manual_ip_.has_value()) {
322  if (this->manual_ip_->dns1.is_set()) {
323  ip_addr_t d;
324  d = this->manual_ip_->dns1;
325  dns_setserver(0, &d);
326  }
327  if (this->manual_ip_->dns2.is_set()) {
328  ip_addr_t d;
329  d = this->manual_ip_->dns2;
330  dns_setserver(1, &d);
331  }
332  } else {
333  err = esp_netif_dhcpc_start(this->eth_netif_);
334  if (err != ESP_ERR_ESP_NETIF_DHCP_ALREADY_STARTED) {
335  ESPHL_ERROR_CHECK(err, "DHCPC start error");
336  }
337 #if ENABLE_IPV6
338  err = esp_netif_create_ip6_linklocal(this->eth_netif_);
339  if (err != ESP_OK) {
340  ESPHL_ERROR_CHECK(err, "IPv6 local failed");
341  }
342 #endif /* ENABLE_IPV6 */
343  }
344 
345  this->connect_begin_ = millis();
346  this->status_set_warning();
347 }
348 
350 
352  esp_netif_ip_info_t ip;
353  esp_netif_get_ip_info(this->eth_netif_, &ip);
354  ESP_LOGCONFIG(TAG, " IP Address: %s", network::IPAddress(&ip.ip).str().c_str());
355  ESP_LOGCONFIG(TAG, " Hostname: '%s'", App.get_name().c_str());
356  ESP_LOGCONFIG(TAG, " Subnet: %s", network::IPAddress(&ip.netmask).str().c_str());
357  ESP_LOGCONFIG(TAG, " Gateway: %s", network::IPAddress(&ip.gw).str().c_str());
358 
359  const ip_addr_t *dns_ip1 = dns_getserver(0);
360  const ip_addr_t *dns_ip2 = dns_getserver(1);
361 
362  ESP_LOGCONFIG(TAG, " DNS1: %s", network::IPAddress(dns_ip1).str().c_str());
363  ESP_LOGCONFIG(TAG, " DNS2: %s", network::IPAddress(dns_ip2).str().c_str());
364 
365 #if ENABLE_IPV6
366  if (this->ipv6_count_ > 0) {
367  esp_ip6_addr_t ip6_addr;
368  esp_netif_get_ip6_linklocal(this->eth_netif_, &ip6_addr);
369  ESP_LOGCONFIG(TAG, " IPv6: " IPV6STR, IPV62STR(ip6_addr));
370 
371  if (esp_netif_get_ip6_global(this->eth_netif_, &ip6_addr) == 0 &&
372  esp_netif_ip6_get_addr_type(&ip6_addr) == ESP_IP6_ADDR_IS_GLOBAL) {
373  ESP_LOGCONFIG(TAG, "IPv6 Addr (Global): " IPV6STR, IPV62STR(ip6_addr));
374  }
375  }
376 #endif /* ENABLE_IPV6 */
377 
378  esp_err_t err;
379 
380  uint8_t mac[6];
381  err = esp_eth_ioctl(this->eth_handle_, ETH_CMD_G_MAC_ADDR, &mac);
382  ESPHL_ERROR_CHECK(err, "ETH_CMD_G_MAC error");
383  ESP_LOGCONFIG(TAG, " MAC Address: %02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
384 
385  eth_duplex_t duplex_mode;
386  err = esp_eth_ioctl(this->eth_handle_, ETH_CMD_G_DUPLEX_MODE, &duplex_mode);
387  ESPHL_ERROR_CHECK(err, "ETH_CMD_G_DUPLEX_MODE error");
388  ESP_LOGCONFIG(TAG, " Is Full Duplex: %s", YESNO(duplex_mode == ETH_DUPLEX_FULL));
389 
390  eth_speed_t speed;
391  err = esp_eth_ioctl(this->eth_handle_, ETH_CMD_G_SPEED, &speed);
392  ESPHL_ERROR_CHECK(err, "ETH_CMD_G_SPEED error");
393  ESP_LOGCONFIG(TAG, " Link Speed: %u", speed == ETH_SPEED_100M ? 100 : 10);
394 }
395 
396 void EthernetComponent::set_phy_addr(uint8_t phy_addr) { this->phy_addr_ = phy_addr; }
397 void EthernetComponent::set_power_pin(int power_pin) { this->power_pin_ = power_pin; }
398 void EthernetComponent::set_mdc_pin(uint8_t mdc_pin) { this->mdc_pin_ = mdc_pin; }
399 void EthernetComponent::set_mdio_pin(uint8_t mdio_pin) { this->mdio_pin_ = mdio_pin; }
401 void EthernetComponent::set_clk_mode(emac_rmii_clock_mode_t clk_mode, emac_rmii_clock_gpio_t clk_gpio) {
402  this->clk_mode_ = clk_mode;
403  this->clk_gpio_ = clk_gpio;
404 }
405 void EthernetComponent::set_manual_ip(const ManualIP &manual_ip) { this->manual_ip_ = manual_ip; }
406 
408  if (this->use_address_.empty()) {
409  return App.get_name() + ".local";
410  }
411  return this->use_address_;
412 }
413 
414 void EthernetComponent::set_use_address(const std::string &use_address) { this->use_address_ = use_address; }
415 
417  ESP_LOGI(TAG, "Powering down ethernet PHY");
418  if (this->phy_ == nullptr) {
419  ESP_LOGE(TAG, "Ethernet PHY not assigned");
420  return false;
421  }
422  this->connected_ = false;
423  this->started_ = false;
424  if (this->phy_->pwrctl(this->phy_, false) != ESP_OK) {
425  ESP_LOGE(TAG, "Error powering down ethernet PHY");
426  return false;
427  }
428  return true;
429 }
430 
432 #define KSZ80XX_PC2R_REG_ADDR (0x1F)
433 
434  esp_err_t err;
435 
436  uint32_t phy_control_2;
437  err = mac->read_phy_reg(mac, this->phy_addr_, KSZ80XX_PC2R_REG_ADDR, &(phy_control_2));
438  ESPHL_ERROR_CHECK(err, "Read PHY Control 2 failed");
439  ESP_LOGVV(TAG, "KSZ8081 PHY Control 2: %s", format_hex_pretty((u_int8_t *) &phy_control_2, 2).c_str());
440 
441  /*
442  * Bit 7 is `RMII Reference Clock Select`. Default is `0`.
443  * KSZ8081RNA:
444  * 0 - clock input to XI (Pin 8) is 25 MHz for RMII – 25 MHz clock mode.
445  * 1 - clock input to XI (Pin 8) is 50 MHz for RMII – 50 MHz clock mode.
446  * KSZ8081RND:
447  * 0 - clock input to XI (Pin 8) is 50 MHz for RMII – 50 MHz clock mode.
448  * 1 - clock input to XI (Pin 8) is 25 MHz (driven clock only, not a crystal) for RMII – 25 MHz clock mode.
449  */
450  if ((phy_control_2 & (1 << 7)) != (1 << 7)) {
451  phy_control_2 |= 1 << 7;
452  err = mac->write_phy_reg(mac, this->phy_addr_, KSZ80XX_PC2R_REG_ADDR, phy_control_2);
453  ESPHL_ERROR_CHECK(err, "Write PHY Control 2 failed");
454  err = mac->read_phy_reg(mac, this->phy_addr_, KSZ80XX_PC2R_REG_ADDR, &(phy_control_2));
455  ESPHL_ERROR_CHECK(err, "Read PHY Control 2 failed");
456  ESP_LOGVV(TAG, "KSZ8081 PHY Control 2: %s", format_hex_pretty((u_int8_t *) &phy_control_2, 2).c_str());
457  }
458 
459 #undef KSZ80XX_PC2R_REG_ADDR
460 }
461 
462 } // namespace ethernet
463 } // namespace esphome
464 
465 #endif // USE_ESP32
std::string format_hex_pretty(const uint8_t *data, size_t length)
Format the byte array data of length len in pretty-printed, human-readable hex.
Definition: helpers.cpp:350
void set_manual_ip(const ManualIP &manual_ip)
esp_eth_phy_t * esp_eth_phy_new_jl1101(const eth_phy_config_t *config)
void ksz8081_set_clock_reference_(esp_eth_mac_t *mac)
Set RMII Reference Clock Select bit for KSZ8081.
std::string str() const
Definition: ip_address.h:97
int speed
Definition: fan.h:35
static void got_ip6_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
uint32_t IRAM_ATTR HOT millis()
Definition: core.cpp:25
EthernetComponent * global_eth_component
void set_clk_mode(emac_rmii_clock_mode_t clk_mode, emac_rmii_clock_gpio_t clk_gpio)
static void got_ip_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
void status_clear_warning()
Definition: component.cpp:153
uint8_t type
Application App
Global storage of Application pointer - only one Application can exist.
const std::string & get_name() const
Get the name of this Application set by pre_setup().
Definition: application.h:160
void status_set_warning()
Definition: component.cpp:145
void set_use_address(const std::string &use_address)
uint8_t status
Definition: bl0942.h:23
virtual void mark_failed()
Mark this component as failed.
Definition: component.cpp:112
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
uint8_t event_id
Definition: tt21100.cpp:15
static void eth_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
void IRAM_ATTR HOT delay(uint32_t ms)
Definition: core.cpp:26