一、Modbus TCP数据帧
ModbusTCP的数据帧可分为两部分:MBAP+PDU。

在 TCP/IP 上使用一种专用报文头识别 MODBUS 应用数据单元。将这种报文头称为 MBAP 报文头(MODBUS 协议报文头)。这种报文头提供一些与串行链路上使用的 MODBUS RTU 应用数据单元比较的差别 :
- 用 MBAP 报文头中的单个字节单元标识符取代 MODBUS 串行链路上通常使用的 MODBUS从地址域。这个单元标识符用于设备的通信,这些设备使用单个IP地址支持多个独立Modbus终端单元,例如:网桥、路由器和网关。
- 用接收者可以验证完成报文的方式设计所有 MODBUS 请求和响应。对于 MODBUS PDU有固定长度的功能码来说,仅功能码就足够了。对于在请求或响应中携带一个可变数据的功能码来说,数据域包括字节数。
- 当在 TCP 上携带 MODBUS 时,即使将报文分成多个信息包来传输,办事在 MBAP 报文头上携带附加长度信息,以便接收者能识别报文边界。显式和隐式长度规则的存在以及CRC-32 差错校验码的使用(在以太网上)将对请求或响应报文产生极小的未检出干扰。
二、MBAP报文头
原文链接:https://www.cnblogs.com/ioufev/articles/10830028.html
MBAP的长度为7个字节,组成如下:
| 事务处理标识 | 协议标识 | 长度 | 单元标识符 |
|---|---|---|---|
| 2字节 | 2字节 | 2字节 | 1字节 |
| 内容 | 解释 |
|---|---|
| 事务处理标识 | 可以理解为报文的序列号,一般每次通信之后就要加1以区别不同的通信数据报文。 |
| 协议标识符 | 00 00表示ModbusTCP协议。 |
| 长度 | 表示接下来的数据长度,单位为字节。 |
| 单元标识符 | 可以理解为设备地址。 |
四、帧结构PDU
PDU由功能码+数据组成。功能码为1字节,数据长度不定,由具体功能决定。
功能码
Modbus的操作对象有四种:线圈、离散输入、输入寄存器、保持寄存器。
| 对象 | 含义 |
|---|---|
| 线圈 | PLC的输出位,开关量,在Modbus中可读可写 |
| 离散量 | PLC的输入位,开关量,在Modbus中只读 |
| 输入寄存器 | PLC中只能从模拟量输入端改变的寄存器,在Modbus中只读 |
| 保持寄存器 | PLC中用于输出模拟量信号的寄存器,在Modbus中可读可写 |
根据对象的不同,常用的Modbus的功能码有:
| 功能码 | 含义 |
|---|---|
| 0x01 | 读线圈 |
| 0x05 | 写单个线圈 |
| 0x0F | 写多个线圈 |
| 0x02 | 读离散量输入 |
| 0x04 | 读输入寄存器 |
| 0x03 | 读保持寄存器 |
| 0x06 | 写单个保持寄存器 |
| 0x10 | 写多个保持寄存器 |
0x01:读线圈
在从站中读1~2000个连续线圈状态,ON=1,OFF=0
- 请求:MBAP 功能码 起始地址H 起始地址L 数量H 数量L(共12字节)
- 响应:MBAP 功能码 数据长度 数据(一个地址的数据为1位)
- 如:在从站0x01中,读取开始地址为0x0002的线圈数据,读0x0008位
00 01 00 00 00 06 01 01 00 02 00 08 - 回:数据长度为0x01个字节,数据为0x01,第一个线圈为ON,其余为OFF
00 01 00 00 00 04 01 01 01 01
0x05:写单个线圈
将从站中的一个输出写成ON或OFF,0xFF00请求输出为ON,0x000请求输出为OFF
- 请求:MBAP 功能码 输出地址H 输出地址L 输出值H 输出值L(共12字节)
- 响应:MBAP 功能码 输出地址H 输出地址L 输出值H 输出值L(共12字节)
- 如:将地址为0x0003的线圈设为ON
00 01 00 00 00 06 01 05 00 03 FF 00 - 回:写入成功
00 01 00 00 00 06 01 05 00 03 FF 00
0x0F:写多个线圈
将一个从站中的一个线圈序列的每个线圈都强制为ON或OFF,数据域中置1的位请求相应输出位ON,置0的位请求响应输出为OFF
- 请求:MBAP 功能码 起始地址H 起始地址L 输出数量H 输出数量L 字节长度 输出值H 输出值L
- 响应:MBAP 功能码 起始地址H 起始地址L 输出数量H 输出数量L
0x02:读离散量输入
从一个从站中读1~2000个连续的离散量输入状态
- 请求:MBAP 功能码 起始地址H 起始地址L 数量H 数量L(共12字节)
- 响应:MBAP 功能码 数据长度 数据(长度:9+ceil(数量/8))
- 如:从地址0x0000开始读0x0012个离散量输入
00 01 00 00 00 06 01 02 00 00 00 12 - 回:数据长度为0x03个字节,数据为0x01 04 00,表示第一个离散量输入和第11个离散量输入为ON,其余为OFF
00 01 00 00 00 06 01 02 03 01 04 00
0x04:读输入寄存器
从一个远程设备中读1~2000个连续输入寄存器
- 请求:MBAP 功能码 起始地址H 起始地址L 寄存器数量H 寄存器数量L(共12字节)
- 响应:MBAP 功能码 数据长度 寄存器数据(长度:9+寄存器数量×2)
- 如:读起始地址为0x0002,数量为0x0005的寄存器数据
00 01 00 00 00 06 01 04 00 02 00 05 - 回:数据长度为0x0A,第一个寄存器的数据为0x0c,其余为0x00
00 01 00 00 00 0D 01 04 0A 00 0C 00 00 00 00 00 00 00 00
0x03:读保持寄存器
从远程设备中读保持寄存器连续块的内容
- 请求:MBAP 功能码 起始地址H 起始地址L 寄存器数量H 寄存器数量L(共12字节)
- 响应:MBAP 功能码 数据长度 寄存器数据(长度:9+寄存器数量×2)
- 如:起始地址是0x0000,寄存器数量是 0x0003
00 01 00 00 00 06 01 03 00 00 00 03 - 回:数据长度为0x06,第一个寄存器的数据为0x21,其余为0x00
00 01 00 00 00 09 01 03 06 00 21 00 00 00 00
0x06:写单个保持寄存器
在一个远程设备中写一个保持寄存器
- 请求:MBAP 功能码 寄存器地址H 寄存器地址L 寄存器值H 寄存器值L(共12字节)
- 响应:MBAP 功能码 寄存器地址H 寄存器地址L 寄存器值H 寄存器值L(共12字节)
- 如:向地址是0x0000的寄存器写入数据0x000A
00 01 00 00 00 06 01 06 00 00 00 0A - 回:写入成功
00 01 00 00 00 06 01 06 00 00 00 0A
0x10:写多个保持寄存器
在一个远程设备中写连续寄存器块(1~123个寄存器)
- 请求:MBAP 功能码 起始地址H 起始地址L 寄存器数量H 寄存器数量L 字节长度 寄存器值(13+寄存器数量×2)
- 响应:MBAP 功能码 起始地址H 起始地址L 寄存器数量H 寄存器数量L(共12字节)
- 如:向起始地址为0x0000,数量为0x0001的寄存器写入数据,数据长度为0x02,数据为0x000F
00 01 00 00 00 09 01 10 00 00 00 01 02 00 0 - 回:写入成功
00 01 00 00 00 06 01 10 00 00 00 01
五、Modbus TCP示例报文
ModBusTcp与串行链路Modbus的数据域是一致的,具体数据域可以参考串行Modbus。这里给出几个ModbusTcp的链路解析说明,辅助新人分析报文。
原文链接:https://blog.csdn.net/mikasoi/article/details/81782159
1、数据请求
|
97 76 00 00 00 06 04 04 00 7D 00 7D |
||||
|
示例 |
长度 |
说明 |
备注 |
|
|
Map报文头 |
0x97 |
1 |
事务处理标识符Hi |
客户机发起,服务器复制,用于事务处理配对 |
|
0x96 |
1 |
事务处理标识符Lo |
||
|
0x0000 |
2 |
协议标识符号 |
客户机发起,服务器复制 Modbus协议 = 0. |
|
|
0x0006 |
2 |
长度 |
从本字节下一个到最后 |
|
|
0x04 |
1 |
单元标识符 |
客户机发起,服务器复制 串口链路或其他总线上远程终端标识 |
|
|
功能码 |
0x04 |
1 |
功能码,读寄存器 |
参考标准modbus协议 |
|
数据 |
0x007D |
2 |
起始地址 |
|
|
0x 007D |
2 |
寄存器数量 |
||
|
校验 |
||||
2、数据请求回复
|
97 76 00 00 00 FD 04 04 FA AB 9E 41 18 7A E1 3F 94 7A E1 3F 94 0A 3D 3F 97 51 EC 3F 98 CC CD C0 6C 33 33 C0 E3 CC CD C0 EC EB 85 41 F1 D7 0A 41 E9 47 AE 41 ED EB 85 41 F1 19 9A 43 D0 E6 66 43 C9 4C CD 43 CF EB 85 41 F3 66 66 42 0F CC CD 41 C2 E6 66 44 0A 1E B8 41 FB A3 D7 42 0C CC CD 41 BC C0 00 44 0A B8 52 41 F6 5C 29 42 0F 47 AE 41 D1 C6 66 44 0A 00 00 00 00 C9 9E FF 7F C9 9E FF 7F C9 9E FF 7F C9 9E FF 7F C9 9E FF 7F C9 9E FF 7F C9 9E FF 7F C9 9E FF 7F C9 9E FF 7F 05 16 00 00 04 11 00 00 05 16 00 00 04 11 00 00 05 16 00 00 04 11 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0A 00 0A 00 0A 00 0A 00 04 00 04 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0F |
||||
|
示例 |
长度 |
说明 |
备注 |
|
|
Map报文头 |
0x97 |
1 |
事务处理标识符Hi |
客户机发起,服务器复制,用于事务处理配对 |
|
0x96 |
1 |
事务处理标识符Lo |
||
|
0x0000 |
2 |
协议标识符号 |
客户机发起,服务器复制 Modbus协议 = 0. |
|
|
0x00FD |
2 |
长度 |
从本字节下一个到最后 |
|
|
0x04 |
1 |
单元标识符 |
客户机发起,服务器复制 串口链路或其他总线上远程终端标识 |
|
|
功能码 |
0x04 |
1 |
功能码,读寄存器 |
参考标准modbus协议 |
|
数据 |
0x FA |
1 |
字节个数 |
|
|
0x---- |
数据 |
|||
|
校验 |
||||
3、写多个寄存器
|
97 79 00 00 00 09 04 10 00 00 00 01 02 00 01 |
||||
|
示例 |
长度 |
说明 |
备注 |
|
|
Map报文头 |
0x97 |
1 |
事务处理标识符Hi |
客户机发起,服务器复制,用于事务处理配对 |
|
0x79 |
1 |
事务处理标识符Lo |
||
|
0x0000 |
2 |
协议标识符号 |
客户机发起,服务器复制 Modbus协议 = 0. |
|
|
0x0009 |
2 |
长度 |
从本字节下一个到最后 |
|
|
0x04 |
1 |
单元标识符 |
客户机发起,服务器复制 串口链路或其他总线上远程终端标识 |
|
|
功能码 |
0x10 |
1 |
功能码,读寄存器 |
参考标准modbus协议 |
|
数据 |
0x0000 |
2 |
起始地址 |
|
|
0x 0001 |
2 |
写寄存器数量 |
||
|
0x 02 |
1 |
写字节的个数 |
||
|
00 01 |
2 |
目标值 |
||
|
校验 |
||||
4、写多个寄存器响应
|
97 79 00 00 00 06 04 10 00 00 00 01 |
||||
|
示例 |
长度 |
说明 |
备注 |
|
|
Map报文头 |
0x97 |
1 |
事务处理标识符Hi |
客户机发起,服务器复制,用于事务处理配对 |
|
0x79 |
1 |
事务处理标识符Lo |
||
|
0x0000 |
2 |
协议标识符号 |
客户机发起,服务器复制 Modbus协议 = 0. |
|
|
0x0006 |
2 |
长度 |
从本字节下一个到最后 |
|
|
0x04 |
1 |
单元标识符 |
客户机发起,服务器复制 串口链路或其他总线上远程终端标识 |
|
|
功能码 |
0x10 |
1 |
功能码,读寄存器 |
参考标准modbus协议 |
|
数据 |
0x0000 |
2 |
起始地址 |
|
|
0x 0001 |
2 |
寄存器个数 |
||
|
校验 |
||||