Put MQTT in the controller with a store library when one PLC talks to one local broker over plain TCP. Put it in a Node-RED gateway when you need TLS, several brokers or JSON payloads, and let the PLC keep serving plain registers over Modbus TCP. The 30-minute demo window and the per-CPU license are what usually decide it.
CODESYS has no MQTT client in the runtime. You add one of two things: a library that runs inside the controller and opens the broker connection itself, or a Node-RED gateway that exchanges Modbus TCP data with the PLC and owns every broker connection. The deciding figures are known before you write a line of code. The store libraries run 30 minutes in demo mode and then want a license for every CPU that executes them. The gateway wants a separate box, but it puts TLS, multiple brokers and payload strings where they are cheap. One controller, one local broker, plain TCP: the library. TLS, several brokers, several machines, JSON: the gateway.
The library route: MQTT inside the controller
A store package puts the MQTT stack, an OASIS messaging standard, into the IEC 61131-3 program. The CODESYS IIoT Libraries SL package bundles MQTT Client SL, which sends messages from the controller to a broker and subscribes to topics. The Janz Tec MQTT library for CODESYS implements the same protocol for your own application code. Both are add-ons: you add the library reference, check target compatibility, and license what runs.
Two figures shape the choice. Without a license, the Janz Tec library runs 30 minutes in demo mode and then stops until it is licensed. The license is per CPU: three controllers publishing means three licenses. Capability also follows the revision. The early Janz Tec revision came without TLS and without a last-will message, and a later revision added SSL, so read the package page for the revision you install before you promise anyone an encrypted connection.
There is a free route too. An open-source CODESYS MQTT client on GitHub lists support for all QoS levels from 0 to 2, JSON helpers and monitoring of the client itself. Free means you carry the integration yourself, and the same rule applies: confirm what the installed revision supports before you design around it.
Whichever package you take, give the client one owner. Call it from one POU on one task, and let the rest of the program hand publish requests to that owner. MQTT keeps connection state, acknowledgments and a receive buffer, and one cyclic call site holds those in a defined order. Publish at the rate the data changes, for example once per second, and keep string work out of the faster logic or motion tasks. String handling is among the heavier things an IEC 61131-3 scan can do, and a JSON payload rebuilt in a 10 ms task takes scan time away from the machine. When the library wants the payload as raw bytes, pack floating point values the way sending a REAL value in a 12-byte TCP telegram shows.
The gateway route: Modbus TCP in, MQTT out
Node-RED runs on a small Linux box on the plant network. The controller keeps a standard Modbus TCP server and maps the values you want to publish into holding registers. Node-RED polls them with a Modbus read node on an interval, or the PLC pushes them with function code 16, write multiple registers, and an mqtt out node publishes to the broker. Conversion to text happens in a JavaScript function node on the gateway, where string work costs the scan nothing.
The MQTT nodes support SSL, so the encrypted leg ends at the gateway and the PLC side stays plain Modbus TCP inside your firewall. Each mqtt out node carries its own broker configuration, so one gateway publishes to several brokers at once: a local one on 1883 for the plant dashboard and a cloud one on 8883 for the enterprise side, from the same flow.
Test the two legs separately. Watch the register values in the gateway's debug pane first. If the numbers arrive as 0, you have the classic register mapping fault covered in fixing CODESYS showing 0 for Modbus TCP analog values. Pack floating point values as two registers and scale on the gateway, the way converting a CODESYS REAL into two Modbus WORD registers shows. And latch events in a register before you poll them: a poll every 1 s sees a counter that changed twice within that second only once, so write alarms and batch counts as held values, never as pulses.
Side by side
| PLC library | Node-RED gateway | |
|---|---|---|
| Where MQTT runs | Inside the controller | On a separate Linux box |
| License model | Per CPU, 30-minute demo before it | None on the PLC; it serves registers |
| TLS to the broker | Revision dependent; check the installed package | Yes, in the MQTT node configuration |
| Several brokers | Depends on the library's client support | Yes, one connection per broker config |
| Payload conversion | String building inside the scan | JavaScript on the gateway |
| Extra hardware | None | One box, one power feed, one switch port |
| Hops from PLC to broker | 1 | 2: PLC to gateway, gateway to broker |
| Failure visibility | Client diagnostics in the PLC | Two places: Modbus status and broker status |
Where the library wins
One network leg. The controller is the MQTT client, so there is no gateway to power, cool, back up or replace, and nothing between the program and the broker but a switch. Event publishing: the library can publish in the scan that raises the event, so the alarm message leaves with the alarm. Visibility: the connection state and client diagnostics sit in the PLC, where the people who maintain the line already look. Use it when one controller talks to one broker on your own network, over plain TCP, and the payload is a few fixed fields.
Where the gateway wins
TLS without shopping for revisions: the MQTT nodes take the certificate configuration, and every cloud IoT platform that accepts only encrypted MQTT on 8883 is reachable. Several brokers from one flow. Conversion off the controller: JSON built in JavaScript, not in the scan. And the license math: one gateway serves every controller on the network, where the library route bills one license per CPU. Three lines publishing is either three licenses or one box.
The 30-minute signature
When a library client stops almost exactly 30 minutes after the program starts, that is the demo window ending, and the fix is a purchase, one license per CPU. A client that stops after hours instead is a different problem, and why a SysLibSockets TCP client stops after hours covers the socket-level causes. Separate the two before you change anything, because one fix is licensing and the other sits in the TCP connection handling.
Worked example: three lines, one cloud dashboard
Three packaging lines, each with a controller, each publishing 10 counter values once per second to a cloud dashboard that accepts only TLS on port 8883.
Library route: 3 licenses, one per CPU. Each controller opens its own TLS session and builds a 10-field JSON payload once per second. At about 20 characters per field, one message is 10 x 20 = 200 characters, and the three controllers together build 3 x 200 = 600 characters of JSON every second inside scans that also run the machines.
Gateway route: one Linux box. Each controller maps its 10 counters into 10 holding registers. The gateway polls 10 registers per line per second: 10 registers x 2 bytes = 20 bytes of register data per read, 3 lines x 20 bytes = 60 bytes per second on the LAN, and 3 x 86,400 = 259,200 small reads per day. The same 600 characters per second of JSON are built in JavaScript on the box. One TLS session to the cloud, or three if the dashboard wants separate client IDs. Zero PLC licenses, and a fourth line is a register map and a flow, not a fourth license.
For this plant the gateway wins on every figure that matters: the TLS requirement, the license count, and the string load.
The decision
- One controller, one broker on the plant network, plain TCP on 1883, a few fixed fields: add the library, or the open-source client if you accept carrying the integration. Demo it inside the 30-minute window, then license the CPU.
- Two or more controllers, a broker that requires TLS, JSON payloads, or several broker destinations: the Node-RED gateway. Latch events in registers so the 1 s poll cannot miss them.
- Both at once is legitimate: a machine that must publish alarms from the scan carries the library on that one CPU while the counters ride the gateway.
- If the same readings also belong on a screen at the line, the shop-floor displays section covers units that run your own Android app, so the sign can read the same broker the dashboard does.
Recommendation: choose the Node-RED gateway when the broker connection needs TLS or serves more than one controller; choose a PLC library when a single controller feeds a single local broker and you want no extra hardware between them. License every CPU that runs the library before the machine works a full shift, and treat a stop at 30 minutes as the demo window, not a fault.
Questions
Does the CODESYS runtime include an MQTT client?
No. The release notes for CODESYS V3.5 SP13 list MQTT client support, but the client ships as a store package you add to the project and license, not as a runtime built-in. Before you plan a machine around it, check the package's library references and target compatibility for your controller.
Which TCP ports does MQTT use?
A broker normally listens on 1883 for plain TCP and 8883 for TLS. The client dials out to that listener, so no connection has to reach back to the controller for MQTT to work. If the connection times out, confirm the port number and the listener configuration before you touch the client code.
How do I test a library past the 30-minute demo window?
License it. A client that stops almost exactly 30 minutes after start is the demo window ending, which looks like a network drop but is a licensing state. Order one license per CPU that will run the library, then test the full publish path again once the license is active.
Can a free CODESYS MQTT client subscribe to several topics?
The open-source client on GitHub lists subscriptions with all QoS levels from 0 to 2. An older free client worked another way: the publish topic was changed before each publish, and receiving several topics meant one wildcard subscription. Capabilities differ by package and revision, so read the page of the one you install.
Planning information, not legal or engineering advice. Rules differ by place: check codes and permits with your local authority and your licensed trades.