Powershell und MQTT

MQTT ist das dominante Protokoll in der IoT-Welt um Meldungen zwischen Komponenten zu übertragen und dabei möglichst wenig Bandbreite zu nutzen und dennoch eine halbwegs sichere Übertragung zu gewährleisten. Die Zusammenhänger dazu finden Sie auf MQTT - Telemetriedaten mit IoT. Diese Seite beschäftigt sich damit, wie Sie mit der M2MQTT-Library per PowerShell als Publisher entsprechende Meldungen an einen MQTT-Broker senden und als Subscriber entsprechende Daten abholen können.

Aus Vereinfachungsgründen gehe ich hier auf Authentifizierung, Verschlüsselung etc. nicht weiter ein. In einem internen abgeschotteten IoT-LAN ist das für Test- und Analysezwecke tolerierbar.

MQTT Libraries

Generell ist MQTT nicht auf TCP beschränkt. Auch andere Übertragungswege sind natürlich ebenso erlaubt. Ich beschränke mich hier aber auf TCP. Zuerst habe ich auf dem Kabel mit WireShark angeschaut, was passiert aber schon schnell wurde klar, dass es viel Bit und Byte-Operationen benötigt hätte, um das alles mit einem TCP-Listener und PowerShell aufzubauen. Also habe ich mich auf die Suche nach vorhandenen Modulen gemacht, die ich in PowerShell nutzen kann. Passend dazu habe ich schon zwei Webseiten gefunden, die eine ganze Menge Module für die unterschiedlichsten Programmiersprachen und Plattformen auflisten:

Es gibt zwar keine fertiges Modul für PowerShell aber doch gleich mehrere Module für .NET auf Windows. Ich habe erst einmal die Projektseiten besucht und geprüft, wann sich da zuletzt etwas getan hat und ob es vielleicht schon fertige DLLs gibt. Auch wenn es viele Libraries gibt, so haben wohl zwei die Nase vorne. (Stand Feb 2018)

Library URL Letztes Update Bemerkung

Paho.MqttDotnet

https://github.com/xljiulang/Paho.MqttDotnet/

Sep 2017

Keine eigenständige Library sondern ein Wrapper für eclipse/paho.mqtt.c

MqttDotNet

http://sourceforge.net/projects/mqttdotnet/

2009

Schon länger nicht mehr passiert ?

nMQTT

https://github.com/markallanson/nmqtt

2013

Auch schon über 4 Jahre her

M2MQTT

https://github.com/eclipse/paho.mqtt.m2mqtt
Blog des Autors https://m2mqtt.wordpress.com/
https://www.nuget.org/packages/M2Mqtt/4.3.0
https://www.hivemq.com/blog/mqtt-client-library-encyclopedia-m2mqtt
https://code.msdn.microsoft.com/windowsdesktop/M2Mqtt-MQTT-client-library-ac6d3858/

2015

Auch wenn die Versoin 4.3.0 schon einige Jahr alt istHier passiert noch was und auch die Beschreibung ist aktuell und installiert wird mit "Install-Package". Kann sowohl MQTT Client als auch Server sein.

KittyHawkMQ

https://github.com/mFourLabs/KittyHawkMQ

2016

Auch nur als Source Code

StriderMqtt

https://github.com/ericvoid/StriderMqtt

2018

Kleiner leichtgewichtiger Client aber zum selbst kompilieren

Das ich niemandem zumuten will, erst mit MonoDevelop oder Visual Studio den Code zu erstellen, habe ich mir M2MQTT genauer angeschaut.

M2MQTT installieren

Durch die Installation in das PowerShell Package Center ist die Einrichtung auf einem Client sehr einfach. Sie starten in einer Admin-PowerShell einfach nur

# Installation per PowerShell
Install-Package M2Mqtt

Ggfls. gibt es noch eine Rückfrage zur Installation aber dann landen die Binaries auf ihrem PC.

Irgendwie werden sich Windows bzw. die PowerShell und Linux immer ähnlicher.

