Modbus通訊協定訊息分析詳解
一、Modbus協議概述
Modbus 是一種串列通訊協議,由 Modicon(現為施耐德電機)於 1979 年開發,用於可程式邏輯控制器 (PLC) 之間的通訊。經過四十多年的發展,它已成為工業自動化領域事實上的標準通訊協定之一。
主要特點:
- 主/從架構:一個主主機和多個從主機,其中主主機發起請求,從主機被動回應。
- 開放協議:無需支付許可費;任何製造商都可以免費實施。
- 多種傳輸模式:RTU(二進位)、ASCII(文字) TCP(乙太網路)。
- 支援多種實體層:RS485(最常用)、RS232、TCP/IP(乙太網路)。
- 清晰的資料模型:四種物件類型-線圈、離散輸入、輸入暫存器和保持暫存器。
1.1三種協議變體的比較

1.2 協議規範約束

二、Modbus 資料模型與位址規則
2.1 四種類型的操作對象

2.2 地址表示(至關重要!)
不同的製造商採用不同的編碼方案。 Modbus 位址這是實際應用中最常見的陷阱:

地址前綴含義(行業標準表示法):
- 從 0 開始:協定位址(十六進位),例如 0x0000
- 4 xxxx:維護暫存器;例如,40001 表示維護暫存器 0 的協定位址。
- 3xxxx:輸入暫存器;例如,30001 表示輸入暫存器 0 的協定位址。
- 0xxxx:線圈;例如,00001 表示線圈 0 的協定位址。
- 1xxxx:離散輸入;例如,10001 表示離散輸入 0 的協定位址。
2.3 大數據處理規則
1.1. 每次操作讀取的最大暫存器數:125(功能碼 0x03/0x04)
2.2. 每次操作讀取的最大線圈/離散輸入數:2000(功能代碼 0x01/0x02)
3.3. 每次操作寫入的最大暫存器數:123(功能碼 0x10)
4.4. 每次操作寫入的最大線圈數:1968(功能代碼 0x0F)
5.5. 超出這些限制需要基於區塊的讀寫操作;確保交錯輪詢間隔以避免總線衝突。
三、Modbus RTU訊息的詳細分析
遠端終端單元 (RTU) 是應用最廣泛的終端單元。 Modbus傳輸協定 在工業應用中,採用二進位編碼以實現高傳輸效率並與 RS485/RS232 串列介面相容。幀之間至少需要 3.5 個字元的靜默間隔來區分幀邊界。
3.1 RTU的完整框架結構
RTU 中的完整指令資料幀由以下四個部分組成:

3.2 RTU 訊息範例 – 讀取保持暫存器(功能碼 0x03)
[請求訊息] 主裝置讀取從裝置 1 的保持暫存器(起始位址 0x0001,讀取 3 個暫存器):
01 03 00 01 00 03 54 0B
對請求訊息進行逐字節分析:

[回應訊息] 從站 1 傳回來自三個暫存器的資料:01、03、06、02、2B、00、00、00、64、D5、87
逐字節解析回應訊息:

3.3 標準功能代碼的詳細說明

3.4 各功能代碼訊息格式的詳細說明
(1)讀取線圈/讀取離散輸入(功能碼 0x01/0x02)
[請求幀格式](共 8 位元組)

[回應幀格式]

【位置分析範例】響應資料由 3 個位元組組成(線圈 20–38):

(2)讀取保持暫存器/讀取輸入暫存器(功能碼 0x03/0x04)
[請求幀格式](共 8 位元組)

[回應幀格式]

(3)寫入單一保持暫存器(功能碼 0x06)
[請求幀格式](共 8 位元組)

[回應訊框] 與請求訊框相同,用於確認寫入成功。
(4)寫入多個維護暫存器(功能碼 0x10)
[請求幀格式]

[回應幀格式](共 8 位元組):[位址][0x10][起始位址(2 位元組)][編號(2 位元組)][CRC(2 位元組)]
(5)寫入單一線圈(功能碼:0x05)
[請求幀格式](共 8 個位元組):寫入時僅支援兩個有效值:
- 0xFF00 – 線圈設定為開啟(閉合)
- 0x0000 – 線圈設定為關閉(開路)

