M2M-Kommunikation mit MQTT

Kuck mal, wer da spricht!
Kommentare

Das Internet der Dinge ist in aller Munde. Dank günstigen Einplatinencomputern wie Raspberry Pi und Arduino ist es möglich, preiswerte Geräte z. B. für die Erhebung und Auswertung von Sensordaten über das Internet zu vernetzen. Grund genug, sich Paho, die Referenzimplementierung des MQTT-Protokolls zur Maschine-zu-Maschine-(M2M-)Kommunikation für Java, genauer anzusehen und in einem konkreten Anwendungsfall einzusetzen.

Der Begriff „Internet of Things“ wurde bereits 2009 von Kevin Ashton geprägt und bezeichnet die Vernetzung von Dingen der realen Welt über das Internet oder geschlossene Netzwerke. Die Grundidee ist simpel: Physische Geräte können miteinander kommunizieren und so Daten untereinander austauschen oder gar gesteuert werden. Populär ist dieser Ansatz heute bereits bei der Erhebung von Sensordaten, die man dann über das Internet an interessierte Clients sendet. Diese Daten werden in der Folge meist aggregiert und für Menschen lesbar in Form von Diagrammen oder Statistiken aufbereitet.
Die Anwendungsszenarien sind praktisch unbegrenzt. Stellen Sie sich vor, Ihr Wecker würde automatisch zehn Minuten später klingeln, da er benachrichtigt wird, dass Ihre Zugverbindung fünfzehn Minuten Verspätung hat. Gleichzeitig würde Ihre Kaffeemaschine sich automatisch auch zehn Minuten später einschalten, damit ihr Frühstückskaffee nicht kalt ist, bis Sie aufstehen. Klingt wie Zukunftsmusik? Das alles ist heute schon möglich. Die Firma Ericsson prognostiziert, dass bis 2020 mindestens 50 Milliarden Geräte über das Internet kommunizieren werden. Möglich ist das dank der zunehmenden Verbreitung von IPv6 und offenen Maschine-zu-Maschine-(M2M-)Kommunikationsprotokollen, die leichtgewichtig genug sind, um mit möglichst wenig Protokoll-Overhead sogar über Mobilfunknetze eine günstige Kommunikation zu erlauben.

MQTT

Das Message-Queue-Telemetry-Transport-(MQTT-)Protokoll wurde 1999 von Andy Stanford-Clark (IBM) und Arlen Nipper (Eurotech) als ein M2M-Kommunikationsprotokoll mit minimalen Protokoll-Overhead entwickelt, um vernetzten Geräten eine Möglichkeit zu bieten, möglichst bandbreiten- und batterieschonend zu kommunizieren. Das Protokoll schlägt mit einem ereignisgesteuerten Ansatz einen anderen Weg ein als beispielsweise HTTP, welches auf Request/Response basiert. MQTT benutzt das Publish Subscribe Pattern (Abb. 1). Das bedeutet, dass die Clients sich untereinander nicht kennen und einen zentralen Verteiler, einen so genannten Broker, zur Kommunikation nutzen. Der Broker ist dafür zuständig, dass eine gesendete Nachricht alle interessierten Clients erreicht. Wenn ein Client sich mit einem Broker verbindet, teilt dieser dem Broker mit, für welche so genannten Topics er benachrichtigt werden möchte, also welche Topics er abonnieren will. Diesen Vorgang nennt man Subscribe. Wenn ein Client eine Nachricht sendet, muss er darin angeben, an welches Topic diese Nachricht gesendet werden soll. Diesen Vorgang nennt man Publish. Durch diese Architektur ist es möglich, hochskalierbare Lösungen mit Tausenden von Clients zu entwickeln, ohne dass Abhängigkeiten zwischen den teilnehmenden Clients entstehen.

Abb. 1: Publish Subscribe mit MQTT

Der Unterschied zu HTTP ist, dass ein Client nicht erst durch einen Request anfragen muss, ob neue Informationen vorhanden sind, sondern dieser automatisch vom Broker benachrichtigt wird. Sollte ein Client nicht verbunden oder der Broker nicht erreichbar sein, implementiert das Protokoll selbst Mechanismen zum Persistieren der Nachrichten und dem automatischen Senden, sobald Broker oder Client wieder erreichbar sind.

