esp8266 / Arduino

ESP8266 core for Arduino
GNU Lesser General Public License v2.1
15.98k stars 13.33k forks source link

mDNS in NodeMCU #8912

Open silasalves opened 1 year ago

silasalves commented 1 year ago

Basic Infos

Platform

Settings in IDE

Problem Description

None of the mDNS samples work for me. I can connect through the IP address, but I am not able to find the device using mDNS.

The sample that works the "best" so far is the HelloServer. After it connects and prints

.......
Connected to Network
IP address: 192.168.50.43
MDNS responder started
HTTP server started

I start pinging esp8266.local and it works once, but then it stops working:

PS C:\Users\silas> ping esp8266.local
Ping request could not find host esp8266.local. Please check the name and try again.
PS C:\Users\silas> ping esp8266.local

Pinging esp8266.local [192.168.50.43] with 32 bytes of data:
Reply from 192.168.50.43: bytes=32 time=491ms TTL=255
Reply from 192.168.50.43: bytes=32 time=88ms TTL=255
Reply from 192.168.50.43: bytes=32 time=769ms TTL=255
Reply from 192.168.50.43: bytes=32 time=214ms TTL=255

Ping statistics for 192.168.50.43:
    Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 88ms, Maximum = 769ms, Average = 390ms
PS C:\Users\silas> ping esp8266.local
Ping request could not find host esp8266.local. Please check the name and try again.
PS C:\Users\silas> ping esp8266.local
Ping request could not find host esp8266.local. Please check the name and try again.

I didn't do any changes to the sample code (other than the network data) and I couldn't find anyone with a solution to this problem either.

Any ideas of what could be preventing MDNS from working after the first time? I am not using OTA and I am erasing all flash contents.

I am pasting the sample code for completion:

MCVE Sketch


#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>

#ifndef STASSID
#define STASSID "..."
#define STAPSK "..."
#endif

const char* ssid = STASSID;
const char* password = STAPSK;

ESP8266WebServer server(80);

const int led = 13;

void handleRoot() {
  digitalWrite(led, 1);
  server.send(200, "text/plain", "hello from esp8266!\r\n");
  digitalWrite(led, 0);
}

void handleNotFound() {
  digitalWrite(led, 1);
  String message = "File Not Found\n\n";
  message += "URI: ";
  message += server.uri();
  message += "\nMethod: ";
  message += (server.method() == HTTP_GET) ? "GET" : "POST";
  message += "\nArguments: ";
  message += server.args();
  message += "\n";
  for (uint8_t i = 0; i < server.args(); i++) { message += " " + server.argName(i) + ": " + server.arg(i) + "\n"; }
  server.send(404, "text/plain", message);
  digitalWrite(led, 0);
}