Bei mir war die NET.4.5-Version auf "C:\Program Files\PackageManagement\NuGet\Packages\M2Mqtt.4.3.0.0\lib\net45\M2Mqtt.Net.dll". Die DLL muss im PowerShell-Script dann noch eingebunden werden, damit Sie die darin enthaltenen Klassen nutzen können.

Add-Type -Path "C:\Program Files\PackageManagement\NuGet\Packages\M2Mqtt.4.3.0.0\lib\net45\M2Mqtt.Net.dll"

Meldung senden (PUBLISH)

Aber mit wenige Zeilen können Sie z.B. einen Wert an einen Broker senden. "broker.hivemq.com" ist ein Test-Broker, den Sie laut Hersteller einfach nutzen können. Einen Status sehen Sie dann auf http://www.mqtt-dashboard.com/index.html. Bei der Menge an Veröffentlichungen (100-200(Sek) werden Sie ihre Meldung aber eher selten dort sehen.

# MQTTClient instanzieren
$MqttClient = [uPLibrary.Networking.M2Mqtt.MqttClient]("broker.hivemq.com")
#
# Verbinden
$mqttclient.Connect([guid]::NewGuid())

# Wert schreiben
$MqttClient.Publish("sensor/temp", [System.Text.Encoding]::UTF8.GetBytes("20"))

Im WireShark sind der 3-Wege-TCP-Handshake und der MQTT-"Connect Command" samt der "Connect Ack" zu sehen. Mit einigen Sekunden Abstand sehen Sie dann auch den "Publish Message", die ich unten im Detail aufgezeichnet habe.

Damit ist es schon einmal möglich einen Wert an einen Broker zu senden. Sie brauchen sicher nur wenige Sekunden u das Potential zu erkennen, wenn Sie z.B. Performance Counter an einen MQTT-Broker senden. Hinsichtlich der "Message" gibt es keine Vorgaben. Es muss einfach nur ein String sein aber ob sie hier nur einfach eine Zahl als String, eine JSON/XML-Struktur oder andere Daten senden.

Insofern ist natürlich MQTT auch aus Sicherheitsaspekten kritisch zu betrachten, denn über einen in Internet erreichbaren Broker könnte ich auch Daten aus dem Firmennetzwerk nach extern ausleiten. Durch die "Subscribe"-Logik wird es sicher nicht lange dauern, bis auch Command&Control-Server diesen Kanal verwenden.

Der Client muss aber auch die Verbindung halten, damit der Broker ja erkennt, dass der Client noch da ist. Der Client kann dem Broker nämlich mitteilen, wie er sich bei einem ungeplanten Disconnect verhalten soll. Dazu gibt es einen "Last Will"

Damit ist dann auch verständlich, dass der Client einen Art "Heartbeat" senden muss. Das übernimmt die Library durch entsprechende PING-Requests.

So kann der Broker überwachen, welche Clients online sind. Diese Funktion ist auch für die nächste Funktion wichtig

Meldungen empfangen (SUBSCRIBE)

Ein Client kann auch ein System sein, welches Meldungen vom Broker empfangen will. Dazu muss es einen "SUBSCRIBE" machen und den Pfad übergeben, der für diesen Client interessant ist. Auch hier ist der Heartbeat erforderlich, da der Broker so einen vom Client aufgebauten Kanal zur Rücksendung der Antworten hat. Eine neue Meldung wird aktiv vom Broker an den Client gesendet. Es gibt so gesehen kein "Polling".

Die Nutzung von Subscriptions ist klassisch ein asynchroner Vorgang. Eine Meldung kann jederzeit kommen und ich möcht mein Skript ja nicht mit einem "Warten auf Message" anhalten. Daher muss ich über die "Subscribe"-Methode der Klasse ein oder mehrere MQTT-Pfade mitgeben, die mich interessieren.

# Subscribe
$MqttClient.Subscribe("msxfaq/temp",0)

Auch diese Aktion ist im WireShark zu sehen. Der "Subscribe Request" wird durch einen "Subscribe Ack" bestätigt.

 

