1

I am new to avahi. And I am not sure how to build a dns-like local area network where all devices can communicate over a mqtt gateway.

I am probably wrong. But it seems to me I want to have a unique name for each raspberry pi device on my network. mqtt-relay-server and mqtt-relay-client. But I don't have a main server. And I want to take the same sd card, copy it to several different Raspberry Pi's. And have them auto-connect into one heterogeneous system.

Question: If they are all called mqtt-relay, how do they not contact themselves? Or how can I get it to name them mqtt-relay1, mqtt-relay2, ... (depending on what it sees on the network)?

Answer:

  1. avahi-browse -al (where -l is domains other than its own).

  2. If the hostname exists on the network, use scripting to adjust the name in the avahi service

  3. Restart avahi-daemon

  4. Have mqtt point towards the original avahi dns name

  5. Whichever Raspberry Pi is connected first becomes the gateway!

Topology: Node-Red connects to my local mqtt service. There is a /subscribe/ (current info) and a /request/ (request current info be sent immediately) endpoint. Inside the payload, each device identifies itself (tank and temperature sensor with a unique id).

  • 1
    This question (what is the actual question?) is too broad and waffly (what is a "dns-like local area network"). If you wand a number of computers (Pi or otherwise) to share a network you should give them a unique hostname – Milliways Aug 5 at 5:06
  • Could you help guide me to make it more specific? Shouldn’t they be able to assign themselves a unique hostname with an appropriate shell script or tool? And a mDNS lan name so they can enumerate all devices on the lan without looping through every ip the local subnet mask allows as IP’s? Just like samba. Hm. Can I enumerate Samba or nfs for mqtt? Little more overhead. But should be fine. Any mqtt examples? scripting or compiled? – TamusJRoyce Aug 5 at 9:37
  • Avahi (aka zeroconf aka Bonjour) is simply a way for mDNS capable machines to find each other by hostname.local. MQTT is a pub/sub messaging system. A publisher or subscriber needs to be able to find the broker (Mosquitto or HiveMQ) and using names rather than IP addresses is easier. Run avahi-daemon and an MQTT client can use raspberrypi.local rather than 192.168.3.14 to find the broker. – Dougie Aug 5 at 15:56
  • 1
    Posted comment as an answer. Please mark it as best answer. Give every machine a unique name. I'm using names from NASA (Apollo, Saturn, Tranquility, Eagle, etc.) for my raspberries. – Dougie Aug 5 at 18:04
  • @Milliways didn't get a chance to thank you. hostname pointed me towards mDNS / avahi / zeroconf. Sometimes the simple answers are the best ones. I apologize for being vague. Thank you! – TamusJRoyce Aug 6 at 0:25
2

Avahi (aka zeroconf aka Bonjour) is simply a way for mDNS capable machines to find each other by hostname.local.

MQTT is a pub/sub messaging system.

A publisher or subscriber needs to be able to find the broker (Mosquitto or HiveMQ) and using names rather than IP addresses is easier.

Run avahi-daemon and an MQTT client can use raspberrypi.local rather than 192.168.3.14 to find the broker.

  • avahi / mDNS seems like the correct way to go. Had been looking at UDP broadcasting. Because it has the ability to send a data feed on what ip a device is assigned to. Even if it is static and outside the submask and gatway of the router. Change the router to the dangling devices ip. login and move the device where it should be. Return the router back to its regularly programmed ip. But that is kind of the basis of avahi / mDNS. Why re-invent the wheel! – TamusJRoyce Aug 6 at 0:31
1

Possible solution with a better answer: https://serverfault.com/questions/615561/avahi-how-do-i-setup-multiple-servers-that-originally-have-the-same-hostname

Avahi with multiple instances on the same network will simply increment an integer at the end of the name. This exactly what I am needing and the below is sort-of trying to accomplish.

Since this is a built-in feature of this implementation and possibly the protocol itself, this satisfies exactly what I need.

Now to enumerate and loop through that list of servers, and I am good-to-go!

My idea is to check the network to see if my avahi service dns exists. If so, increment it by 1 until it doesn't exist. Then set this to run before avahi-daemon starts (or after...and if a change is needed, apply the change and restart avahi-daemon).

nano -w /etc/avahi/services/canrelay-mqtt.service

Insert the text:

<!-- Put this in /etc/avahi/services/mosquitto.service -->

<!DOCTYPE service-group SYSTEM "avahi-service.dtd">
<service-group>
 <name replace-wildcards="yes">Mosquitto MQTT server on %h</name>
  <service>
   <type>_canrelay._tcp</type>
   <port>1883</port>
   <txt-record>info=Publish, Publish! Read all about it! mqtt.org</txt-record>
  </service>
</service-group>

Setup avahi-brows to look at the network and find similar mDNS domains:

sudo apt install avahi-browse
avahi-browse -apt | cut -d';' -f5 | grep _canrelay._tcp | sort | uniq
cat canrelay-mqtt.service | sed "s/<type>/;/" | sed "s@</type>@;@" | tr -d '\n' | tr -d '\r' | cut -d';' -f2

