Modbus RTU vs Modbus TCP: what actually differs, and which to use
Equipment manuals say "supports Modbus" and leave you to work out whether that means RTU or TCP. The relationship is simpler than the names suggest: the command you send is exactly the same, and only the wrapper around it changes. That single difference is what decides your wiring, your update rate, how many devices you can hang off one line, and how you debug it.
Same command, different wrapper
Modbus dates to 1979 and is deliberately plain: "read N registers starting at address A from device X" is most of it. RTU and TCP are two envelopes for that same letter.
Because of that, function codes are shared. When a manual says "read with 03, write with 16", it applies to both transports.
| Code | What it does | Target |
|---|---|---|
01 | Read coils | Output bits (on/off) |
02 | Read discrete inputs | Input bits (read-only) |
03 | Read holding registers | 16-bit values (the common one) |
04 | Read input registers | 16-bit values (read-only) |
05 / 06 | Write single coil / register | One value |
15 / 16 | Write multiple coils / registers | A contiguous block |
If you remember one line: **RTU checks itself with a CRC; TCP delegates that to TCP.** There is no CRC in a Modbus TCP frame at all — a classic source of confusion when reading a packet capture.
Wiring and topology
RTU usually runs over two RS-485 wires. Devices chain along one pair, which is cheap and reaches well past a kilometre at 9600 bps. The cost is that everyone shares the line, so the master can only talk to one device at a time.
- Fit **120 Ω terminators** at both ends of the line. Without them reflections cause intermittent errors — fine on a short bench run, broken once the cable gets long.
- Get A/B (D+/D-) polarity right. Vendors label these inconsistently, so swapping the pair is the fastest test.
- A large **ground potential difference** between devices makes the link flaky. Run a common signal ground with the pair.
- Slave addresses run 1–247. Two devices sharing an address will collide on every reply.
TCP gives each device an IP and a port on a switch. If the plant network already exists there is almost no new cabling, and several clients can talk to the same device at once. In exchange you get the 100 m per-segment limit and switches and IPs to manage.
How much faster is it, really?
What you feel is not raw bit rate but the time for one round trip. Work it out for RTU at 9600 bps: with 8N1 each byte carries 10 bits, so 960 bytes per second — roughly 1 ms per byte.
request 8 bytes + response 9 bytes = 17 bytes ~ 18 ms
+ inter-frame silence (3.5 chars) x2 ~ 7 ms
+ device processing ~ 5-20 ms
──────────────────────────────────────────────────────
one round trip ~ 30-45 ms -> 20-30 per secondWith ten slaves that is two or three updates per device per second. Plenty for temperature or flow; not enough when you need tens of milliseconds. On Ethernet the same exchange is typically 1–5 ms and devices can be polled in parallel, so update periods drop to single digits.
| RTU (RS-485) | TCP (Ethernet) | |
|---|---|---|
| Round trip | Tens of ms | A few ms |
| Parallel requests | No — strictly in turn | Yes — per device |
| Distance | ~1.2 km (further at lower speed) | 100 m per segment, extended by switches |
| Cabling cost | One pair, daisy-chained | A drop per device plus a switch |
| Integrity check | CRC in the frame | Handled by TCP |
| Debug tools | Serial monitor, scope | Wireshark and friends |
The addressing trap
Most lost hours are not about the protocol but the address. When a manual says 40001, the number that goes on the wire is 0. The 40001–49999 notation is a legacy way of naming holding registers; frames carry a zero-based offset.
| Manual says | Address on the wire | Function code |
|---|---|---|
40001 | 0 | 03 holding register |
40100 | 99 | 03 |
30001 | 0 | 04 input register |
00001 | 0 | 01 coil |
32-bit values (temperature ×100, totalised flow) span two registers, and which half comes first varies by device. If a reading is wildly large or unexpectedly negative, the arithmetic is usually fine and the **word order** is reversed — swap the two registers and recombine.
In Python, only two lines change
Since the command is the same, so is the code. With pymodbus only the client constructor differs.
# pip install pymodbus
from pymodbus.client import ModbusSerialClient, ModbusTcpClient
# RTU — serial port
client = ModbusSerialClient(port="COM3", baudrate=9600,
parity="N", stopbits=1, bytesize=8)
# TCP — IP address (this line instead of the one above)
# client = ModbusTcpClient("192.168.0.50", port=502)
client.connect()
rr = client.read_holding_registers(address=0, count=2, slave=1)
print(rr.registers) # e.g. [258, 16000]
client.close()This is pymodbus 3.x. Version 2.x used unit=1 instead of slave=1, so check your version first if the example fails (pip show pymodbus).
To practise without hardware, run a Modbus server on your PC and point a client at it on the same machine. The robot communication track walks through exactly that setup.
Which one should you pick?
| Situation | Pick | Why |
|---|---|---|
| New build, network already in place | TCP | No new cabling, faster, parallel access |
| Existing RS-485 instrumentation | RTU | Keeps the devices you already own |
| Devices hundreds of metres away | RTU | Ethernet stops at 100 m per segment |
| Monitoring at tens of milliseconds | TCP | Polling cannot keep that period |
| Many cheap sensors on one run | RTU | Lowest cost per device |
| A mix of both | Gateway | Convert TCP↔RTU and unify at one side |
With a gateway, the Unit ID is the key: the value in the TCP request is passed through as the RS-485 slave address behind it, so it has to match the device address to get an answer.
When nothing answers
RTU
- 1Match the serial parameters exactly — baud rate, parity, stop bits. 9600 8N1 against 9600 8E1 gives you nothing at all.
- 2Swap A/B. Vendor labelling is inconsistent and this is a common cause.
- 3Check for duplicate slave addresses, and whether the manual's addresses are 0- or 1-based.
- 4Check the 120 Ω terminators at both ends and the common ground — the usual suspects behind intermittent errors.
- 5Still nothing: connect a single slave directly. If one works and several fail, it is wiring or termination.
TCP
- 1
pingthe device first, then check that port 502 is open — firewalls block it regularly. - 2Check the Unit ID. Some devices expect something other than 1, and behind a gateway it must match the serial address.
- 3Check the connection limit. Cheap devices accept one or two sockets and refuse you if another program already holds them.
- 4Capture with Wireshark: is the request going out unanswered, or coming back as an exception (function code + 0x80)? Exception 02 means a bad address, 03 a bad quantity.
FAQ
Q. Can RTU and TCP devices coexist in one system?
Yes. A Modbus gateway (TCP↔RTU) lets the supervisory software speak only TCP while the gateway polls the RS-485 devices behind it. The Unit ID in the TCP request becomes the serial slave address.
Q. What about Modbus ASCII?
An older variant that sends the same commands as readable characters. Frames are about twice as long and therefore slower; almost nobody chooses it for new work. You mainly meet it on inherited equipment.
Q. How do I secure it?
Standard Modbus has neither authentication nor encryption — anything readable is also writable. Keep the plant network separate from the office network and require a VPN for remote access. A TLS-based Modbus/TCP Security spec exists, but device support is still thin.
Q. How many devices fit on one RS-485 line?
Electrically, 32 with classic transceivers, and 128–256 with modern ones. In practice the polling cycle limits you first: every extra device stretches the time before each one is read again.
Q. Replies break only now and then.
Intermittent faults are usually electrical. Missing terminators, ground potential differences, and comms cable run alongside motor or inverter wiring are the three big causes. Separate the cable from power runs and ground the shield at one end only. Raising the timeout just hides the symptom.