diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index edfb93bba2..020ad294c2 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -632,11 +632,14 @@ void WiFiComponent::setup() { #endif if (this->enable_on_boot_) { +#ifdef USE_ESP32 + this->wifi_lazy_init_(); +#endif this->start(); } else { -#ifdef USE_ESP32 - esp_netif_init(); -#endif + // Note: esp_netif_init() used to be called here as a fallback for non-IDF builds. + // It is now centralized in NetworkComponent::setup(), which runs at AFTER_BLUETOOTH + // (300) ahead of WiFi (250). No action needed. this->state_ = WIFI_COMPONENT_STATE_DISABLED; } } @@ -1278,6 +1281,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..b5ca4e1e4f 100644 --- a/esphome/components/wifi/wifi_component_esp_idf.cpp +++ b/esphome/components/wifi/wifi_component_esp_idf.cpp @@ -163,6 +163,16 @@ 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. +} + +void WiFiComponent::wifi_lazy_init_() { + if (this->wifi_initialized_) + return; s_sta_netif = esp_netif_create_default_wifi_sta(); @@ -175,7 +185,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 +195,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) {