ContentHub
20250818-MQTT-JSON_String-DE.mp4
26 views
View transcript
Hallo, in diesem WAGO-Tutorial zeigen wir, wie man in CODESYS 3.5 als MQTT Client einen JSON-String abonniert, analysiert und Werte aus dem String Variablen zuordnet. Wir verwenden in diesem Tutorial den gleichen Aufbau wie im WAGO Load Management Video, in dem wir zeigen wie man Daten an einen MQTT-Broker sendet. Zusätzlich haben wir in diesem Aufbau eine PFC 200 auf dem wir mit CODESYS 3.5 die bereitgestellten Daten empfangen und auswerten wollen. Unser Ziel ist den 1. digitalen Eingang am CC100 zu erfassen und ihn zum Beispiel in einer Visualisierung anzuzeigen. Zunächst wollen wir einmal anschauen, was beim Broker ankommt. Wir nutzen dafür das Softwaretool MQTT Explorer. Einmal auf dem Broker eingewählt wird auch das Haupt Topic WALM aufgelistet, klappen wir dieses einmal auf, ist da auch das Subtopic cm_vars, welches die 8 digitalen Eingänge des CC100 sein könnten. Geben wir 24V auf den ersten digitalen Eingang wechselt auch im Broker der Werte von 0 auf 1. Wir haben unser Topic gefunden und kopieren den String, um diesen einmal grafisch, aussagekräftiger darzustellen. Dafür empfiehlt sich die Homepage JSON Crack. Hier fügen wir unseren String ein und die Struktur wird uns angezeigt. Ein Punkt fällt uns direkt auf, den wir für die Analyse berücksichtigen müssen. Der Wert VAL ist einmal hier oben vorhanden und dann nochmal in jedem der 8 Eingänge – dazu später, aber mehr. Wir senden also erfolgreich an den MQTT-Broker, haben den String analysiert und wissen an welche Stelle sich die relevanten Daten befinden und wie die Struktur des Strings aussieht. Jetzt schreiben wir in CODESYS 3.5 ein Programm um die Daten abzuholen und auszuwerten. Das gezeigt CODESYS Programm ist auch als Beispielprogramm verfügbar und wird als Download Link bereitgestellt. Dies erleichtert dann die Adaption auf eigene Projekte. Um bei der Verwendung von MQTT in CODESYS 3.5 das Rad nicht neu erfinden zu müssen, nutzen wir hier die CODESYS 3.5 Bibliothek: CODESYS IIoT Libraries SL. Die Bibliothek ist zwar zunächst lizenzpflichtig, bei der Verwendung eines WAGO Controllers ab Firmware 24 ist diese Lizenz aber bereits integriert. Wir installieren die Bibliothek über den CODESYS 3.5 Installer. Unser Projekt hier erstellen wir in der CODESYS 3.5 Service Pack 21 Patch 1 Version, da unser PFC200 mit der Firmware 29 läuft. Mit einem Klick auf „Ändern“ öffnet sich das Add-ons Fenster und wir suchen nach IIoT. Dann installieren und anschließend CODESYS 3.5 öffnen. Ein neues Standardprojekt ist angelegt, der PFC200 wurde gefunden und auch der K-Bus ist gescannt. Im CODESYS 3.5 müssen wir aus der IIoT Bibliothek drei Bibliotheken in unsere Projekt laden. Wir benötige die MQTT_Client_SL Bibliothek, die JSON Utilitys SL und die Bibliothek Web Client SL. Damit haben wir alles vorbereitet und beginnen mit einem neuen Programm. Mit diesem Programm verwalten wir die Verbindung zum Broker. Zunächst fügen wir aus der MQTT Bibliothek den Function Block MQTT Client hinzu. Alle Eingänge und Ausgänge des Function Blocks werden uns angezeigt und wir vergeben und deklarieren dazu Variablen. Zunächst müssen wir den Baustein deklarieren. Mit xEnable wir die MQTT Verbindung aktiviert, dann folgen ein paar Variablen zur Statusanzeige. In diesem Beispiel nutzen wir bei uiPort den unverschlüsselten MQTT- Port 1883, das heißt wir verwenden keine TLS-Verschlüsselung und setzen diese auf FALSE. Das Keep Alive Telegram setzen wir auf 5 Sekunden, dann wird nach einem Verbindungsabbruch noch zweimal im Abstand von 5 Sekunden versucht die Verbindung wieder aufzubauen, danach geht der Baustein in TimeOut und muss über Enable wieder neu gestartet werden. Mit xWillRetain TRUE würden bei Verbindungsabbruch verpasste Telegramme im Broker gepuffert und sobald die Kommunikation wieder steht, versendet. Mit Clean Session TRUE, wird bei jedem Verbindungsaufbau eine neue Session gestartet, bei FALSE wird eine evtl. bereits vorhandenen fortgesetzt. Username und Passwort verwenden wir in unserem Beispiel nicht. Die Client ID muss eindeutig sein, da die Anmeldungen mehrerer Clients am Broker mit gleicher Client ID nicht möglich ist. Wird keine ClientID vergeben, wird automatisch eine generiert. Mit der Vergabe einer Client ID erleichtern wir uns aber die Zuordnung der Clients. Dann könnten wir ein Ping Intervall angeben und es folgen ein paar Einstellungen für der Verwendung von TLS oder Zertifikaten. Mit TimeOut bestimmen wir die Zeit, nach der ein erfolgloser Verbindungsaufbau mit xError abbricht. Dann folgt die Auswahl, ob wir eine TCP oder eine Web Socket Verbindung haben und die Angabe der MQTT Version. Die Connection Properties sind nur bei der Verwendung von MQTT V5 relevant. Der Hostname ist dann die IP Adresse des Brokers und dann folgen noch ein paar Status Rückmeldungen der Kommuniktaion. Die Variable xConnect to Broker haben wir als Globale Variable angelegt, da wir diese in einem weiteren Programm benötigen. Zunächst legen wir uns einen Timer an. Mit dem Timer starten wir nach einem Verbindungsabbruch die Kommunikation automatisiert wieder neu. Der TON wird gestartet, wenn entweder xError TRUE ist order xEnable nicht TRUE wird. Die Zeit legen wir hier oben mit 15s fest. Dann verwenden wir eine State Machine mit Cases OF: init, activ und error. Wir starten mit der Initialisierung, also mit dem Case init. Wenn Enable nicht TRUE ist, setzten wir Enable auf TRUE und springen zum nächsten Schritt: Activ. Außerdem prüfen wir die Verbindung zum Broker, ist diese nicht TRUE springen wir auf den Schritt Error. Ist die Verbindung erfolgreich initialisiert, landen wir im Schritt „Activ“ und bleiben da so lange bis xError TRUE wird, in dem Fall springen wir dann in den Schritt Error. Im Error Case wird Enable zurück auf FALSE gesetzt und der Timer startet. Sind die 15 Sekunden abgelaufen, setzten wir den Timer zurück, erhöhen den Error Zähler um eins und springen wir wieder in den Initialisierungsschritt. Die Schrittkette läuft dann wieder von vorne durch. Die Verbindung zum Broker steht und wir machen mit dem Auslesen der Daten weiter. Auch dafür verwenden wir wieder eine State Machine und legen dafür eine Enumeration an. Als erstes kommt der Initialisierungsschritt, dann wollen wir die Daten abholen, dann wollen wir den Daten Variablen zuordnen und eine Error Abarbeitung soll es auch geben. Wir legen ein neues Programm an und benötigen zwei Bausteine. Als erstes aus der MQTT-Bibliothek den Funktions- Baustein MQTTSubscribe und dann aus der JSON Bibliothek den Funktions- Baustein JSONByteArrayReader. Mit dem MQTTSubscriber baustein, holen wir die Daten beim Broker ab. Zunächst haben wir wieder ein paar Statusmeldungen und das Enable Bit. Mit eSubscribeQoS geben wir den Quality of Service der MQTT Verbindung an. Da der Publisher bereits mit einem QoS 1 an den Broker sendet, empfiehlt es sich hier auch ein QoS 1 anzugeben. Dann kommen wir zum eigentlichen Inhalt des Telegramms. pbPayload ist ein Pointer auf einen Speicherplatz, in dem der JSON String abgelegt wird. Dafür legen wir eine String-Variable in der Größe 2048 an. Damit es zu keinem Buffer Überlauf kommt, also dass angrenzende Speicherbereiche überschrieben werden, muss die Speicherplatzgröße mit angegeben. Einen Filter verwenden wir nicht und die Subscribe Properties werden nur bei MQTT V5 benötigt. Bei Time Out geben wir in Mikrosekunden an, wie lange wir auf eine Rückmeldung nach einer Abonnement Anfrage warten. Dann müssen wir die Brokerverbindung angeben, diese haben wir ja bereits im Programm MQTT Client aufgebaut, daher geben wir hier den Baustein an. Dann müssen wir das MQTT Topic angeben. Wenn wir kurz in den MQTT Explorer schauen, sind da von verschiedenen Clients unterschiedliche Haupt Topics mit weiteren Unter-topics bereitgestellt. Wir wolle aber nur aus dem Haupt Topic WALM, das Topic cm_vars. In diesem Fall ist die Struktur des Strings WALM/device_info/cm_vars ganz gut erkennbar. Bei komplexeren Strings kann aber der JSON Crack sehr hilfreich sein. Hinzu kommt, dass der Funktions Baustein einen W-String benötigt also einen String der den Zeichensatz UTF-16 beinhaltet. Über MQTT bereitgestellte String sind aber einfache Strings im UTF-8 Format. Daher müssen wir den String nochmal in einen Wide String umwandeln. Die MQTT Client Bibliothek stellt dafür einen Converter zur Verfügung, wofür wir hier zwei Variablen deklarieren einmal die wsTopicFilter und die sTopic. Im Programmfenster legen wir schon einmal die Konvertierung an, Der Variable sTopic weisen wir die Topic-Struktur zu. Dann deklarieren wir noch ein paar eine Variable für die Bausteinausgänge zunächst für Errormeldungen, ob etwas Empfangen wurde, wie groß das Datenpaket ist, ob das Abonnement aktiv ist, die Auflösung des letzten Topics als REAL benötigen wir nicht und zum Schluss noch die zuletzt empfangenen Properties. Damit empfangen wir den gewünschten JSON String und Filtern runter bis auf das Topic ‚cm_vars‘. Jetzt müssen wir den String zerlegen und den einzelnen Werten Variablen zuordnen. Das machen wir mit dem zweiten Funktions- Baustein JSON Array Reader. Dieser beginnt mit dem Input Execute. Der Input löst zwei Funktionen aus. Bei steigender Flanke wird die Aktion gestartet und bei fallender Flanke wird der Output des Bausteins zurückgesetzt. Dann folgen wie gewohnt die Status anzeigen. PW- Data ist ein Pointer auf den Daten String allerding benötigen wir diesen auch wieder im Wide String UTF-16 Format. Leider hilft uns in diesem Fall der einfache Converter, wie wir ihn für das Topic genutzt haben nicht weiter. Der Converter String to WString ist auf 256 Zeichen beschränkt. Alles was darüber hinaus geht wird ohne weiter Rückmeldung abgeschnitten. Aber für diesen Fall haben wir die Bibliothek WebClient aufgerufen, diese enthält die Funktion: Convert UTF8 to UTF 16. Diese Funktion soll nachher im Schritt „Fetch“ laufen aber wir legen Sie bereits jetzt an. Ingnore Value benötigen wir hier nicht, damit lassen sich Errormeldungen überspringen. Den Asynchron Modus belassen wir bei der Standardeinstellung TRUE, wodurch eine Abarbeitung der Anweisungen über einen Zyklus hinaus möglich ist. Bei Json Data geben wir an welche Daten aus dem JSON String wir benötigen – in unserem Fall durchsuchen wir den String nachher nach Keywörter. Und dann bekommen wir noch zwei Ausgänge einmal für Errormeldungen und einen BOOL Wert ob mind. 1 String die maximale String Größe überschritten hat. Damit ist der Funktionsbaustein zum Auswerten des JSON String auch fertig und wir können mit der State Machine weitermachen. Kurz zur Erinnerung wir wollen in drei bzw. vier Schritten, wenn man Error dazuzählt, die Daten beim Broker abholen und zuordnen. Wir haben also einen CASE OF der Enumeration eGetJsonData und wir beginnen mit dem erste Schritt, dem Initialisierungsschritt. Wenn die Verbindung zum Broker Aufgebaut ist und kein Error anliegt, starten wir mit xEnable den zuvor angelegten Baustein GetWALMData und springen in den nächste Schritt Fetch. Andernfalls, wenn xError TRUE ist oder xEnable jetzt nicht TRUE wird, springen wir in den Schritt Error. Der erste Schritt ist fertig. Mit dem zweiten Schritt „Fetch“ holen wir die Daten ab. Dann folgt die If Anweisung: Wenn eine Nachricht beim Broker abholbereit ist, wechselt xRecived von FALSE auf TRUE. Dann wandeln wir zunächst den Daten String mit der bereits angelegten Funktion der Web Client Bibliothek als Wide Stringe um. Nun Starten wir den eigentlichen Lesevorgang. Danach springen wir in den nächsten Schritt Assign oder wenn es Probleme gibt nach Error. Im Schritt Assign werten wir dann das gefilterten JSON Array aus und weisen den bereitgestellten Werten Variablen zu. Zunächst stoppen wir den Lesevorgang. Dann wollen wir den Zeitstempel auslesen, wofür wir aus der Bibliothek JSON Data die Methode Find First Value By Key, mit der wir den ersten Wert, eines bestimmten Schlüsselworts suchen können. Das Schlüsselwort für den Zeitstempel ist in diesem Fall tst, der Startindex ist 1 und wir legen den Wert in die Variable json Element Data TST. Aus diesem erzeugen wir einen String. Jetzt haben wir den Timestamp im Unix Format, also in Sekunden seit dem 1.1.1970 00:00:00. Das nur noch in date and Time konvergieren und wir haben einen lesbaren Zeitstempel. Kommen wir nun zu den Kern- Daten. Wenn wir uns den String nochmal anschauen, kommt zuerst das Keyword: „vars“ anschließend folgt die Auflistung der Digitaleingänge. Damit ist für unsere Datenauswertung das Keyword „vars“ der Startpunkt. Wir suchen also mit der Methode Find First Element by Key nach “vars” und speichern uns das Element in Json Element Data ab. Jetzt wird ab der Stelle, wo „vars“ gefunden wurde, nach allen Elementen mit dem Schlüssel "val" gesucht. Die gefundenen Elemente werden in das Array aResultArray geschrieben. iMax gibt an, wie viele Ergebnisse maximal gespeichert werden dürfen. Im Falle des CC100 haben wir 8 digitale Eingänge also legen wir eine Konstante Variable mit iMax gleich 8 an. udiResultSize enthält am Ende die tatsächliche Anzahl der gefundenen "val"-Elemente. Damit haben wir alle Adressen und Größen die wir benötigen um die Daten aus dem JSON zu holen. Im JSON String besteht jeder digital Eingang aus 5 Topics: name, expr, val, mode, tdur. Diesen Topics müssen wir jetzt eine theoretisch nicht bekannte Anzahl an Werte zuordnen. Dafür legen wir uns drei integer Variablen an und verwenden eine äußere und eine innere FOR- Schleife. Die äußere Schleife (j) geht durch die 5 Begriffe. Die innere Schleife (k) sucht für jeden Begriff alle passenden Werte im JSON String. Die gefundenen Werte werden in einer Art Tabelle mit den Elementen aus [j] und aus [k] gespeichert. Jetzt sollen die gefundenen Werte in eine Daten Strucktur übertragen werden. Also legen wir eine Struktur: aWALMData, mit den 5 Topics an. Das Array starte mit dem Namen, den wir als String deklariert haben, daher suchen wir uns den String Value, die Expression ist ein Integer daher Value li. Bei Value, dem dritten Element, haben wir das Problem, das der JSON String das Keyword „Val“ mehrfach verwendet. Einmal hier oben und dann bei jedem Eingang. Also benötigen wir immer das Value Element, das einmal mehr als in „i“ gefunden wurde. Einmal plus 1 und wir haben das richtige Element. Die weitern beiden Topics folgen dann wie gehabt. Die Schleifenkombination läuft jetzt so oft durch, bis alle gefundenen Werte zugeordnet sind. Alle Werte liegen geordnet im Array aWALMData und wir können den JSON String löschen bzw. leeren. Würde wir das an dieser Stelle nicht machen, würde jeder neu String angereiht und der ojsonData würde immer größer und größer. Danach springen wir wieder in den Schritt Fetch und warten auf den nächsten String. Zum Schluss noch das Error Handling und wir können unsere Applikation Testen. Schalten wir den ersten digitalen Eingang am CC100 auf TRUE, wird auch das entsprechende Bit im Array WALMData im CODESYS Programm TRUE. Das könnten wir auf einen Ausgang Mappen oder wie hier in einer Visualisierung anzeigen. Wie man sieht, kann die Bearbeitung eines JSON Strings einfach und kurz sein, wie im Beispiel des Zeitstempels oder man muss doch einiges an Arbeit, Tricks du Know How investieren bevor man zu den gewünschten Ergebnissen kommt. Je nachdem wie der JSON String aufgebaut ist und welche Topics man aus diesen benötigt. Natürlich, wie immer in der Programmierung, gibt es auch andere Wege, Methoden oder Bibliotheken, um die Daten auszuwerten. Dieses Beispiel ist da auch nur eine Möglichkeit von vielen, schreibt uns gerne welches eure bevorzugte Methode ist. Damit sind wir am Ende vom Tutorial wie man in CODESYS 3.5 über MQTT bereitgestellte Daten als Subscriber abholt und auswertet. Hinterlasst uns gerne einen Daumen hoch, wenn euch das Video geholfen hat, abonniert den Kanal und meldet euch im WAGO Support Center wenn ihr noch Fragen habt.