Kommunikation

Pub/Sub-Systeme

Im Internet der Dinge (Internet of Things, IoT) tauschen viele Millionen Geräte, Sensoren und Aktoren untereinander Daten aus. Jedoch sind diese oft nicht sehr leistungsfähig und nur vorübergehend erreichbar, weil die Netzverbindung unterbrochen ist oder sie nur zeit- und ortsabhängig benötigt wird. Wie baut man nun eine Infrastruktur mit unzuverlässigen Teilnehmern (Clients), die nicht einmal in der Lage sind, die Liste der Empfänger für ihre Nachrichten zu verwalten?

Eine mögliche Lösung bieten sogenannte Publish/Subscribe-Systeme (Pub/Sub). Hier übernimmt ein Vermittler (Broker) die Aufgabe Nachrichten an die richtigen Empfänger weiterzuleiten. Jedes Gerät kann Daten senden (publish) oder dem Vermittler gegenüber mitteilen (subscribe), an welcher Art Nachrichten (topic) es interessiert ist und diese später empfangen.

Pub/Sub
Figure 1. Pub/Sub mit verschiedenen Teilnehmern

MQTT

MQTT[1] ist ein standardisiertes Nachrichtenprotokoll für Pub/Subs. Es wird seit 1999 stetig weiterentwickelt, bietet verschiedene Übertragungsgarantien (Quality of Service oder QoS) für den Nachrichtentransport und zeichnet sich durch geringe Ansprüche an Verbindungsqualität und Rechenleistung aus.

QoS Level

Für die Operationen subscribe und publish können als Option verschiedene QoS-Level festgelegt werden. Das MQTT-Protokoll sichert daraufhin über ggf. zusätzliche Nachrichten deren Einhaltung ab.

QoS Level 0

Die Nachricht erreicht die Empfänger höchstens einmal.

QoS Level 1

Die Nachricht erreicht die Empfänger mindestens einmal.

QoS Level 2

Die Nachricht erreicht die Empfänger genau einmal.

Mit Erhöhen des QoS-Level steigt die Komplexität des Protokolls und die Anzahl der verschickten Nachrichten im Hintergrund.

Im RoboLab wird MQTT auf QoS Level 1 eingesetzt.

Nutzung im RoboLab

Jeder Roboter nimmt während der Erkundung regelmäßig Kontakt zu seinem Mutterschiff auf, das ihn zuvor auf dem Planeten abgesetzt hat. Aufgrund der dichten Atmosphäre gelingt dies jedoch nur mit den Verstärkern der Versorgungsstationen, die an den Kreuzungspunkten der Pfade eingerichtet wurden.

Dort eröffnet der Roboter eine Übertragung und schickt seine geschätzte neue Position. Er empfängt vom Mutterschiff eine Bestätigung und möglicherweise verschiedene andere Nachrichten. Es wird dabei zwischen Planeten-Nachrichten, zum Beispiel nützlichen Informationen wie neuen Pfaden und den direkten Anweisungen des Mutterschiffs, also einem Erkundungsziel unterschieden.

Nach der letzten gesendeten oder empfangenen Nachricht wird ein Timeout von 3 Sekunden abgewartet, bevor die Kommunikation an dem Knoten beendet und die Erkundung fortgesetzt wird. Allgemein gesprochen gilt eine Übertragung an einem Knoten also als beendet, wenn der Roboter 3 Sekunden lang keine Nachricht mehr empfangen hat.

Das Timeout von 3 Sekunden bedeutet nicht, dass zwischen zwei gesendeten oder empfangenen Nachrichten jeweils 3 Sekunden Funkstille herrschen sollen.
Es ist auch nicht erforderlich, an jedem Knoten eine neue Verbindung zu erstellen und diese danach wieder zu schließen.

Verbindungsmöglichkeiten

Die Kommunikation mit dem MQTT-Broker erfolgt grundsätzlich TLS-verschlüsselt. Im RoboLab bieten wir sowohl die Standard-Verbindung als auch Websockets an.

Standard-Verbindung

URL: mothership.inf.tu-dresden.de
PORT: 8883

Websocket-Verbindung

URL: mothership.inf.tu-dresden.de
PORT: 9002
Client-Parameter: transport="websockets"

Unverschlüsselte Verbindung

URL: mothership.inf.tu-dresden.de
PORT: 1883

Die unverschlüsselte Variante sollte nur in Ausnahmefällen zum Debugging auf der Console (z.B. vom Brick mit mosquitto_pub bzw. mosquitto_sub) genutzt werden!

