Modbus

From BMSpedia
Revision as of 23:22, 24 February 2026 by AlexDriv (talk | contribs)
Jump to navigation Jump to search

Baud Rate

File:modbus baudrate diagram.png
Diagram showing how baud rate affects the time width of each bit on the signal line.

Baud rate defines the number of signal changes per second on the communication line. In Modbus RTU, one symbol = one binary bit, so baud rate = bits per second.

Common Baud Rate Values

Baud Rate Bits per Second Typical Use Case
1200 1,200 bps Legacy devices, long cable runs
2400 2,400 bps Older meters and sensors
4800 4,800 bps Moderate speed legacy devices
9600 9,600 bps Most common default
19200 19,200 bps Standard BMS installations
38400 38,400 bps Higher performance systems
57600 57,600 bps Fast networks, shorter cable runs
115200 115,200 bps Maximum practical RS-485 speed

How Baud Rate Affects the Signal

Each bit occupies a fixed time window: Bit Period = 1 / Baud Rate

At 9600 baud each bit lasts 104 microseconds. At 115200 baud each bit lasts 8.7 microseconds.


Modbus RTU — Bit-Level Breakdown: Single Byte at 9600 Baud, 8N1

The byte 0x41 (decimal 65) on the RS-485 wire at 9600 baud:

Bit:     Start  D0   D1   D2   D3   D4   D5   D6   D7   Stop
Value:     0    1    0    0    0    0    0    1    0     1
µs each: 104µs per bit window
  • Start bit (1 bit, always 0): Pulls line LOW — signals receiver to begin reading
  • D0–D7 (8 bits): Binary data, transmitted LSB first. 0x41 = 01000001 → wire order: 1,0,0,0,0,0,1,0
  • Stop bit (1 bit, always 1): Returns line HIGH — marks end of byte

Total frame = 10 bits × 104µs = 1.04ms per byte


Modbus RTU — Full Message Example: FC03 Read Holding Register

Master reads 1 holding register (address 0x0000 = register 40001) from slave address 0x01.

Frame in hex — 8 bytes total:

Byte:  [  1  ] [  2  ] [  3  ] [  4  ] [  5  ] [  6  ] [  7  ] [  8  ]
Field: [Slave ] [ FC  ] [RegHi] [RegLo] [QtyHi] [QtyLo] [CRCLo] [CRCHi]
Hex:   [ 0x01 ] [0x03 ] [0x00 ] [0x00 ] [0x00 ] [0x01 ] [0x84 ] [0x0A ]

Each byte in Modbus RTU binary (8N1, LSB first, 9600 baud):

Byte 1 — Slave Address 0x01 (00000001):
  [S] D0  D1  D2  D3  D4  D5  D6  D7 [P]
   0   1   0   0   0   0   0   0   0   1

Byte 2 — Function Code 0x03 (00000011):
  [S] D0  D1  D2  D3  D4  D5  D6  D7 [P]
   0   1   1   0   0   0   0   0   0   1

Byte 3 — Register Address High 0x00 (00000000):
  [S] D0  D1  D2  D3  D4  D5  D6  D7 [P]
   0   0   0   0   0   0   0   0   0   1

Byte 4 — Register Address Low 0x00 (00000000):
  [S] D0  D1  D2  D3  D4  D5  D6  D7 [P]
   0   0   0   0   0   0   0   0   0   1

Byte 5 — Quantity High 0x00 (00000000):
  [S] D0  D1  D2  D3  D4  D5  D6  D7 [P]
   0   0   0   0   0   0   0   0   0   1

Byte 6 — Quantity Low 0x01 (00000001):
  [S] D0  D1  D2  D3  D4  D5  D6  D7 [P]
   0   1   0   0   0   0   0   0   0   1

Byte 7 — CRC Low 0x84 (10000100):
  [S] D0  D1  D2  D3  D4  D5  D6  D7 [P]
   0   0   0   1   0   0   0   0   1   1

Byte 8 — CRC High 0x0A (00001010):
  [S] D0  D1  D2  D3  D4  D5  D6  D7 [P]
   0   0   1   0   1   0   0   0   0   1

[S] = Start bit (always 0)   [P] = Stop bit (always 1)

