Modbus: Difference between revisions
No edit summary |
No edit summary |
||
| Line 1: | Line 1: | ||
= | === Baud Rate === | ||
[[File:modbus_baudrate_diagram.png|thumb|400px|right|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 ==== | |||
== | |||
== | |||
{| class="wikitable" | {| class="wikitable" | ||
|- | |- | ||
! | ! 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: <code>Bit Period = 1 / Baud Rate</code> | ||
At 9600 baud each bit lasts '''104 microseconds'''. At 115200 baud each bit lasts '''8.7 microseconds'''. | |||
Modbus RTU | {{Mbox | ||
|type=notice | |||
|text= | |||
'''Modbus RTU — Bit-Level Breakdown: Single Byte at 9600 Baud, 8N1''' | |||
The byte <code>0x41</code> (decimal 65) on the RS-485 wire at 9600 baud: | |||
<pre> | |||
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 | |||
</pre> | |||
* '''Start bit''' (1 bit, always 0): Pulls line LOW — signals receiver to begin reading | |||
* Binary | * '''D0–D7''' (8 bits): Binary data, transmitted LSB first. <code>0x41</code> = <code>01000001</code> → 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 | {{Mbox | ||
|type=notice | |||
|text= | |||
'''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:''' | |||
<pre> | |||
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 ] | |||
</pre> | |||
'''Each byte in Modbus RTU binary (8N1, LSB first, 9600 baud):''' | |||
<pre> | |||
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) | |||
</pre> | |||
'''What each field does:''' | |||
{| class="wikitable" | {| class="wikitable" | ||
|- | |- | ||
! | ! Bytes !! Field !! Value !! Purpose | ||
|- | |- | ||
| | | 1 || Slave Address || 0x01 || Target device on the RS-485 bus | ||
|- | |- | ||
| | | 2 || Function Code || 0x03 || Instruction: Read Holding Registers | ||
|- | |- | ||
| | | 3–4 || Starting Address || 0x0000 || Begin at register 0 (Modbus register 40001) | ||
|- | |- | ||
| | | 5–6 || Quantity || 0x0001 || Read 1 register (16-bit value) | ||
|- | |- | ||
| | | 7–8 || CRC-16 || 0x0A84 || Error check — slave silently discards frame if CRC fails | ||
| | |||
|} | |} | ||
'''Transmission time at 9600 baud, 8N1:''' | |||
8 bytes × 10 bits = 80 bits → 80 × 104µs = '''8.32ms''' to send the full request frame | |||
}} | }} | ||
==== Effect on Cable Length ==== | ==== Effect on Cable Length ==== | ||
* 9600 baud — up to ~1200m on good RS-485 twisted pair | |||
* 19200 baud — up to ~600m | |||
* 9600 baud — | * 115200 baud — up to ~100m | ||
* 19200 baud — | |||
* 115200 baud — | |||
---- | ---- | ||
| Line 176: | Line 135: | ||
=== Parity === | === Parity === | ||
[[File:modbus_parity_diagram.png|thumb|400px|right| | [[File:modbus_parity_diagram.png|thumb|400px|right|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 ==== | ==== Parity Modes ==== | ||
| Line 186: | Line 143: | ||
{| class="wikitable" | {| class="wikitable" | ||
|- | |- | ||
! Mode !! | ! Mode !! Code !! Description | ||
|- | |- | ||
| Even | | Even || E || Parity bit makes total count of 1s in the frame even | ||
|- | |- | ||
| Odd | | 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 | | Mark || M || Parity bit always 1. Rarely used. | ||
|- | |- | ||
| Space | | Space || S || Parity bit always 0. Rarely used. | ||
|} | |} | ||
The Modbus specification recommends '''Even parity''' as | 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. | ||
{{Mbox | {{Mbox | ||
|type=notice | |type=notice | ||
|text= | |text= | ||
''' | '''Modbus RTU — Bit-Level Breakdown: Parity Bit Calculation''' | ||
Example byte: <code>0x31</code> = binary <code>00110001</code> | Example byte: <code>0x31</code> = binary <code>00110001</code> | ||
Count of 1-bits in data = 3 | |||
'''Even Parity (8E1)''' — 3 is odd, so parity bit = 1 to reach even total (3+1=4): | |||
<pre> | |||
Bit: [S] D0 D1 D2 D3 D4 D5 D6 D7 [Par] [P] | |||
Value: 0 1 0 0 0 1 1 0 0 1 1 | |||
</pre> | |||
''' | '''Odd Parity (8O1)''' — 3 is already odd, so parity bit = 0 to keep total odd (3+0=3): | ||
<pre> | |||
Bit: [S] D0 D1 D2 D3 D4 D5 D6 D7 [Par] [P] | |||
Value: 0 1 0 0 0 1 1 0 0 0 1 | |||
</pre> | |||
'''No Parity (8N1)''' — no parity bit, one stop bit only: | |||
<pre> | <pre> | ||
Bit: | Bit: [S] D0 D1 D2 D3 D4 D5 D6 D7 [P] | ||
Value: | Value: 0 1 0 0 0 1 1 0 0 1 | ||
</pre> | </pre> | ||
''' | '''No Parity (8N2)''' — no parity bit, two stop bits to maintain 11-bit frame: | ||
<pre> | <pre> | ||
Bit: | Bit: [S] D0 D1 D2 D3 D4 D5 D6 D7 [P1] [P2] | ||
Value: | Value: 0 1 0 0 0 1 1 0 0 1 1 | ||
</pre> | </pre> | ||
''' | [S] = Start bit [Par] = Parity bit [P] = Stop bit | ||
}} | |||
{{Mbox | |||
|type=notice | |||
|text= | |||
'''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. | |||
<pre> | <pre> | ||
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 | |||
</pre> | |||
=== | 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|thumb|400px|right|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). | |||
{{Mbox | {{Mbox | ||
|type=notice | |type=notice | ||
|text= | |text= | ||
''' | '''Modbus RTU — Bit-Level Breakdown: Data Bit Weighting''' | ||
Example byte: <code>0xB4</code> = decimal 180 = binary <code>10110100</code> | Example byte: <code>0xB4</code> = decimal 180 = binary <code>10110100</code> | ||
Modbus RTU transmits data bits LSB first (D0 first, D7 last): | |||
<pre> | <pre> | ||
Bit | Bit: [S] D0 D1 D2 D3 D4 D5 D6 D7 [P] | ||
Value: 0 0 0 1 0 1 1 0 1 1 | |||
</pre> | </pre> | ||
{| class="wikitable" | {| class="wikitable" | ||
|- | |- | ||
! Bit !! | ! Bit !! Wire Order !! Weight !! Bit Value in 0xB4 !! Contributes | ||
|- | |||
| D0 || 1st transmitted || 2⁰ = 1 || 0 || 0 | |||
|- | |- | ||
| | | D1 || 2nd || 2¹ = 2 || 0 || 0 | ||
|- | |- | ||
| | | D2 || 3rd || 2² = 4 || 1 || 4 | ||
|- | |- | ||
| | | D3 || 4th || 2³ = 8 || 0 || 0 | ||
|- | |- | ||
| | | D4 || 5th || 2⁴ = 16 || 1 || 16 | ||
|- | |- | ||
| | | D5 || 6th || 2⁵ = 32 || 1 || 32 | ||
|- | |- | ||
| | | D6 || 7th || 2⁶ = 64 || 0 || 0 | ||
|- | |- | ||
| | | D7 || 8th (last) || 2⁷ = 128 || 1 || 128 | ||
|- | |- | ||
| | | '''Total''' || — || — || — || '''4+16+32+128 = 180 = 0xB4 ✓''' | ||
|} | |} | ||
}} | }} | ||
{{Mbox | {{Mbox | ||
|type=notice | |type=notice | ||
|text= | |text= | ||
''' | '''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:''' | ||
<pre> | |||
Byte: [ 1 ] [ 2 ] [ 3 ] [ 4 ] [ 5 ] [ 6 ] [ 7 ] | |||
Field: [Slave ] [ FC ] [ByteCt] [DatHi] [DatLo] [CRCLo] [CRCHi] | |||
Hex: [ 0x01 ] [0x03 ] [ 0x02 ] [0x01 ] [0x90 ] [0xF8 ] [0x4B ] | |||
</pre> | |||
'''Critical bytes expanded in Modbus RTU binary (8N1, LSB first):''' | |||
<pre> | <pre> | ||
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 | |||
</pre> | </pre> | ||
''' | '''Reconstructing the 16-bit register value (Big Endian — high byte first):''' | ||
<pre> | |||
High byte: 0x01 × 256 = 256 | |||
Low byte: 0x90 = 144 | |||
───── | |||
Register value: 400 = 0x0190 | |||
Applied scale factor ÷10: 400 ÷ 10 = 40.0°C | |||
</pre> | </pre> | ||
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: | [[File:modbus_stopbits_diagram.png|thumb|400px|right|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 ==== | ||
{| class="wikitable" | {| class="wikitable" | ||
|- | |- | ||
! | ! Config !! Stop Bits !! Parity !! Total Bits per Frame !! Notes | ||
|- | |- | ||
| None || | | 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 | ||
|} | |} | ||
{{Mbox | {{Mbox | ||
|type=notice | |type=notice | ||
|text= | |text= | ||
''' | '''Modbus RTU — Bit-Level Breakdown: Stop Bit Comparison''' | ||
Example byte: <code>0x55</code> = binary <code>01010101</code> — four 1-bits (even count) | |||
'''8N1 — No Parity, 1 Stop Bit (10 bits total):''' | |||
<pre> | <pre> | ||
Bit: [S] D0 D1 D2 D3 D4 D5 D6 D7 [P] | |||
Value: 0 1 0 1 0 1 0 1 0 1 | |||
←————————————— 10 bits ——————————————→ | |||
</pre> | |||
'''8E1 — Even Parity, 1 Stop Bit (11 bits total):''' | |||
Four 1-bits in data = even count → parity = 0 | |||
<pre> | |||
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 ———————————————→ | |||
</pre> | |||
'''8N2 — No Parity, 2 Stop Bits (11 bits total):''' | |||
<pre> | |||
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 ———————————————→ | |||
</pre> | </pre> | ||
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. | |||
}} | }} | ||
{{Mbox | |||
|type=notice | |||
|text= | |||
'''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:''' | |||
<pre> | |||
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 ] | |||
</pre> | |||
Modbus | '''Full frame in Modbus RTU binary, 8N1 format:''' | ||
<pre> | |||
[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 | |||
</pre> | |||
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|thumb|400px|right|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 ==== | |||
=== | |||
{| class="wikitable" | {| class="wikitable" | ||
|- | |- | ||
! | ! 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 | ||
|} | |} | ||
== | {{Mbox | ||
|type=notice | |||
|text= | |||
'''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. | |||
<pre> | |||
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 ] | |||
</pre> | |||
* 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 | |||
}} | |||
== | {{Mbox | ||
|type=notice | |||
|text= | |||
'''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:''' | |||
<pre> | |||
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 ] | |||
</pre> | |||
'''Slave response frame — 9 bytes:''' | |||
<pre> | |||
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 ] | |||
</pre> | |||
'''Both frames in Modbus RTU binary, 8N1 (LSB first):''' | |||
<pre> | |||
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 | |||
</pre> | |||
'''Decoding the two register values from the response (Big Endian):''' | |||
<pre> | |||
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 | |||
</pre> | |||
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:''' | |||
<pre> | |||
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 | |||
</pre> | |||
}} | |||
Revision as of 23:22, 24 February 2026
Baud Rate
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 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
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
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: 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
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: 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
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: 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
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 ]
|
|
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
|