TwinCAT and MQTT – Part 2 JSON messages

In TwinCAT and MQTT – Part 1 Getting started we covered the basics for MQTT in TwinCAT. Using the Mosquitto broker and two PLC’s we were quickly able to exchange messages between the PLC’s. Secondly we read the messages with a smartphone.

In part 1 we published a simple string, indicating the room temperature. However in real life data soon becomes more complex than a single temperature and you quickly want to send more data and parameters in one message. Composing such strings is hard, but parsing it back to actual data is even harder. Luckily we can make use of one of the world most popular data formats : ‘JSON‘. In this post I show how to publish and receive more complex data structures using JSON.

Readers note: This post continues where part 1 stopped so make sure you have read this before diving in to this topic.

Publish a JSON message with TwinCAT

The message we are gonna publish will be a ST data structure (STRUCT). For this example we’ll use the the following STRUCT:

TYPE TemperatureStructure :
    STRUCT
        RoomTemperature : REAL;
        BathRoomTemperature : REAL;
        SomeOtherData : DINT;
    END_STRUCT
END_TYPE

After adding the TemperatureStructure we need to add some variables to the main program:

TopicToPublishJson   : STRING(255) := 'Temperatures_JSON'; // Mqtt topic on which we will broadcast the acual temperatures in JSON format
MessageToPublishJson : STRING(255); //String to send in JSON format
fbJsonWriter : FB_JsonSaxWriter;
fbJsonDataType : FB_JsonReadWriteDataType;
TemperatureStructure:TemperatureStructure; 	

The new variable ‘TopicToPublishJson ‘ contains the topic we will public the JSON data on. A data string ‘MessageToPublishJson‘ will contain the formatted JSON message. The function blocks ‘fbJsonWriter‘ and ‘fbJsonDataType‘ are needed to convert ‘TemperatureStructure’, our temperature structure, to a JSON string.
Note, the TC3_JSONXML library has to be added to your project as reference.

To publish the messages  I used the same program as in part 1 but added the JSON messages as second topic:

IF fbMqttClient.bConnected THEN
    fbSendMessageIntervalTimer(IN:=TRUE);
    IF fbSendMessageIntervalTimer.Q THEN
        fbSendMessageIntervalTimer(IN:=FALSE);

        // Code to send non JSON message removed for clarity.
        // Fill data
        TemperatureStructure.RoomTemperature := ai_RoomTemperature / 10.0;
        TemperatureStructure.BathRoomTemperature :=TemperatureStructure.RoomTemperature + 0.5; // No second temperature available, so this simulation value will do.
        TemperatureStructure.SomeOtherData := 33;// Random data.

        //Convert to JSON string
        fbJsonWriter.ResetDocument();
        fbJsonDataType.AddJsonValueFromSymbol(fbJsonWriter, 'TemperatureStructure', SIZEOF(TemperatureStructure), ADR(TemperatureStructure));
        MessageToPublishJson := fbJsonWriter.GetDocument();

        //Publish
        fbMqttClient.Publish(sTopic:= TopicToPublishJson,
        pPayload:= ADR(MessageToPublishJson),
        nPayloadSize:= LEN2(ADR(MessageToPublishJson))+1,
        eQoS:= TcIotMqttQos.AtMostOnceDelivery,
        bRetain:= FALSE,
        bQueue:= FALSE);
     END_IF
END_IF

Publishing the JSON message is pretty straight forward. First the temperature structure get’s some demo data. After that it’s TC3_JSONXML library doing the hardwork. The function block ‘fbJsonWriter‘ reset it’s internal document. Then then ‘fbJsonDataType‘ parses the temperature structure and adds it to the JSON document in ‘fbJsonWriter‘. The last step is retrieving the JSON data from the ‘fbJsonWriter‘ and store it in ‘MessageToPublishJson ‘.

Publishing the message goes the same as in part 1. But the message is published with a different topic: ‘Temperatures_JSON’.

Running this program will publish a JSON message with the following JSON format:

{
   "RoomTemperature":19.0,
   "BathRoomTemperature":19.5,
   "SomeOtherData":33
}

Success! our first MQTT message in Structured Text with JSON 🙂 .

Subscribe on a topic JSON with TwinCAT

Ok, we published a JSON message but how do we receive it? In our second PLC project we are gonna subscribe on the messages published by first PLC project. (Part 1 for details.)

Add the TC3_JSONXML library to the receiving PLC project and add a copy of the of the ‘TemperatureStructure’ STRUCT. Yes in real life it is good practice to create an library containing common data structures.

In the declaration part of  the main program add the following variables:

TopicToSubscribeJSon   : STRING(255) := 'Temperatures_JSON'; // Mqtt topic on which we will subscribe for JSON messages.
messageJsonString:STRING(255);
TemperatureStructure:TemperatureStructure;
fbJsonDataType : FB_JsonReadWriteDataType;

The received JSON string will be stored in ‘messageJsonString’ . With the ‘fbJsonDataType‘ function block the JSON data back can be parsed and copied to the the STRUCT : ‘TemperatureStructure‘.

After adding the variables we have to subscribe to the second topic ‘Temperatures_JSON’ on which the JSON messages will be published:

IF fbMqttClient.bConnected THEN
    IF NOT Subscribed THEN
        Subscribed := fbMqttClient.Subscribe(sTopic:=TopicToSubscribe,
			eQoS:=TcIotMqttQos.AtMostOnceDelivery);
			
        Subscribed := Subscribed AND fbMqttClient.Subscribe(sTopic:=TopicToSubscribeJSon,
			eQoS:=TcIotMqttQos.AtMostOnceDelivery);
    END_IF
END_IF

To parse the received data back it we first need to distinguish between the different received topics. The JSON messages can then be parsed back to temperature structure using the ‘fbJsonDataType‘ function block. The following code implements these features:

IF fbMessageQueue.nQueuedMessages › 0 THEN
    IF fbMessageQueue.Dequeue(fbMessage:=fbMessage) THEN
        fbMessage.GetTopic(pTopic:=ADR(ReceivingTopic), nTopicSize:=SIZEOF(ReceivingTopic) );
        IF ReceivingTopic = TopicToSubscribeJSon THEN
            fbMessage.GetPayload(pPayload:=ADR(messageJsonString), nPayloadSize:=SIZEOF(messageJsonString), 
            bSetNullTermination:=FALSE);
            fbJsonDataType.SetSymbolFromJson(messageJsonString, 'TemperatureStructure', SIZEOF(TemperatureStructure), ADR(TemperatureStructure));
        ELSE
            fbMessage.GetPayload(pPayload:=ADR(ReceivingData), nPayloadSize:=SIZEOF(ReceivingData), 
            bSetNullTermination:=FALSE);
        END_IF
    END_IF
END_IF

And that was it! We now have a publishing and subscribing PLC in Structured Text, composing JSON messages. Running both project gives the following result:

Conclusion

Publishing JSON messages over MQTT is a good and quick way to send more complex data messages. The TwinCAT build in library TC3_JSONXML does als the hard work like compiling and parsing ST STRUCTS. The sample project can be found on my Github.

In part 3 of TwinCAT and MQTT  we’ll discuss several advanced  MQTT topics such as callbacks and last wills.

Until then:

Happy coding 🙂 .

 

Gerhard Barteling

Gerhard is a mechatronic engineer with a predilection for software engineering.