Topics
Jeder Client kann frei wählen, zu welchem Topic er publishen oder subscriben möchte. Topics sind frei wählbare Strings und können mittels einem „/“ hierarchisch aufgebaut werden. Beispiele wären home/temperature und home/brightness. Das Root Topic wäre hier „zuhause“ und die untergeordneten Topics wären temperature und brightness. Ein Client könnte entweder ein konkretes Kind-Topic abonnieren oder mittels einer Wildcard (#) alle Kind-Topics eines Eltern-Topics abonnieren. Beispielsweise könnte er home/# abonnieren, was zur Folge hätte, dass er alle Nachrichten für home/temperature und home/brightness zugestellt bekommt. Bei den Topic-Namen wird zwischen Groß- und Kleinschreibung unterschieden.

Eclipse Paho

Anfang 2012 gründete sich das Paho-Projekt unter der Schirmherrschaft der Eclipse Foundation mit dem Ziel, Open-Source-Implementierungen von M2M-Protokollen bereitzustellen. Zurzeit wird die Referenzimplementierung des MQTT-Protokolls in Form von Clients für Java, C, C++, JavaScript, Lua und Python bereitgestellt. Das Paho-Projekt ist zwar noch recht jung, die Java- und C-Implementierungen werden jedoch seit Jahren bei IBM und Eurotech, welche auch die initiale Implementierung bereitgestellt haben, eingesetzt und sind für den produktiven Einsatz geeignet. Wir sehen uns nun anhand der Implementierung eines konkreten Anwendungsfalls die Paho-Bibliothek für Java genauer an.

Anwendungsfall

Wie bereits angesprochen ist MQTT ideal für bandbreitenschonende Kommunikation von Sensordaten, weshalb wir in folgendem Beispiel einen solchen Anwendungsfall herausgreifen und implementieren. In diesem Fall realisieren wir eine Überwachung von Temperatur und Helligkeit in einem Raum. Das heißt wir haben ein Gerät, dass Temperatur und Helligkeit misst, beispielsweise einen Raspberry Pi, und diese Daten zu einem öffentlichen Broker schickt. Wie Abbildung 2 zeigt, empfängt ein zweites Gerät die Daten, zeigt diese an und gibt eine Warnung aus, sollte der Raspberry Pi ausfallen. Damit kann man, auch wenn man nicht zu Hause ist, die Sensordaten im Blick behalten und benachrichtigt werden, sofern der Sensorclient nicht mehr richtig funktionieren sollte.

Abb. 2: Darstellung des Anwendungsfalls

Aufmacherbild: Business Communications von Shutterstock / Urheberrecht: Rawpixel

[ header = Seite 2: Eclipse Paho und Maven ]

Eclipse Paho und Maven

Nachdem der Anwendungsfall festgelegt ist, können wir mit der Implementierung der Clients beginnen. Zuvor aber noch kurz einige Worte zum Einrichten eines neuen Projekts unter Verwendung von Eclipse Paho und Maven. Die Paho-Bibliothek befand sich zum Zeitpunkt der Fertigstellung dieses Artikels noch nicht im Maven Central Repository. Es ist deshalb notwendig, das Eclipse Paho Maven Repository in das Projekt einzubinden, da ansonsten die Bibliothek nicht gefunden wird. Das vollständige Beispiel ist bei GitHub verfügbar und kann hier heruntergeladen werden.

Implementierung Sensorclient

Wir implementieren zunächst den Sensorclient, welcher ein Thermometer und die Helligkeitsmessung simuliert und jede Sekunde einen Datensatz an einen öffentlich zugänglichen Broker sendet. Zunächst müssen wir eine Instanz der Klasse MqttClient erzeugen (Listing 1). Als Parameter für den Konstruktor wird der URL zum Broker und eine eindeutige Client-ID benötigt. Als Client-ID bietet sich die MAC-Adresse des Rechners an, da diese eindeutig ist. Zusätzlich wird noch -pub an die Client-ID gehängt, da unsere zwei unabhängigen Clients (einer zum Senden, einer zum Empfangen) sonst dieselbe Client-ID besäßen. Es ist wichtig, dass die Client-ID eindeutig ist, da der MQTT-Broker pro ID nur einen Client zulässt. Falls schon ein Client mit derselben ID gerade eine Verbindung zum Broker aufgebaut hat, wird die Verbindung des „älteren“ Clients abgebrochen. Die maximale Länge einer Client-ID beträgt laut der Spezifikation 23 Zeichen, viele Broker erlauben jedoch eine Erweiterung auf mehr Zeichen.
Nachdem wir eine Instanz von MqttClient haben, können wir uns mittels der connect(…)-Methode zum Broker verbinden (Listing 2). Die connect(…)-Methode bietet uns an, eine Instanz von MqttConnectOptions zu übergeben. Wir erzeugen uns ein neues Objekt von MqttConnectOptions und setzen cleanSession auf false. Damit signalisieren wir dem Broker, dass wir bei einer Verbindung zum Broker mit einer alten Session weiterarbeiten möchten, falls der Client schon einmal verbunden war. Damit ist es möglich, dass bei einer Quality of Service von 1 oder 2 (Kasten: „Quality of Service“) keine ausstehenden Nachrichten verlorengehen. Zusätzlich geben wir für den Client ein „Last Will and Testament“-(LWT-)Topic an. Ein LWT-Topic ist ein Topic, für das der Broker eine Nachricht sendet, falls die Verbindung zu einem Client unvorhergesehen abbricht. Damit ist es möglich, dass man interessierte Clients darüber benachrichtigt, dass ein anderer Client nicht mehr an der Kommunikation teilnimmt.

Quality of Service
Das MQTT-Protokoll definiert drei Arten von Quality of Services (QoS):• QoS 0: At most once delivery
• QoS 1: At least once delivery
• QoS 2: Exactly once deliveryMit einer QoS definiert ein Client, wie wichtig es ist, dass die Nachricht auch wirklich bei den Empfängern ankommt. Eine QoS von 0 bedeutet, dass die Nachricht höchstens einmal beim Empfänger ankommt. Es ist unter gewissen Umständen auch möglich, dass eine Nachricht gar nicht beim Empfänger ankommt. QoS 1 bedeutet, dass garantiert wird, dass die Nachricht mindestens einmal bei den Empfängern ankommt. Es kann aber auch passieren, dass die Nachricht mehrmals ankommt. QoS 2 bedeutet, dass garantiert wird, dass die Nachricht exakt einmal bei den jeweiligen Empfängern ankommt. Umso höher der QoS-Level ist, desto weniger performant wird die Übertragung, da eine zusätzliche Kommunikation zwischen den Clients und dem Broker nötig ist, um sicherzustellen, dass die Nachricht auch wirklich angekommen ist.

Zuletzt implementieren wir eine einfache Endlosschleife, in der der Publish-Client jede Sekunde einen zufälligen Wert für die Helligkeit an das Topic home/brightness und einen zufälligen Temperaturwert an das Topic home/temperature sendet (Listing 3). Dazu holen wir uns ein Topic-Object mittels der Methode client.getTopic(…) und führen dort die Methode publish(…) mit unserer gewünschten MqttMessage aus. Um zu überprüfen, dass die Nachricht auch wirklich gesendet wurde, kann man auf dieser Seite das gewünschte Topic abonnieren und sehen, was an den Broker bisher gesendet wurde.

public class Publisher {
  public static final String BROKER_URL = "tcp://broker.mqttdashboard.com:1883";
  private MqttClient client;

  public Publisher() {
    String clientId = Utils.getMacAddress() + "-pub";
    try {
      client = new MqttClient(BROKER_URL, clientId);
    } catch (MqttException e) {
      e.printStackTrace();
      System.exit(1);
    }
  }
}
MqttConnectOptions options = new MqttConnectOptions();
options.setCleanSession(false);
options.setWill(client.getTopic("home/LWT"), 
"I'm gone".getBytes(), 0, false);

client.connect(options);
public static final String TOPIC_TEMPERATURE = "home/temperature";

//... 
while (true) {
  publishBrightness();
  Thread.sleep(500);
  publishTemperature();
  Thread.sleep(500);
}
//...

private void publishTemperature() throws MqttException {
  final MqttTopic temperatureTopic = client.getTopic(TOPIC_TEMPERATURE);

  final int temperatureNumber = Utils.createRandomNumberBetween(20, 30);
  final String temperature = temperatureNumber + "°C";

  temperatureTopic.publish(new MqttMessage(temperature.getBytes()));
}
// publishBrightness() wird analog zu publishTemperature() implementiert

Implementierung Control-Center

Nun wollen wir einen weiteren Client implementieren, welcher die Topics home/temperature und home/brightness abonniert. Auch hierzu erzeugen wir ein MqttClient-Objekt und übergeben den Broker-URL und die Client-ID. Als Client-ID benutzen wir diesmal die MAC-Adresse ergänzt um ein -sub, damit wir nicht dieselbe Client-ID wie für den Publisher benutzen.
Wir erzeugen nun eine neue Klasse, die das Interface MQTTCallback implementiert. Das MqttCallback-Interface definiert drei Methoden, die es nun zu implementieren gilt: connectionLost, messageArrived und deliveryComplete. connectionLost wird aufgerufen, sobald ein unerwarteter Abbruch der Verbindung mit dem MQTT-Broker entsteht. Hier könnte man beispielsweise eine Logik zum erneuten Verbinden zum Broker implementieren. Die Methode messageArrived wird aufgerufen, sobald der Broker eine neue Nachricht sendet. Zuletzt gibt es noch die Methode deliveryComplete, die aufgerufen wird, sobald man eine Nachricht selbst gesendet hat und die Nachricht bei den Clients angekommen ist. Das ist besonders nützlich, wenn als Quality of Service Level 1 oder 2 gewählt wurde, da es unter Umständen ein bisschen dauert, bis alle Nachrichten bei den Empfängern angekommen sind. Wir entscheiden uns, nur die messageArrived-Methode zu implementieren. Die Implementierung findet sich in Listing 4.
Nachdem wir nun den Callback implementiert haben, können wir den Callback dem MqttClient-Objekt übergeben und uns zum MQTT-Broker verbinden. Danach abonnieren wir das Topic zuhause inklusive aller Subtopics mittels einer Wildcard (Listing 5).

public class SubscribeCallback implements MqttCallback {
  @Override
  public void connectionLost(Throwable cause) {}

  @Override
  public void messageArrived(MqttTopic topic, MqttMessage message){
    System.out.println("Message arrived. Topic: " + topic.getName() + "
                        Message: " + message.toString());

    if ("home/LWT".equals(topic.getName())) {
      System.err.println("Sensor gone!");
    }
  }

  @Override
  public void deliveryComplete(MqttDeliveryToken token) {}
}
mqttClient.setCallback(new SubscribeCallback());
mqttClient.connect();
mqttClient.subscribe("home/#");

Ausführen der Clients

Nachdem nun sowohl der Sensorclient als auch das Control-Center implementiert wurden, ist es an der Zeit, beide Applikationen auszuführen. Wir starten nun zuerst das Control Center und danach den Sensorclient. Sobald der Sensorclient startet, kommen Nachrichten beim Control-Center an. Wenn man nun den Sensorclient beendet, erhält das Control-Center die Nachricht, dass der Sensorclient ausgefallen ist, da auch das Last Will and Testament Topic abonniert wird.
Herzlichen Glückwunsch, damit haben Sie nun Ihre erste Internet-der-Dinge-Anwendung mit MQTT implementiert.

Ausblick

Dieser konkrete Anwendungsfall ist nur einer von vielen und kann für andere Zwecke leicht angepasst und wiederverwendet werden. Man könnte beispielsweise den bereits vorhandenen Code einfach erweitern, sodass mehrere Clients in verschiedenen Räumen die Temperatur und Helligkeit messen und das Control-Center diese auch anzeigt.
Darüber hinaus ist das hier gezeigte Anwendungsbeispiel auf die Kommunikation unter verschiedenen Geräten ausgelegt und besitzt keine einfache und intuitive Anzeige für Menschen. Alternativ könnte man deshalb das Control-Center durch ein Web-Dashboard ersetzen, auf dem die Werte mithilfe von Diagrammen angezeigt werden, um so Sensordaten und Liveupdates zu visualisieren. Dafür kann die Paho-JavaScript-Bibliothek verwendet werden. Wie man ein solches Web-Dashboard realisiert, werden wir in der nächsten Ausgabe des Entwickler Magazins zeigen.

Fazit

Wie wir gesehen haben, ist es mit sehr wenig Aufwand möglich, selbst Programme zu erstellen, die über MQTT kommunizieren. Die Eclipse-Paho-Bibliothek bietet alle Funktionen, die in der MQTT-Spezifikation beschrieben sind, und ermöglicht eine Kommunikation unter Geräten mit sehr wenig Protokoll-Overhead. MQTT sollte ein Tool im Repertoire eines jeden Entwicklers sein, der sich mit dem Internet der Dinge und der Kommunikation von Geräten in mobilen Netzwerken beschäftigt.

Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -