Heimautomatisierung mit Alexa und Raspberry Pi — Teil 2

AWS und Raspberry — Verbindung über MQTT

Einleitung

In diesem Teil unserer Reihe beschäftigen wir uns damit, einen Raspberry in unserem lokalen Netzwerk dauerhaft mit einer MQTT-Instanz in AWS zu verbinden. Diese Verbindung nutzen wir in einem späteren Beitrag dazu, Alexa-Sprachbefehle an unseren Raspberry weiterzuleiten. Die Details dieser Zusammenhänge finden sich im ersten Teil.

Voraussetzungen

Wir gehen von den Voraussetzungen aus, die im ersten Teil genannt sind. Insbesondere sind dies ein AWS-Account (Anmeldung über https://aws.amazon.com/de/), ein Raspberry mit aktuellem Raspbian und installiertem Node-Red.

Achtung: Für die spätere Interaktion mit Alexa ist (zumindest zum Zeitpunkt des Schreibens) wichtig, in der AWS-Region “EU-West-1 (Ireland)” zu arbeiten. Dies ist die AWS-Region, in der auch die Alexa-Skills für Deutschland laufen. Da jede Region eine eigene zugeordnete MQTT-Instanz für unseren Account hat, würde Alexa zwar die Befehle an die MQTT-Instanz in Irland weiterleiten, unser Raspberry würde aber bei einer anderen MQTT-Instanz (z.B. Frankfurt) lauschen.

Einrichtung der Infrastruktur in AWS

In AWS legen wir ein Objekt an, das die Repräsentation unseres Raspberry in der IoT-Infrastruktur darstellt. Da wir aus der gesamten Funktionalität, die uns zur Verfügung gestellt wird, nur die Kommunikation über MQTT benötigen, ist die Konfiguration eigentlich sehr einfach.

Seit kurzem gibt es zwei Varianten, dies zu erreichen. Die kompliziertere ist das Anlegen von Objekt, Zertifikaten etc. von Hand. Die einfache, neue ist ein Assistent für das Anlegen des Objektes, der für den ersten Schritt sehr gut funktioniert.

In beiden Fällen benötigen wir aber eine eigene Zugriffsrichtlinie, die sehr einfach gestaltet jedem, der im Besitz der korrekten Zertifikate ist, alle Zugriffsrechte für MQTT erlaubt. Später können wir hier die Rechte einschränken, so dass nur bestimmte Clients publizieren oder lesen dürfen, aber für die ersten Experimente behindert dies zu sehr.

Erstellung einer eigenen Zugriffsrichtlinie

Von der Einstiegsseite aus wechseln wir zu “IoT Core” (falls nicht sowieso schon angeboten, geben wir den Text einfach in das Suchfeld ein). Wie bereits erwähnt, kontrollieren wir, dass die Region “Ireland” (oder “EU (Ireland)”) ausgewählt ist, oder wechseln dorthin (falls Sie sich gerade erst angemeldet haben, klicken Sie auf “Erste Schritte”).

Auf der linken Seite wählen wir im Menü “Sicher” und dann den Auswahlpunkt “Richtlinien”.

Mit Klick auf Erstellen öffnen wir ein Formular zur Erzeugung unserer neuen Richtlinie. Als Namen wählen wir “access-all”, für die Aktion “iot:*”, für die Ressourcen-ARN “*” und setzen den Haken bei Effekt->Erlauben (siehe Bild 1).

Bild 1: Erstellen einer Richtlinie.

Wir erlauben damit alle IOT-Aktionen (Connect, Publish, Subscribe, Subscribe…) für beliebige Client-IDs. Ein Klick auf “Erstellen” (unten rechts) erzeugt die Richtlinie endgültig.

Diese Richtlinie werden wir später verwenden, um den Zugriff auf unser Objekt in AWS zu regulieren.

Die Richtlinie wird als ein JSON-Objekt repräsentiert, das wir später auch direkt editieren und anpassen können (der Klick auf den erweiterten Modus führt uns bereits während des Anlegens dorthin). Unsere gerade angelegte Richtlinie “access-all” sieht in der JSON-Repräsentation wie folgt aus:

{
“Version”: “2012–10–17”,
“Statement”: [
{
“Effect”: “Allow”,
“Action”: “iot:*”,
“Resource”: “*”
}
]
}

Nachdem wir die Vorarbeiten erledigt haben, kommt jetzt das eigentliche Anlegen des Objekts, das uns den Zugriff auf die AWS-MQTT-Instanz ermöglicht.

Anlegen von Objekten mit dem Assistenten

Wir beginnen mit der einfachen Variante und nutzen den Assistenten, den wir im Menü unter “Integrieren” im Punkt “Konfigurieren eines Geräts” finden. Ein folgender Klick auf “Erste Schritte” führt uns zu den ersten Auswahlmöglichkeiten. Für unseren Zweck wählen wir als Plattform “Linux/OSX” und beim SDK “Node.js” und klicken auf “Weiter”.

Hier wählen als Namen unseres Objektes “home-automation”. Der Name hat keine größere Bedeutung für unsere spätere Kommunikation, er sollte aber natürlich sprechend sein für unseren Zweck (und eindeutig über alle Regions, siehe Tipp am Ende des Texts). Wir klicken auf “Nächster Schritt”.

Auf dieser Seite wird uns jetzt das Verbindungs-Kit für den Verbindungsaufbau zum Download angeboten. Dieses Kit enthält nicht nur die von uns später benötigten Zertifikate, sondern auch einen ersten Test-Client, mit dem wir verifizieren können, ob der Verbindungsaufbau prinzipiell funktioniert.

Wir laden das Verbindungs-Kit herunter und transferieren es zu unserem Raspberry Pi. Wir klicken auf “Weiter” und bleiben auf der Folgeseite, die die nächsten Schritte detailliert und einen Test-Client für die ersten ausgetauschten Daten enthält.

Auf dem Raspberry legen wir ein Unterverzeichnis an, in dem wir das Archiv auspacken. Wir folgen der Anleitung auf der Folgeseite im Assistenten. Das Skript start.sh prüft zuerst, ob im aktuellen Verzeichnis das Stammzertifikat für AWS vorhanden ist und lädt es bei Bedarf herunter. Dann installiert es das AWS Device SDK im aktuellen Verzeichnis und versucht zum Schluss den Kontakt mit AWS aufzunehmen. Wenn alles gut geht, dann sehen wir die folgende Meldung in unserer Konsole:

Running pub/sub sample application…
connect

und der Assistent auf der Website gibt “Mit Ihrem Gerät verbunden” aus mit zusätzlichen Nachrichten von unserem Raspberry ({“mode1Process”:1}).

Mit dem Eingabefeld können wir eine Nachricht in die andere Richtung, zu unserem Gerät schicken. Damit haben wir verifiziert, dass die Kommunikation zwischen der AWS-MQTT-Instanz und unserem Raspberry prinzipiell funktioniert. Das hilft uns bei den späteren Tests Probleme in der Kommunikationsstrecke (zum Beispiel mit dem Firewall oder den Zertifikaten) auszuschließen. Wenn wir jetzt auf “Fertig” klicken, beenden wir den Assistenten und befinden uns wieder auf unserer “AWS IoT”-Hauptseite.

Der Assistent hat jetzt für uns ein Objekt angelegt, das unseren Raspberry in AWS repräsentiert, ein Zertifikatsobjekt, dass die Zertifikate kapselt (wenn wir also jemals die Zertifikate austauschen müssen, dann können wir hier ein neues anlegen), und eine Richtlinie. Unser Objekt und das Zertifikat hängen direkt aneinander, die Zugriffsrichtlinie hängt nur am Zertifikat. Das erlaubt zum Beispiel unterschiedliche Zugriffsrechte auf das gleiche Objekt über verschiedene Zertifikate mit unterschiedlich gestalteten Zugriffsrichtlinien.

Aktivieren unserer Zugriffsrichtlinie

Der letzte Schritt ist jetzt, unsere laxere Zugriffsrichtlinie aktiv zu schalten. Wir navigieren zum Zertifikat, indem wir im Menü “Sicherheit” den Menüpunkt “Zertifikate” anwählen. Dort klicken wir bei unserem Zertifikat auf das Menü “…”, wählen dort den Menüpunkt “Richtlinie anfügen” aus und wählen unsere Richtlinie “access-all” aus. Damit ordnen wir dem Zertifikat jetzt unsere Richtlinie zusätzlich zur ersten zu und erlauben beliebige IOT-Zugriffe.

Damit ist die Einrichtung von AWS abgeschlossen und wir könnten uns der Einrichtung des Raspberry widmen.

Im folgenden werfen wir aber erst noch einen Blick darauf, wie der Prozess ohne Assistenten funktioniert, um einen kleinen Blick hinter die Kulissen zu werfen.

Anlegen von Objekten ohne Assistent

Die Schritte, die vorher der Assistent für uns übernommen hat, müssen wir jetzt von Hand durchführen. Außerdem gibt es kein einfaches Skript wie im durch den Assistenten zur Verfügung gestellten Archiv, um die Konnektivität zu testen. Das müssen wir dann auf die Einrichtung des Raspberry verschieben.

Um ein neues Objekt zu erstellen, wählen wir im Menü “Verwalten” den Menüpunkt “Objekte” an und klicken auf dieser Seite “Erstellen”. Hier besteht nicht nur die Möglichkeit, ein einzelnes neues Objekt zu erzeugen sondern auch große Mengen, falls das benötigt wird. Wir wählen “Einzelnes AWS IoT-Objekt registrieren”, geben auf der neuen Seite den Namen unseres Objektes “home-automation” an und klicken auf “Weiter” (die zusätzlichen Optionen können wir ignorieren).

Hier gibt es verschiedene Möglichkeiten, auch eigene Zertifikate zu nutzen, aber wir wählen “Zertifikat mit einem Klick erstellen”. Auf der nächsten Seite werden uns Zertifikat, öffentlicher und privater Schlüssel angeboten, die wir alle herunterladen, genau wie das CA-Stammzertifikat für AWS IoT. Beim Stammzertifikat sollten wir auf jeden Fall das Verisign-Zertifikat herunterladen (zum Zeitpunkt des Schreibens ist es das einzige, das funktioniert).

Wir klicken auf “Aktivieren”, um das Zertifikat in AWS zur Verfügung zu stellen und wählen “Eine Richtlinie anfügen”. Dort wählen wir unsere Richtlinie “access-all” und klicken auf “Objekt registrieren”.

Damit ist auch hier das Anlegen abgeschlossen, allerdings bekommen wir kein Archiv mit einem Testskript, sondern haben nur die heruntergeladenen Zertifikate und Schlüssel, die zudem noch anders benannt sind, als später auf dem Raspberry erwartet. Die benötigten Namen sind:

  • Private Key → home-automation.private.key
  • Unser Zertifikat → home-automation.cert.pem
  • Stammzertifikat → root-CA.crt

Wir können die Dateien entweder umbenennen oder auf dem Raspberry symbolische Links verwenden.

Einrichtung des Raspberry

Auf unserem Raspberry verwenden wir Node-Red. In der Standardinstallation findet sich das Node-Red-Verzeichnis im Stammverzeichnis des Benutzers “pi” im Unterverzeichnis “.node-red”. In diesem Verzeichnis installieren wir mit

npm install node-red-contrib-aws-iot-hub

einen Node-Typ, den wir zur Kommunikation mit AWS benutzen (siehe Dokumentation unter https://flows.nodered.org/node/node-red-contrib-aws-iot-hub).

Dieser bietet uns weitergehende Funktionalität für die Verwendung von Thing-Shadows, die wir hier zwar nicht verwenden, später aber durchaus brauchen können.

Falls die Installation dieses Nodes nicht gewünscht ist, können wir alternativ auch einen der mitgelieferten MQTT-Standard-Nodes verwenden. Die Konfiguration eines solchen Nodes für die Verbindung mit AWS schauen wir uns im Anschluss an.

Zertifikate

Wir platzieren die Zertifikate in einem Unterverzeichnis .node-red/aws. Die Dateien sollten die richtigen Namen haben, was bei der Erstellung durch den Assistenten gewährleistet ist. Bei der manuellen Erstellung hatten wir die Umbenennung beziehungsweise das Erstellen symbolischer Links als den letzten Schritt durchgeführt.

pi@home2:~/.node-red/aws $ dir
home-automation.cert.pem home-automation.private.key
home-automation.public.key root-CA.crt

Konfiguration in Node-Red

Wir können jetzt die neu installierten Nodes nutzen, um eine Verbindung mit unserer MQTT-Instanz aufzubauen. Zuerst erzeugen wir einen Flow, der dem Folgenden ähnelt (dargestellt ist der Endzustand):

Wir verwenden einen AWS-MQTT-Input-Node zusammen mit einem Debug-Node und einen Inject-Node mit dem entsprechenden AWS-MQTT-Output-Node.

Das Beispielskript von AWS verwendet “topic_1” zum Empfangen und “topic_2” zum Senden. Wir verwenden der Einfachheit halber “topic_1”, sowohl im Input-Node als auch im Inject-Node.

In beiden AWS-MQTT-Nodes können wir ein neu zu konfigurierendes Gerät (device) auswählen. Dieses konfigurieren wir mit dem Namen unseres Geräts, wählen explizit “MQTT-Broker”, definieren Client ID, Endpoint und Verzeichnis für die Zertifikate.

Die Client ID wird hierbei verwendet, um die Namen der Zertifikate zu bestimmen. Wir können also keine andere Client ID als durch unsere Dateinamen vorgegeben verwenden. Es hindert uns aber auch niemand daran, die Dateien umzubenennen, wenn wir dies benötigen.

Der Endpoint ist benutzerspezifisch und wir finden ihn sowohl auf der “IoT Core”-Seite unter dem Menü “Einstellungen” als auch bei jedem Objekt unter dem Menüpunkt “Interagieren”.

Nach Abschluss der Konfiguration und Deployment zu Node-Red sollte der Input-Node jetzt ein grünes Merkmal “subscribed” haben (wie im obigen Bild). Wenn wir jetzt mit Inject den ersten Wert in unsere MQTT-Instanz schreiben, sollte nach einem Moment das grüne Merkmal “done” dargestellt werden und wir sehen in der Debug-Ausgabe von Node-Red die erste Ausgabe.

Falls dies nicht geklappt hat, ist der erste Schritt, im Log zu prüfen, wie die angegebenen Namen und Pfade für die Zertifikate sind und diese mit den tatsächlichen zu vergleichen. Der nächste Schritt ist die Prüfung der Zugriffsrichtlinie. Zu guter Letzt sollten Sie nur eine derartige Verbindung pro Node-Red-Instanz konfigurieren. Ansonsten kann die Fehlersuche sehr schnell sehr schwierig werden.

Verwendung des Standard-MQTT-Node

Die Konfiguration eines Standard-MQTT-Nodes ist zwar etwas komplizierter, aber nicht übermäßig schwierig. Wenn wir einen neuen MQTT-Node editieren, werden wir nach Server, Topic, QoS und Namen gefragt. Der Name ist frei vergebbar (z.B. “AWS”), bei QoS wählen wir 0 (keine Garantien), bei Topic wählen wir für die ersten Tests auch wieder “topic_1”. Um den Server zu konfigurieren, wählen wir “Add new MQTT-Broker…” und klicken auf den Edit-Button.

Das neu geöffnete Fenster erlaubt uns die Konfiguration des Brokers:

Unter Server tragen wir unseren Endpoint ein, ändern den Port auf 8883, wählen eine SSL/TLS-Verbindung und lassen den Rest der Einträge auf der Voreinstellung. Später können wir hier eine spezifische Client-ID eintragen (z.B. “home-automation”). “Use clean session” stellt immer eine neue Session ohne Historie her. Dies ist für die Kommunikation mit AWS sinnvoll, da uns frühere Sprachkommandos von Alexa nicht interessieren.

Jetzt müssen wir noch eine neue TLS-Konfiguration hinzufügen, die unsere Zertifikate kapselt.

Wir können die Zertifikate hochladen oder im lokalen Dateisystem referenzieren. Da wir die Zertifikate unter /home/pi/.node-red/aws abgelegt haben, ist die zweite Variante die, die wir wählen. Wir tragen für unser Zertifikat, den privaten Schlüssel und das Stammzertifikat die korrekten Pfade ein, vergeben einen Namen für die Konfiguration und klicken auf “Add/Update”.

Sobald wir die Konfiguration und die MQTT-Broker-Konfiguration hinzugefügt und alles abgespeichert und deployed haben, sollte der Node nach einem kurzen Moment das grüne Merkmal “connected” darstellen.

Verifikation

Um ganz sicherzugehen, dass alles funktioniert (und um unseren Spieltrieb zu befriedigen), können wir jetzt folgenden Test machen:

Wir navigieren im Browser in AWS zum Test-Client der “IoT-Core”-Seite (Menü “Test”) und abonnieren dort als Thema “topic_1”. Im nächsten Schritt starten wir über die Shell das vom Assistenten gelieferte Beispielskript “start.sh”.

Wenn wir jetzt in Node-Red über den Inject-Node eine Nachricht in die AWS-MQTT-Instanz publizieren, sollte das sowohl im Test-Client als auch durch das Beispielskript ausgegeben werden (und natürlich auch im Node-Red-Debug-Fenster).

Wenn wir im Test-Client die Beispielnachricht publizieren, dann sollte dies nicht nur im Test-Client und in der Ausgabe des Beispielskripts ausgegeben werden (das hatten wir ja bereits getestet), sondern jetzt auch im Debug-Fenster von Node-Red erscheinen.

Zusammenfassung

Die Konfiguration einer MQTT-Instanz, zu der sich unser Raspberry verbindet, ist nicht im eigentlichen Sinn schwierig, aber durch die unterliegende Komplexität sehr schnell fehlerbehaftet. Durch unseren Weg vermeiden wir viele übliche Fehler, die durch fehlende Fehlermeldungen nur raten lassen, was gerade schief geht. Und falls tatsächlich gar nichts funktioniert, ist ein akzeptabler Ansatz, alles zu löschen (Objekt, Zertifikat, Zugriffsrichtlinie in AWS, den Flow und die Zertifikate auf unserem Raspberry), um noch einmal neu zu beginnen (dies gilt sowohl für die Konfiguration des AWS-MQTT-Nodes als auch des Standard-MQTT-Nodes). Das ist in den meisten Fällen der schnellste Weg, um zum Erfolg zu kommen. Und falls zum Beispiel der Test-Client im “IoT Core” nicht funktioniert, einfach einen Tag später nochmal testen (dies ist ein tatsächlicher Erfahrungswert…).

Ein weiterer Tipp in diesem Zusammenhang: Die Verbindung zum MQTT-Broker wird über Zertifikate gesichert, und hier scheint es (zumindest zum Zeitpunkt des Schreibens) Kollisionen geben zu können, wenn wir in verschiedenen Regionen Objekte exakt gleich benennen. AWS legt uns zwar die Objekte korrekt an und wir können auch die Zertifikate erfolgreich herunterladen, aber die spätere Verbindungsaufnahme ist nicht erfolgreich. Hier ist das Neuanlegen eines Objektes mit einem über alle Regionen hinweg eindeutigen Namen der bessere und sicherere Weg.

Sobald die Verbindung funktioniert, ist sie allerdings sehr zuverlässig und bei Internet-Problemen schneller wieder aufgebaut als die Alexa-Verbindung.


Vielen Dank fürs Lesen und ich freue mich auf Feedback. Die weiteren Artikel dieser Serie, genau wie andere interessante Artikel, erscheinen im Blog der Digital Frontiers und werden auch auf unserem Twitter-Account angekündigt.