ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
network_component.cpp
Go to the documentation of this file.
1#include "network_component.h"
2
4#if defined(USE_NETWORK) && defined(USE_ESP32)
5#include "esphome/core/log.h"
6#include "esp_err.h"
7#include "esp_netif.h"
8#include "esp_event.h"
9#include "lwip/opt.h"
10
11#ifdef USE_NETWORK_DEFAULT_ROUTE
14#include "esp_netif_net_stack.h"
15#include "lwip/netif.h"
16#ifdef USE_ETHERNET
18#endif
19#ifdef USE_WIFI
21#endif
22#endif
23
24namespace esphome::network {
25
26ESPHOME_LOG_TAG(TAG, "network");
27
29 // Initialize ESP-IDF network interfaces and ensure the default event loop exists
30 esp_err_t err;
31 err = esp_netif_init();
32 if (err != ESP_OK) {
33 ESP_LOGE(TAG, "esp_netif_init failed: (%d) %s", err, esp_err_to_name(err));
34 this->mark_failed();
35 return;
36 }
37 err = esp_event_loop_create_default();
38 // ESP_ERR_INVALID_STATE is returned if the default loop already exists,
39 // which is fine since we just want to make sure it exists
40 if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
41 ESP_LOGE(TAG, "esp_event_loop_create_default failed: (%d) %s", err, esp_err_to_name(err));
42 this->mark_failed();
43 return;
44 }
45}
46
48 // The effective compile-time lwIP value, so the log reflects tcp_send_buffer
49 // or the high performance bundle when either changed it.
50 ESP_LOGCONFIG(TAG,
51 "Network:\n"
52 " TCP send buffer: %d bytes",
53 TCP_SND_BUF);
54}
55
56#ifdef USE_NETWORK_DEFAULT_ROUTE
57static esp_netif_t *connected_wifi_netif() {
58#ifdef USE_WIFI
59 auto *wifi = wifi::global_wifi_component;
60 if (wifi != nullptr && wifi->is_connected())
61 return wifi->get_esp_netif_sta();
62#endif
63 return nullptr;
64}
65
66static esp_netif_t *connected_ethernet_netif() {
67#ifdef USE_ETHERNET
69 if (eth != nullptr && eth->is_connected())
70 return eth->get_esp_netif();
71#endif
72 return nullptr;
73}
74
76 // Pin the default route to the first connected interface in the user's priority
77 // order; ESP-IDF's own route_prio selection would always favor WiFi.
78 // USE_NETWORK_PRIMARY_INTERFACE_WIFI is emitted for a wifi-first priority list;
79 // it selects the reported address in util.cpp and doubles as the route-order
80 // pivot here — the two uses must stay in sync.
81 esp_netif_t *wifi = connected_wifi_netif();
82 esp_netif_t *ethernet = connected_ethernet_netif();
83#ifdef USE_NETWORK_PRIMARY_INTERFACE_WIFI
84 esp_netif_t *best = wifi != nullptr ? wifi : ethernet;
85#else
86 esp_netif_t *best = ethernet != nullptr ? ethernet : wifi;
87#endif
89 if (best != nullptr) {
91 }
92 if (active != this->active_interface_) {
93 this->active_interface_ = active;
94#ifdef NETWORK_ACTIVE_INTERFACE_LISTENER_COUNT
95 for (auto *listener : this->active_interface_listeners_)
96 listener->on_active_interface(active);
97#endif
98 }
99 if (best == nullptr) {
100 // Forget the last winner: stopping its netif cleared lwIP's default route and
101 // IDF's manual override suppresses re-election, so reconnect must re-assert it.
102 this->default_netif_ = nullptr;
103 return;
104 }
105 if (best == this->default_netif_) {
106 // Same winner as the last assert. Still re-assert if lwIP's default route is
107 // not the winner's netif: a winner whose netif bounced down and up between two
108 // polls would otherwise stay routeless (stopping a netif nulls lwIP's
109 // netif_default). Checking lwIP directly keeps this independent of IDF's
110 // re-election bookkeeping (esp_netif_get_default_netif() cannot detect it).
111 // Throttled: LwIPLock is the global lwIP core mutex, and this branch runs on
112 // every pass once the route has settled.
115 return;
116 this->last_route_check_ = now;
117 bool route_is_ours;
118 {
120 route_is_ours = static_cast<void *>(netif_default) == esp_netif_get_netif_impl(best);
121 }
122 if (route_is_ours)
123 return;
124 }
125 esp_err_t err = esp_netif_set_default_netif(best);
126 if (err != ESP_OK) {
127 ESP_LOGW(TAG, "Failed to set default interface: (%d) %s", err, esp_err_to_name(err));
128 // Cache the intent anyway: subsequent passes take the same-winner branch
129 // above, so retries are throttled to ROUTE_CHECK_INTERVAL_MS and the lwIP
130 // verification keeps re-attempting until the route is actually ours.
131 this->default_netif_ = best;
133 return;
134 }
135 this->default_netif_ = best;
136 ESP_LOGI(TAG, "Default interface: %s", esp_netif_get_desc(best));
137}
138#endif // USE_NETWORK_DEFAULT_ROUTE
139
140} // namespace esphome::network
141#endif
uint32_t IRAM_ATTR HOT get_loop_component_start_time() const
Get the cached time in milliseconds from when the current component started its loop execution.
void mark_failed()
Mark this component as failed.
Helper class to lock the lwIP TCPIP core when making lwIP API calls from non-TCPIP threads.
Definition helpers.h:2107
esp_netif_t * get_esp_netif()
esp_netif handle, used by network for default-route arbitration.
StaticVector< ActiveInterfaceListener *, NETWORK_ACTIVE_INTERFACE_LISTENER_COUNT > active_interface_listeners_
static constexpr uint32_t ROUTE_CHECK_INTERVAL_MS
esp_netif_t * get_esp_netif_sta()
esp_netif handle of the station interface, used by network for default-route arbitration.
EthernetComponent * global_eth_component
ESPHOME_LOG_TAG(TAG, "network")
NetworkInterface
The interface holding the default route.
WiFiComponent * global_wifi_component
Application App
Global storage of Application pointer - only one Application can exist.
struct esp_netif_obj esp_netif_t
static void uint32_t
SemaphoreHandle_t lock