Modbus: Difference between revisions
No edit summary |
No edit summary |
||
| (5 intermediate revisions by the same user not shown) | |||
| Line 1: | Line 1: | ||
= Modbus = | = Modbus = | ||
== Overview == | == Overview == | ||
| Line 106: | Line 105: | ||
[[File:modbus_baudrate_diagram.png|thumb|400px|right|Diagram showing how baud rate affects the time width of each bit on the signal line. A higher baud rate means narrower bit windows and faster data transmission.]] | [[File:modbus_baudrate_diagram.png|thumb|400px|right|Diagram showing how baud rate affects the time width of each bit on the signal line. A higher baud rate means narrower bit windows and faster data transmission.]] | ||
'''Baud rate''' defines the number of signal changes | '''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 ==== | ==== Common Baud Rate Values ==== | ||
| Line 133: | Line 132: | ||
The default baud rate for Modbus RTU is '''9600 bps''', though 19200 bps is very common in BMS environments. | The default baud rate for Modbus RTU is '''9600 bps''', though 19200 bps is very common in BMS environments. | ||
==== How Baud Rate | ==== How Baud Rate Affects the Signal ==== | ||
Each bit occupies a fixed time window called the '''bit period''', calculated as: | Each bit occupies a fixed time window called the '''bit period''', calculated as: | ||
| Line 139: | Line 138: | ||
<code>Bit Period = 1 / Baud Rate</code> | <code>Bit Period = 1 / Baud Rate</code> | ||
At 9600 baud | At 9600 baud each bit lasts approximately '''104 microseconds'''. At 115200 baud each bit lasts approximately '''8.7 microseconds'''. | ||
{{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 | |||
* '''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''' | |||
}} | |||
{{Mbox | {{Mbox | ||
|type=notice | |type=notice | ||
|text= | |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> | <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> | </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" | |||
|- | |||
! 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 | |||
}} | }} | ||
| Line 168: | Line 235: | ||
Higher baud rates require better quality cabling and are more sensitive to signal degradation over distance. As a general rule: | Higher baud rates require better quality cabling and are more sensitive to signal degradation over distance. As a general rule: | ||
* 9600 baud — reliable up to approximately 1200 metres on | * 9600 baud — reliable up to approximately 1200 metres on shielded RS-485 cable | ||
* 19200 baud — reliable up to approximately 600 metres | * 19200 baud — reliable up to approximately 600 metres | ||
* 115200 baud — reliable up to approximately 100 metres | * 115200 baud — reliable up to approximately 100 metres | ||
| Line 176: | Line 243: | ||
=== Parity === | === Parity === | ||
[[File:modbus_parity_diagram.png|thumb|400px|right|Diagram illustrating Even, Odd, and No parity bit positions within a serial data frame. The parity bit is inserted between the last data bit and the stop bit.]] | [[File:modbus_parity_diagram.png|thumb|400px|right|Diagram illustrating Even, Odd, and No parity bit positions within a Modbus RTU serial data frame. The parity bit is inserted between the last data bit and the stop bit.]] | ||
'''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 251: | ||
{| class="wikitable" | {| class="wikitable" | ||
|- | |- | ||
! Mode !! | ! Mode !! Code !! Description | ||
|- | |- | ||
| Even Parity || E || | | Even Parity || E || Parity bit set so total count of 1s in data + parity = '''even''' | ||
|- | |- | ||
| Odd Parity || O || | | Odd Parity || O || Parity bit set so total count of 1s in data + parity = '''odd''' | ||
|- | |- | ||
| No Parity || N || No parity bit | | No Parity || N || No parity bit. Error checking relies on CRC-16 only. 2 stop bits recommended per Modbus spec. | ||
|- | |- | ||
| Mark Parity || M || Parity bit | | Mark Parity || M || Parity bit always 1. Rarely used. | ||
|- | |- | ||
| Space Parity || S || Parity bit | | Space Parity || S || Parity bit always 0. Rarely used. | ||
|} | |} | ||
The Modbus specification recommends '''Even parity''' as the default. However | The Modbus RTU specification recommends '''Even parity''' as the default. However '''8N1''' (no parity, 1 stop bit) is the de facto standard for most BMS devices in practice. | ||
{{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 — shown with Even parity applied to every byte. | |||
Parity rule: count the 1s in D0–D7. If count is odd → parity bit = 1. If count is even → parity bit = 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> | </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. | |||
}} | }} | ||
==== Parity Limitation ==== | ==== Parity Limitation ==== | ||
Parity can only detect an '''odd number''' of bit errors. If two bits flip simultaneously | Parity can only detect an '''odd number''' of bit errors. If two bits flip simultaneously the parity check still passes even though the data is corrupted. This is why Modbus RTU also uses CRC-16 at the message level — the two mechanisms complement each other. | ||
---- | ---- | ||
| Line 251: | Line 358: | ||
=== Data Bits === | === Data Bits === | ||
[[File:modbus_databits_diagram.png|thumb|400px|right| | [[File:modbus_databits_diagram.png|thumb|400px|right|Data bit positions D0–D7 within a Modbus RTU character frame. Data bits carry the actual payload and are transmitted LSB first.]] | ||
'''Data bits''' refers to 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 floating point 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 transmitted 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 || | | D0 || 1st transmitted || 2⁰ = 1 || 0 || 0 | ||
|- | |- | ||
| D1 || | | D1 || 2nd || 2¹ = 2 || 0 || 0 | ||
|- | |- | ||
| D2 || | | D2 || 3rd || 2² = 4 || 1 || 4 | ||
|- | |- | ||
| D3 || | | D3 || 4th || 2³ = 8 || 0 || 0 | ||
|- | |- | ||
| D4 || | | D4 || 5th || 2⁴ = 16 || 1 || 16 | ||
|- | |- | ||
| D5 || | | D5 || 6th || 2⁵ = 32 || 1 || 32 | ||
|- | |- | ||
| D6 || | | D6 || 7th || 2⁶ = 64 || 0 || 0 | ||
|- | |- | ||
| D7 || | | D7 || 8th (last) || 2⁷ = 128 || 1 || 128 | ||
|- | |||
| '''Total''' || — || — || — || '''4+16+32+128 = 180 = 0xB4 ✓''' | |||
|} | |} | ||
}} | |||
{{Mbox | |||
|type=notice | |||
|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 with a scale factor of ÷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> | |||
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> | |||
'''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> | |||
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. | |||
}} | }} | ||
| Line 309: | Line 448: | ||
=== Stop Bits === | === Stop Bits === | ||
[[File:modbus_stopbits_diagram.png|thumb|400px|right|Diagram comparing 1 stop bit vs 2 stop bits in a | [[File:modbus_stopbits_diagram.png|thumb|400px|right|Diagram comparing 1 stop bit vs 2 stop bits in a Modbus RTU character frame. Stop bits return the RS-485 line to idle HIGH after each transmitted byte.]] | ||
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 ==== | ==== Stop Bit Configurations ==== | ||
| Line 319: | Line 456: | ||
{| class="wikitable" | {| class="wikitable" | ||
|- | |- | ||
! | ! Config !! Stop Bits !! Parity !! Total Bits per Frame !! Notes | ||
|- | |- | ||
| 8N1 || 1 | | 8N1 || 1 || None || 10 || Most common Modbus RTU configuration in BMS | ||
|- | |- | ||
| 8E1 || 1 | | 8E1 || 1 || Even || 11 || Modbus RTU spec default — even parity + 1 stop | ||
|- | |- | ||
| 8O1 || 1 | | 8O1 || 1 || Odd || 11 || Odd parity + 1 stop | ||
|- | |- | ||
| 8N2 || 2 | | 8N2 || 2 || None || 11 || No parity, 2 stops — maintains 11-bit frame on noisy lines | ||
|} | |} | ||
{{Mbox | |||
|type=notice | |||
|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> | |||
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 bit = 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> | |||
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 | {{Mbox | ||
|type=notice | |type=notice | ||
|text= | |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> | <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> | </pre> | ||
''' | '''Full frame in Modbus RTU binary, 8N1 (LSB first):''' | ||
<pre> | <pre> | ||
[S] D0 D1 D2 D3 D4 D5 D6 D7 [P] | |||
Value: | 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> | </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''' | |||
}} | }} | ||
| Line 367: | Line 535: | ||
=== Flow Control === | === Flow Control === | ||
[[File:modbus_flowcontrol_diagram.png|thumb|400px|right| | [[File:modbus_flowcontrol_diagram.png|thumb|400px|right|RS-485 RTS direction switching during a Modbus RTU request/response cycle. The master asserts DE HIGH to transmit, then releases the bus before the slave responds.]] | ||
'''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 ==== | ==== Flow Control Types ==== | ||
| Line 375: | Line 543: | ||
{| class="wikitable" | {| class="wikitable" | ||
|- | |- | ||
! Type !! Method !! | ! Type !! Method !! Compatible with Modbus RTU | ||
|- | |- | ||
| None || — || | | None || — || ✓ Standard for RS-485 Modbus RTU | ||
|- | |- | ||
| Hardware | | Hardware RTS/CTS || Physical signal lines || Only on RS-232 point-to-point connections | ||
|- | |- | ||
| Software | | Software XON/XOFF || In-band bytes 0x11 / 0x13 || ✗ Incompatible — Modbus RTU is a binary protocol | ||
|- | |- | ||
| RTS toggle | | RTS as DE toggle || RTS drives RS-485 DE pin || ✓ Used by USB-RS485 adapters for direction switching | ||
|} | |} | ||
== | {{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 owns the bus. Only one device may drive the bus at any 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 (~3.6ms at 9600 baud) | |||
marks end of Modbus RTU 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 is how Modbus RTU detects frame boundaries — there is no delimiter byte | |||
* If DE is not released fast enough, the master's driver will collide with the slave's response on the bus | |||
}} | |||
{{Mbox | {{Mbox | ||
|type=notice | |type=notice | ||
|text= | |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> | <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 at 9600 baud) — master releases bus — slave responds —] | |||
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 1 1 0 1 0 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 — high byte first):''' | |||
<pre> | |||
Register 1 (40017): | |||
High byte 0x00 = 0 × 256 = 0 | |||
Low byte 0xC8 = = 200 | |||
Value = 0 + 200 = 200 | |||
Register 2 (40018): | |||
High byte 0x01 = 1 × 256 = 256 | |||
Low byte 0x2C = = 44 | |||
Value = 256 + 44 = 300 | |||
</pre> | </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> | |||
The silent gap between the master frame and slave response is critical. If the master releases its driver too late it will collide with the slave's response. If the slave responds before the gap completes, the master will misinterpret the response as a continuation of its own transmission. | |||
}} | }} | ||
---- | ---- | ||
| Line 437: | Line 675: | ||
Coils are single-bit read/write values. They represent binary output states such as a relay, a digital output, or an on/off command. The address range is 1–9999 (using 1-based Modbus convention) or 0x0000–0xFFFF (using the 0-based PDU addressing). | Coils are single-bit read/write values. They represent binary output states such as a relay, a digital output, or an on/off command. The address range is 1–9999 (using 1-based Modbus convention) or 0x0000–0xFFFF (using the 0-based PDU addressing). | ||
Examples in BMS: | Examples in BMS: fan on/off command, valve open/close output, alarm reset. | ||
=== Discrete Inputs === | === Discrete Inputs === | ||
| Line 443: | Line 681: | ||
Discrete inputs are single-bit read-only values. They represent binary input states such as a digital sensor, a status contact, or a switch position. Address range: 10001–19999. | Discrete inputs are single-bit read-only values. They represent binary input states such as a digital sensor, a status contact, or a switch position. Address range: 10001–19999. | ||
Examples in BMS: | Examples in BMS: door open sensor, high temperature alarm contact, filter dirty status. | ||
=== Holding Registers === | === Holding Registers === | ||
| Line 449: | Line 687: | ||
Holding registers are 16-bit read/write registers. They are the most commonly used data type in Modbus and can hold values such as setpoints, configurations, and output values. Address range: 40001–49999. | Holding registers are 16-bit read/write registers. They are the most commonly used data type in Modbus and can hold values such as setpoints, configurations, and output values. Address range: 40001–49999. | ||
A single holding register holds values from 0 to 65535 (unsigned) or -32768 to 32767 (signed). For larger values | A single holding register holds values from 0 to 65535 (unsigned) or -32768 to 32767 (signed). For larger values such as floating point temperatures or energy readings, two consecutive registers are used together to form a 32-bit value. | ||
Examples in BMS: | Examples in BMS: temperature setpoint, VFD speed reference, valve position command. | ||
=== Input Registers === | === Input Registers === | ||
| Line 457: | Line 695: | ||
Input registers are 16-bit read-only registers. They represent measured or computed values provided by the slave device. Address range: 30001–39999. | Input registers are 16-bit read-only registers. They represent measured or computed values provided by the slave device. Address range: 30001–39999. | ||
Examples in BMS: | Examples in BMS: supply air temperature, room CO₂ concentration, energy meter active power reading. | ||
---- | ---- | ||
| Line 463: | Line 701: | ||
== Function Codes == | == Function Codes == | ||
Function codes tell the slave device what action to perform. They are carried in the second byte of every Modbus frame. | Function codes tell the slave device what action to perform. They are carried in the second byte of every Modbus RTU frame. | ||
=== Read Functions === | === Read Functions === | ||
| Line 516: | Line 754: | ||
=== CRC and LRC Checking === | === CRC and LRC Checking === | ||
'''CRC-16 (Cyclic Redundancy Check)''' is used in Modbus RTU. It is a 16-bit error detection value calculated from all bytes in the message | '''CRC-16 (Cyclic Redundancy Check)''' is used in Modbus RTU. It is a 16-bit error detection value calculated from all bytes in the message excluding the CRC bytes themselves. The master calculates the CRC before sending, and the slave recalculates it upon receipt. If the values do not match, the message is discarded silently — no response is sent, and the master will timeout. | ||
The CRC polynomial used is: <code>0xA001</code> (reflected form of CRC-16-IBM). | The CRC polynomial used is: <code>0xA001</code> (reflected form of CRC-16-IBM). | ||
'''LRC (Longitudinal Redundancy Check)''' is used in Modbus ASCII. It is calculated as the two's complement of the sum of all byte values in the message. It is simpler but less robust than CRC-16. | '''LRC (Longitudinal Redundancy Check)''' is used in Modbus ASCII only. It is calculated as the two's complement of the sum of all byte values in the message. It is simpler but less robust than CRC-16. | ||
=== Exception Codes === | === Exception Codes === | ||
| Line 538: | Line 776: | ||
| 04 || 0x04 || Slave Device Failure || An unrecoverable error occurred in the slave | | 04 || 0x04 || Slave Device Failure || An unrecoverable error occurred in the slave | ||
|- | |- | ||
| 05 || 0x05 || Acknowledge || Slave accepted the request but needs more time | | 05 || 0x05 || Acknowledge || Slave accepted the request but needs more time | ||
|- | |- | ||
| 06 || 0x06 || Slave Device Busy || Slave is busy processing a previous request | | 06 || 0x06 || Slave Device Busy || Slave is busy processing a previous request | ||
| Line 568: | Line 806: | ||
=== Wiring and Topology === | === Wiring and Topology === | ||
Modbus RTU on RS-485 uses a '''daisy-chain bus topology'''. Devices are connected in series from the first to the last node. The two conductors of the RS-485 pair are labelled A (−) and B (+), though manufacturers often use inconsistent naming | Modbus RTU on RS-485 uses a '''daisy-chain bus topology'''. Devices are connected in series from the first to the last node. The two conductors of the RS-485 pair are labelled A (−) and B (+), though manufacturers often use inconsistent naming — some label them D− / D+, or even reverse the A/B convention entirely. | ||
Key wiring rules: | Key wiring rules: | ||
* '''Termination resistors''' of 120Ω must be fitted at both physical ends of the RS-485 bus to prevent signal reflections. Only the two end devices are terminated — never intermediate devices. | * '''Termination resistors''' of 120Ω must be fitted at both physical ends of the RS-485 bus to prevent signal reflections. Only the two end devices are terminated — never intermediate devices. | ||
* '''Bias resistors''' (typically 560Ω to 1kΩ) are recommended to hold the bus at a defined state when no device is transmitting, preventing false start bit detection. | * '''Bias resistors''' (typically 560Ω to 1kΩ) are recommended to hold the bus at a defined state when no device is transmitting, preventing false start bit detection on an idle line. | ||
* '''Maximum cable length''' depends on baud rate (see Baud Rate section), but 1200 metres at 9600 baud is a typical practical limit for standard twisted pair cable. | * '''Maximum cable length''' depends on baud rate (see Baud Rate section), but 1200 metres at 9600 baud is a typical practical limit for standard twisted pair cable. | ||
* The maximum number of addressable slave devices is 247, though the RS-485 electrical standard supports up to 32 unit loads on a single segment without repeaters (256 with 1/8 unit load devices). | * The maximum number of addressable slave devices is 247, though the RS-485 electrical standard supports up to 32 unit loads on a single segment without repeaters (256 with 1/8 unit load devices). | ||
| Line 579: | Line 818: | ||
=== Common Issues and Troubleshooting === | === Common Issues and Troubleshooting === | ||
The following are the most frequently encountered Modbus communication problems in BMS installations: | The following are the most frequently encountered Modbus RTU communication problems in BMS installations: | ||
'''No response from slave / timeout''' | '''No response from slave / timeout''' | ||
| Line 591: | Line 830: | ||
* Check for missing or incorrectly placed termination resistors | * Check for missing or incorrectly placed termination resistors | ||
* Reduce baud rate to test if the issue is speed-related | * Reduce baud rate to test if the issue is speed-related | ||
* Check for ground loops | * Check for ground loops — ensure cable shield is grounded at one end only | ||
'''Intermittent communication''' | '''Intermittent communication''' | ||
| Line 600: | Line 839: | ||
'''Incorrect register values''' | '''Incorrect register values''' | ||
* Confirm register addressing convention — some devices use 0-based addressing, others 1-based | * Confirm register addressing convention — some devices use 0-based addressing, others 1-based | ||
* Check byte order (endianness) for 32-bit floating point values — some devices use Big Endian, others Little Endian or mixed ( | * Check byte order (endianness) for 32-bit floating point values — some devices use Big Endian, others Little Endian or mixed (byte-swapped) formats | ||
* Verify scaling factors — a register reading of 1234 may represent 12.34 depending on the device | * Verify scaling factors — a register reading of 1234 may represent 12.34 depending on the device register map | ||
---- | |||
Latest revision as of 23:47, 24 February 2026
Modbus
Overview
Modbus is an open, serial communication protocol originally developed for use in programmable logic controllers (PLCs). It has since become one of the most widely deployed industrial communication protocols in the world, particularly in Building Management Systems (BMS), SCADA environments, energy metering, and industrial automation.
Modbus operates on a master-slave (also referred to as client-server in newer documentation) architecture, where a single master device initiates all communication and one or more slave devices respond. It is valued for its simplicity, robustness, and vendor-neutral design — making it ideal for integrating devices from multiple manufacturers into a unified control system.
In BMS applications, Modbus is commonly used to communicate with energy meters, variable frequency drives (VFDs), chillers, boilers, air handling units (AHUs), and a wide range of sensors and actuators.
History
Modbus was developed in 1979 by Modicon (now a brand of Schneider Electric) for use with their programmable logic controllers. It was designed as a simple and robust protocol for serial communication between controllers on a production floor.
In 1996, Modbus over TCP/IP (Modbus TCP) was introduced, allowing the protocol to be used over modern Ethernet networks. This dramatically expanded its use cases and kept it relevant into the internet era.
In 2004, the rights to the Modbus specification were transferred to the Modbus Organization, a non-profit trade association that maintains and promotes the standard to this day. The protocol remains publicly available and royalty-free, which is a significant reason for its continued adoption over proprietary alternatives.
Today Modbus exists in three primary variants: Modbus RTU and Modbus ASCII (both for serial communication), and Modbus TCP/IP (for Ethernet communication).
Protocol Architecture
Master-Slave Model
Modbus uses a strict master-slave architecture. There is always one master and up to 247 addressable slave devices on a single serial bus. Each slave is assigned a unique address between 1 and 247. Address 0 is reserved for broadcast messages, which all slaves receive but do not respond to.
The master is always the initiator of communication. A slave device never transmits data unless it has first been queried by the master. This eliminates bus collisions and keeps the protocol deterministic — an important quality in control systems where timing and reliability are critical.
In a BMS context, the master is typically the Building Management Controller (BMC) or a gateway device, and the slaves are field devices such as energy meters, controllers, or sensors.
Request-Response Cycle
Every Modbus transaction follows a simple two-step cycle:
- The master sends a request frame to a specific slave address (or broadcast to address 0).
- The slave receives the request, processes it, and returns a response frame to the master.
If the slave receives a valid request but cannot comply (e.g. the register address does not exist), it returns an exception response containing an error code. If the master receives no response within its configured timeout window, it may retry the request or log a communication fault.
A full Modbus RTU frame consists of the following fields in order:
| Field | Size | Description |
|---|---|---|
| Device Address | 1 byte | Slave address (1–247), or 0 for broadcast |
| Function Code | 1 byte | Defines the type of action requested |
| Data | Variable | Register addresses, values, or quantities |
| CRC | 2 bytes | Error checking (Cyclic Redundancy Check) |
Transmission Modes
Modbus RTU
Modbus RTU (Remote Terminal Unit) is the most common implementation of the protocol in BMS and industrial environments. Data is transmitted as binary bytes over an RS-232 or RS-485 serial connection.
Each byte of data is encoded as a single binary character. The framing of messages is defined by silent intervals (gaps) between characters — a gap of at least 3.5 character times signals the start or end of a message frame.
RTU is preferred over ASCII for most applications because it is more efficient: it transmits the same data in roughly half the number of bytes, making better use of available bandwidth.
Key characteristics:
- Binary encoding
- CRC-16 error checking
- Message framing via inter-character silence (3.5 character times)
- Physical layer: RS-485 (most common), RS-232, RS-422
Modbus ASCII
Modbus ASCII transmits data as ASCII text characters rather than raw binary. Each byte of data is represented as two hexadecimal ASCII characters (e.g. the byte 0x5B is sent as the characters 5 and B).
ASCII mode uses a colon (:) as a start delimiter and CR/LF characters as an end delimiter. It uses LRC (Longitudinal Redundancy Check) for error checking rather than CRC.
ASCII mode is less bandwidth-efficient than RTU but is easier to debug with simple terminal tools and is more tolerant of timing variations on noisy or slow serial lines. It is rarely used in modern BMS installations.
Modbus TCP/IP
Modbus TCP/IP wraps the standard Modbus PDU (Protocol Data Unit) inside a TCP packet and transmits it over a standard Ethernet network. It uses port 502 by default.
The serial-specific fields (device address and CRC) are replaced by a MBAP header (Modbus Application Protocol header), which contains a transaction identifier, protocol identifier, message length, and unit identifier.
Modbus TCP removes the need for RS-485 wiring and allows Modbus devices to be integrated into IP-based building networks. Many modern BMS gateways and energy meters support Modbus TCP natively or via an RS-485-to-Ethernet converter.
Communication Parameters
Communication parameters are the settings that define how data is physically transmitted between the master and slave devices on a serial Modbus network. All devices on the same bus must be configured with identical parameters — a mismatch in any one setting will cause communication failures.
The parameters covered here apply primarily to Modbus RTU and Modbus ASCII (serial variants). Modbus TCP uses standard IP networking parameters instead.
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 with poor quality cable |
| 2400 | 2,400 bps | Older meters and sensors |
| 4800 | 4,800 bps | Moderate speed legacy devices |
| 9600 | 9,600 bps | Most common default — widely supported by all devices |
| 19200 | 19,200 bps | Standard BMS installations with moderate device counts |
| 38400 | 38,400 bps | Higher performance systems |
| 57600 | 57,600 bps | Fast networks, shorter cable runs |
| 115200 | 115,200 bps | Maximum practical speed for RS-485 in most installations |
The default baud rate for Modbus RTU is 9600 bps, though 19200 bps is very common in BMS environments.
How Baud Rate Affects the Signal
Each bit occupies a fixed time window called the bit period, calculated as:
Bit Period = 1 / Baud Rate
At 9600 baud each bit lasts approximately 104 microseconds. At 115200 baud each bit lasts approximately 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
Higher baud rates require better quality cabling and are more sensitive to signal degradation over distance. As a general rule:
- 9600 baud — reliable up to approximately 1200 metres on shielded RS-485 cable
- 19200 baud — reliable up to approximately 600 metres
- 115200 baud — reliable up to approximately 100 metres
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 Parity | E | Parity bit set so total count of 1s in data + parity = even |
| Odd Parity | O | Parity bit set so total count of 1s in data + parity = odd |
| No Parity | N | No parity bit. Error checking relies on CRC-16 only. 2 stop bits recommended per Modbus spec. |
| Mark Parity | M | Parity bit always 1. Rarely used. |
| Space Parity | S | Parity bit always 0. Rarely used. |
The Modbus RTU specification recommends Even parity as the default. However 8N1 (no parity, 1 stop bit) is the de facto standard for most BMS devices in practice.
|
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 — shown with Even parity applied to every byte. Parity rule: count the 1s in D0–D7. If count is odd → parity bit = 1. If count is even → parity bit = 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. |
Parity Limitation
Parity can only detect an odd number of bit errors. If two bits flip simultaneously the parity check still passes even though the data is corrupted. This is why Modbus RTU also uses CRC-16 at the message level — the two mechanisms complement each other.
Data Bits
Data bits refers to 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 floating point values).
|
Modbus RTU — Bit-Level Breakdown: Data Bit Weighting Example byte: Modbus RTU transmits data bits LSB first (D0 transmitted 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 with a scale factor of ÷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 RTU 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 bit = 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 (LSB first): [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 Modbus RTU |
| Hardware RTS/CTS | Physical signal lines | Only on RS-232 point-to-point connections |
| Software XON/XOFF | In-band bytes 0x11 / 0x13 | ✗ Incompatible — Modbus RTU is a binary protocol |
| RTS as DE toggle | RTS drives RS-485 DE pin | ✓ Used by USB-RS485 adapters for direction switching |
|
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 owns the bus. Only one device may drive the bus at any 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 (~3.6ms at 9600 baud)
marks end of Modbus RTU 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 at 9600 baud) — master releases bus — slave responds —]
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 1 1 0 1 0 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 — high byte first): Register 1 (40017): High byte 0x00 = 0 × 256 = 0 Low byte 0xC8 = = 200 Value = 0 + 200 = 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
The silent gap between the master frame and slave response is critical. If the master releases its driver too late it will collide with the slave's response. If the slave responds before the gap completes, the master will misinterpret the response as a continuation of its own transmission. |
Data Model
Modbus organises data into four distinct object types, each with a defined address space. The master accesses these objects using function codes.
Coils
Coils are single-bit read/write values. They represent binary output states such as a relay, a digital output, or an on/off command. The address range is 1–9999 (using 1-based Modbus convention) or 0x0000–0xFFFF (using the 0-based PDU addressing).
Examples in BMS: fan on/off command, valve open/close output, alarm reset.
Discrete Inputs
Discrete inputs are single-bit read-only values. They represent binary input states such as a digital sensor, a status contact, or a switch position. Address range: 10001–19999.
Examples in BMS: door open sensor, high temperature alarm contact, filter dirty status.
Holding Registers
Holding registers are 16-bit read/write registers. They are the most commonly used data type in Modbus and can hold values such as setpoints, configurations, and output values. Address range: 40001–49999.
A single holding register holds values from 0 to 65535 (unsigned) or -32768 to 32767 (signed). For larger values such as floating point temperatures or energy readings, two consecutive registers are used together to form a 32-bit value.
Examples in BMS: temperature setpoint, VFD speed reference, valve position command.
Input Registers
Input registers are 16-bit read-only registers. They represent measured or computed values provided by the slave device. Address range: 30001–39999.
Examples in BMS: supply air temperature, room CO₂ concentration, energy meter active power reading.
Function Codes
Function codes tell the slave device what action to perform. They are carried in the second byte of every Modbus RTU frame.
Read Functions
| Function Code | Hex | Name | Reads |
|---|---|---|---|
| 01 | 0x01 | Read Coils | Coils (1-bit, read/write) |
| 02 | 0x02 | Read Discrete Inputs | Discrete Inputs (1-bit, read-only) |
| 03 | 0x03 | Read Holding Registers | Holding Registers (16-bit, read/write) |
| 04 | 0x04 | Read Input Registers | Input Registers (16-bit, read-only) |
Write Functions
| Function Code | Hex | Name | Writes |
|---|---|---|---|
| 05 | 0x05 | Write Single Coil | Single coil ON (0xFF00) or OFF (0x0000) |
| 06 | 0x06 | Write Single Register | Single holding register |
| 15 | 0x0F | Write Multiple Coils | Multiple coils |
| 16 | 0x10 | Write Multiple Registers | Multiple holding registers |
Diagnostic Functions
| Function Code | Hex | Name | Purpose |
|---|---|---|---|
| 07 | 0x07 | Read Exception Status | Returns device error status byte |
| 08 | 0x08 | Diagnostics | Various sub-functions for testing comms |
| 11 | 0x0B | Get Comm Event Counter | Returns count of successful messages |
| 17 | 0x11 | Report Server ID | Returns device type and status |
Error Handling
CRC and LRC Checking
CRC-16 (Cyclic Redundancy Check) is used in Modbus RTU. It is a 16-bit error detection value calculated from all bytes in the message excluding the CRC bytes themselves. The master calculates the CRC before sending, and the slave recalculates it upon receipt. If the values do not match, the message is discarded silently — no response is sent, and the master will timeout.
The CRC polynomial used is: 0xA001 (reflected form of CRC-16-IBM).
LRC (Longitudinal Redundancy Check) is used in Modbus ASCII only. It is calculated as the two's complement of the sum of all byte values in the message. It is simpler but less robust than CRC-16.
Exception Codes
When a slave receives a valid request but cannot fulfil it, it returns an exception response. The function code in the response has its most significant bit set to 1 (i.e. the original function code + 0x80), followed by an exception code byte.
| Exception Code | Hex | Name | Meaning |
|---|---|---|---|
| 01 | 0x01 | Illegal Function | The function code is not supported by this device |
| 02 | 0x02 | Illegal Data Address | The register address does not exist on this device |
| 03 | 0x03 | Illegal Data Value | The value in the data field is not permitted |
| 04 | 0x04 | Slave Device Failure | An unrecoverable error occurred in the slave |
| 05 | 0x05 | Acknowledge | Slave accepted the request but needs more time |
| 06 | 0x06 | Slave Device Busy | Slave is busy processing a previous request |
| 08 | 0x08 | Memory Parity Error | Slave detected a parity error reading extended memory |
| 10 | 0x0A | Gateway Path Unavailable | Gateway could not allocate a path (Modbus TCP) |
| 11 | 0x0B | Gateway Target No Response | Target device on gateway failed to respond |
Modbus in BMS Applications
Typical Devices
Modbus is one of the most commonly encountered protocols when integrating third-party field devices into a BMS. The following device types frequently use Modbus RTU as their primary or secondary communication interface:
- Energy meters — active power (kW), reactive power (kVAR), energy (kWh), power factor, voltage and current readings via input registers
- Variable Frequency Drives (VFDs) — speed reference (Hz or %), run/stop command, fault status, output frequency and current feedback
- Chillers and heat pumps — leaving water temperature, setpoint, mode command, alarm status
- Air Handling Units (AHUs) — supply/return air temperature, damper position, fan speed feedback
- CO₂ and air quality sensors — CO₂ concentration (ppm), temperature, humidity
- Heat meters — flow rate, inlet/outlet temperatures, cumulative energy
- UPS systems — battery voltage, charge level, load percentage, alarm status
- Generator controllers — run status, fuel level, output voltage, frequency
Wiring and Topology
Modbus RTU on RS-485 uses a daisy-chain bus topology. Devices are connected in series from the first to the last node. The two conductors of the RS-485 pair are labelled A (−) and B (+), though manufacturers often use inconsistent naming — some label them D− / D+, or even reverse the A/B convention entirely.
Key wiring rules:
- Termination resistors of 120Ω must be fitted at both physical ends of the RS-485 bus to prevent signal reflections. Only the two end devices are terminated — never intermediate devices.
- Bias resistors (typically 560Ω to 1kΩ) are recommended to hold the bus at a defined state when no device is transmitting, preventing false start bit detection on an idle line.
- Maximum cable length depends on baud rate (see Baud Rate section), but 1200 metres at 9600 baud is a typical practical limit for standard twisted pair cable.
- The maximum number of addressable slave devices is 247, though the RS-485 electrical standard supports up to 32 unit loads on a single segment without repeaters (256 with 1/8 unit load devices).
- Shielded twisted pair (STP) cable is strongly recommended in BMS environments due to electrical noise from motors, VFDs, and other inductive loads.
Common Issues and Troubleshooting
The following are the most frequently encountered Modbus RTU communication problems in BMS installations:
No response from slave / timeout
- Verify slave address matches the configured address on the physical device
- Check baud rate, parity, data bits, and stop bits match on master and slave
- Check A/B wiring polarity — reversed polarity is a very common installation error
- Verify termination resistors are fitted at the correct endpoints only
CRC errors / corrupted data
- Inspect cable routing — avoid running alongside power cables or VFD output cables
- Check for missing or incorrectly placed termination resistors
- Reduce baud rate to test if the issue is speed-related
- Check for ground loops — ensure cable shield is grounded at one end only
Intermittent communication
- Often caused by missing bias resistors — the bus floats to an indeterminate state when idle
- Check for multiple termination resistors incorrectly fitted to intermediate devices
- Investigate RTS/driver enable timing issues if using a custom RS-485 adapter
Incorrect register values
- Confirm register addressing convention — some devices use 0-based addressing, others 1-based
- Check byte order (endianness) for 32-bit floating point values — some devices use Big Endian, others Little Endian or mixed (byte-swapped) formats
- Verify scaling factors — a register reading of 1234 may represent 12.34 depending on the device register map