sudo /etc/avahi/setup-services.sh

So far I have this script:

#!/bin/bash

#avahi-browse -apt | cut -d';' -f5 | grep _canrelay._tcp | sort | uniq
#cat canrelay-mqtt.service | sed "s/<type>/;/" | sed "s@</type>@;@" | tr -d '\n' | tr -d '\r' | cut -d';' -f2

mkdir /etc/avahi/service-scripts/

## Only copy what doesn't exist - Well, once I learn how to send -pm flags when ran this way...
##find . -iname '*mqtt*' -type f -exec cp -pn  {} /etc/avahi/services /etc/avahi/service-scripts

## Backup files if they do not already exist... Should this only migrate mqtt files? Probably...Lets do what is above.
yes n | cp -p -n /etc/avahi/services/*mqtt* /etc/avahi/service-scripts/
## Copies them back to their original. service-scripts now drives services!
yes y | cp -f -p /etc/avahi/service-scripts/* /etc/avahi/services/

for f in "/etc/avahi/services/*mqtt*"
do
    # Should this only apply to mqtt?  Append "| grep mqtt"???
    service=`cat $f | sed "s/<type>/;/" | sed "s@</type>@;@" | tr -d '\n' | tr -d '\r' | cut -d';' -f2`

    # If service can't be found, continue processing...
    if [ -z "$service" ]
    then
        continue
    fi

    # Check if the non-local service is already running
    foundservice=`avahi-browse -apt --ignore-local | cut -d';' -f5 | grep $service | sort | uniq | head -n 1`

    index=0
    currentservice="$service"
    while [ "$currentservice" = "$foundservice" ]
    do
        index=$((index+1))
        currentservice="${service}${index}"
        foundservice=`avahi-browse -aplt | cut -d';' -f5 | grep $currentservice | sort | uniq | head -n 1`
    done

    if [ "$currentservice" != "service" ]
    then
        # Appended number at the end, since the service already exists
        sed -i 's/$currentservice/$service/g' $f
    fi
done

I haven't got to test this out yet. I only have one of the three Raspberry pi's up.

I also plan on having something similar with ESP32 and my Kendryte risc-v and their mDNS servers they can run (using the Arduino firmware - because it is quick for prototyping).

The protocol I am using is can bus. Which is why I chose can-relay. Though it is falling out of favor. Bluetooth mesh is a much better protocol.

Thank you @Dougie! I will keep editing this until I get it working how I need. With the first device having the main dns name. And all others being numbered and being secondary.

To get a list of IP's to try:

avahi-browse  _canrelay._tcp --resolve -tp | grep ^= | cut -d';' -f8-9 | sed 's/;/:/g' | sort

Since this won't act like a normal dns. It is more like an advertised address. Nice that it includes the port.

Now to use this command to setup MQTT! In progress...

Let's kick off only the server. This does not yet account for the server dropping off. Load balancing mqtt might be a solution? But anyway. Here it is:

mkdir /etc/mqtt/run.d
nano -w /etc/mqtt/run.d/canrelay.sh

Contents:

#!/bin/bash

while read -r line
do
  nc -vz $line || continue
  ip=`echo "$line" | cut -d' ' -f1`
  port=`echo "$line" | cut -d' ' -f2`

  while read -r currentip
  do
    if [ "$ip" = "$currentip" ]
    then
      echo "port $port -> ip $ip"
      echo "port $port" > /etc/mosquitto/conf.d/canrelay.conf
      mosquitto -c /etc/mosquitto/conf.d/canrelay.conf && echo "Server mqtt has been established!" && exit 0
    fi
  done <<< `ifconfig | grep -oE "\b([0-9]{1,3}\.){3}[0-9]{1,3}\b"`
done <<< `avahi-browse _canrelay._tcp --resolve -tp | grep ^= | cut -d';' -f8-9 | sed 's/;/ /g' | sort`

while read -r line
do
  nc -vz $line || continue
  ip=`echo "$line" | cut -d' ' -f1`
  port=`echo "$line" | cut -d' ' -f2`

  while read -r currentip
  do
    if [ "$ip" != "$currentip" ]
    then
      echo "port $port -> ip $ip"
      mosquitto_pub -h $ip -p $port -t can_relay -m "Publish stared" && echo "Client mqtt has been established!" && exit 0
    fi
  done <<< `ifconfig | grep -oE "\b([0-9]{1,3}\.){3}[0-9]{1,3}\b"`
done <<< `avahi-browse _canrelay._tcp --resolve -tp | grep ^= | cut -d';' -f8-9 | sed 's/;/ /g' | sort`

echo "Warning: Could not successfully establish a server or a client."

chmod +x canrelay.sh

  • Automatically establishing a server(s), load balancing, subscribe and publish clients, trusted clients, etc. is becoming a bigger project than the typical script setup is probably good at handling. Works for my use case. Hope this guides someone with this idea on a better direction for mqtt orchestration. – TamusJRoyce Aug 8 at 3:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.