IotMqttSampleUsingCallback

Beispiel zur MQTT Kommunikation über eine Callback-Methode

In diesem Beispiel wird die Kommunikation zu einem MQTT Broker dargestellt. Es werden Nachrichten verschickt („Publish“) und empfangen. Dies erfolgt in zwei Schritten. Zuerst wird entschieden, welche Nachrichten generell empfangen werden sollen („Subscribe“). Anschließend werden, während dem zyklischen Aufruf der FB_IotMqttClient.Execute()-Methode, neue Nachrichten über eine Callback-Methode empfangen.

Projektstruktur

1. Erstellen Sie ein TwinCAT-Projekt mit einer SPS und fügen Sie die Tc3_IotBase als Bibliotheksreferenz an.
2. Die Callback-Methode, in der die empfangenen MQTT-Nachrichten bereitgestellt werden, möchten Sie selbst implementieren. Damit der TwinCAT-Treiber diese Methode aufrufen kann, wird das Prinzip der Vererbung genutzt. Legen Sie zunächst einen Funktionsbaustein an und lassen Sie diesen von dem Funktionsbaustein FB_IotMqttClient erben. In diesem Funktionsbaustein können Sie bereits Teile der MQTT-Kommunikation kapseln. Im Beispiel werden hier empfangene Nachrichten ausgewertet. Deklarieren Sie aus diesem Grund Variablen für Topic und Payload.
{attribute 'c++_compatible'}
FUNCTION_BLOCK FB_MyMqtt EXTENDS FB_IotMqttClient

VAR
    (* received message *)
    {attribute 'TcEncoding':='UTF-8'}
    sTopicRcv   : STRING(255);
    {attribute 'TcEncoding':='UTF-8'}
    sPayloadRcv : STRING(255);
END_VAR
3. Legen Sie die Methode OnMqttMessage() an und überschreiben Sie die Basisimplementierung.
IotMqttSampleUsingCallback 1:
4. Die Methode mit der von Ihnen vorzunehmenden Implementierung wird nicht in der Applikation aufgerufen, sondern implizit vom Treiber. Dieser Callback findet während des zyklischen Triggerns des Clients statt und kann je nach Anzahl der seit dem letzten Trigger empfangenen Nachrichten keinmal, einmal oder mehrfach erfolgen. Wie folgendes Code Snippet zeigt, implementieren Sie in diesem Beispiel nur eine einfache Auswertung.
{attribute 'c++_compatible'}
{attribute 'pack_mode' := '4'}
{attribute 'show'}
{attribute 'minimal_input_size' := '4'}
METHOD OnMqttMessage : HRESULT
VAR_IN_OUT CONSTANT
    topic    : STRING;
END_VAR
VAR_INPUT
    payload  : PVOID;
    length   : UDINT;
    qos      : TcIotMqttQos;
    repeated : BOOL;
END_VAR
VAR
    nPayloadRcvLen : UDINT;
END_VAR
-----------------------------------------------------------------------

SUPER^.nMessagesRcv := SUPER^.nMessagesRcv + 1;

STRNCPY( ADR(sTopicRcv), ADR(topic), SIZEOF(sTopicRcv) );
nPayloadRcvLen := MIN(length, DINT_TO_UDINT(SIZEOF(sPayloadRcv))-1);
MEMCPY( ADR(sPayloadRcv), payload, nPayloadRcvLen );
sPayloadRcv[nPayloadRcvLen] := 0;  // ensure a null termination of received string

OnMqttMessage := S_OK;
5. Die weiteren Schritte sind ähnlich zum Beispiel MQTT Kommunikation mithilfe einer Message-Queue.
Legen Sie einen Programmbaustein an und deklarieren Sie eine Instanz von Ihrem zuvor definierten Funktionsbaustein FB_MyMqtt sowie zwei Hilfsvariablen, um den Programmablauf bei Bedarf steuern zu können.
PROGRAM PrgMqttCom
VAR
    fbMqttClient  : FB_MyMqtt;
    bSetParameter : BOOL := TRUE;
    bConnect      : BOOL := TRUE;
END_VAR
6. Deklarieren Sie für die zu verschickende MQTT-Nachricht zwei Variablen für Topic und Payload. Im Beispiel soll jede Sekunde eine Nachricht verschickt werden.
    (* published message *)
    sTopicPub : STRING(255) := 'MyTopic';
    sPayloadPub : STRING(255);
    i: UDINT;
    fbTimer : TON := (PT:=T#1S);
7. Deklarieren Sie für den Nachrichtenempfang eine Variable, die das zu empfangene Topic enthält.
    bSubscribed : BOOL;
    sTopicSub   : STRING(255) := 'MyTopic';
8. Im Programmteil muss der MQTT Client zyklisch getriggert werden, um den Verbindungsaufbau zum Broker, den Verbindungserhalt und den Nachrichtenempfang zu gewährleisten. Setzen Sie die Parameter der gewünschten Verbindung und initialisieren Sie den Verbindungsaufbau mit dem Übergabeparameter bConnect := TRUE. Im Beispiel werden die Parameter einmalig vor dem Client-Aufruf im Programmcode zugewiesen. Weil dies meist nur einmalig nötig ist, können die Parameter auch bereits im Deklarationsteil bei der Instanziierung des MQTT Client angegeben werden. Nicht alle Parameter müssen zugewiesen werden. Im Beispiel ist der Broker lokal. Sie können auch die IP-Adresse oder den Namen angeben.
IF bSetParameter THEN
    bSetParameter              := FALSE;
    fbMqttClient.sHostName     := 'localhost';
    fbMqttClient.nHostPort     := 1883;
//  fbMqttClient.sClientId     := 'MyTcMqttClient'; 
    fbMqttClient.sTopicPrefix  := ''; 
//  fbMqttClient.nKeepAlive    := 60; 
//  fbMqttClient.sUserName     := ;
//  fbMqttClient.sUserPassword := ; 
//  fbMqttClient.stWill        := ; 
//  fbMqttClient.stTLS         := ;
END_IF

fbMqttClient.Execute(bConnect);
9. Sobald die Verbindung zum Broker aufgebaut wird, soll sich der Client auf ein bestimmtes Topic anmelden. Ebenso soll jede Sekunde eine Nachricht versandt werden.
Im Beispiel ist sTopicPub = sTopicSub, so dass ein Loopback entsteht. In anderen Applikationen unterscheiden sich die Topics typischerweise.
IF fbMqttClient.bConnected THEN
    IF NOT bSubscribed THEN
        bSubscribed := fbMqttClient.Subscribe(sTopic:=sTopicSub, eQoS:=TcIotMqttQos.AtMostOnceDelivery);
    END_IF
    fbTimer(IN:=TRUE);
    IF fbTimer.Q THEN // publish new payload every second
        fbTimer(IN:=FALSE);
        i := i + 1;
        sPayloadPub := CONCAT('MyMessage', TO_STRING(i));
        fbMqttClient.Publish(    sTopic:= sTopicPub, 
                                pPayload:= ADR(sPayloadPub), nPayloadSize:= LEN2(ADR(sPayloadPub))+1, 
                                eQoS:= TcIotMqttQos.AtMostOnceDelivery, bRetain:= FALSE, bQueue:= FALSE );
    END_IF
END_IF

Voraussetzungen

Entwicklungsumgebung

Zielplattform

Einzubindende SPS-Bibliotheken

TwinCAT v3.1.4022.0

IPC oder CX (x86, x64, ARM)

Tc3_IotBase,
Tc2_Utilities (>= v3.3.19.0)