Nachrichten

Die Kommunikation zwischen Roboter (Client) und Mutterschiff (Server) erfolgt im JSON-Format[2].
Nachrichten des Roboters haben dabei immer den from-Typ "client", Antwortnachrichten darauf vom Mutterschiff den from-Typ "server".
Alle anderen Nachrichten haben den from-Typ "debug".
Weiterhin werden alle Keywords in camelCase-Notation angegebenen.

Ein JSON-Schema mit allen möglichen Nachrichtentypen und Feldern findet ihr hier.

Nachrichten-Arten

Nicht alle vom Mutterschiff verschickten Nachrichten werden hier aufgeführt.
Um die Entwicklung zu erleichtern, sendet das Mutterschiff Nachrichten mit dem from-Typ "debug".
Diese enthalten nützliche Informationen, dadurch sind Fehler schneller erkenn- und behebbar.

Wichtig: Debug-Nachrichten werden zur Prüfung NICHT gesendet!

Einige Platzhalter kurz erläutert:

<GROUP> = Group name / ID (z.B. 001)
<PLANET> = Planet name
<TEXT> = Integer / String placeholder
Xs, Ys, Ds = Start coordinates and direction
Xe, Ye, De = End coordinates and direction
Xc, Yc, Dc = End coordinates and direction, possibly corrected
Xt, Yt = Target coordinates

Beispiele:

Explorer: explorer/001
Planet: planet/Gromit/001

Nachrichten, die vom Server initiiert werden

Häufige Fehler & Hinweise

  • Verschickte Nachrichten liegen im Format UTF-8 vor und müssen dementsprechend zuerst mit message.payload.decode('utf-8') dekodiert werden, damit es bei der Weiterverarbeitung der enthaltenen Strings nicht zu Fehlern kommt.

  • Häufig werden keine Nachrichten empfangen, da das Skript oder Modul für die Kommunikation direkt nach Start des Listeners schon wieder beendet wird. Eine while-Schleife kann hier Abhilfe schaffen.

  • Koordinaten, Richtungsangaben sowie Gewichte der Pfade werden als ganze Zahlen (Integer) geschickt.

    • Für die Richtungsangaben werden Abkürzungen der Himmelsrichtungen in Form von Grad-Angaben 0 (Nord), 90 (Ost), 180 (Süd), 270 (West) verwendet.

  • Ihr könnt die Kommunikation, nach Installation geeigneter Werkzeuge, auch zuerst lokal auf eurem Rechner testen.
    Wir empfehlen dazu die Nutzung des Clients MQTTfx anstatt der mosquitto-Befehle auf der Console (Konfiguration: Bild 1, Bild 2).
    Bitte beachtet, dass das Mutterschiff (also der Server) nur über das WLAN-Netzwerk RoboLab Playground erreichbar ist.

Code-Beispiel

Zur Herstellung der zuvor beschriebenen Kommunikation wird im RoboLab die Bibliothek paho-mqtt verwendet. Es ist sinnvoll, dass Ihr Euch einen Überblick über die API und das eingesetzte Protokoll MQTT verschafft und nicht einfach das Code-Beispiel verwendet.

#!/usr/bin/env python3

import json
import paho.mqtt.client as mqtt
import ssl

# this is a helper method that catches errors and prints them
# it is necessary because on_message is called by paho-mqtt in a different thread and exceptions
# are not handled in that thread
#
# you don't need to change this method at all
def on_message_excepthandler(client, data, message):
    try:
        on_message(client, data, message)
    except:
        import traceback
        traceback.print_exc()
        raise

# Callback function for receiving messages
def on_message(client, data, message):
    print('Got message with topic "{}":'.format(message.topic))
    data = json.loads(message.payload.decode('utf-8'))
    print(json.dumps(data, indent=2))
    print("\n")


# Basic configuration of MQTT
client = mqtt.Client(client_id="<GROUP>", clean_session=False, protocol=mqtt.MQTTv31)

client.on_message = on_message_excepthandler # Assign pre-defined callback function to MQTT client
client.tls_set(tls_version=ssl.PROTOCOL_TLS)
client.username_pw_set('<GROUP>', password='<PASS>') # Your group credentials
client.connect('mothership.inf.tu-dresden.de', port=8883)
client.subscribe('explorer/<GROUP>', qos=1) # Subscribe to topic explorer/xxx

# Start listening to incoming messages
client.loop_start()

while True:
	input('Press Enter to continue...\n')

client.loop_stop()
client.disconnect()