mDNS, Sonoff-Tasmota, code spelunking, and when is a host not a host! Or a domain a domain...

I've finally worked out how to load the Tasmota code into NodeMCU devices, or probably any other for that matter - sadly, I had to re-do it before I realised what I'd done the first few times, which was actually follow the instructions! >Sigh<.

Anyway, I really like mDNS, the multicast DNS mechanism whereby service providers advertise their services using multicast. This is an ideal mechanism for local services on one's own home network, and is a part of Apple's OS X and iOS, Linux distros, and (even!) Windoze. Sadly not Android. Tools. It was known as Bonjour, and is more usually known as Avahi in the Linux world. Basically, you use a DNS name of the form "yourhost.local", where "local" triggers the special magic, and your application finds the service without needing to know the IP address. Pretty much like standard DNS eh? Except it's a completely separate code base and specific applications or OSes have to make separate calls to resolve "local" addresses. Boo.

So I was intrigued to see that the Sonoff-Tasmota code seemed to allow mDNS-enabled detection of the MQTT broker. Nice, as my MQTT broker is on "server.local" at port 1883, the standard port. I thus entered "server.local" initially into the user_config.h file. Compilee... no findee! Plus I didn't seem to be able to connect to the Tasmota config web server using "hostname.local", which I had on earlier efforts. So two problems

  1. Can't find my server.local
  2. Can't find sonoff-xxxx.local

i.e. broken in both directions. Both work if I use explicit IP addresses, so no problem actually connecting etc.

What's going on?? Time for some digging, or better still, spelunking - lowering myself gingerly over the edge, with head torch struggling to illuminate the vast caverns echoing with the sound of falling water...

First, a visit to this page, which explains what support ESP8266 libraries have for mDNS. The example there shows

#include <ESP8266mDNS.h>        // Include the mDNS library
...
if (!MDNS.begin("esp8266")) {             // Start the mDNS responder for esp8266.local    Serial.println("Error setting up MDNS responder!");  }  Serial.println("mDNS responder started");

In other words, you have to do something to register your ESP8266 host into the mDNS space i.e. start it using multicast. Fine. What about picking up the MQTT host?

user_config.h:

/ -- MQTT ----------------------------------------
#define MQTT_USE               1                 // [SetOption3] Select default MQTT use (0 = Off, 1 = On)
// !!! TLS uses a LOT OF MEMORY (20k) so be careful to enable other options at the same time !!!
//#define USE_MQTT_TLS                             // EXPERIMENTAL Use TLS for MQTT connection (+53k code, +20k mem) - Disable by //
                                                 //   Needs Fingerprint, TLS Port, UserId and Password
#ifdef USE_MQTT_TLS
  #define MQTT_HOST            "m20.cloudmqtt.com"  // [MqttHost]
  #define MQTT_FINGERPRINT     "A5 02 FF 13 99 9F 8B 39 8E F1 83 4F 11 23 65 0B 32 36 FC 07"  // [MqttFingerprint]
  #define MQTT_PORT            20123                // [MqttPort] MQTT TLS port
  #define MQTT_USER            "cloudmqttuser"      // [MqttUser] Mandatory user
  #define MQTT_PASS            "cloudmqttpassword"  // [MqttPassword] Mandatory password
#else
//JG  #define MQTT_HOST            "server.local"          // [MqttHost]
  #define MQTT_HOST            ""          // [MqttHost]
  #define MQTT_PORT            1883              // [MqttPort] MQTT port (10123 on CloudMQTT)
  #define MQTT_USER            ""       // [MqttUser] Optional user
  #define MQTT_PASS            ""       // [MqttPassword] Optional password
#endif

See where I initially entered my MQTT hostname, thinking it would be resolved into its IP address using standard, if mDNS_based, techniques. But no... Let's see where MDNS is used...

sonoff.ino:

void MqttReconnect()
{
...
  AddLog_P(LOG_LEVEL_INFO, S_LOG_MQTT, PSTR(D_ATTEMPTING_CONNECTION));
#ifndef USE_MQTT_TLS
#ifdef USE_DISCOVERY
#ifdef MQTT_HOST_DISCOVERY
  if (!strlen(Settings.mqtt_host)) {
    MdnsDiscoverMqttServer();
  }
#endif  // MQTT_HOST_DISCOVERY
#endif  // USE_DISCOVERY
#endif  // USE_MQTT_TLS
  MqttClient.setServer(Settings.mqtt_host, Settings.mqtt_port);

Aha - MdnsDiscoverMqttServer() is interesting, and presumably sets the mqtt_host/port globals, but it's only invoked if the configured mqtt_host is zero(0) length!

support.ino:
#ifdef MQTT_HOST_DISCOVERY
boolean MdnsDiscoverMqttServer()
{
  if (!mdns_begun) {
    return false;
  }

  int n = MDNS.queryService("mqtt", "tcp");  // Search for mqtt service

  snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_MDNS D_QUERY_DONE " %d"), n);
  AddLog(LOG_LEVEL_INFO);

  if (n > 0) {
    // Note: current strategy is to get the first MQTT service (even when many are found)
    snprintf_P(Settings.mqtt_host, sizeof(Settings.mqtt_host), MDNS.IP(0).toString().c_str());
    Settings.mqtt_port = MDNS.port(0);

    snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_MDNS D_MQTT_SERVICE_FOUND " %s, " D_IP_ADDRESS " %s, " D_PORT " %d"),
      MDNS.hostname(0).c_str(), Settings.mqtt_host, Settings.mqtt_port);
    AddLog(LOG_LEVEL_INFO);
  }

  return n > 0;
}
#endif  // MQTT_HOST_DISCOVERY

