Complete guide: Modbus TCP and RTU on Raspberry PLCs, with Python and Node-RED
To use Modbus on a Raspberry PLC with CODESYS, add the Modbus devices to the device tree of the project: under Ethernet, a Modbus TCP Client (master) with one Modbus TCP Server per remote device, or a ModbusTCP Server Device if the PLC must answer a SCADA. For Modbus RTU on RS-485, add a Modbus COM port with a Modbus client below it and one Modbus server per slave address (1 to 247). Each request is a channel: function code, start register, length and cycle time, mapped to variables in the I/O mapping tab. Modbus TCP and serial master and slave are delivered with the CODESYS Control for Raspberry Pi SL runtime package.
This guide continues Setting up CODESYS for Raspberry Pi PLC in industrial automation. Before you start you need CODESYS V3.5 SP19 Patch 3 or later on a Windows PC, CODESYS Control for Raspberry Pi SL 4.11 or later on the PLC, and a working login (the PLC answers at 10.10.10.20 on its side Ethernet port by default). Newer CODESYS versions call the Modbus roles client and server; older ones say master and slave. The menus are the same.
Which CODESYS device do you need?
| The Raspberry PLC must... | Device to add | Insert it under |
|---|---|---|
| Read or write a Modbus TCP device (meter, drive, another PLC) | Modbus TCP Client, then one Modbus TCP Server per device | Ethernet |
| Answer a SCADA, HMI or other master over Ethernet | ModbusTCP Server Device | Ethernet |
| Read or write RS-485 field devices with Modbus RTU | Modbus COM port, Modbus client, one Modbus server per address | The PLC (Device) |
| Answer an RTU master on RS-485 | Modbus COM port and a Modbus server device below it | The PLC (Device) |
A Modbus client in CODESYS can have up to 64 servers below it.
Modbus TCP client (master): read a remote device
- In Devices, right-click the PLC and add an Ethernet adapter if the project does not have one. In its settings, choose the network interface of the PLC (
eth0is the side port). - Right-click Ethernet > Add Device > Fieldbuses > Modbus and add the Modbus TCP Client (Modbus TCP Master in older versions). Keep Auto-reconnect enabled so the client connects again after a network cut.
- Right-click the client and add a Modbus TCP Server (Modbus TCP Slave) for each remote device. Set its IP address, port 502 and the Unit ID that the device expects (often 1, or 255 for some TCP-only devices).
- In the channel tab of the server, click Add Channel and choose the access type, for example Read Holding Registers (function code 03), the trigger (cyclic, with its cycle time in ms), the start register offset and the length.
- In the I/O mapping tab, give a variable name to each register, or map it to a variable that already exists in your program.
Modbus function codes and data types in a channel
| Function code | Access | Data | Map it to |
|---|---|---|---|
| 01 Read Coils | Read | Bits that the master can also write | BOOL |
| 02 Read Discrete Inputs | Read | Input bits | BOOL |
| 03 Read Holding Registers | Read | 16-bit registers: setpoints, configuration | WORD or INT |
| 04 Read Input Registers | Read | 16-bit measured values | WORD or INT |
| 05 / 15 Write Single / Multiple Coils | Write | Bits | BOOL |
| 06 / 16 Write Single / Multiple Registers | Write | 16-bit registers | WORD or INT |
A Modbus register has 16 bits. A value with decimals usually comes as an integer with a scale (215 for 21.5 °C) or as a 32-bit float in two consecutive registers. Read the register map of the device: it gives the address, the scale and, for 32-bit values, the word order.
Use the mapped values in the program
Suppose a temperature transmitter sends the temperature × 10 in holding register 0. Map the register to iTempRaw (INT) in the I/O mapping tab and scale it in Structured Text:
VAR
iTempRaw : INT; // mapped to holding register 0 (temperature x 10)
rTemperature : REAL;
bOverheatAlarm : BOOL;
END_VAR
rTemperature := INT_TO_REAL(iTempRaw) / 10.0;
bOverheatAlarm := rTemperature > 75.0;
CODESYS updates the mapped variables with the bus cycle task, and each channel sends its request at its own cycle time. A cycle time shorter than the time the device needs to answer only fills the bus: start with 100 ms or more and lower it if the process needs it. This code is an example for this post and has not been run on a PLC.
Modbus TCP server: let a SCADA read the PLC
- Right-click Ethernet > Add Device and add the ModbusTCP Server Device (ModbusTCP Slave Device in older versions).
- In its configuration, keep port 502, set the Unit ID that the master will use and the number of coils, discrete inputs, holding registers and input registers that the PLC exposes.
- In the I/O mapping tab, link those areas to variables of your program. The master reads and writes them at the configured addresses.
Write the register map down for the SCADA team: address, data type, scale and units of each value.
Modbus RTU on the RS-485 port
On the Raspberry PLC, the RS-485 ports are /dev/ttySC0 to /dev/ttySC3, depending on the version (list them with ls /dev/ttySC*). CODESYS on Linux names serial ports COM1, COM2 and so on, and takes the device file from the [SysCom] section, entry Linux.Devicefile, of /etc/CODESYSControl_User.cfg. Check which COM number points to the RS-485 port of your PLC before you configure the master.
- Right-click the PLC > Add Device > Fieldbuses > Modbus and add the Modbus COM port. Set the COM port number, the baud rate, the parity, the data bits and the stop bits. They must be the same in every device on the bus.
- Right-click the COM port and add the Modbus client (Modbus Master, COM Port in older versions). Set the response timeout.
- Below the client, add one Modbus server (slave) for each device, with its address from 1 to 247, and configure its channels and I/O mapping as in the TCP client.
To make the Raspberry PLC an RTU slave, add the Modbus server device below the COM port instead, with the address that the master will use, and map your variables to its register areas.
Troubleshooting Modbus in CODESYS
| Symptom | What to check |
|---|---|
| Modbus TCP server does not answer | Ping its IP from the PLC, check port 502 and the Unit ID |
| Modbus RTU device does not answer | RS-485 wiring A+ to A+ and B- to B-, termination at both ends of the bus, same baud rate and parity, the correct COM port, the slave address |
| Values are shifted by one register | 0-based or 1-based addresses: register 40001 in a datasheet is offset 0 in the channel |
| 32-bit values make no sense | Word order of the device: swap the two registers |
| Variables do not update | The application is running (not only logged in), the device status in Devices, the messages of the device, the channel cycle time and the response timeout |
| The runtime stops after two hours | CODESYS Control for Raspberry Pi SL is in demo mode without a license |
FAQ about Modbus with CODESYS on a Raspberry PLC
Do I need an extra license for Modbus in CODESYS?
CODESYS lists Modbus TCP master and slave and Modbus serial master and slave as fieldbuses delivered with the CODESYS Control for Raspberry Pi SL runtime package. Without a runtime license, the runtime works for two hours and then stops.
What is the difference between Modbus client and master?
None in practice. Newer CODESYS versions use client and server; older ones use master and slave.
Can the PLC be Modbus TCP server and RTU master at the same time?
Yes. Each device in the tree works on its own, so a Raspberry PLC can read RS-485 devices and serve the values to a SCADA over Ethernet, as a gateway.
How fast can CODESYS poll Modbus?
It depends on the baud rate, the number of registers and the response time of each device. Set the cycle time of each channel to what the process needs, not to the minimum.
Can I test Modbus without the field devices?
Yes, with a Modbus simulator on a PC, or in the Modbus network simulator of the PLC Solution Library.
More about CODESYS and Modbus on the Raspberry PLC
- Setting up CODESYS for Raspberry Pi PLC in industrial automation
- Ladder programming with CODESYS on a Raspberry PLC
- Modbus TCP and RTU with Raspberry Pi PLCs: Python and Node-RED examples
- How to use PyModbus with a Raspberry Pi PLC
- Choosing an industrial controller
- OpenPLC guide for industrial PLCs
See all the models in the Raspberry PLC family.
Take it further
From the Modbus setup in CODESYS to real work: a Modbus gateway on a ship, a network simulator and a register map for a plant.
Case study
Propulsion data from every ship. Without touching the control.
A Linux PLC on board reads the propulsion control over Modbus TCP and RTU, listens to NMEA 2000 and sends the data to shore over MQTT.
Read the case study
Simulator
Run a Modbus RTU and TCP network and find the fault
An M-Duino 21 polls four Modbus RTU slaves on RS-485 and serves the data on Modbus TCP. Switch a slave off or change the baud rate and find the fault in the frames.
Open the simulator
PLC Solution Library
A Modbus TCP register map for the whole plant
The map an M-Duino exposes on port 502: coils for relays and resets, holding registers for setpoints and commands, input registers for live values.
See the example