RS-485 and Modbus RTU: Wiring, Frame Format and CRC Explained

Walk into any factory, solar plant or building-management room and you will find RS-485 cables connecting energy meters, VFDs, temperature controllers and PLCs. RS-485 is the electrical standard: a differential pair that carries data reliably over hundreds of metres. Modbus RTU is the most common protocol running on top of it: a simple master-slave scheme for reading and writing numbered registers.

This article covers RS-485 wiring rules, then decodes real Modbus RTU frames byte by byte, including the CRC, so you can talk to an industrial device from a microcontroller or a USB-RS485 adapter.

RS-485: The Physical Layer

RS-485 (TIA/EIA-485) sends each bit as a voltage difference between two wires, usually labelled A and B. Receivers detect a difference as small as 200 mV, while drivers must produce at least 1.5 V across a loaded bus, so there is plenty of noise margin. Common-mode noise picked up equally on both wires is ignored.

ParameterTypical RS-485 value
TopologyMultidrop bus, usually 2-wire half duplex
Receiver threshold+/- 200 mV
Driver differential outputAt least 1.5 V into the bus load
Nodes32 unit loads; more with 1/4 or 1/8 unit-load transceivers
LengthUp to about 1200 m at low bit rates; shorter at high rates
Termination120 ohm at each end of the trunk

Wiring a Bus Correctly

  • Daisy chain, not star: run one twisted-pair trunk from node to node, with short stubs.
  • Terminate both ends only: one 120 ohm resistor at each end of the trunk, none in the middle.
  • Fail-safe biasing: when no driver is active the bus floats. A pull-up on A and pull-down on B (often 560 ohm to 1 k on one node, or built into the adapter) holds a defined idle state so receivers do not see random characters.
  • Ground reference: run a third wire for signal ground; RS-485 tolerates common-mode voltage, but only within the transceiver's limits.
  • Direction control: in 2-wire mode only one node may drive at a time. On a MAX485 module, DE (driver enable) and /RE (receiver enable) are often tied together to one GPIO: high to transmit, low to receive.
  MCU             MAX485               Bus
  TX  ----------> DI
  RX  <---------- RO              A ----+---- to next node A
  GPIO ---------> DE + /RE        B ----+---- to next node B
                  VCC, GND

Modbus RTU Frame Format

Modbus RTU is a request-response protocol. One master sends a request to a slave address from 1 to 247 (0 is broadcast) and that slave replies. Each frame is sent as continuous binary bytes:

| Slave address | Function code | Data (N bytes)   | CRC low | CRC high |
|    1 byte     |    1 byte     | depends on code  | 1 byte  |  1 byte  |

There is no start or end character. Frames are separated by silence of at least 3.5 character times, and gaps inside a frame must not exceed 1.5 character times. The Modbus serial specification fixes these at 1.75 ms and 750 microseconds for bit rates above 19200.

Character time at 9600 baud, 8E1 (11 bits) = 11 / 9600 = 1.146 ms
3.5 characters = 4.01 ms   (minimum gap between frames)
1.5 characters = 1.72 ms   (maximum gap inside a frame)

The specification's default serial format is 8 data bits with even parity and 1 stop bit; if no parity is used, 2 stop bits are required. In practice many devices ship set to 8N1, so read the device manual and match it.

CodeFunctionData type
01 (0x01)Read coilsSingle-bit outputs
02 (0x02)Read discrete inputsSingle-bit inputs
03 (0x03)Read holding registers16-bit read/write registers
04 (0x04)Read input registers16-bit read-only registers
05 (0x05)Write single coilOne bit
06 (0x06)Write single registerOne 16-bit register
15 (0x0F)Write multiple coilsMany bits
16 (0x10)Write multiple registersMany 16-bit registers

Decoding Real Frames

Request: Read 2 Holding Registers from Slave 1

01 03 00 00 00 02 C4 0B

01     slave address 1
03     read holding registers
00 00  starting register address 0 (high byte first)
00 02  quantity: 2 registers
C4 0B  CRC-16, LOW byte first

Response

01 03 04 00 E6 01 2C 1B 89

01     slave address
03     function code echoed
04     byte count (2 registers x 2 bytes)
00 E6  register 0 = 0x00E6 = 230
01 2C  register 1 = 0x012C = 300
1B 89  CRC-16, low byte first

Register values are big-endian (high byte first) while the CRC is sent low byte first, a frequent source of confusion. If register 0 holds voltage in volts and register 1 holds current in units of 0.01 A, this device reports 230 V and 3.00 A; the scaling always comes from the device's register map.

Exception Response

If the request is invalid, the slave sets the top bit of the function code and returns an exception code: 0x83 instead of 0x03, followed by 01 (illegal function), 02 (illegal data address), 03 (illegal data value) or 04 (slave device failure).

Computing the Modbus CRC-16

Standard Modbus CRC-16 (polynomial 0xA001 reflected, initial value 0xFFFF)
#include <stdint.h>
#include <stdio.h>

uint16_t modbus_crc16(const uint8_t *buf, uint16_t len) {
    uint16_t crc = 0xFFFF;
    for (uint16_t i = 0; i < len; i++) {
        crc ^= buf[i];
        for (uint8_t b = 0; b < 8; b++) {
            if (crc & 0x0001)
                crc = (crc >> 1) ^ 0xA001;
            else
                crc >>= 1;
        }
    }
    return crc;  // send (crc & 0xFF) first, then (crc >> 8)
}

int main(void) {
    uint8_t req[] = {0x01, 0x03, 0x00, 0x00, 0x00, 0x02};
    uint16_t crc = modbus_crc16(req, sizeof(req));
    printf("%02X %02X\n", crc & 0xFF, crc >> 8);  // prints C4 0B
    return 0;
}

Run this on a PC first and confirm it prints C4 0B before porting it to your microcontroller. A receiver can also run the CRC over the whole frame including the two CRC bytes; a correct frame gives 0.

Troubleshooting

  • No reply: wrong slave ID, baud rate or parity; A and B swapped; DE pin left high so your node keeps driving the bus.
  • First byte of reply lost: DE released too late or receiver enabled too late; switch to receive immediately after the last stop bit has left the UART.
  • Exception 02: register address off by one. Some manuals list registers as 40001 (one-based); on the wire that is address 0.
  • Random characters when idle: missing fail-safe bias.
  • Errors only at long range: missing termination or a star topology.

Summary

RS-485 is a differential multidrop bus, daisy-chained, terminated with 120 ohms at both ends and biased for a defined idle state. Modbus RTU frames carry slave address, function code, data and a CRC-16 sent low byte first, separated by 3.5 character times of silence. Once you can decode 01 03 00 00 00 02 C4 0B by hand, any energy meter or controller register map becomes straightforward to use.