[回應訊框] 與請求訊框相同,用於確認寫入成功。
IV. Modbus TCP 訊息的詳細分析
Modbus TCP 是基於乙太網路 TCP/IP 協定運行,預設使用連接埠 502。與 RTU 模式不同,Modbus TCP 使用 MBAP(Modbus 應用協定)標頭來定義訊息邊界,並且不需要 CRC 校驗(而是依賴 TCP 協定內建的錯誤檢查機制)。
4.1 MBAP 頭部結構(7 位元組,包含在所有 TCP 封包中)

4.2 Modbus TCP 訊息範例
[請求訊息] 讀取從裝置 1 的保持暫存器(起始位址 0x0000,讀取 3 個值):
00 01 00 00 00 06 01 03 00 00 00 03
逐字段分析請求訊息:

[回應訊息] 從裝置傳回來自三個暫存器的資料:
00 01 00 00 00 09 01 03 06 02 2B 00 00 00 64
逐字段解析回應訊息:

4.3 RTU 和 TCP 封包結構的核心區別

五、CRC-16 校驗的詳細說明
CRC(循環冗餘校驗)演算法是 Modbus RTU 的幀驗證機制,旨在偵測訊息傳輸過程中的錯誤。 CRC-16 產生多項式為 x¹⁶ + x¹⁵ + x² + 1(等價於 0x8005);實際應用中,通常使用其逆多項式 0xA001。
5.1 CRC-16 計算步驟
6.1. 將16位元CRC暫存器初始化為0xFFFF
7.2. 對第一位元組和 CRC 暫存器的低 8 位元執行異或運算;將結果儲存回 CRC 暫存器的低 8 位元。
8.3. 將 CRC 暫存器右移 1 位,高位填入 0;確定移出的位元(LSB):
9.4. 若移出位為 0:則繼續右移一位
10.5. 如果移出位元為 1:對 CRC 暫存器和 0xA001 執行異或運算
11.6. 重複步驟 3,直到處理完目前位元組的所有 8 位元。
12.7. 處理下一個位元組,重複步驟 2-6,直到所有位元組都處理完畢。 13.8. 完成所有位元組處理後,交換 CRC 暫存器的高位元組和低位元組,得到最終校驗和。
14.9. 將最終校驗和附加到訊息末尾,低位元組在前,高位元組在後。
5.2 CRC-16 計算範例(Python 程式碼)
以下Python函數可直接用於計算Modbus RTU的CRC-16校驗和:
def modbus_crc16(data: bytes) -> bytes:
"""計數 Modbus RTU CRC-16 校驗碼(返回前兩個字節,最低有效位在前)"""
crc = 0xFFFF
對於數據中的每個位元組:
CRC ^= 位元組
for _ in range(8):
如果 CRC 和 0x0001:
crc = (crc >> 1) ^ 0xA001
別的:
CRC >>= 1
# 先返回低字節,再返回高字節
返回 bytes([crc & 0xFF, (crc >> 8) & 0xFF])
# 給出典型範例:計數 01 03 00 01 00 03 CRC
frame = bytes([0x01, 0x03, 0x00, 0x01, 0x00, 0x03])
crc = modbus_crc16(frame)
print(crc.hex()) # export:540b( 0x54 0x0B)
5.3 訊息中 CRC 校驗的位置
以請求訊息 01 03 00 01 00 03 54 0B 為例:

接收器重新計算前 6 個位元組的 CRC 值,並與封包的最後 2 個位元組進行比較。如果兩者不同,則偵測到傳輸錯誤,並且不回傳回應。
Ⅵ.異常處理和錯誤代碼詳情
當從站無法執行來自主站的請求時(例如,由於不支援的功能碼或位址超出範圍),它會傳回一個異常回應訊框。異常響應功能碼等於原始功能碼 + 0x80(即,最高位元設定為 1)。
6.1 異常應答格式 (RTU)
[從機位址][功能碼 | 0x80][異常碼][CRC-16]

