Modbus由MODICON公司於1979年開發,是一種工業現場總線協議標準。1996年施耐德公司推出基於以太網TCP/IP的Modbus協議,ModbusTCP。
Modbus通信的設備分爲主站(mater)和從站(slave),主站爲主動方,從站爲被動方。
通信過程
通信的過程爲:
- 主站設備主動向從站設備發送請求
- 從站設備處理主站的請求後,向主站返回結果。
- 如果從站設備處理請求出現異常,則向主站設備返回異常功能碼。
數據傳輸方式
modbus的數據傳輸被定義爲對以下4個存儲塊的讀寫:
- 線圈(coils) 操作單位爲1位字的開關量,PLC的輸出位,在Modbus中可讀可寫
- 離散量(discreteinputs) 操作單位爲1位字的開關量,PLC的輸入位,在Modbus中只讀
- 輸入寄存器(inputregisters) 操作單位爲16位字(兩個字節)數據,PLC中只能從模擬量輸入端改變的寄存器,在Modbus中只讀
- 保持寄存器(holdingregisters) 操作單位爲16位字(兩個字節)數據,PLC中用於輸出模擬量信號的寄存器,在Modbus中可讀可寫
MODBUS-TCP報文結構
modbus-tcp報文結構:
如上圖所示:modbus-tcp的報文由MBAP+PDU組成。
MBAP報文頭
其中MBAP報文頭的組成爲:
域 | 長度 | 描述 |
---|---|---|
事務元標識符 | 2 個字節 | MODBUS 請求/響應事務處理的識別碼,主要用於在主站設備在接收到響應時能知道是哪個請求的響應 |
協議標識符 | 2 個字節 | 對於MODBUS 協議來說,這裏恆爲0 |
長度 | 2 個字節 | 以下字節的數量,也就是完整報文的字節數減去6 |
單元標識符 | 1 個字節 | 串行鏈路或其它總線上連接的遠程從站的識別碼,也就是要訪問的從站的標識號,因爲只有一個字節,所以一個主站最多隻能訪問256個從站設備 |
由上表可知,報文頭爲 7 個字節長。
PDU報文體
PDU的組成爲功能碼(一個字節)和數據(n個字節)
其中功能碼爲一個字節,modbus定義的功能碼有:
- 01 讀線圈(coils)狀態,讀取單個或多個
- 02 讀離散輸入(discreteinputs)狀態,讀取單個或多個
- 03 讀保持寄存器(holdingregisters),讀取單個或多個
- 04 讀輸入寄存器(inputregisters),讀取單個或多個
- 05 寫單個線圈(coils)狀態,單個寫入
- 06 寫單個保持寄存器(holdingregisters),單個寫入
- 15 寫多個線圈(coils),多個寫入
- 16 寫多個保持寄存器(holdingregisters),多個寫入
另外,當響應報文的功能碼最高位爲1時(即:(function & 0x80) != 0),表示爲異常響應,這時數據爲一個字節的異常碼,具體的異常碼定義有:
- 01 功能碼不能被從機識別
- 02 從機的單元標識符不正確
- 03 值不被從機接受
- 04 當從機試圖執行請求的操作時,發生了不可恢復的錯誤。
- 05 從機已接受請求並正在處理,但需要很長時間。返回此響應是爲了防止在主機中發生超時錯誤。主站可以在下一個輪詢程序中發出一個完整的消息,以確定處理是否完成。
- 06 從站正在處理長時間命令。Master應該稍後重試。
- 07 從站不能執行程序功能。主站應該向從站請求診斷或錯誤信息。
- 08 從站在內存中檢測到奇偶校驗錯誤。主設備可以重試請求,但從設備上可能需要服務。
- 10 專門用於Modbus網關。表示配置錯誤的網關。
- 11 專用於Modbus網關的響應。當從站無法響應時發送。
PDU報文詳情
1、讀線圈
請求報文:
功能碼:01,一個字節 | 偏移量offset(讀取數據的開始位置),兩個字節 | 讀取數量,兩個字節 |
---|
正常響應報文:
功能碼:01,一個字節 | 數據長度(字節數),一個字節 | 線圈狀態數據,n個字節(由於網絡傳輸的數據都是以整字節爲單位的,所以收到的數據可能比請求中要讀的位數要多,這時按位將數據轉換爲開關量,只需解析請求中讀取數量字段設定的位數就可以了) |
---|
異常響應報文:
功能碼:129(0x81),一個字節 | 異常碼,一個字節 |
---|
2、讀離散輸入
請求報文:
功能碼:02,一個字節 | 偏移量offset(讀取數據的開始位置),兩個字節 | 讀取數量,兩個字節 |
---|
正常響應報文:
功能碼:02,一個字節 | 數據長度(字節數),一個字節 | 離散輸入狀態數據,n個字節(由於網絡傳輸的數據都是以整字節爲單位的,所以收到的數據可能比請求中要讀的位數要多,這時按位將數據轉換爲開關量,只需解析請求中讀取數量字段設定的位數就可以了) |
---|
異常響應報文:
功能碼:130(0x82),一個字節 | 異常碼,一個字節 |
---|
3、讀保持寄存器
請求報文:
功能碼:03,一個字節 | 偏移量offset(讀取數據的開始位置),兩個字節 | 讀取數量,兩個字節 |
---|
正常響應報文:
功能碼:03,一個字節 | 數據長度(字節數,這裏應該是請求報文中的讀取數量的2倍),一個字節 | 保持寄存器數據,n個字節(數據的字節數應該是請求報文中的讀取數量的2倍) |
---|
異常響應報文:
功能碼:131(0x83),一個字節 | 異常碼,一個字節 |
---|
4、讀輸入寄存器
請求報文:
功能碼:04,一個字節 | 偏移量offset(讀取數據的開始位置),兩個字節 | 讀取數量,兩個字節 |
---|
正常響應報文:
功能碼:04,一個字節 | 數據長度(字節數,這裏應該是請求報文中的讀取數量的2倍),一個字節 | 輸入寄存器數據,n個字節(數據的字節數應該是請求報文中的讀取數量的2倍) |
---|
異常響應報文:
功能碼:132(0x84),一個字節 | 異常碼,一個字節 |
---|
5、寫單個線圈
請求報文:
功能碼:05,一個字節 | 偏移量offset(寫入數據的開始位置),兩個字節 | 要寫入的線圈狀態值(只關注0和非0),兩個字節 |
---|
正常響應報文:
功能碼:05,一個字節 | 偏移量offset(寫入數據的開始位置),兩個字節 | 要寫入的線圈狀態值(只關注0和非0),兩個字節 |
---|
異常響應報文:
功能碼:133(0x85),一個字節 | 異常碼,一個字節 |
---|
6、寫單個保持寄存器
請求報文:
功能碼:06,一個字節 | 偏移量offset(寫入數據的開始位置),兩個字節 | 要寫入的保持寄存器數據,兩個字節 |
---|
正常響應報文:
功能碼:06,一個字節 | 偏移量offset(寫入數據的開始位置),兩個字節 | 要寫入的保持寄存器數據,兩個字節 |
---|
異常響應報文:
功能碼:134(0x86),一個字節 | 異常碼,一個字節 |
---|
7、寫多個線圈
請求報文:
功能碼:15,一個字節 | 偏移量offset(寫入數據的開始位置),兩個字節 | 要寫入的數量,兩個字節 | 數據長度(字節數),一個字節 | 線圈狀態數據,n個字節 |
---|
正常響應報文:
功能碼:15,一個字節 | 偏移量offset(寫入數據的開始位置),兩個字節 | 要寫入的數量,兩個字節 |
---|
異常響應報文:
功能碼:143(0x8f),一個字節 | 異常碼,一個字節 |
---|
8、寫多個保持寄存器
請求報文:
功能碼:16,一個字節 | 偏移量offset(寫入數據的開始位置),兩個字節 | 要寫入的數量,兩個字節 | 數據長度(字節數),一個字節 | 保持寄存器數據,n個字節 |
---|
正常響應報文:
功能碼:16,一個字節 | 偏移量offset(寫入數據的開始位置),兩個字節 | 要寫入的數量,兩個字節 |
---|
異常響應報文:
功能碼:144(0x90),一個字節 | 異常碼,一個字節 |
---|