diff --git a/esphome/components/mdns/mdns_component.h b/esphome/components/mdns/mdns_component.h index 13c8ccf288..47cad4bf71 100644 --- a/esphome/components/mdns/mdns_component.h +++ b/esphome/components/mdns/mdns_component.h @@ -129,6 +129,10 @@ class MDNSComponent final : public Component { #endif #ifdef USE_MDNS_STORE_SERVICES StaticVector services_{}; +#endif +#ifdef USE_RP2040 + bool was_connected_{false}; + bool initialized_{false}; #endif void compile_records_(StaticVector &services, char *mac_address_buf); }; diff --git a/esphome/components/mdns/mdns_rp2040.cpp b/esphome/components/mdns/mdns_rp2040.cpp index 05d991c1fa..c0b22aa84f 100644 --- a/esphome/components/mdns/mdns_rp2040.cpp +++ b/esphome/components/mdns/mdns_rp2040.cpp @@ -36,12 +36,32 @@ static void register_rp2040(MDNSComponent *, StaticVectorsetup_buffers_and_register_(register_rp2040); - // Schedule MDNS.update() via set_interval() instead of overriding loop(). - // This removes the component from the per-iteration loop list entirely, - // eliminating virtual dispatch overhead on every main loop cycle. - // See MDNS_UPDATE_INTERVAL_MS comment in mdns_component.h for safety analysis. - this->set_interval(MDNS_UPDATE_INTERVAL_MS, []() { MDNS.update(); }); + // RP2040's LEAmDNS library registers a LwipIntf::stateUpCB() callback to restart + // mDNS when the network interface reconnects. However, stateUpCB() is stubbed out + // in arduino-pico's LwipIntfCB.cpp because the original ESP8266 implementation used + // schedule_function() which doesn't exist in arduino-pico, and the callback can't + // safely run directly since netif status callbacks fire from IRQ context + // (PICO_CYW43_ARCH_THREADSAFE_BACKGROUND) while _restart() allocates UDP sockets. + // + // Workaround: defer MDNS.begin() and service registration until the network is + // connected (has an IP), then call notifyAPChange() on subsequent reconnects to + // restart mDNS probing and announcing — all from main loop context so it's + // thread-safe. + this->set_interval(MDNS_UPDATE_INTERVAL_MS, [this]() { + bool connected = network::is_connected(); + if (connected && !this->was_connected_) { + if (!this->initialized_) { + this->setup_buffers_and_register_(register_rp2040); + this->initialized_ = true; + } else { + MDNS.notifyAPChange(); + } + } + this->was_connected_ = connected; + if (this->initialized_) { + MDNS.update(); + } + }); } void MDNSComponent::on_shutdown() { diff --git a/esphome/components/wifi/__init__.py b/esphome/components/wifi/__init__.py index 480ccd65c5..9f73b1cc6f 100644 --- a/esphome/components/wifi/__init__.py +++ b/esphome/components/wifi/__init__.py @@ -563,13 +563,6 @@ async def to_code(config): cg.add_library("ESP8266WiFi", None) elif CORE.is_rp2040: cg.add_library("WiFi", None) - # RP2040's mDNS library (LEAmDNS) relies on LwipIntf::stateUpCB() to restart - # mDNS when the network interface reconnects. However, this callback is disabled - # in the arduino-pico framework. As a workaround, we block component setup until - # WiFi is connected via can_proceed(), ensuring mDNS.begin() is called with an - # active connection. This define enables the loop priority sorting infrastructure - # used during the setup blocking phase. - cg.add_define("USE_LOOP_PRIORITY") if CORE.is_esp32: if config[CONF_ENABLE_BTM] or config[CONF_ENABLE_RRM]: diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index 60764955cc..09f883ed61 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -2109,20 +2109,6 @@ void WiFiComponent::retry_connect() { } } -#ifdef USE_RP2040 -// RP2040's mDNS library (LEAmDNS) relies on LwipIntf::stateUpCB() to restart -// mDNS when the network interface reconnects. However, this callback is disabled -// in the arduino-pico framework. As a workaround, we block component setup until -// WiFi is connected, ensuring mDNS.begin() is called with an active connection. - -bool WiFiComponent::can_proceed() { - if (!this->has_sta() || this->state_ == WIFI_COMPONENT_STATE_DISABLED || this->ap_setup_) { - return true; - } - return this->is_connected_(); -} -#endif - void WiFiComponent::set_reboot_timeout(uint32_t reboot_timeout) { this->reboot_timeout_ = reboot_timeout; } bool WiFiComponent::is_connected_() const { return this->state_ == WIFI_COMPONENT_STATE_STA_CONNECTED && diff --git a/esphome/components/wifi/wifi_component.h b/esphome/components/wifi/wifi_component.h index f340b708c9..883cc1344b 100644 --- a/esphome/components/wifi/wifi_component.h +++ b/esphome/components/wifi/wifi_component.h @@ -437,10 +437,6 @@ class WiFiComponent : public Component { void retry_connect(); -#ifdef USE_RP2040 - bool can_proceed() override; -#endif - void set_reboot_timeout(uint32_t reboot_timeout); bool is_connected() const { return this->connected_; }