Wenn sich nun auf dem Broker dieser  Wert ändert, sollte dieser Client auch die Information dazu bekommen.

Auf Meldungen warten

Die Frage ist nun , wie ich in PowerShell auf diese Events warte. Es gibt hier kein "$mqttclient.getMessage()" o.ä., bei der das Skript dann auf eine Meldung wartet. Die Klasse M2MQTT implementiert aber einen Eventhandler als Callback-Funktion. Ich muss quasi ein Stück Code hinterlegen, welcher von der MQTT.Client-Instanz dann aufgerufen wird. Das geht in PowerShell über "Register-ObjectEvent"

Da ich aber erst mal nicht wusste, was da zurück kommt, habe ich einen einfachen Handler an das Objekt gehängt.

Register-ObjectEvent `
   -inputObject $MqttClient `
   -EventName MqttMsgPublishReceived `
   -Action { $args | Export-Clixml .\mqtt.xml}

Nachdem ich dann in einem anderen Fenster eine neue Meldung per "Publish" gesendet habe, finde ich im WireShark dann folgende Pakete:

Das erste Paket ist der Versand der Meldung aus einer PowerShell-Umgebung mit der TCP-Quittung in Paket 2. Erst das Paket 3 zeigt, dass auch der Broker die MQTT-Meldung ebenfalls über einen "Publish" an den Subscriber sendet. Die MQTT-Klasse starte dann den hinterlegten Event, der die übergebenen Daten erst einmal in eine XML-Datei ablegen.

Diese kann ich dann losgelöst wieder einfach einlesen und analysieren. Sie enthält zwei Elemente.

PS C:\> (Import-Clixml .\mqtt.xml)[0]

IsConnected     : True
ClientId        : broker.hivemq.com
CleanSession    : True
WillFlag        : False
WillQosLevel    : 0
WillTopic       :
WillMessage     :
ProtocolVersion : Version_3_1_1
Settings        : uPLibrary.Networking.M2Mqtt.MqttSettings

PS C:\> (Import-Clixml .\mqtt.xml)[1]

Topic    : msxfaq/temp2
Message  : {49, 48}
DupFlag  : False
QosLevel : 0
Retain   : False

Damit habe ich mich dann auf den Weg gemacht, die Daten einzusammeln. Natürlich könnte die Action direkt und sofort mit den Daten die Weiterverarbeitung anzugehen. Zuerst habe ich aber eine Hashtabelle definiert, in der der Event die Ergebnisse ablegt. Durch die Nutzung des "topic" als Key landet in der Hashtabelle immer nur der letzte Wert.

# Variable zum Sammeln der Daten vorbereiten
[hashtable]$mqttresults=@{} 

#Eventhandler registrieren
Register-ObjectEvent `
   -inputObject $MqttClient `
   -EventName MqttMsgPublishReceived `
   -Action {Write-host "Event Found Topic: $($args[1].topic)  Message $([System.Text.Encoding]::ASCII.GetString($args[1].Message))" -nonewline;
            $mqttresults[$args[1].topic] = $args[1].Message;
            write-host " done"}

# Entsprechenden Kanal per SUBSCRIBE abonieren.
$MqttClient.Subscribe("msxfaq/#",0)

#Weitere Verarbeitungen und manchmal auslesen der Events aus $mqttresults

Mein Programm kann nun also diverse andere Dinge tun und letztlich drauf warten, bis es Werte zur Verarbeitung übernehmen soll.

Bei den Eventhandlern sollten Sie wissen, das diese nicht "parallel" ablaufen sondern solange der Event aktiv ist, das Hauptprogramm angehalten wird. Damit ist das Risiko auch überschaubar, wenn der Eventhandler die gleich Variable verarbeitet, wie das eigentliche Programm. Wenn mehrere Events hintereinander anstehen, dann werden diese auch sequentiell abgearbeitet. Es geht also auch kein Event verloren, wenn die Verarbeitung länger dauert. Wenn das Hauptprogramm mit einem "Start-Sleep" gerade wartet, dann wird aber auch kein Event abgearbeitet. Der Event kommt nur zum Tragen, wenn das eigentliche Skript den nächten Befehl abarbeitet.

