在一般工業場景使用modbus?RTU的場景還是更多一些,modbus?RTU基于串行協議進行收發數據,包括RS232/485等工業總線協議。
與modbus?TCP不同的是RTU沒有報文頭MBAP字段,但是在尾部增加了兩個CRC檢驗字節(CRC16),因為網絡協議中自帶校驗,所以在TCP協議中不需要使用CRC校驗碼。
RTU和TCP的總體使用方法基本一致,只是在創建modbus對象時有所不同,TCP需要傳入網絡socket信息;而RTU需要傳入串口相關信息。
二、Modbus?RTU特點
Modbus?RTU也是主從問答協議,由主機發起,一問一答
Modbus?RTU通過串口進行通信
設置串口參數:
設置串口參數時要求:
波特率為9600
8位數據位
1位停止位
無流控
三、Modbus?RTU協議格式
ModbusRTU數據幀包含:地址碼 功能碼 數據 校驗碼
地址碼:一個字節?從機ID
功能碼:一個字節?同TCP(八種)
數據:起始地址?數量??數據
校驗碼:2個字節,對?地址碼+功能碼+數據進行校驗,可以通過函數自動生成。
四、報文詳解
03功能碼為例
主機---》從機:
01?03?00?00?00?01?84?0a
01:地址碼
03:功能碼
00?00?:起始地址
00?01:讀的數量
84?0a?:校驗碼
從機--》主機:
01?03?02?00?14?b8?44
01:地址碼
03:功能碼
02:字節計數(只計算數據的個數)
00?14:數據
b8?44:校驗碼?
參考示例:
值得收藏 Modbus RTU 協議詳解-CSDN博客
五、模擬器的使用
由于實際硬件產品成本較高,我們這里可以使用Modbus軟件模擬器,進行數據模擬從而分析Modbus協議。
使用工具:
1.?ModbusPoll(模擬主機)和ModbusSlave(模擬從機)
2.?vspd虛擬串口
3.?UartAssist串口調試工具
設置串口參數要求:波特率為9600???8位數據位??1位停止位?無流控??無校驗
虛擬串口的使用:
- 虛擬串口的安裝
- 將壓縮包解壓后,雙擊vspd.exe文件進行安裝
2.安裝完成后,找到安裝目錄,將Cracked下的文件復制到軟件安裝目錄
- 打開軟件
,添加COM1和COM2端口(用完之后記得刪除端口)
4.添加完端口后,打開設備管理器,這里出現如下圖所示即可。
或
二、虛擬機綁定端口
- ?將虛擬機在系統關機(必須是關機狀態,掛起不行)狀態下,點擊虛擬機->設置->硬件->添加串行端口,添加COM1
- 添加完成后,第一次使用需要將電腦重啟
- 重啟之后,打開虛擬機,點擊虛擬機->可移動設備->串行端口->連接
- 當連接上虛擬串口后,在終端輸入dmesg?|?grep?tty,可以查看到對應的設備文件,其中默認的會有ttyS0文件,剩下的就是虛擬串口對應的設備文件
- 測試通信
1.Windows打開串口調試工具,選擇好串口COM2->COM1,設置對應的波特率
2.?在虛擬機運行minicom
在虛擬機安裝minicom軟件
sudo?apt-get?install?minicom
在終端執行sudo?minicom?-s
1)選擇serial?port?setup,回車
2)設置設備文件,波特率,關閉流控,按如下圖設置(文件改成自己的)
dmesg?|?grep?tty?查看文件名
3)修改完成后,回車,保存修改,選擇save?setup?as?dfl,敲回車,再次選擇exit回車
4)退出后就可以和windows下的串口調試工具進行通信測試
5)也可以在這個界面輸入字符,查看串口助手的顯示情況。
6)退出:ctrl+A、Z,在彈出的界面里輸入X,即可退出。
四、將Modbus?Slave模擬器作為RTU設備的從機
虛擬機綁定COM1端口,slave連接COM2端口,虛擬機通過編程測試串口通信
Modbus?Slave端的配置如下:
五、可能會遇到的問題
1.?虛擬串口完成主機與vmware下虛擬機進行串口通信
虛擬串口完成主機與vmware下虛擬機進行串口通信_xcom2v2.0怎么用-CSDN博客
2.?VSPD虛擬串口工具——從此告別硬件串口調試
『實用教程』VSPD虛擬串口工具——從此告別硬件串口調試_虛擬串口vspd-CSDN博客
3.?vmware虛擬機檢測不到vspd虛擬串口問題
vmware虛擬機檢測不到vspd虛擬串口問題_vmware虛擬機 串口 vspd-CSDN博客
練習:代碼完成通過串口讀取slave端
串口初始化與校驗碼獲取函數一起編譯,管理員身份執行命令,
六、Modbus庫
- 安裝庫
- 安裝配置
1.?在linux中解壓壓縮包
tar?-xvf?libmodbus-3.1.7.tar.gz
2.?進入源碼目錄,創建文件夾(存放頭文件、庫文件)
cd?libmodbus-3.1.7
mkdir?install?
3.?執行腳本configure,進行安裝配置(指定安裝目錄)
./configure?--prefix=$PWD/install
4.?執行make和make?install
make//編譯
make?install//安裝
執行完成后會在install文件夾下生產對應的頭文件、庫文件件夾install,用于存放產生的頭文件、庫文件等
-
- 庫的使用
要想編譯方便,可以將頭文件和庫文件放到系統路徑下
sudo??cp?install/include/modbus/*.h??/usr/include?
sudo??cp?install/lib/*??-r?/lib?-d
后期編譯時,可以直接gcc?xx.c?-lmodbus
頭文件默認搜索路徑:/usr/include??、/usr/local/include
庫文件默認搜索路徑:/lib、/usr/lib
- 函數接口
- 以TCP方式創建Modbus實例,并初始化
modbus_t*???modbus_new_tcp(const?char?*ip,?int?port)
功能:以TCP方式創建Modbus實例,并初始化
參數:
????ip???:ip地址
????port:端口號
返回值:成功:Modbus實例
??????失敗:NULL
- 設置從機ID
int?modbus_set_slave(modbus_t?*ctx,?int?slave)
功能:設置從機ID
參數:
????ctx???:Modbus實例
????slave:從機ID
返回值:成功:0
???????失敗:-1
- 和從機(slave)建立連接
一個modbus實例只能鏈接一個從機:根據提供的引用內容,一個modbus實例只能連接一個從機ID。因為modbus通信協議是基于主從模式的,每個從機都有一個唯一的從機地址,主機通過從機地址來訪問不同的從機。因此,一個modbus實例只能連接一個從機ID,如果需要連接多個從機,需要創建多個modbus實例。
int???modbus_connect(modbus_t?*ctx)
功能:和從機(slave)建立連接
參數:
????ctx:Modbus實例
返回值:成功:0
???????失敗:-1
- 釋放Modbus實例
void???modbus_free(modbus_t?*ctx)
功能:釋放Modbus實例
參數:ctx:Modbus實例
- 關閉套接字
void???modbus_close(modbus_t?*ctx)
功能:關閉套接字
參數:ctx:Modbus實例
- 讀取線圈狀態,可讀取多個連續線圈的狀態(對應功能碼為0x01)
int?modbus_read_bits(modbus_t?*ctx,?int?addr,?int?nb,?uint8_t?*dest)
功能:讀取線圈狀態,可讀取多個連續線圈的狀態(對應功能碼為0x01)
參數:
????ctx???:Modbus實例
????addr?:寄存器起始地址
????nb????:寄存器個數
????dest?:得到的狀態值
7)讀取輸入狀態,可讀取多個連續輸入的狀態(對應功能碼為0x02)
int??modbus_read_input_bits(modbus_t?*ctx,?int?addr,?int?nb,?uint8_t?*dest)
功能:讀取輸入狀態,可讀取多個連續輸入的狀態(對應功能碼為0x02)
參數:
????ctx???:Modbus實例
????addr?:寄存器起始地址
????nb???:寄存器個數
????dest?:得到的狀態值
返回值:成功:返回nb的值
- 讀取保持寄存器的值,可讀取多個連續保持寄存器的值(對應功能碼為0x03)
int??modbus_read_registers(modbus_t?*ctx,?int?addr,?int?nb,?uint16_t?*dest)
功能:讀取保持寄存器的值,可讀取多個連續保持寄存器的值(對應功能碼為0x03)
參數:
????ctx???:Modbus實例
????addr?:寄存器起始地址
????nb????:寄存器個數
????dest?:得到的寄存器的值
返回值:成功:讀到寄存器的個數
???????失敗:-1
- 讀輸入寄存器的值,可讀取多個連續輸入寄存器的值(對應功能碼為0x04)
int???modbus_read_input_registers(modbus_t?*ctx, int?addr, int?nb,?uint16_t?*dest)
功能:讀輸入寄存器的值,可讀取多個連續輸入寄存器的值(對應功能碼為0x04)
參數:
????ctx???:Modbus實例
????addr?:寄存器起始地址
????nb????:寄存器個數
????dest?:得到的寄存器的值
返回值:成功:讀到寄存器的個數
???????失敗:-1
- 寫入單個線圈的狀態(對應功能碼為0x05)
int??modbus_write_bit(modbus_t?*ctx,?int?addr,?int?status);
功能:寫入單個線圈的狀態(對應功能碼為0x05)
參數:
????ctx?????:Modbus實例
????addr??:線圈地址
????status:線圈狀態
返回值:成功:0
??????失敗:-1
- 寫入多個連續線圈的狀態(對應功能碼為15)
int??modbus_write_bits(modbus_t?*ctx,?int?addr,?int?nb,?const?uint8_t?*src);
功能:寫入多個連續線圈的狀態(對應功能碼為15)
參數:
????ctx?????:Modbus實例
????addr??:線圈地址
????nb?????:線圈個數
????src????:多個線圈狀態
返回值:成功:0
??????失敗:-1
- 寫入單個寄存器(對應功能碼為0x06)
int??modbus_write_register(modbus_t?*ctx,?int?addr,?int?value);
功能:??寫入單個寄存器(對應功能碼為0x06)
參數:?
????ctx????:Modbus實例
????addr??:寄存器地址
????value?:寄存器的值?
返回值:成功:0
???????失敗:-1
12)寫入多個連續寄存器(對應功能碼為16)
int modbus_write_registers(modbus_t?*ctx, int?addr, int?nb, const uint16_t *src);
功能:寫入多個連續寄存器(對應功能碼為16)
參數:
????ctx????:Modbus實例
????addr??:寄存器地址
????nb?????:寄存器的個數
????src????:多個寄存器的值?
返回值:成功:0
??????失敗:-1
- 編程流程
- 創建實例?modbus_new_tcp
- 設置從機id?modbus_set_slave
- 建立連接?modbus_connect
- 寄存器操作
- 關閉套接字?modbus_close
- 釋放實例?modbus_free
- 練習
通過庫函數實現03功能碼采集數據
注意:編譯不要忘了鏈接庫、查看網絡是否能用,查看slave端協議是否正確,查看slave端是否有對應的寄存器類型,查看slave?id是否一致
gcc?modbus.c?-lmodbus
#include?<stdio.h>
#include?<sys/types.h>?/*?See?NOTES?*/
#include?<sys/socket.h>
#include?<netinet/in.h>
#include?<netinet/ip.h>?/*?superset?of?previous?*/
#include?<arpa/inet.h>
#include?<unistd.h>
#include?<stdlib.h>
#include?<string.h>
#include?<modbus.h>
int?main(int?argc,?char?const?*argv[])
{
????int?ret;
????uint16_t?buf[32];
????//?1.創建實例?modbus_new_tcp,端口號字符型轉整型
????modbus_t*?md?=?modbus_new_tcp(argv[1],atoi(argv[2]));
????//?2.設置從機id?modbus_set_slave
????ret?=?modbus_set_slave(md,?1);
????if?(ret?<?0)
????{
????????printf("set?err\n");
????}
????//?3.建立連接?modbus_connect
????ret?=?modbus_connect(md);
????if?(ret?<?0)
????{
????????printf("connect?err.\n");
????}
????//?4.寄存器操作
????//從0開始讀十個寄存器值
????ret?=?modbus_read_registers(md,?0,?10,?buf);
????for?(int?i?=?0;?i?<?10;?i++)
????{
????????printf("%#x?",?buf[i]);
????}
????//?5.關閉套接字?modbus_close,先關閉套接字,再釋放實例
????modbus_close(md);???
????//?6.釋放實例?modbus_free
????modbus_free(md);
????return?0;
}
1.任務:編程實現采集傳感器數據和控制硬件設備(傳感器和硬件通過slave模擬)
傳感器:2個,光線傳感器、加速度傳感器(x\y\z)
硬件設備:2個,led燈、蜂鳴器
要求:
1.多任務編程:多線程
2.循環1s采集一次數據,并將數據打印至終端
3.同時從終端輸入指令控制硬件設備
0??1?:led燈打開
0??0:led燈關閉
1??1:蜂鳴器開
1?0?:??蜂鳴器關