6.2 標準異常代碼的含義

6.3 異常反應範例
【場景】主站請求從從站 1 讀取無效位址的暫存器,從站傳回異常:
請求:01 03 00 20 00 01 XX XX(讀取位址 0x0020 處的暫存器);
異常響應:01 83 02 C0 F1;
異常響應的逐字節分解:

六、Modbus ASCII 訊息簡介
Modbus ASCII 模式將所有資料位元組編碼為兩個 ASCII 字元(例如,0x0F 編碼為“0F”)。雖然可讀性很高,但傳輸效率較低,主要用於調試目的。
7.1 ASCII 幀結構
完整的ASCII幀格式:

7.2LRC驗證演算法
計算縱向冗餘校驗 (LRC) 的步驟:
15.1. 對位址欄位、功能碼和資料欄位的所有位元組(不包括起始標記和結束標記)執行二進位求和。
16.2.忽略進位位(如果結果超過 255,則自動循環並僅保留低 8 位)。
17.3. 計算求和結果模 1 的補(即 0xFF − sum)。
18.4. 將補碼加 1 得到最終的 LRC 校驗碼(1 位元組)。
19.5. 將 LRC 校驗碼編碼為兩個 ASCII 字符,並將它們附加到訊息的末尾。
八、實用技巧和常見問題
8.1 位元組序(大端/微端)問題
這 Modbus 標準 使用大端字節序(高位元組在前)傳輸 16 位元或更長的資料。某些裝置(例如,某些基於 ARM 的裝置)使用微端字節序;需要進行位元組序轉換以避免數值解析錯誤。

8.2 註冊單元大小限制(PDU ≤ 253 位元組)

8.3 通訊逾時和重試配置建議
推薦配置:
- 輪詢間隔:設定為從裝置最大回應時間的 2-3 倍(通常為 100-500 毫秒)• 逾時時間:建議範圍為 1000-3000 毫秒,可根據從裝置回應速度進行調整
- 重試次數:建議限制為 3 次;如果失敗則觸發警報
- 多重從站輪詢:在從站之間安排交錯的輪詢間隔,以防止總線衝突。 • 位元組逾時:在 RTU 模式下,超過 1.5 個字元時間的字元間隔被視為訊框錯誤。
8.4 推薦的調試工具
附錄:通用訊息快速參考表
讀取線圈(功能代碼 0x01)
[請求]:從站 1 讀取起始位址 0(線圈 1)處的數據,取得 8 條線圈狀態資訊:01 01 00 00 00 08 3D CC
[響應]:八個線圈狀態:0x55 (01010101B);
訊息:01 01 01 55 94 36
向單一線圈寫入資料(功能碼 0x05)
[請求] 將位址 0 處的線圈設定為 ON(寫入值 0xFF00):
訊息:01 05 00 00 FF 00 8C 3A
[請求] 將位址 0 處的線圈設定為關閉(寫入值 0x0000):
訊息:01 05 00 00 00 00 CD CA
[回應] 與請求幀相同
向單一暫存器寫入資料(功能碼 0x06):
[請求] 將值 0x1234 寫入位址為 0 的暫存器。
訊息:01 06 00 00 12 34 XX XX(XX 為 CRC,待計算)。
[回應] 與請求幀相同。
寫入多個暫存器(功能碼 0x10)
[請求]:從位址 0 開始寫入兩個暫存器值 0x1234 和 0xABCD。
訊息:01 10 00 00 00 02 04 12 34 AB CD XX XX
[回應]:傳回起始位址和寫入的暫存器數量。
訊息:01 10 00 00 00 02 41 C8
讀取輸入暫存器(功能碼 0x04)
[請求]:從機 1 讀取起始位址 0 處的數據,讀取兩個輸入暫存器
訊息:01 04 00 00 00 02 71 CB
[回應]:傳回兩個輸入暫存器值
訊息:01 04 04 00 64 01 90 XX XX(傳回值:100 和 400)