void setup(void) {
  pinMode(led, OUTPUT);
  digitalWrite(led, 0);
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.println("");

  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  if (MDNS.begin("esp8266")) { Serial.println("MDNS responder started"); }

  server.on("/", handleRoot);

  server.on("/inline", []() {
    server.send(200, "text/plain", "this works as well");
  });

  server.on("/gif", []() {
    static const uint8_t gif[] PROGMEM = {
      0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x10, 0x00, 0x10, 0x00, 0x80, 0x01,
      0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x2c, 0x00, 0x00, 0x00, 0x00,
      0x10, 0x00, 0x10, 0x00, 0x00, 0x02, 0x19, 0x8c, 0x8f, 0xa9, 0xcb, 0x9d,
      0x00, 0x5f, 0x74, 0xb4, 0x56, 0xb0, 0xb0, 0xd2, 0xf2, 0x35, 0x1e, 0x4c,
      0x0c, 0x24, 0x5a, 0xe6, 0x89, 0xa6, 0x4d, 0x01, 0x00, 0x3b
    };
    char gif_colored[sizeof(gif)];
    memcpy_P(gif_colored, gif, sizeof(gif));
    // Set the background to a random set of colors
    gif_colored[16] = millis() % 256;
    gif_colored[17] = millis() % 256;
    gif_colored[18] = millis() % 256;
    server.send(200, "image/gif", gif_colored, sizeof(gif_colored));
  });

  server.onNotFound(handleNotFound);

  /////////////////////////////////////////////////////////
  // Hook examples

  server.addHook([](const String& method, const String& url, WiFiClient* client, ESP8266WebServer::ContentTypeFunction contentType) {
    (void)method;       // GET, PUT, ...
    (void)url;          // example: /root/myfile.html
    (void)client;       // the webserver tcp client connection
    (void)contentType;  // contentType(".html") => "text/html"
    Serial.printf("A useless web hook has passed\n");
    Serial.printf("(this hook is in 0x%08x area (401x=IRAM 402x=FLASH))\n", esp_get_program_counter());
    return ESP8266WebServer::CLIENT_REQUEST_CAN_CONTINUE;
  });

  server.addHook([](const String&, const String& url, WiFiClient*, ESP8266WebServer::ContentTypeFunction) {
    if (url.startsWith("/fail")) {
      Serial.printf("An always failing web hook has been triggered\n");
      return ESP8266WebServer::CLIENT_MUST_STOP;
    }
    return ESP8266WebServer::CLIENT_REQUEST_CAN_CONTINUE;
  });

  server.addHook([](const String&, const String& url, WiFiClient* client, ESP8266WebServer::ContentTypeFunction) {
    if (url.startsWith("/dump")) {
      Serial.printf("The dumper web hook is on the run\n");

      // Here the request is not interpreted, so we cannot for sure
      // swallow the exact amount matching the full request+content,
      // hence the tcp connection cannot be handled anymore by the
      // webserver.
#ifdef STREAMSEND_API
      // we are lucky
      client->sendAll(Serial, 500);
#else
      auto last = millis();
      while ((millis() - last) < 500) {
        char buf[32];
        size_t len = client->read((uint8_t*)buf, sizeof(buf));
        if (len > 0) {
          Serial.printf("(<%d> chars)", (int)len);
          Serial.write(buf, len);
          last = millis();
        }
      }
#endif
      // Two choices: return MUST STOP and webserver will close it
      //                       (we already have the example with '/fail' hook)
      // or                  IS GIVEN and webserver will forget it
      // trying with IS GIVEN and storing it on a dumb WiFiClient.
      // check the client connection: it should not immediately be closed
      // (make another '/dump' one to close the first)
      Serial.printf("\nTelling server to forget this connection\n");
      static WiFiClient forgetme = *client;  // stop previous one if present and transfer client refcounter
      return ESP8266WebServer::CLIENT_IS_GIVEN;
    }
    return ESP8266WebServer::CLIENT_REQUEST_CAN_CONTINUE;
  });

  // Hook examples
  /////////////////////////////////////////////////////////

  server.begin();
  Serial.println("HTTP server started");
}

void loop(void) {
  server.handleClient();
  MDNS.update();
}
mcspr commented 1 year ago

Latency always that high?

Reply from 192.168.50.43: bytes=32 time=491ms TTL=255 Reply from 192.168.50.43: bytes=32 time=88ms TTL=255 Reply from 192.168.50.43: bytes=32 time=769ms TTL=255 Reply from 192.168.50.43: bytes=32 time=214ms TTL=255

Any difference when dealing with DNS client directly?

> Resolve-DnsName -llmnronly ESP-4A2914.local

Name                                           Type   TTL   Section    IPAddress
----                                           ----   ---   -------    ---------
ESP-4A2914.local                               A      120   Answer     10.10.10.137

(check out get-help resolve-dnsname and dnsmodule docs)

afaik we are dealing with multicast replies; looking at ping, there is a single query. PS cmdlet sends out more than one, thus also receiving more than one reply (with consideration that it might miss some requests in the process)

silasalves commented 1 year ago

@mcspr Thanks for the response! I think I found a solution, so I am detailing it here.

Latency always that high?

I am afraid so... I placed the device close to the router and ran the ping command again. This is the result:

> ping esp8266.local -n 30

