IotMqttSampleUsingCallback

Sample for MQTT communication via a callback method

This sample illustrates the communication with an MQTT broker. Messages are sent (publish mode) and received. This is done in two steps. First, a general decision is made on which types of messages are to be received ("Subscribe"). Subsequently, new messages are received via a callback method during the cyclic call of the FB_IotMqttClient.Execute() method.

Project structure

1. Create a TwinCAT project with a PLC and add Tc3_IotBase as library reference.
2. The callback method, in which the received MQTT messages are provided, should be implemented by users themselves. The inheritance principle is used to ensure that the TwinCAT driver can call this method. First, create a function block and let the function block FB_IotMqttClient inherit it. Part of the MQTT communication can already be encapsulated in this function block. In the sample, received messages are evaluated here. It is therefore advisable to declare variables for topic and 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. Create the method OnMqttMessage() and overwrite the basic implementation.
IotMqttSampleUsingCallback 1:
4. The method with the implementation to be carried out by the user is not called in the application, but implicitly by the driver. This callback takes place during the cyclic triggering of the client and can take place either not at all, once or several times, depending on the number of messages received since the last trigger. This sample only implements a simple evaluation, as shown in the following code snippet.
{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. The other steps are similar to the sample MQTT communication via a message queue.
Create a program block and declare an instance of the previously declared function block FB_MyMqtt and two auxiliary variables to control the program sequence, if required.
PROGRAM PrgMqttCom
VAR
    fbMqttClient  : FB_MyMqtt;
    bSetParameter : BOOL := TRUE;
    bConnect      : BOOL := TRUE;
END_VAR
6. Declare two variables (for topic and payload) for the MQTT message to be sent. In the sample a message is to be sent every second.
    (* published message *)
    sTopicPub : STRING(255) := 'MyTopic';
    sPayloadPub : STRING(255);
    i: UDINT;
    fbTimer : TON := (PT:=T#1S);
7. To receive messages, declare a variable that contains the topic to be received.
    bSubscribed : BOOL;
    sTopicSub   : STRING(255) := 'MyTopic';
8. In the program part, the MQTT client must be triggered cyclically, in order to ensure that a connection to the broker is established and maintained and the message is received. Set the parameters of the desired connection and initialize the connection with the transfer parameter bConnect := TRUE. In the sample the parameters are assigned once in the program code before the client call. Since this is usually only required once, the parameters can already be specified in the declaration part during instantiation of the MQTT client. Not all parameters have to be assigned. In the sample the broker is local. The IP address or the name can also be specified.
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. As soon as the connection to the broker is established, the client should subscribe to a particular topic. A message should be sent every second.
In the sample sTopicPub = sTopicSub applies, so that a loop-back occurs. In other applications the topics usually differ.
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

Requirements

Development environment

Target platform

PLC libraries to include

TwinCAT v3.1.4022.0

IPC or CX (x86, x64, ARM)

Tc3_IotBase,
Tc2_Utilities (>= v3.3.19.0)