最近有个项目要用modbus TCP,更详细了解了一下modbus这个协议
首先modbus有两种传输数据的方式一种是RTU一种是ASCII,前者是基于byte的,后者是基于可读的ascii码的。也就是说前者是规定了每个byte数值的特定含义,后者传输的数据则更可以直接看懂。所以前者更像是传输的时候数据压缩后的ascii协议,一般单片机和模块之间通讯的时候用的就是和前者类似的包头+长度+内容+校验的方式。
对于modbus TCP来说,因为tcp本身是有校验功能的,所以取消了RTU里的CRC校验,不过也有会在后面加上校验的。数据包的构成如下
事务处理标识符 | 协议标识符 | 长度 | 从机地址 | 功能码 | 数据 |
---|---|---|---|---|---|
2 Byte | 2 Byte | 2 Byte | 1 Byte | 1 Byte | - |
用于标识不同的事件,主机发起事件的时候带一个事件的标识符,从机返回的时候带相同的标识符,这样网络就可以允许同时发生多个未处理的请求 | modbus这里都是0x0000 | 表示这次传输的包有多少个byte,从下一个开始到结束 | 就是slave address,网络中不同的从机用来区分这个包是不是给自己发的 | 具体功能码的含义可以看下面 | 具体的数据 |
功能码 原文在这里
0类代码
0类代码通常被认为是有用Modbus设备的最低配置,因为它们使主设备能够读取或写入数据模型。
代码 | 说明 |
---|---|
3 | 读多寄存器 |
16 | 写多寄存器 |
1类代码
1类代码
1类功能代码由访问所有类型的数据模型所需的其他代码组成。 在原始定义中,这个列表包含功能代码7(读取异常)。 但是,此代码由当前规范定义为仅限于串行的代码。
代码 | 说明 |
---|---|
1 | 读线圈 |
2 | 读离散输入 |
4 | 读输入寄存器 |
5 | 写单线圈 |
6 | 写单寄存器 |
7 | 读取异常状态(仅限串行) |
以下测试都是在ESP32上基于这个库的测试
05-写单个线圈
当功能码为05,即写入单个线圈的时候,如果要打开线圈,数据部分应该是0xFF00,关闭则是0x0000
所以比如 如果要打开100号线圈的话,主机应该发送
事务处理标识符 | 协议标识符 | 长度 | 从机地址 | 功能码:写单线圈 | 线圈编号100 的十六进制大端模式 | 数据:打开线圈 | CRC校验码 |
---|---|---|---|---|---|---|---|
00 00 | 00 00 | 00 08 | 01 | 05 | 00 64 | FF' 00 | CD E5 |
返回和发送的数据相同
01-读线圈
读线圈的话应该发送
事务处理标识符 | 协议标识符 | 长度 | 从机地址 | 功能码:读线圈 | 线圈编号100 的十六进制大端模式 | 数据:读取线圈数量 | CRC校验码 |
---|---|---|---|---|---|---|---|
00 00 | 00 00 | 00 08 | 01 | 01 | 00 64 | 00 01 | BC 15 |
返回的数据是如果正常的话应该是
事务处理标识符 | 协议标识符 | 长度 | 从机地址 | 功能码:读线圈 | 不知道是什么。。猜测是表示后面bytes的数量 | 状态值:01 |
---|---|---|---|---|---|---|
00 00 | 00 00 | 00 04 | 01 | 01 | 01 | 01 |
这里最后状态值其实是每一个二进制位代表一个线圈,也就是理论上这一个byte是可以传递8个线圈的,但是因为我们前面值查询了1个线圈,所以只用了最后一个代表开关,如果这个线圈是关闭的,就会返回00
网友评论