See what I mean about having to do something non-standard to resolve the mDNS thing? In fact, it's not really doing any DNS_like resolution at all... it's looking for hosts that are advertising their "mqtt"  service on a "tcp" port! Um, what's my server doing?? Oh yes, and it's nailing discovered values into the global variables as predicted.

Time to check my server. Yes, avahi is running... but I need a new service to be defined!

/etc/avahi/services/mqtt.service:

<?xml version="1.0" standalone='no'?>
<!DOCTYPE service-group SYSTEM "avahi-service.dtd">
<service-group>
 <name replace-wildcards="yes">MQTT on %h</name>
  <service>
   <type>_mqtt._tcp</type>
   <port>1883</port>
  </service>

</service-group>

And a quick avahi service restart. At this point I also made the change seen above to the user_config.h file to set mqtt_host to a null value, so as to trigger the discovery code. And hurray!! It works...

00:00:00 Project sonoff Sonoff (Topic sonoff, Fallback DVES_0F15EC, GroupTopic sonoffs) Version 5.11.1h
00:00:00 WIF: Connecting to AP1 mySSId in mode 11N as sonoff-5612...
00:00:09 WIF: Connected
00:00:09 DNS: Initialized
00:00:09 HTP: Web server active on sonoff-5612.local with IP address 192.168.1.22
00:00:12 MQT: Attempting connection...
10:53:14 DNS: Query done. MQTT services found 1
10:53:14 DNS: MQTT service found on server, IP Address 192.168.1.4, Port 1883
10:53:14 MQT: Connected
10:53:14 MQT: tele/sonoff/LWT = Online (retained)
10:53:14 MQT: cmnd/sonoff/POWER = 
10:53:14 MQT: tele/sonoff/INFO1 = {"Module":"Sonoff Basic","Version":"5.11.1h","FallbackTopic":"DVES_0F15EC","GroupTopic":"sonoffs"}
10:53:14 MQT: tele/sonoff/INFO2 = {"WebServerMode":"Admin","Hostname":"sonoff-5612","IPAddress":"192.168.1.22"}
10:53:14 MQT: tele/sonoff/INFO3 = {"RestartReason":"Software/System restart"}
10:53:15 MQT: stat/sonoff/RESULT = {"POWER":"ON"}
10:53:15 MQT: stat/sonoff/POWER = ON
10:53:22 MQT: tele/sonoff/STATE = {"Time":"2018-02-07T10:53:22","Uptime":0,"Vcc":2.956,"POWER":"ON","Wifi":{"AP":1,"SSId":"mySSId","RSSI":100,"APMac":"84:16:F9:F4:DB:B4"}}

Now what's happening with the web service?? TBC...

Comments

  1. Clearly an old post... but I've got a question for you. I've got a SONOFF NSPanel running Tasmota. I've got a SONOFF TH16 running stock firmware.

    There is a temperature readout on the NSPanel, and I can set it via the Command Window in Tasmota web console.

    The TH16 'sends' a temperature reading. As it is stock firmware, that reading is visible in the Sonoff Ewelink app.

    Is there a way, to somehow have Tasmota on the NSPanel (maybe through your mDNS wizardry) read/catch/request the temperature value that the TH16's stock firmware is sending?

    I ran across SONOFFLan project for Home Assistant, and they seem to be doing this, but I'm not good enough reading code to figure out how they access the value from the TH16.
    https://github.com/AlexxIT/SonoffLAN

    ReplyDelete

Post a Comment

Popular posts from this blog

Replacing Coffee Machine Pump (Dualit Espressivo)

Multiple SHT30 sensors on a single I2C bus with Sonoff-Tasmota

NodeMCU + Tasmota code + SHT30