Pinging esp8266.local [192.168.50.43] with 32 bytes of data:
Reply from 192.168.50.43: bytes=32 time=289ms TTL=255
Reply from 192.168.50.43: bytes=32 time=270ms TTL=255
Reply from 192.168.50.43: bytes=32 time=1259ms TTL=255
Reply from 192.168.50.43: bytes=32 time=4ms TTL=255
Reply from 192.168.50.43: bytes=32 time=640ms TTL=255
Reply from 192.168.50.43: bytes=32 time=5ms TTL=255
Reply from 192.168.50.43: bytes=32 time=149ms TTL=255
Reply from 192.168.50.43: bytes=32 time=60ms TTL=255
Reply from 192.168.50.43: bytes=32 time=274ms TTL=255
Reply from 192.168.50.43: bytes=32 time=185ms TTL=255
Reply from 192.168.50.43: bytes=32 time=99ms TTL=255
Reply from 192.168.50.43: bytes=32 time=308ms TTL=255
Reply from 192.168.50.43: bytes=32 time=212ms TTL=255
Reply from 192.168.50.43: bytes=32 time=126ms TTL=255
Reply from 192.168.50.43: bytes=32 time=46ms TTL=255
Reply from 192.168.50.43: bytes=32 time=254ms TTL=255
Reply from 192.168.50.43: bytes=32 time=167ms TTL=255
Reply from 192.168.50.43: bytes=32 time=73ms TTL=255
Reply from 192.168.50.43: bytes=32 time=282ms TTL=255
Reply from 192.168.50.43: bytes=32 time=192ms TTL=255
Reply from 192.168.50.43: bytes=32 time=105ms TTL=255
Reply from 192.168.50.43: bytes=32 time=12ms TTL=255
Reply from 192.168.50.43: bytes=32 time=228ms TTL=255
Reply from 192.168.50.43: bytes=32 time=139ms TTL=255
Reply from 192.168.50.43: bytes=32 time=48ms TTL=255
Reply from 192.168.50.43: bytes=32 time=282ms TTL=255
Reply from 192.168.50.43: bytes=32 time=165ms TTL=255
Reply from 192.168.50.43: bytes=32 time=81ms TTL=255
Reply from 192.168.50.43: bytes=32 time=297ms TTL=255
Reply from 192.168.50.43: bytes=32 time=210ms TTL=255

Ping statistics for 192.168.50.43:
    Packets: Sent = 30, Received = 30, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 4ms, Maximum = 1259ms, Average = 215ms

Is there anything that can be done to improve this?


Any difference when dealing with DNS client directly?

Without any changes to the code, not really. It couldn't be found:

> Resolve-DnsName -llmnronly esp8266.local
Resolve-DnsName : esp8266.local : This operation returned because the timeout period expired
At line:1 char:1
+ Resolve-DnsName -llmnronly esp8266.local
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : OperationTimeout: (esp8266.local:String) [Resolve-DnsName], Win32Exception
    + FullyQualifiedErrorId : ERROR_TIMEOUT,Microsoft.DnsClient.Commands.ResolveDnsName

What was bothering me is that the MDNS worked for a short while before stopping completely, so I decided to read the source code and see if there was anything there that could help me. That's how I found the bool MDNSResponder::announce(void) which seemed worth try using -- maybe MDNS was announcing just once and that is why it worked only once as well, who knows? Then I created a quick-and-dirty workaround to test this hypothesis.

First, I added this to the beginning of the file:

// counter used for delaying the call to MDNS.announce()
long int advertiseCounter = 0;

and then I changed the loop() function to:

void loop(void) {
  server.handleClient();
  MDNS.update();

  // call MDNS.announce() periodically when the counter exceeds the threshold, then resets the counter.
  if (advertiseCounter++ > 100000)
  {
    MDNS.announce();
    advertiseCounter = 0;
  }
}

And now I can find the device successfully on Windows =)

> Resolve-DnsName -llmnronly esp8266.local

Name                                           Type   TTL   Section    IPAddress
----                                           ----   ---   -------    ---------
esp8266.local                                  A      120   Answer     192.168.50.43

Name      : 43.50.168.192.in-addr.arpa
QueryType : PTR
TTL       : 120
Section   : Answer
NameHost  : esp8266.local

The browser can also find it, so I think this issue is at least partially solved... And the reason for the "partial" is that although it works in Windows, it doesn't work at all with Android... On Windows it works whether I use http://esp8266.local or http://192.168.50.43; in Android, both fail, no matter the browser I am using (Firefox or Chrome). The phone just returns ERR_ADDRESS_UNREACHABLE and that's it. Any ideas? I wonder if it's related to the high latency.

aromprg commented 1 year ago

https://stackoverflow.com/questions/30449988/how-to-enable-mdns-support-in-android-browser-address-bar Now mdns support avilable in android 12 or higher (I'm not tested)

silasalves commented 1 year ago

https://stackoverflow.com/questions/30449988/how-to-enable-mdns-support-in-android-browser-address-bar Now mdns support avilable in android 12 or higher (I'm not tested)

It's not a MDNS error; it fails even when I use the IP itself. I will try with a different phone and see what happens.