diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index fdbd70bc61..07cb2ac243 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -632,6 +632,9 @@ void WiFiComponent::setup() { #endif if (this->enable_on_boot_) { +#ifdef USE_ESP32 + this->wifi_lazy_init_(); +#endif this->start(); } else { this->state_ = WIFI_COMPONENT_STATE_DISABLED; @@ -1275,6 +1278,11 @@ void WiFiComponent::enable() { ESP_LOGD(TAG, "Enabling"); this->state_ = WIFI_COMPONENT_STATE_OFF; +#ifdef USE_ESP32 + // Idempotent — only allocates DMA buffers + netifs on the first call. After this, + // start() can safely run. + this->wifi_lazy_init_(); +#endif this->start(); } diff --git a/esphome/components/wifi/wifi_component.h b/esphome/components/wifi/wifi_component.h index 0437267a1f..d0521e548a 100644 --- a/esphome/components/wifi/wifi_component.h +++ b/esphome/components/wifi/wifi_component.h @@ -694,6 +694,12 @@ class WiFiComponent final : public Component { bool wifi_apply_hostname_(); bool wifi_sta_connect_(const WiFiAP &ap); void wifi_pre_setup_(); +#ifdef USE_ESP32 + // ESP-IDF only: defers esp_wifi_init() + netif creation (which allocate ~15-30KB of + // DMA-capable internal SRAM) until wifi actually needs to come up. Idempotent. + // Called from setup() only when enable_on_boot_=true, and from enable() on first use. + void wifi_lazy_init_(); +#endif WiFiSTAConnectStatus wifi_sta_connect_status_() const; bool is_connected_() const { return this->state_ == WIFI_COMPONENT_STATE_STA_CONNECTED && @@ -889,6 +895,12 @@ class WiFiComponent final : public Component { bool rrm_{false}; #endif bool enable_on_boot_{true}; +#ifdef USE_ESP32 + // Tracks whether esp_wifi_init() + netif creation has happened. Allows enable() + // to be called at runtime without re-allocating, and ensures the heavy init is + // skipped entirely when enable_on_boot_ is false until first enable(). + bool wifi_initialized_{false}; +#endif bool got_ipv4_address_{false}; bool keep_scan_results_{false}; bool has_completed_scan_after_captive_portal_start_{ diff --git a/esphome/components/wifi/wifi_component_esp_idf.cpp b/esphome/components/wifi/wifi_component_esp_idf.cpp index 11b39b5000..b395c77141 100644 --- a/esphome/components/wifi/wifi_component_esp_idf.cpp +++ b/esphome/components/wifi/wifi_component_esp_idf.cpp @@ -163,11 +163,26 @@ void WiFiComponent::wifi_pre_setup_() { ESP_LOGE(TAG, "esp_event_handler_instance_register failed: %s", esp_err_to_name(err)); return; } + // NOTE: netif creation + esp_wifi_init() used to live here. They allocate ~15-30KB of + // DMA-capable internal SRAM, which competes with W5500 SPI DMA and I2S DMA on + // memory-tight devices. They are now deferred to wifi_lazy_init_(), called from + // setup() when enable_on_boot_ is true, or from enable() on first runtime enable. + // This makes enable_on_boot:false genuinely skip the wifi DMA allocation. +} - s_sta_netif = esp_netif_create_default_wifi_sta(); +void WiFiComponent::wifi_lazy_init_() { + if (this->wifi_initialized_) + return; + + // Guard each creation so partial init (e.g. a failed esp_wifi_init() below) + // followed by a retry via enable() does not leak the existing netif handle + // nor re-register the default WiFi handlers. + if (s_sta_netif == nullptr) + s_sta_netif = esp_netif_create_default_wifi_sta(); #ifdef USE_WIFI_AP - s_ap_netif = esp_netif_create_default_wifi_ap(); + if (s_ap_netif == nullptr) + s_ap_netif = esp_netif_create_default_wifi_ap(); #endif // USE_WIFI_AP wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); @@ -175,7 +190,7 @@ void WiFiComponent::wifi_pre_setup_() { ESP_LOGW(TAG, "starting wifi without nvs"); cfg.nvs_enable = false; } - err = esp_wifi_init(&cfg); + esp_err_t err = esp_wifi_init(&cfg); if (err != ERR_OK) { ESP_LOGE(TAG, "esp_wifi_init failed: %s", esp_err_to_name(err)); return; @@ -185,6 +200,7 @@ void WiFiComponent::wifi_pre_setup_() { ESP_LOGE(TAG, "esp_wifi_set_storage failed: %s", esp_err_to_name(err)); return; } + this->wifi_initialized_ = true; } bool WiFiComponent::wifi_mode_(optional sta, optional ap) { diff --git a/tests/components/wifi/test-lifecycle.esp32-idf.yaml b/tests/components/wifi/test-lifecycle.esp32-idf.yaml new file mode 100644 index 0000000000..229a24b2d1 --- /dev/null +++ b/tests/components/wifi/test-lifecycle.esp32-idf.yaml @@ -0,0 +1,15 @@ +wifi: + ssid: MySSID + password: password1 + enable_on_boot: false + +esphome: + on_boot: + priority: 200 + then: + - if: + condition: + not: + wifi.enabled: + then: + - wifi.enable: