ESPHome 2026.10.0-dev
Loading...
Searching...
No Matches
mdns_esp8266.cpp
Go to the documentation of this file.
2#if defined(USE_ESP8266) && defined(USE_ARDUINO) && defined(USE_MDNS)
3
4#include <ESP8266mDNS.h>
8#include "esphome/core/hal.h"
9#include "esphome/core/log.h"
10#include "mdns_component.h"
11// wifi_component.h is pulled in transitively by mdns_component.h when
12// USE_MDNS_WIFI_LISTENER is defined.
13
14namespace esphome::mdns {
15
16// Main-loop calls into LEAmDNS that send (update() and close(); begin(), addService() and
17// the scheduled restart never reach a send) can yield inside UdpContext::sendTimeout(); a
18// packet arriving then re-enters LEAmDNS from lwIP on the same UdpContext and both sides
19// free the same tx pbufs (#18760). Received packets stay queued during such a call and are
20// processed from the main loop afterwards.
21class GuardedMDNSResponder : public ::esp8266::MDNSImplementation::MDNSResponder {
22 public:
23 void update_guarded() { this->run_guarded_(&GuardedMDNSResponder::update); }
24 void close_guarded() { this->run_guarded_(&GuardedMDNSResponder::close); }
25
26 private:
27 void run_guarded_(bool (GuardedMDNSResponder::*fn)()) {
28 UdpContext *ctx = this->m_pUDPContext;
29 if (ctx == nullptr) {
30 (this->*fn)();
31 return;
32 }
33 // Set every time: a restart replaces the context together with its stock handler. Only
34 // begin() and the scheduled netif callback restart, never update() or close(), so the
35 // context cannot change underneath this call.
36 ctx->onRx([this]() {
37 if (!this->in_loop_call_) {
38 this->_callProcess();
39 }
40 });
41 this->in_loop_call_ = true;
42 (this->*fn)();
43 // close() releases the context; a yield in here queues further packets for this loop too
44 while (this->m_pUDPContext != nullptr && this->m_pUDPContext->next()) {
45 this->_parseMessage();
46 }
47 this->in_loop_call_ = false;
48 }
49
50 volatile bool in_loop_call_{false};
51};
52
53static GuardedMDNSResponder mdns_responder; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
54
55static void register_esp8266(MDNSComponent *, StaticVector<MDNSService, MDNS_SERVICE_COUNT> &services) {
56 mdns_responder.begin(App.get_name().c_str());
57
58 for (const auto &service : services) {
59 // Strip the leading underscore from the proto and service_type. While it is
60 // part of the wire protocol to have an underscore, and for example ESP-IDF
61 // expects the underscore to be there, the ESP8266 implementation always adds
62 // the underscore itself.
63 auto *proto = MDNS_STR_ARG(service.proto);
64 while (progmem_read_byte((const uint8_t *) proto) == '_') {
65 proto++;
66 }
67 auto *service_type = MDNS_STR_ARG(service.service_type);
68 while (progmem_read_byte((const uint8_t *) service_type) == '_') {
69 service_type++;
70 }
71 uint16_t port = service.port.value();
72 mdns_responder.addService(FPSTR(service_type), FPSTR(proto), port);
73 for (const auto &record : service.txt_records) {
74 mdns_responder.addServiceTxt(FPSTR(service_type), FPSTR(proto), FPSTR(MDNS_STR_ARG(record.key)),
75 FPSTR(MDNS_STR_ARG(record.value)));
76 }
77 }
78}
79
80#ifdef USE_MDNS_EVENT_DRIVEN_POLLING
82 // uint32_t-ID set_interval/set_timeout already does atomic cancel-and-add.
84#ifdef USE_MDNS_WIFI_LISTENER
85 // MDNS.update() can suspend the loop in UdpContext::sendTimeout() while a send is
86 // failing (radio off-channel during a roam scan, or mid reconnect); an incoming
87 // packet then re-enters LEAmDNS from lwIP and corrupts shared UdpContext state.
88 // Skip the tick while the radio cannot transmit (#18760), but keep polling while
89 // the AP is serving clients (AP-only or fallback AP with the STA down).
90 auto *wifi = wifi::global_wifi_component;
91 if (wifi->is_roaming() || (!wifi->is_connected() && !wifi->is_ap_active()))
92 return;
93#endif
94 mdns_responder.update_guarded();
95 });
97}
98#endif
99
101 this->setup_buffers_and_register_(register_esp8266);
102#ifdef USE_MDNS_WIFI_LISTENER
103 // LEAmDNS's own LwipIntf::statusChangeCB drives _restart() on netif changes; we just
104 // arm the window around the initial probe/announce and each reconnect. Unconditional
105 // here is safe: setup_priority::AFTER_CONNECTION guarantees the network is up.
107 this->start_polling_window_();
108#endif
109}
110
111#ifdef USE_MDNS_WIFI_LISTENER
113 const network::IPAddress &) {
114 // IP listener only fires on acquisition (not loss), so any notification is a fresh
115 // IP worth re-arming for. start_polling_window_() is idempotent.
116 if (ips[0].is_set()) {
117 this->start_polling_window_();
118 }
119}
120#endif
121
123 mdns_responder.close_guarded();
124 delay(10);
125}
126
127} // namespace esphome::mdns
128
129#endif
const StringRef & get_name() const
Get the name of this Application set by pre_setup().
bool cancel_interval(const char *name)
Cancel an interval function.
Definition component.cpp:92
void set_timeout(const char *name, uint32_t timeout, std::function< void()> &&f)
Set a timeout function with a const char* name.
Definition component.cpp:96
void set_interval(const char *name, uint32_t interval, std::function< void()> &&f)
Set an interval function with a const char* name.
Definition component.cpp:88
constexpr const char * c_str() const
Definition string_ref.h:73
static constexpr uint32_t MDNS_POLL_STOP_ID
void start_polling_window_()
Arm a fresh MDNS_POLL_WINDOW_MS polling window.
void on_ip_state(const network::IPAddresses &ips, const network::IPAddress &dns1, const network::IPAddress &dns2) override
static constexpr uint32_t MDNS_POLL_WINDOW_MS
void setup_buffers_and_register_(PlatformRegisterFn platform_register)
static constexpr uint32_t MDNS_POLL_ID
static constexpr uint32_t MDNS_UPDATE_INTERVAL_MS
void add_ip_state_listener(WiFiIPStateListener *listener)
Add a listener for IP state changes.
std::array< IPAddress, 5 > IPAddresses
Definition ip_address.h:301
WiFiComponent * global_wifi_component
void HOT delay(uint32_t ms)
Definition hal.cpp:85
Application App
Global storage of Application pointer - only one Application can exist.
uint8_t progmem_read_byte(const uint8_t *addr)
Definition hal.h:43