參考資料#
各博客網站都是缺章少句,排版混亂,偶有錯誤,不建議參考。
這裡有 modbus 中文網的資料
MODBUS 協議中文版 / 英文版預覽及下載 | Modbus 物聯網雲平台
更權威的是官方文檔
MODBUS Application Protocol 1 1 b
以及官方其他文檔,在這裡尋找
當然官方文檔我也不是很滿意,為了保持他們的遠古設計架構,各個幀段的介紹不直觀了,同時還有少量筆誤。
下面有自己重寫的有效信息。
另外有我自己實現的庫
stbanana/modbusX: modbus protocol support (github.com)
協議特性#
整體幀結構#
報文頭 | 地址域 | 功能碼 | 數據域 | 校驗域 | |
---|---|---|---|---|---|
RTU | 1 字節 (從機 ID) | 1 字節 | n 字節不定 | 2 字節 (對其他所有內容 CRC-MB16) | |
TCP | 6 字節 (2 字節事務號 + 2 字節協議標識 - 就是全 0+ 2 字節後續字節總長 - 包括從機 ID) | 1 字節 (從機 ID) | 1 字節 | n 字節不定 |
關於報文頭,在官方文檔的總體模型中劃分到地址域的,但實際上的字節總長又包括從機 ID。大概是早期只有串口協議時,整體模型已經定下了,新加的 modbusTCP 只能為了兼容亂搞,
我這裡直接重新歸類好了,不標準,但我樂意。
寄存器屬性#
RW 屬性 | bit 位數 | |
---|---|---|
線圈 (0x01) | 可讀寫 | 1 位 bit |
離散輸入寄存器 (0x02) | 只讀 | 1 位 bit |
保持寄存器 (0x03) | 可讀寫 | 16 位 bit |
輸入寄存器 (0x04) | 只讀 | 16 位 bit |
整體模型#
使用軟件抽象一個內存結構,每個地址存有不同意義的數據,和 FPGA 模擬 SRAM 是一個道理。不過每個寄存器地址不都是 16 位,也可以是 1 位。屬性是 RO\RW。
RTU 系列#
(0x01) 讀線圈#
主機請求#
地址域 | 功能碼 | 起始地址 | 線圈數量 | 校驗域 |
---|---|---|---|---|
1 字節 | 1 字節 | 2 字節 | 2 字節 | 2 字節 (CRC-MB16) |
從機響應#
地址域 | 功能碼 | 字節數 | 線圈狀態 | 校驗域 |
---|---|---|---|---|
1 字節 | 1 字節 | 1 字節 (計算線圈狀態部分的字節數) | n 字節 | 2 字節 (CRC-MB16) |
示例#
請求讀 20~38 地址的線圈數據,總之回覆地址是由低到高,最終字節不齊在高位填 0。
[!NOTE]
最終的輸出狀態 38-36 回覆字節,用零填充 5 個剩餘 bit(一直到高位端)。
請求 | 響應 | ||
---|---|---|---|
地址域 (從機 ID) | 0x01 | 地址域 (從機 ID) | 0x01 |
功能碼 | 0x01 | 功能碼 | 0x01 |
起始地址高 8 位 | 0x00 | 字節數 | 0x03 |
起始地址低 8 位 | 0x13 | 輸出狀態 27-20 | 0xCD |
輸出數量高 8 位 | 0x00 | 輸出狀態 35-28 | 0x6B |
輸出數量低 8 位 | 0x13 | 輸出狀態 38-36 | 0x05 |
校驗 CRC 低 8 位 | 0xA9 | 校驗 CRC 低 8 位 | 0x42 |
校驗 CRC 高 8 位 | 0xC8 | 校驗 CRC 高 8 位 | 0x82 |
(0x02) 讀離散輸入寄存器#
主機請求#
地址域 | 功能碼 | 起始地址 | 離散輸入數量 | 校驗域 |
---|---|---|---|---|
1 字節 | 1 字節 | 2 字節 | 2 字節 | 2 字節 (CRC-MB16) |
從機響應#
地址域 | 功能碼 | 字節數 | 離散輸入狀態 | 校驗域 |
---|---|---|---|---|
1 字節 | 1 字節 | 1 字節 (計算離散輸入狀態部分的字節數) | n 字節 | 2 字節 (CRC-MB16) |
示例#
請求讀 197~218 地址的離散輸入寄存器數據,總之回覆地址是由低到高,最終字節不齊在高位填 0。
[!NOTE]
最終的輸入狀態 218-213 回覆字節,用零填充 2 個剩餘 bit(一直到高位端)。
請求 | 響應 | ||
---|---|---|---|
地址域 (從機 ID) | 0x01 | 地址域 (從機 ID) | 0x01 |
功能碼 | 0x02 | 功能碼 | 0x02 |
起始地址高 8 位 | 0x00 | 字節數 | 0x03 |
起始地址低 8 位 | 0xC4 | 輸入狀態 204-197 | 0xAC |
輸出數量高 8 位 | 0x00 | 輸入狀態 212-205 | 0xDB |
輸出數量低 8 位 | 0x16 | 輸入狀態 218-213 | 0x35 |
校驗 CRC 低 8 位 | 0xB8 | 校驗 CRC 低 8 位 | 0x22 |
校驗 CRC 高 8 位 | 0x39 | 校驗 CRC 高 8 位 | 0x88 |
(0x03) 讀保持寄存器#
主機請求#
地址域 | 功能碼 | 起始地址 | 保持寄存器數量 | 校驗域 |
---|---|---|---|---|
1 字節 | 1 字節 | 2 字節 | 2 字節 | 2 字節 (CRC-MB16) |
從機響應#
地址域 | 功能碼 | 字節數 | 保持寄存器狀態 | 校驗域 |
---|---|---|---|---|
1 字節 | 1 字節 | 1 字節 (計算保持寄存器狀態部分的字節數) | n 字節 | 2 字節 (CRC-MB16) |
示例#
請求讀 108~110 地址的保持寄存器數據
請求 | 響應 | ||
---|---|---|---|
地址域 (從機 ID) | 0x01 | 地址域 (從機 ID) | 0x01 |
功能碼 | 0x03 | 功能碼 | 0x03 |
起始地址高 8 位 | 0x00 | 字節數 | 0x06 |
起始地址低 8 位 | 0x6B | 寄存器值高八位(108) | 0x02 |
寄存器數量高 8 位 | 0x00 | 寄存器值低八位(108) | 0x2B |
寄存器數量低 8 位 | 0x03 | 寄存器值高八位(109) | 0x00 |
校驗 CRC 低 8 位 | 0x74 | 寄存器值低八位(109) | 0x00 |
校驗 CRC 高 8 位 | 0x17 | 寄存器值高八位(110) | 0x00 |
寄存器值低八位(110) | 0x64 | ||
校驗 CRC 低 8 位 | 0x05 | ||
校驗 CRC 高 8 位 | 0x7A |
(0x04) 讀輸入寄存器#
主機請求#
地址域 | 功能碼 | 起始地址 | 輸入寄存器數量 | 校驗域 |
---|---|---|---|---|
1 字節 | 1 字節 | 2 字節 | 2 字節 | 2 字節 (CRC-MB16) |
從機響應#
地址域 | 功能碼 | 字節數 | 輸入寄存器狀態 | 校驗域 |
---|---|---|---|---|
1 字節 | 1 字節 | 1 字節 (計算輸入寄存器狀態部分的字節數) | n 字節 | 2 字節 (CRC-MB16) |
示例#
請求讀 9~10 地址的保持寄存器數據
請求 | 響應 | ||
---|---|---|---|
地址域 (從機 ID) | 0x01 | 地址域 (從機 ID) | 0x01 |
功能碼 | 0x04 | 功能碼 | 0x04 |
起始地址高 8 位 | 0x00 | 字節數 | 0x06 |
起始地址低 8 位 | 0x6B | 寄存器值高八位(9) | 0x02 |
寄存器地址高 8 位 | 0x00 | 寄存器值低八位(9) | 0x2B |
寄存器地址低 8 位 | 0x03 | 寄存器值高八位(10) | 0x00 |
校驗 CRC 低 8 位 | 0xC1 | 寄存器值低八位(10) | 0x00 |
校驗 CRC 高 8 位 | 0xD7 | 校驗 CRC 低 8 位 | 0xF3 |
校驗 CRC 高 8 位 | 0xF4 |
(0x05) 寫單個線圈#
[!NOTE]
寫單個線圈,輸出值部分僅允許 FF 00 表示 ON、00 00 表示 OFF,其他值都不合法
主機請求#
地址域 | 功能碼 | 輸出地址 | 輸出值 | 校驗域 |
---|---|---|---|---|
1 字節 | 1 字節 | 2 字節 | 2 字節 | 2 字節 (CRC-MB16) |
從機響應#
地址域 | 功能碼 | 地址 | 輸出值 | 校驗域 |
---|---|---|---|---|
1 字節 | 1 字節 | 2 字節 | 2 字節 | 2 字節 (CRC-MB16) |
示例#
請求寫 173 地址的線圈數據為 ON
請求 | 響應 | ||
---|---|---|---|
地址域 (從機 ID) | 0x01 | 地址域 (從機 ID) | 0x01 |
功能碼 | 0x05 | 功能碼 | 0x05 |
寄存器地址高 8 位 | 0x00 | 寄存器地址高 8 位 | 0x00 |
寄存器地址低 8 位 | 0xAC | 寄存器地址低 8 位 | 0xAC |
寄存器值高 8 位 | 0xFF | 寄存器值高 8 位 | 0xFF |
寄存器值低 8 位 | 0x00 | 寄存器值低 8 位 | 0x00 |
校驗 CRC 低 8 位 | 0x4C | 校驗 CRC 低 8 位 | 0x4C |
校驗 CRC 高 8 位 | 0x1B | 校驗 CRC 高 8 位 | 0x1B |
(0x06) 寫單個保持寄存器#
主機請求#
地址域 | 功能碼 | 保持寄存器地址 | 寄存器值 | 校驗域 |
---|---|---|---|---|
1 字節 | 1 字節 | 2 字節 | 2 字節 | 2 字節 (CRC-MB16) |
從機響應#
地址域 | 功能碼 | 保持寄存器地址 | 寄存器值 | 校驗域 |
---|---|---|---|---|
1 字節 | 1 字節 | 2 字節 | 2 字節 | 2 字節 (CRC-MB16) |
示例#
請求寫 2 地址的保持寄存器數據為 0x0003
請求 | 響應 | ||
---|---|---|---|
地址域 (從機 ID) | 0x01 | 地址域 (從機 ID) | 0x01 |
功能碼 | 0x06 | 功能碼 | 0x06 |
寄存器地址高 8 位 | 0x00 | 寄存器地址高 8 位 | 0x00 |
寄存器地址低 8 位 | 0x02 | 寄存器地址低 8 位 | 0x02 |
寄存器值高 8 位 | 0x00 | 寄存器值高 8 位 | 0x00 |
寄存器值低 8 位 | 0x03 | 寄存器值低八位 | 0x03 |
校驗 CRC 低 8 位 | 0x2C | 校驗 CRC 低 8 位 | 0x2C |
校驗 CRC 高 8 位 | 0x0B | 校驗 CRC 高 8 位 | 0x0B |
(0x0F) 寫多個線圈#
主機請求#
地址域 | 功能碼 | 起始地址 | 設置數量 | 字節數 | 設置值 | 校驗域 |
---|---|---|---|---|---|---|
1 字節 | 1 字節 | 2 字節 | 2 字節 | 1 字節 (計算設置值部分的字節數) | n 字節 | 2 字節 (CRC-MB16) |
從機響應#
地址域 | 功能碼 | 起始地址 | 設置數量 | 校驗域 |
---|---|---|---|---|
1 字節 | 1 字節 | 2 字節 | 2 字節 | 2 字節 (CRC-MB16) |
示例#
請求寫 20 地址開始的 10 個線圈
[!NOTE]
共需寫入 2 字節 (16bit),用零填充 6 個剩餘 bit(一直到高位端)。
線圈地址 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | — | — | — | — | — | — | 29 | 28 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
對應值 | 1 | 1 | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
依據規範填充後,實際設置值段應當是 0xCD 0x01
請求 | 響應 | ||
---|---|---|---|
地址域 (從機 ID) | 0x01 | 地址域 (從機 ID) | 0x01 |
功能碼 | 0x0F | 功能碼 | 0x0F |
起始地址高 8 位 | 0x00 | 起始地址高 8 位 | 0x00 |
起始地址低 8 位 | 0x13 | 起始地址低 8 位 | 0x13 |
設置數量高 8 位 | 0x00 | 設置數量高 8 位 | 0x00 |
設置數量低 8 位 | 0x0A | 設置數量低 8 位 | 0x0A |
字節數 | 0x02 | 校驗 CRC 低 8 位 | 0x24 |
設置值 (27~20 地址) | 0xCD | 校驗 CRC 高 8 位 | 0x09 |
設置值 (29~28 地址) | 0x01 | ||
校驗 CRC 低 8 位 | 0x72 | ||
校驗 CRC 高 8 位 | 0xCB |
(0x10) 寫多個保持寄存器#
主機請求#
地址域 | 功能碼 | 起始地址 | 設置數量 | 字節數 | 設置值 | 校驗域 |
---|---|---|---|---|---|---|
1 字節 | 1 字節 | 2 字節 | 2 字節 | 1 字節 (計算設置值部分的字節數) | n 字節 | 2 字節 (CRC-MB16) |
從機響應#
地址域 | 功能碼 | 起始地址 | 設置數量 | 校驗域 |
---|---|---|---|---|
1 字節 | 1 字節 | 2 字節 | 2 字節 | 2 字節 (CRC-MB16) |
示例#
請求寫 34 地址開始的 4 個保持寄存器
請求 | 響應 | ||
---|---|---|---|
地址域 (從機 ID) | 0x01 | 地址域 (從機 ID) | 0x01 |
功能碼 | 0x10 | 功能碼 | 0x10 |
起始地址高 8 位 | 0x00 | 起始地址高 8 位 | 0x00 |
起始地址低 8 位 | 0x22 | 起始地址低 8 位 | 0x22 |
設置數量高 8 位 | 0x00 | 設置數量高 8 位 | 0x00 |
設置數量低 8 位 | 0x04 | 設置數量低 8 位 | 0x04 |
字節數 | 0x08 | 校驗 CRC 低 8 位 | 0x61 |
設置值高 8 位 (34 地址) | 0x00 | 校驗 CRC 高 8 位 | 0xC0 |
設置值低 8 位 (34 地址) | 0x40 | ||
設置值高 8 位 (35 地址) | 0x00 | ||
設置值低 8 位 (35 地址) | 0x24 | ||
設置值高 8 位 (36 地址) | 0x00 | ||
設置值低 8 位 (36 地址) | 0x01 | ||
設置值高 8 位 (37 地址) | 0xBF | ||
設置值低 8 位 (37 地址) | 0x52 | ||
校驗 CRC 低 8 位 | 0x5F | ||
校驗 CRC 高 8 位 | 0xCC |
異常響應幀#
地址域 | 功能碼 | 異常碼 | 校驗域 |
---|---|---|---|
1 字節 | 1 字節 (功能碼 + 0x80) | 1 字節 | 2 字節 (CRC-MB16) |
異常碼 | 名稱 | 含義 |
---|---|---|
0x01 | 非法功能碼 | 接收到的請求指令功能碼是不可允許的操作。可能是功能碼在其中不被支持,也可能是其正處於錯誤狀態中處理請求。 |
0x02 | 非法數據地址 | 詢問中接收到的數據地址是不可允許的地址。特別是,起始地址和傳輸長度的組合是無效的。對於帶有 100 個寄存器的控制器來說,帶有起始地址 96 和長度 4 的請求會成功,帶有起始地址 96 和長度 5 的請求將產生異常碼 0x02。 |
0x03 | 非法數據值 | 實際上是數據段非法的意思。例如非法數據段長度,或者寫入或者讀取的寄存器數量和數據段不匹配。注意的是,這並不代表寄存器被寫入一個期望範圍以外的值、實際寫入失敗 (這種情況是 0x04)。 |
0x04 | 從站設備故障 | 當伺服器 (或從站),對寄存器執行請求的操作時出現差錯,例如寄存器被寫入一個期望範圍以外的值等。 |
0x05 | 確認 | 其實並非錯誤,而是收到長耗時指令,表明已收到並開始處理. |
0x06 | 從屬設備忙 | 正在處理耗時命令在忙。(當從機空閒後,應當重發引起此錯誤的請求) |
0x08 | 存儲奇偶性差錯 | 設法讀取記錄文件,但是在存儲器中發現一個奇偶校驗錯誤。 |
0x0A | 不可用網關路徑 | 與網關一起使用,指示網關不能為處理請求分配輸入端口至輸出端口的內部通信路徑。通常意味著網關是錯誤配置的或過載的。 |
0x0B | 網關目標設備響應失敗 | 與網關一起使用,指示沒有從目標設備中獲得響應。通常意味著設備未在網絡中。 |
TCP 系列#
總之 modbusTCP 相比於 RTU 只是包裝了前面的報文頭,同時去除了 CRC 校驗👍。== 因為 TCP 管理層的鏈路傳輸已經很穩健了,而串口傳輸是不穩健的 ==
報文頭中的事務號會在主機 (TCP 客戶端) 發起請求時不斷累加,從機 (TCP 伺服器端) 響應請求會回覆相同的事務號表示處理的是什麼請求。所以 modbusTCP 是天生支持多幀連發的,主機不會因此誤解回覆👍。
個人是更喜歡這樣流暢的通信協議的。|| 信息準確性什麼的交給物理鏈路層就好了,大霧大霧,校驗還是有必要的 ||
(0x01) 讀線圈#
主機請求#
事務號 | 協議標識 | 字節總長 | 地址域 | 功能碼 | 起始地址 | 線圈數量 |
---|---|---|---|---|---|---|
2 字節 | 2 字節 (全 0) | 2 字節 (後續字節總長) | 1 字節 | 1 字節 | 2 字節 | 2 字節 |
從機響應#
事務號 | 協議標識 | 字節總長 | 地址域 | 功能碼 | 字節數 | 線圈狀態 |
---|---|---|---|---|---|---|
2 字節 | 2 字節 (全 0) | 2 字節 (後續字節總長) | 1 字節 | 1 字節 | 1 字節 (計算線圈狀態部分的字節數) | n 字節 |
示例#
請求讀 20~38 地址的線圈數據,總之回覆地址是由低到高,最終字節不齊在高位填 0。
[!NOTE]
最終的輸出狀態 38-36 回覆字節,用零填充 5 個剩餘 bit(一直到高位端)。
請求 | 響應 | ||
---|---|---|---|
事務號高 8 位 | 0x00 | 事務號高 8 位 | 0x00 |
事務號低 8 位 | 0x01 | 事務號低 8 位 | 0x01 |
協議標識 16 位 (全 0) | 0x00 0x00 | 協議標識 16 位 (全 0) | 0x00 0x00 |
字節總長高 8 位 | 0x00 | 字節總長高 8 位 | 0x00 |
字節總長低 8 位 | 0x06 | 字節總長低 8 位 | 0x06 |
地址域 (從機 ID) | 0x01 | 地址域 (從機 ID) | 0x01 |
功能碼 | 0x01 | 功能碼 | 0x01 |
起始地址高 8 位 | 0x00 | 字節數 | 0x03 |
起始地址低 8 位 | 0x13 | 輸出狀態 27-20 | 0xCD |
輸出數量高 8 位 | 0x00 | 輸出狀態 35-28 | 0x6B |
輸出數量低 8 位 | 0x13 | 輸出狀態 38-36 | 0x05 |
其他協議不贅述了
其他協議不贅述了
總之 modbusTCP 相比於 RTU 只是包裝了前面的報文頭,同時去除了 CRC 校驗👍。
此文由 Mix Space 同步更新至 xLog
原始鏈接為 https://www.yono233.cn/posts/shoot/24_7_26_modbus