Why an AV Integrator Ends Up Doing Modbus
You did not sign up for industrial protocols, but the moment an exhibition hall includes lighting relays, curtain motors, HVAC, power sequencers, environmental sensors or a PLC, Modbus is what those devices speak. It is the lingua franca of building and industrial equipment, and it shows up in AV work exactly where the AV system has to touch the building.
The good news: Modbus is genuinely simple β simpler than most projector protocols. It has one job, which is reading and writing numbered registers.
RTU vs TCP: The Same Commands, Two Wrappers
This is the key insight: RTU and TCP carry the identical command payload. Only the wrapper differs.
Modbus RTU : [Slave ID][Function][Dataβ¦β¦β¦][CRC-lo][CRC-hi]
Modbus TCP : [MBAP header 7 bytes][Function][Dataβ¦β¦β¦]| Modbus RTU | Modbus TCP | |
|---|---|---|
| Physical layer | RS-485 (usually), RS-232 | Ethernet |
| Address | Slave ID 1β247 in the frame | IP address + Unit ID |
| Error check | CRC-16 | None β TCP handles it |
| Port | n/a | 502 |
| Topology | Daisy-chain bus, one master | Star, multiple clients |
| Speed | 9600β115200 baud | Network speed |
| Distance | Up to ~1200 m on RS-485 | Anywhere the network reaches |
Choose TCP when you can. No CRC to compute, no bus wiring, no termination resistors, no single-master constraint, and you can reach the device from anywhere on the network. Choose RTU when the device only has RS-485 β which is still very common on cheap relay boards and older HVAC gear.
The pragmatic third option is a gateway. A Modbus RTU-to-TCP converter puts the RS-485 bus on the network, and from the control system's point of view everything becomes TCP. In an exhibition hall with one RS-485 lighting bus and a network everywhere else, this is almost always the right architecture β it means one protocol path in your control system instead of two.
Function Codes You Actually Use
Modbus has a long list of function codes. In AV and building work you will use four:
| Code | Name | Use |
|---|---|---|
0x01 | Read Coils | Read on/off outputs (relay states) |
0x03 | Read Holding Registers | Read values (temperature, setpoints, status) |
0x05 | Write Single Coil | Switch a relay on/off |
0x06 | Write Single Register | Write a value (dimmer level, mode) |
0x0F and 0x10 write multiple coils/registers at once, useful for setting a whole scene in one transaction.
Coils are bits, registers are 16-bit words. A relay board exposes coils. A temperature sensor exposes registers. Reading the wrong space returns an exception rather than a wrong value, which at least fails loudly.
The CRC Trap: Low Byte First
Modbus RTU appends a 16-bit CRC. Two facts about it cause most RTU failures:
1. The polynomial is 0xA001 with an initial value of 0xFFFF. This is CRC-16/MODBUS specifically β not the CRC-16 in a generic library, which will give a different answer.
2. The CRC is appended low byte first. Every other multi-byte field in Modbus is big-endian. The CRC is the exception, and it catches people constantly.
Worked example β read one holding register from slave 1, starting at address 0:
Frame body : 01 03 00 00 00 01
CRC-16 : 0x0A84
Appended : 84 0A β low byte, then high byte
Full frame : 01 03 00 00 00 01 84 0AThat last line is the canonical Modbus example you will find in every textbook, and it is a good self-test: if your CRC routine produces 0A 84 instead of 84 0A, you have the byte order backwards and no device will ever answer you.
More verified frames to check your implementation against:
| Action | Frame |
|---|---|
| Read 1 holding register @0 | 01 03 00 00 00 01 84 0A |
| Read 2 holding registers @0 | 01 03 00 00 00 02 C4 0B |
| Read 1 coil @0 | 01 01 00 00 00 01 FD CA |
| Write coil @0 = ON | 01 05 00 00 FF 00 8C 3A |
| Write coil @0 = OFF | 01 05 00 00 00 00 CD CA |
| Write register @0 = 1 | 01 06 00 00 00 01 48 0A |
Note the ON value is FF 00, not 00 01. Function 0x05 uses FF00 for on and 0000 for off β a fixed pair of magic values, not a boolean. Sending 00 01 does nothing.
The Off-By-One That Wastes Afternoons
Modbus documentation from device vendors is written in two incompatible conventions, and mixing them is the most common non-wiring failure:
- Protocol addresses start at 0. This is what goes in the frame.
- Register numbers in vendor manuals often start at 1, or use the 4xxxx convention (holding register 1 =
40001).
So a manual saying "temperature is in register 40001" means protocol address 0. A manual saying "relay 1 is at coil 1" often means address 0.
If you read a plausible-looking value from the wrong register, nothing errors β you just get the neighbouring data point and spend an hour wondering why the temperature reads 47. When a value looks wrong by exactly one position, try shifting the address by one before doubting anything else.
32-Bit Values and Byte Order
Registers are 16 bits. Anything larger β a 32-bit float for power consumption, a 32-bit counter β is split across two consecutive registers, and there is no standard for how.
Four orderings are in the wild. Given the two raw words 0x1234 and 0x5678:
| Order | Bytes | Also called |
|---|---|---|
| ABCD | 12 34 56 78 | Big-endian |
| BADC | 34 12 78 56 | Big-endian byte-swapped |
| CDAB | 56 78 12 34 | Word-swapped β very common |
| DCBA | 78 56 34 12 | Little-endian |
CDAB is the one you will hit most often, because many devices store the low word first. The symptom is unmistakable: a float that should read 23.5 comes back as an absurd number like 1.2e-38, or an integer reads as millions instead of tens.
The fix is a config setting, not a code change β but you have to recognise the symptom. If a 32-bit value is wildly wrong rather than slightly wrong, it is byte order, not scaling.
Scaling is the other half: many devices store 235 and expect you to divide by 10 to get 23.5. That is documented per register and is a multiply, not a byte problem. Wrong scale gives you a value off by a power of ten; wrong byte order gives you nonsense. Telling those two symptoms apart saves a lot of time.
RS-485 Wiring for Modbus RTU
RS-485 is a two-wire differential bus, and its rules are unforgiving in a way Ethernet is not:
- Daisy-chain only. No star, no spurs. Device to device to device.
- Polarity matters. A/B (sometimes labelled D+/Dβ) must be consistent along the whole bus. Reversed on one device and that device is silent β everything else keeps working, which makes it look like a device fault.
- Common ground. Long runs between separately-powered enclosures need the ground reference, or you get intermittent corruption that looks like noise.
- Termination. 120 Ξ© resistors at both physical ends of the bus, and only there. Extra terminators mid-bus load it down.
- One master. Modbus RTU has exactly one master. Two control systems polling the same bus will collide and both will see random timeouts.
- All devices must share baud rate, parity and stop bits, and each needs a unique slave ID. Two devices with the same ID answer simultaneously and corrupt each other.
The duplicate-slave-ID case is worth calling out because it produces such a confusing symptom: occasional garbage replies rather than consistent failure. Out-of-the-box relay boards very often ship as slave ID 1.
Why Your Modbus Device Does Not Answer
1. CRC byte order reversed. See above. Test against 01 03 00 00 00 01 84 0A.
2. Wrong slave ID, or two devices sharing one.
3. A/B reversed on that one device.
4. Missing or excess termination.
5. Address off by one β the 40001 convention.
6. Wrong function code for the data type β reading coils where registers live.
7. Serial parameters mismatched. Modbus RTU commonly uses even parity by default (8E1), unlike most AV gear. If a device is silent at 8N1, try 8E1 before anything else.
8. For TCP: wrong Unit ID. Even over TCP the frame carries a unit identifier. Gateways use it to select which RTU device on the downstream bus you mean; direct-Ethernet devices often accept anything, or require exactly 1 or 255.
With SoftControl
SoftControl supports both modbus_tcp and modbus_rtu natively, with function code, slave ID, address, quantity, data type, byte order, scale, offset and precision configurable per command β so ABCD/BADC/CDAB/DCBA is a dropdown rather than a byte-shuffling exercise, and CRC is computed for you. The command tester shows the exact frame sent and the raw response, which is how you tell a CRC rejection apart from a device that is not on the bus at all.
That matters in exhibition work because it means lighting relays, curtain motors and sensors sit in the same interface as projectors and displays β see AV control protocols explained for how the protocols fit together, and the industrial Modbus dashboard for a worked application.