Communication - MQTT in practice
The MQTT protocol is interesting from several perspectives. It is relatively simple to implement and low on PLC resources - compared to others such as OPC UA. Unlike most other protocols, it is not a client-server protocol, but a client-broker protocol. In this case, the PLC and SCADA clients are both clients of a central server called broker, whose role is to exchange messages between clients and other auxiliary functions (authentication, access rights control, logging).
Such a configuration has additional advantages for PLCs. First, the PLC is not burdened with multiple parallel communications - all data is sent to the broker only once, which then relays it to the interested parties. Secondly, all names and passwords (or certificates) are managed centrally by the broker.
The MQTT client uses the "report by exception" principle for data reporting, which is basically the change principle used in other protocols (IEC-101, IEC-104, OPC, OPC UA). This means an additional saving of PLC resources compared to the standard polling used e.g. by the Modbus protocol.
MQTT in D2000
The MQTT protocol was implemented in D2000 in 2017 as a by-product of the LoRaWAN protocol implementation. Devices using the low-power LoRaWAN protocol communicated either directly with the IoT gateway or with the cloud infrastructure (loriot.io, loralink.slovanet.sk or TheThings.Network), from where the D2000 KOM process read them using the MQTT protocol.
Configuration
Some time ago I had the opportunity to configure MQTT communication. The other side was a PowerShaper battery system from PIXII, which contains an MQTT server (broker).
The communication uses TLS encryption - from PIXII we also received the server certificate (pixii.crt file) and the client public key (mqtt_client.crt) and private key (mqtt_client.key) that we had to use in the communication.
First of all, the firewalls had to be addressed. For testing of the communication, the MQTT server was "published" on the Internet using address translation (NAT) on a static public IP address, on the standard TCP port 8883, which is reserved for encrypted MQTT. So we asked our network admin to allow access from the dedicated application server to this port.
Since this is a TLS secured communication and the D2000 KOM process supports only the unencrypted version, we installed the stunnel utility on the application server in the standard directory C:\Program Files (x86)\stunnel.
We copied the supplied certificate and keys to the config subdirectory and modified the stunnel.conf configuration file located there.
In the figure you can see the contents of the configuration file. At the beginning, the highest logging level (7) is enabled and a log file is defined (by default, it is also created in the config subdirectory).
Then the mqtt service is defined. Stunnel should listen on port 1883 on the local interface and connect to the IP address and port specified by the connect parameter (the IP address in the figure is not real 😊) after the client (D2000 KOM process) is connected.
The next lines define the server certificate (CAfile), the public and private keys for the stunnel (cert/key) used to authenticate against the MQTT broker.
Using the telnet utility (telnet localhost 1883), we verified the functionality of the connection.
Next, we will continue in the D2000 CNF environment. We have created a new L.Battery line of TCP/IP-TCP type and set it to connect to local port 1883, where stunnel listens.
We didn't configure anything in the line protocol parameters - not even a username and password were needed, since TLS level is used for authentication.
On this line we have created the B.Battery station. On it we set up the MQTT Client Protocol communication protocol and on the Address tab we entered the address #, which means that messages with arbitrary topic sent by the MQTT broker will be routed to the station.
At this station we created 5 I/O tags with addresses according to the MQTT protocol documentation:
- A text I/O tag with address IN_TOPIC will contain the topic of the received message.
- A text I/O tag with address IN_DATA will contain the text data of the received message.
- An integer I/O tag with address IN_ID shall contain the numeric ID of the received message. If the acknowledgement level is at least 1 (the default value of the Subscribe QoS parameter configurable in the line parameters is QoS 1), this is a 16-bit integer. If the QoS acknowledgement level was 0, the I/O tag would have a value of 0.
If the acknowledgement level is higher and there is also an integer the I/O tag with an ACK_ID address, the D2000 KOM process expects to acknowledge the processing of each message by writing the value of the IN_ID tag to the ACK_ID tag. Thus, it is possible to achieve that the message is acknowledged to the MQTT broker only after it has been processed in the D2000 (e.g. parsed and stored in the database). If no such the I/O tag exists, the message is acknowledged as soon as it is received.
- Output text I/O tags with addresses OUT_TOPIC and OUT_VALUE will be used to send commands.
Communication got off to a smooth start. Figure 5 shows the current value of the I/O tag M.Battery.IN_DATA - it is clear that the data (payload) is a JSON string. In order to avoid colleagues having to deal with parsing it in an ESL script, we implemented the Payload Type line parameter in the MQTT protocol. The default value of the parameter is Text only - in this case the D2000 KOM process does not parse the data further. But if we change the parameter value to JSON, the D2000 KOM will parse the data as a JSON string and it is possible to define I/O tags with addresses JA=address, where JA means "JSON address" and address is the address of the item in the JSON data.
In the future, additional ways of parsing and addressing I/O tags can be added as needed (e.g. for XML, CSV or Protocol Buffers formats used by MQTT Sparkplug).
We have created separate stations for each Topic. The following figure shows the configuration of the I/O tags for the status of a specific battery. The topic of message has a hierarchical structure - it contains the company name, the serial number of the battery and the information that this is its status: pixii/200599000012/status/battery.
To make this station "come alive", we changed the address of B.Battery from "#" to ".*". This station receives "leftover" messages whose subject does not match the address of any other station.
Since it can sometimes be convenient to handle different topics within a single station, we also supported interpreting the station address as a regular expression. Thus, if the topic is not the same as the address of any station, the stations are searched again, but this time their address is interpreted as a regular expression. If no match is found in this way either, the station with the address ".*" is used as the "last resort".
My colleague then set the Publish QoS link parameter to QoS 1 (so that our commands are acknowledged) and tried controlling the battery. When writing, it is necessary to first write to the I/O tag with the address OUT_TOPIC (if the topic is repeated, just once write is enough). Then, writing to the I/O tag with the address OUT_VALUE will work as a trigger and cause a PUBLISH message to be sent.
In the trace file of the line it was possible to see the sending of the PUBLISH message with the Id 12 and its acknowledgement with the PUBACK message.
Conclusion
Due to its simplicity and low cost, the MQTT protocol is starting to gain traction not only in the IoT field, but also in "home automation" and is gradually being pushed into more industrial applications as well. The advantage is that a central broker can distribute the messages of a single device to multiple subscribers, centrally addressing authentication, user management, access rights, logging, and so on. Further deployments and extensions of the MQTT protocol in the Ipesoft D2000 real-time application server are highly likely to follow.
November 2, 2021, Ing. Peter Humaj, www.ipesoft.com