What each field does: {

Effect on Cable Length

  • 9600 baud — up to ~1200m on good RS-485 twisted pair
  • 19200 baud — up to ~600m
  • 115200 baud — up to ~100m

Parity

File:modbus parity diagram.png
Parity bit position within a Modbus RTU serial frame.

Parity adds a single calculated bit after the 8 data bits in each Modbus RTU character frame, providing basic single-bit error detection at the byte level. CRC-16 handles error detection at the full message level.

Parity Modes

Mode Code Description
Even E Parity bit makes total count of 1s in the frame even
Odd O Parity bit makes total count of 1s in the frame odd
None N No parity bit. 2 stop bits recommended per Modbus spec to maintain frame length.
Mark M Parity bit always 1. Rarely used.
Space S Parity bit always 0. Rarely used.

The Modbus RTU specification recommends Even parity as default. In practice 8N1 (no parity, 1 stop bit) is the most common configuration in BMS installations.


Modbus RTU — Bit-Level Breakdown: Parity Bit Calculation

Example byte: 0x31 = binary 00110001 Count of 1-bits in data = 3

Even Parity (8E1) — 3 is odd, so parity bit = 1 to reach even total (3+1=4):

Bit:   [S]  D0  D1  D2  D3  D4  D5  D6  D7  [Par] [P]
Value:  0    1   0   0   0   1   1   0   0    1     1

Odd Parity (8O1) — 3 is already odd, so parity bit = 0 to keep total odd (3+0=3):

Bit:   [S]  D0  D1  D2  D3  D4  D5  D6  D7  [Par] [P]
Value:  0    1   0   0   0   1   1   0   0    0     1

No Parity (8N1) — no parity bit, one stop bit only:

Bit:   [S]  D0  D1  D2  D3  D4  D5  D6  D7  [P]
Value:  0    1   0   0   0   1   1   0   0    1

No Parity (8N2) — no parity bit, two stop bits to maintain 11-bit frame:

Bit:   [S]  D0  D1  D2  D3  D4  D5  D6  D7  [P1] [P2]
Value:  0    1   0   0   0   1   1   0   0    1    1

[S] = Start bit [Par] = Parity bit [P] = Stop bit


Modbus RTU — Full Message Example: FC03 Request with Even Parity (8E1)

Same request as the Baud Rate example — slave 0x01, FC03, register 0x0000, quantity 0x0001, CRC 0x840A — but now shown with Even parity applied to every byte.

Parity bit rule: count the 1s in D0–D7. If count is odd → parity = 1. If count is even → parity = 0.

Byte 1 — Slave 0x01 (00000001) — one 1-bit → parity = 1:
  [S]  D0  D1  D2  D3  D4  D5  D6  D7  [Par] [P]
   0    1   0   0   0   0   0   0   0    1     1

Byte 2 — FC 0x03 (00000011) — two 1-bits → parity = 0:
  [S]  D0  D1  D2  D3  D4  D5  D6  D7  [Par] [P]
   0    1   1   0   0   0   0   0   0    0     1

Byte 3 — RegHi 0x00 (00000000) — zero 1-bits → parity = 0:
  [S]  D0  D1  D2  D3  D4  D5  D6  D7  [Par] [P]
   0    0   0   0   0   0   0   0   0    0     1

Byte 4 — RegLo 0x00 (00000000) — zero 1-bits → parity = 0:
  [S]  D0  D1  D2  D3  D4  D5  D6  D7  [Par] [P]
   0    0   0   0   0   0   0   0   0    0     1

Byte 5 — QtyHi 0x00 (00000000) — zero 1-bits → parity = 0:
  [S]  D0  D1  D2  D3  D4  D5  D6  D7  [Par] [P]
   0    0   0   0   0   0   0   0   0    0     1

Byte 6 — QtyLo 0x01 (00000001) — one 1-bit → parity = 1:
  [S]  D0  D1  D2  D3  D4  D5  D6  D7  [Par] [P]
   0    1   0   0   0   0   0   0   0    1     1

Byte 7 — CRCLo 0x84 (10000100) — two 1-bits → parity = 0:
  [S]  D0  D1  D2  D3  D4  D5  D6  D7  [Par] [P]
   0    0   0   1   0   0   0   0   1    0     1

Byte 8 — CRCHi 0x0A (00001010) — two 1-bits → parity = 0:
  [S]  D0  D1  D2  D3  D4  D5  D6  D7  [Par] [P]
   0    0   1   0   1   0   0   0   0    0     1

8E1 framing = 11 bits per byte (1 start + 8 data + 1 parity + 1 stop) 8 bytes × 11 bits = 88 bits → at 9600 baud = 9.17ms to transmit the full request

The parity bit is recalculated independently for each byte — it is not a fixed value across the frame.


Data Bits

File:modbus databits diagram.png
Data bit positions D0–D7 within a Modbus RTU character frame, transmitted LSB first.

Data bits is the number of bits carrying actual payload within each Modbus RTU character frame. This is always 8 in Modbus RTU. 8 bits = 1 byte = values 0–255 per frame. For values larger than 255, Modbus RTU uses two consecutive bytes (one 16-bit register) or four bytes (two registers for 32-bit values).


Modbus RTU — Bit-Level Breakdown: Data Bit Weighting

Example byte: 0xB4 = decimal 180 = binary 10110100

Modbus RTU transmits data bits LSB first (D0 first, D7 last):

Bit:   [S]  D0  D1  D2  D3  D4  D5  D6  D7  [P]
Value:  0    0   0   1   0   1   1   0   1    1

{


Modbus RTU — Full Message Example: FC03 Slave Response with Register Decoding

Slave 0x01 responds to the FC03 request. Register 40001 holds value 0x0190 (decimal 400), representing a supply air temperature of 40.0°C (scale factor ÷10).

Response frame in hex — 7 bytes total:

Byte:  [  1  ] [  2  ] [  3  ] [  4  ] [  5  ] [  6  ] [  7  ]
Field: [Slave ] [ FC  ] [ByteCt] [DatHi] [DatLo] [CRCLo] [CRCHi]
Hex:   [ 0x01 ] [0x03 ] [ 0x02 ] [0x01 ] [0x90 ] [0xF8 ] [0x4B ]

Critical bytes expanded in Modbus RTU binary (8N1, LSB first):

Byte 3 — Byte Count 0x02 (00000010):
  [S]  D0  D1  D2  D3  D4  D5  D6  D7  [P]
   0    0   1   0   0   0   0   0   0    1
  D1 = 2¹ = 2 → 2 data bytes follow (one 16-bit register) ✓

Byte 4 — Data High Byte 0x01 (00000001):
  [S]  D0  D1  D2  D3  D4  D5  D6  D7  [P]
   0    1   0   0   0   0   0   0   0    1
  D0 = 1 → High byte value = 1

Byte 5 — Data Low Byte 0x90 (10010000):
  [S]  D0  D1  D2  D3  D4  D5  D6  D7  [P]
   0    0   0   0   0   1   0   0   1    1
  D4 = 16, D7 = 128 → 16+128 = 144 = 0x90

Reconstructing the 16-bit register value (Big Endian — high byte first):

High byte:  0x01 × 256  =  256
Low byte:   0x90        =  144
                          ─────
Register value:            400  =  0x0190

Applied scale factor ÷10:  400 ÷ 10  =  40.0°C

Modbus RTU always sends the high byte of a 16-bit register before the low byte. This is Big Endian byte order. Some devices deviate from this — always check the device register map.


Stop Bits

File:modbus stopbits diagram.png
1 stop bit vs 2 stop bits in a Modbus RTU character frame.

Stop bits return the RS-485 line to the idle HIGH state after each Modbus RTU character frame. The receiver requires this idle period to correctly detect the falling edge of the next start bit.

Stop Bit Configurations

Config Stop Bits Parity Total Bits per Frame Notes
8N1 1 None 10 Most common Modbus RTU configuration in BMS
8E1 1 Even 11 Modbus spec default — even parity + 1 stop
8O1 1 Odd 11 Odd parity + 1 stop
8N2 2 None 11 No parity, 2 stops — maintains 11-bit frame on noisy lines


Modbus RTU — Bit-Level Breakdown: Stop Bit Comparison

Example byte: 0x55 = binary 01010101 — four 1-bits (even count)

8N1 — No Parity, 1 Stop Bit (10 bits total):

Bit:   [S]  D0  D1  D2  D3  D4  D5  D6  D7  [P]
Value:  0    1   0   1   0   1   0   1   0    1
        ←————————————— 10 bits ——————————————→

8E1 — Even Parity, 1 Stop Bit (11 bits total): Four 1-bits in data = even count → parity = 0

Bit:   [S]  D0  D1  D2  D3  D4  D5  D6  D7  [Par] [P]
Value:  0    1   0   1   0   1   0   1   0    0     1
        ←—————————————— 11 bits ———————————————→

8N2 — No Parity, 2 Stop Bits (11 bits total):

Bit:   [S]  D0  D1  D2  D3  D4  D5  D6  D7  [P1] [P2]
Value:  0    1   0   1   0   1   0   1   0    1    1
        ←—————————————— 11 bits ———————————————→

If the line goes LOW before the stop bit period ends, the receiver flags a framing error and discards the byte. The resulting CRC mismatch causes the master to timeout with no response.


Modbus RTU — Full Message Example: FC06 Write Single Register

Master writes value 0x0064 (decimal 100 = setpoint 10.0°C at scale ÷10) to register 40002 (address 0x0001) on slave 0x01.

Request frame in hex — 8 bytes:

Byte:  [  1  ] [  2  ] [  3  ] [  4  ] [  5  ] [  6  ] [  7  ] [  8  ]
Field: [Slave ] [ FC  ] [RegHi] [RegLo] [ValHi] [ValLo] [CRCLo] [CRCHi]
Hex:   [ 0x01 ] [0x06 ] [0x00 ] [0x01 ] [0x00 ] [0x64 ] [0x49 ] [0xA4 ]

Full frame in Modbus RTU binary, 8N1 format:

           [S] D0  D1  D2  D3  D4  D5  D6  D7 [P]
Byte 1 0x01: 0  1   0   0   0   0   0   0   0   1   ← Slave address
Byte 2 0x06: 0  0   1   1   0   0   0   0   0   1   ← Write Single Register
Byte 3 0x00: 0  0   0   0   0   0   0   0   0   1   ← Register address high
Byte 4 0x01: 0  1   0   0   0   0   0   0   0   1   ← Register address low (reg 40002)
Byte 5 0x00: 0  0   0   0   0   0   0   0   0   1   ← Value high byte
Byte 6 0x64: 0  0   0   1   0   0   1   1   0   1   ← Value low byte (100 decimal)
Byte 7 0x49: 0  1   0   0   1   0   0   1   0   1   ← CRC low
Byte 8 0xA4: 0  0   0   1   0   0   1   0   1   1   ← CRC high

On a successful FC06 write the slave echoes the entire 8-byte request frame back unchanged as its response. If the stop bit on any byte is corrupted, that byte is discarded as a framing error, the CRC check fails, and the master receives no response.

Transmission time at 9600 baud, 8N1: 8 bytes × 10 bits × 104µs = 8.32ms


Flow Control

File:modbus flowcontrol diagram.png
RS-485 RTS direction switching during a Modbus RTU request/response cycle.

Flow control in Modbus RTU on RS-485 is almost always set to None in software. The physical RS-485 half-duplex bus requires direction switching between transmit and receive, but this is handled by the hardware driver enable (DE) pin on the RS-485 transceiver, not by a software flow control protocol.

Flow Control Types

Type Method Compatible with Modbus RTU
None ✓ Standard for RS-485
Hardware RTS/CTS Physical signal lines Only on RS-232 point-to-point
Software XON/XOFF In-band bytes 0x11 / 0x13 ✗ Incompatible — binary protocol
RTS as DE toggle RTS drives RS-485 DE pin ✓ Used by USB-RS485 adapters


Modbus RTU — Bit-Level Breakdown: RS-485 Half-Duplex Direction Control

In Modbus RTU on RS-485, the driver enable (DE) signal controls which device is transmitting. Only one device may drive the bus at a time.

              MASTER TRANSMITTING              MASTER LISTENING
DE (Master):  ________________________
             |                        |________________________________
             ^ DE asserted HIGH        ^ DE released LOW

TX (Master):  [ Start bits of frame...data bytes...CRC ]
             |←————————————————————————————————————————→|

                                       ↑
                               3.5 character-time
                               silent gap here
                               marks end of frame

DE (Slave):                                        ______________________
                                                  |
                                                  ^ Slave asserts its DE HIGH

RX (Master):                                      [ Slave response frame ]
  • DE HIGH = RS-485 driver active, device is transmitting
  • DE LOW = RS-485 driver disabled, device is listening
  • The 3.5 character-time silent gap at 9600 baud = ~3.6ms — this is what Modbus RTU uses to detect frame boundaries, not a delimiter byte
  • If DE is not released fast enough, the master's driver will collide with the slave's response on the bus


Modbus RTU — Full Message Example: FC03 Read 2 Registers — Complete Request/Response Cycle

Master reads 2 holding registers from slave 0x02, starting at address 0x0010 (registers 40017 and 40018). These could represent, for example, active power high and low bytes from an energy meter.

Master request frame — 8 bytes:

Byte:  [  1  ] [  2  ] [  3  ] [  4  ] [  5  ] [  6  ] [  7  ] [  8  ]
Field: [Slave ] [ FC  ] [RegHi] [RegLo] [QtyHi] [QtyLo] [CRCLo] [CRCHi]
Hex:   [ 0x02 ] [0x03 ] [0x00 ] [0x10 ] [0x00 ] [0x02 ] [0xC4 ] [0x38 ]

Slave response frame — 9 bytes:

Byte:  [  1  ] [  2  ] [  3  ] [  4  ] [  5  ] [  6  ] [  7  ] [  8  ] [  9  ]
Field: [Slave ] [ FC  ] [ByteCt] [R1Hi ] [R1Lo ] [R2Hi ] [R2Lo ] [CRCLo] [CRCHi]
Hex:   [ 0x02 ] [0x03 ] [ 0x04 ] [0x00 ] [0xC8 ] [0x01 ] [0x2C ] [0x77 ] [0xDF ]

Both frames in Modbus RTU binary, 8N1 (LSB first):

REQUEST:
           [S] D0  D1  D2  D3  D4  D5  D6  D7 [P]
0x02 Addr:  0   0   1   0   0   0   0   0   0   1
0x03 FC:    0   1   1   0   0   0   0   0   0   1
0x00 RegHi: 0   0   0   0   0   0   0   0   0   1
0x10 RegLo: 0   0   0   0   0   1   0   0   0   1
0x00 QtyHi: 0   0   0   0   0   0   0   0   0   1
0x02 QtyLo: 0   0   1   0   0   0   0   0   0   1
0xC4 CRCLo: 0   0   0   1   0   0   0   1   1   1
0x38 CRCHi: 0   0   0   0   1   1   1   0   0   1

[— 3.5 char silent gap (~3.6ms) — bus released by master — slave begins responding —]

RESPONSE:
           [S] D0  D1  D2  D3  D4  D5  D6  D7 [P]
0x02 Addr:  0   0   1   0   0   0   0   0   0   1
0x03 FC:    0   1   1   0   0   0   0   0   0   1
0x04 BytCt: 0   0   0   1   0   0   0   0   0   1
0x00 R1Hi:  0   0   0   0   0   0   0   0   0   1
0xC8 R1Lo:  0   0   0   0   1   0   0   1   1   1
0x01 R2Hi:  0   1   0   0   0   0   0   0   0   1
0x2C R2Lo:  0   0   0   1   1   0   1   0   0   1
0x77 CRCLo: 0   1   1   1   0   1   1   1   0   1
0xDF CRCHi: 0   1   1   1   1   1   0   1   1   1

Decoding the two register values from the response (Big Endian):

Register 1 (40017):
  High byte 0x00 = 0 × 256 =   0
  Low  byte 0xC8 = 200
  Value = 200

Register 2 (40018):
  High byte 0x01 = 1 × 256 = 256
  Low  byte 0x2C = 44
  Value = 256 + 44 = 300

If these were active power registers with a scale factor of ÷10: Register 1 = 20.0 kW, Register 2 = 30.0 kW

Transmission times at 9600 baud, 8N1:

Request:   8 bytes × 10 bits × 104µs =  8.32ms
Silent gap:                           ~  3.60ms
Response:  9 bytes × 10 bits × 104µs =  9.36ms
                                        ───────
Total cycle time:                      ~21.28ms