Modbus
(👆 百度百科,放心跳轉)
起源
Modbus 由 Modicon 公司于 1979 年開發,是一種工業現場總線協議標準。
Modbus 通信協議具有多個變種,支持串口,以太網多個版本,其中最著名的是 Modbus RTU、
Modbus ASCII 和 Modbus TCP 三種。Modbus TCP 是在施耐德收購 Modicon 后 1997 年發布的。
分類
1、Modbus RTU(Remote Terminal Unit)
運行在串口上的協議,采用二進制表現形式以及緊湊的數據結構,通信效率高,應用廣泛。
2、Modbus ASCII
運行在串口上的協議,采用 ASCII 碼進行傳輸,并且在每個字節的開始和結束 都有特殊字符作為標志,傳輸效率遠遠低于 Modbus RTU,只有傳輸數據量較小時,才會考慮。
3、Modbus TCP
運行在以太網上的協議。
優勢
免費、簡單、容易使用。
應用場景
Modbus 協議是現在國內工業領域應用最多的協議,不只 PLC 設備,各種終端設備,比如水控機、水表、電表、工業秤、各種采集設備,都應用此協議。
通信
1、Modbus 采用主從問答式(master / slave)通信;
有一個節點是 master 節點,其他使用 Modbus 協議參與通信的節點是 slave 節點(可多個), 每個 slave 設備都有唯一一個地址。
Modbus TCP
Modbus TCP 協議 和 Modbus RTU 協議非常相似,只要把 RTU 協議中兩個字節的校驗碼去掉,然后在 RTU 協議的開始加上 5 個 0 和 1 個 6,通信時通過 TCP/IP 網絡協議發送出去即可。
特點
1、見“Modbus ——> 通信”;
2、該協議是 應用層的協議,基于傳輸層的 TCP協議 進行通信;
3、Modbus TCP 默認接收報文的端口號為 502。
協議格式(報文頭 + 功能碼 + 數據)
Modbus TCP/IP 協議 最大數據幀長度為 260 字節。報文格式如下:
報文頭
功能碼
寄存器
線圈寄存器,類比為開關量,每一個 bit 都對應一個信號的開關狀態,所以 一個 byte 就可以同時控制8 路的信號。 線圈寄存器支持讀也支持寫,寫又分為寫單個線圈寄存器和寫多個線圈寄存器。
對應功能碼:0x01 0x05 0x0f
離散輸入寄存器,相當于線圈寄存器的只讀模式,也是每個 bit 表示一個開關量,其開關量只能讀取輸入的開關信號,是不能寫的。比如讀取外部按鍵的按下還是松開。
對應功能碼: 0x02
保持寄存器,單位不再是 bit 而是兩個 byte,是可以存放具體的數據量的。比如設置時間年月日,不但可以寫入也可以讀出。該寄存器并可讀寫的,寫也分為寫單個保持寄存器和寫多個保持寄存器。
對應功能碼: 0x03 0x06 0x10
輸入寄存器,和保持寄存器類似,但也只支持讀而不能寫。一個寄存器也是占據兩個 byte 的空間。比如,通過讀取輸入寄存器獲取現在的 AD 采集值。
對應功能碼: 0x04
讀數據
主機 ——>從機:
報文頭 + 功能碼 + 起始地址 + 數量
7 + 1 + 2 + 2 = 12
從機 ——>主機:
報文頭 + 功能碼 + 字節計數 + 數據
7 + 1 + 1 + n = 9 + n
0x01(讀線圈狀態)
0x02(讀離散輸入狀態)
0x03(讀保持寄存器)
0x04(讀輸入寄存器)
寫單個
主機 ——>從機:
報文頭 + 功能碼 + 地址 + 斷通標志 / 數據
7 + 1 + 2 + 2 = 12
從機 ——>主機:
原文返回
0x05(寫單個線圈)
0x06(寫單個保持寄存器)
寫多個
主機 ——>從機:
報文頭 + 功能碼 + 起始地址 + 數量 + 字節計數 + 數據
7 + 1 + 2 + 2 + 1 + n = 13 + n
從機 ——>主機:
報文頭 + 功能碼 + 起始地址 + 數量
7 + 1 + 2 + 2 = 12
0x0F(寫多個線圈)
0x10(寫多個保持寄存器)
工具軟件的安裝與使用
Modbus poll
破解
點擊 connection -> connect,輸入序列號即可。
使用
先設置,后連接。
Modbus slave
破解
點擊 connection -> connect,輸入序列號即可。
使用
先設置,后連接。
網絡調試助手
Wireshark(Windows 版)
捕獲器選擇:
如果連接有線網絡,選擇本地連接 / 以太網;
如果連接無線網絡,選擇 WLAN;
如果只是在本機上的通信,可以選擇 NPCAP Loopback apdater 或 Adapter for loopback traffic capture。
過濾條件:
1、過濾端口:tcp.port == 502
2、過濾IP:ip.addr == Windows 的IP
練習:
1、讀傳感器數據,讀1個寄存器數據,寫出主從數據收發協議。
2、寫出控制 IO 設備開關的協議數據,操作1個線圈,置1。
3、在虛擬機編寫客戶端,實現 poll 端功能,和 Slave 通信,讀保持寄存器的三個值。
uint8_t hldreg[12] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, 0x03};send(sockfd, hldreg, sizeof(hldreg), 0);uint8_t buf[32] = {};recv(sockfd, buf, sizeof(buf), 0);for (int i = 0; i < buf[8]; i++)printf("%#x ", buf[9+i]);putchar(10);
運行結果如下:
4、編寫客戶端程序,實現對 Slave 單個線圈的控制(置一)。
uint8_t coil[12] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x05, 0x00, 0x00, 0xff, 0x00};send(sockfd, coil, sizeof(coil), 0);
運行結果如下:
5、封裝函數:設置單元標識符(從機地址)
void set_slave_id(uint8_t *p, int slave_id){p[6] = slave_id;
}
6、封裝函數:讀保持寄存器
void read_hldreg(int addr, int num, uint8_t *hldreg, uint8_t *dest){hldreg[5] = 0x06;hldreg[7] = 0x03;hldreg[8] = addr >> 8;hldreg[9] = addr & 0xff;hldreg[10] = num >> 8;hldreg[11] = num & 0xff;send(sockfd, hldreg, 12, 0); // 指針類型,不能 sizeof(hldreg)recv(sockfd, dest, 64, 0); // 64 為數組 dest 的長度,sockfd 為全局變量
}