Verbindung abbauen

Jeder verbundene Client belegt natürlich Ressourcen  beim Broker. Natürlich räumt die Garbage Collection eines Betriebssystems den Speicher nach dem Ende wieder auf und gibt auch die TCP-Sockets frei. Aber für den Broker ist damit ja nicht erkennbar, ob er den "Last Will" ausführen soll. Daher ist der Aufruf der "Disconnect"-Methode wünschenswert.

$MqttClient.Disconnect()

In WireShark ist zu sehen, dass der Client ein "Disconect Req" sendet und darauf hin der Broker die TCP-Connection direkt beendet.

Das ist dann ein schnelles aber doch geordnetes Ende einer Verbindung.

MQTT und Retained Messages

Über die Subscribe-Funktion bekommt der Client natürlich sofort mit, wenn eine neue Meldung anliegt. Wenn er aber erst einmal startet, dann bekommt er per Default nicht die früher eingegangenen Meldungen. Es ist ja kein Postfach sondern ein Abonnement. Wenn Sie heute ein Abonnement für eine Zeitschrift kaufen, dann bekommen Sie ja auch erst die nächste Ausgabe und keine vorherigen Aufgaben. Bei MQTT ist es aber möglich, dass der Broker die letzte vorherige Meldung auch puffert und dem Client beim ersten SUBSCRIBE gleich mit liefert. MQTT bezeichnet dies als "Retained Messages"

Voraussetzung hierfür ist aber, dass der Client schon beim PUBLISH das entsprechende Flag setzt um die Meldung als "Retained" zu setzen.

In der Regel ist es also erforderlich, dass Sie etwas "warten". Als Subscriber kann ich weder den Publisher noch den Broker zwingen, die Meldungen als "Retained" zu senden.

Was kommt noch?

Wie Sie sich sicher denken können, ist diese Seite erst der Anfang. Ich musst hier erst mal die Grundlagen zur Nutzung von MQTT in PowerShell legen, um basierend darauf nun weitere Dinge anzugehen.

Ich habe einige Seiten schon angefangen und hier verlinkt, damit ich das später nicht vergesse. Wenn Sie aktuell noch auf einen 404 laufen, dann dauert es einfach noch etwas, bis die Seiten "fertig" sind.

Es gibt noch ganz viele Dinge, die nun möglich sind.

  • eigener lokaler Broker
    Vielleicht lasse ich auf meinem PRTG-Server einen kleinen Broker mitlaufen, der die MQTT-Publish-Meldungen im LAN erfasst
  • PRTG und MQTT
    PRTG hat selbst noch keinen MQTT Support aber mit dem Wissen hier sollte es ja kein Problem sein, die Daten von einem Broker per SUBSCRIBE einzusammeln und an PRTG zu melden.
  • SONOFF mit ESP8266 Energiemessung mit MQTT
    Von der Firma SONOFF gibt es Energiemesser mit ESP8266 und andere Personen haben eine Firmware veröffentlicht, die diese Daten per MQTT versendet.
  • IoT/MQTT und Azure
    Auch Microsoft ist auf dem IoT-Zug und wie wäre mein eigener Broker in Azure ?
    https://azure.microsoft.com/de-de/resources/samples/iot-gateway-mqtt-http/
    https://www.linkedin.com/pulse/how-mqtt-microsoft-azure-iot-hub-satish-pagare/
  • Perfmon-Counter per MQTT senden
    Sobald ich auch ein MQTT-Anzeigeclient für meinen Broker habe, möchte ich natürlich auch IT-Daten (Server-last etc) an den Broker senden und nicht immer nur schnöde IoT-Werte eines Temperatursensor.

Sie sehen also, dass diese Seite erst der Anfang einer längeren Reise sein kann.

Weitere Links