引言:项目中经常要进行设备对接,之前一直用的是OPC协议,OPC-DA不能跨平台,所以最近开始研究学习Modbus协议和OPC-UA,这次先总结下Modbus TCP的学习掌握情况,供日后参考。
工具:Modbus Poll,Modbus Slave,C#开发的Modbus客户端。
先了解一些名词:
1.主站,从站:Modbus是使用主从关系实现的请求 - 响应协议。 在主从关系中,通信总是成对发生 —— 一个设备必须发起请求,然后等待响应,并且发起设备(主设备)负责发起每次交互。 通常,主设备是人机界面(HMI)或监控和数据采集(SCADA)系统,从设备是传感器、可编程逻辑控制器(PLC)或可编程自动化控制器(PAC)。
网上看到一个通俗容易帮助理解的解释:主站就是个大帅哥,从站就是一堆的痴情女子。帅哥可以随便读写从站,想来就来,想走就走,痴情女子们能做的唯一的一件事就只能被动的等待帅哥的电话,接到帅哥电话了才能向帅哥倾诉一下,如果帅哥不打电话给她就只能干等着,直到地老天荒,帅哥只能电话给这些女子,不能电话给另外一个帅哥搞基,帅哥同一时间只能打出一个电话,问候下各位美女,这叫做“轮询”。
主站和从站是一对多的关系,Modbus Poll(主站),Modbus Slave(从站),C#开发的客户端(主站)。
2.功能码(部分常用):
代码 |
中文名称 |
寄存器PLC地址 |
位操作/字操作 |
操作数量 |
01 |
读线圈状态 |
00001-09999 |
位操作 |
单个或多个 |
02 |
读离散输入状态 |
10001-19999 |
位操作 |
单个或多个 |
03 |
读保持寄存器 |
40001-49999 |
字操作 |
单个或多个 |
04 |
读输入寄存器 |
30001-39999 |
字操作 |
单个或多个 |
05 |
写单个线圈 |
00001-09999 |
位操作 |
单个 |
06 |
写单个保持寄存器 |
40001-49999 |
字操作 |
单个 |
15 |
写多个线圈 |
00001-09999 |
位操作 |
多个 |
16 |
写多个保持寄存器 |
40001-49999 |
字操作 |
多个 |
功能码可以分为位操作和字操作两类。位操作的最小单位为BIT,字操作的最小单位为两个字节。
【位操作指令】 读线圈状态01H,读(离散)输入状态02H,写单个线圈06H和写多个线圈0FH。
【字操作指令】 读保持寄存器03H,写单个寄存器06H,写多个保持寄存器10H。
1.打开Modbus Slave,点击Setup下的Slave Definition设置好参数,再选择Connection选择TCP/IP连接,从站设置完成。
2.打开C#开发的Modbus客户端,在Modbus Slave里面设置几个数字,利用Modbus规则组装发送命令来测试。
读取:
发送命令:00 00 00 00 00 06 C9 03 00 00 00 04
byte[0] byte[1]: 消息号---------随便指定,服务器返回的数据的前两个字和这个一样
byte[2] byte[3] :modbus标识,强制为0即可
byte[4] byte[5] :指示排在byte[5]后面所有字节的个数,也就是总长度6
byte[6] :站号,随便指定,00 -- FF 都可以,这里站号是201,转成十六进制为C9
byte[7] :功能码,这里就需要填入我们的真正的想法了,这里是03
byte[8] byte[9]:起始地址,比如我们想读取地址0的数据,就填 00 00 ,如果我们想读取地址1000的数据,怎么办,填入 03 E8 ,也就是将1000转化十六进制填进去。这里是00 00
byte[10] byte[11] :指定想读取的数据长度,比如我们就想读取地址0的一个数据,这里就写 00 01,如果我们想读取地址0-999共计一个数据的长度,就写 03 E8。和起始地址是一样的。这里读到4所以为00 04
组装好后发送,我们看下返回值:00 00 00 00 00 0B C9 03 08 00 01 00 02 00 00 00 0A
前面代表的意思和发送一样,C9代表201站,03代表功能码,08后面代表长度为8,第0位读数00 01,第1位读数00 02,第2位读数00 00,第3位读数00 00,第4位读数00 0A,转换为十进制就是10。
若读取数据异常,则会返回异常数据(16进制):
04 05 00 00 00 03 21 83 03
04 05:为Tcp头校验,每次发送的时候都可以不一致
00 00:代表Tcp里面包含Modbus协议数据
00 03:代表后面包含的Modbus-Tcp数据长度
21:代表从机地址
83:代表读取异常
03:读取功能码
写入:
再看下写入操作:写入用功能码06,把第0位写入9,发送命令:00 00 00 00 00 06 C9 06 00 00 00 09,规则跟上面读取规则类似就不多说了,直接发送,
返回命令:00 00 00 00 00 06 C9 06 00 00 00 09,可以看到数字9已成功写入,写入的返回值和发送值一样。
总结:只要了解的规则其实还是不算太难,主要还是专属名词的理解,十六进制和十进制转换比较头疼。