Modbus协议——ModbusTCP
Modbus 是一项应用层报文传输协议,用于在通过不同类型的总线或网络连接的设备之间的客户机/服务器通信。常用于串口、以太网上的数据传输。

- ADU:应用数据单元。最大不超过256个字节。一次Modbus通讯报文,可以被成为1个ADU。
- PDU:协议数据单元。指代Modbus通讯报文中的 功能码 + 数据
TCP/IP上的实现 ModbusTCP
数据帧格式
| 事务元标识符 | 协议标识符 | 数据长度 | 单元标识符(站号) | 功能码 | 数据 |
|---|---|---|---|---|---|
| 2 byte | 2 byte | 2 byte | 1 byte | 1 byte | * byte |
注:
- MBAP报文头:事务元标识符 + 协议标识符 + 数据长度 + 单元标识符(站号)
- ModbusTCP通过TCP/IP协议进行数据校验,故Modbus数据帧无需校验
事务元标识符
用于事务处理配对。在响应中,Modbus服务器复制请求的事务处理标识符。一般为每条报文都自加1。如果保持不变,从站也可以正常识别该报文。
协议标识符
用于系统内的多路复用。0 代表Modbus协议。
数据长度
单元标识符(站号) + 功能码 + 数据 所包含的字节数
单元标识符(站号)
| 广播地址 | 从站地址 | 保留 |
|---|---|---|
| 0 | 1~247 | 248~255 |
- 因为ModbusTCP一般使用IP进行定位,所以站号在一般应用中基本失去了其作用。大部分ModbusTCP从站会忽略站号,也有小部分从站会进行站号校验。
功能码
| 功能描述 | 功能码(十进制) | 功能码(十六进制) | 备注 |
|---|---|---|---|
| 读线圈 | 01 | 0x01 | 常用 |
| 写单个线圈 | 05 | 0x05 | |
| 写多个线圈 | 15 | 0x0F | |
| 读输入寄存器 | 04 | 0x04 | |
| 读保持寄存器 | 03 | 0x03 | 常用 |
| 写单个保持寄存器 | 06 | 0x06 | |
| 写多个保持寄存器 | 16 | 0x10 | 常用 |
| 读/写多个保持寄存器 | 23 | 0x17 |
功能码 0x03 命令格式解析
请求命令格式
| 大小 | 取值范围 | 备注 | |
|---|---|---|---|
| 事务元标识符 | 2 byte | 0x0000 至 0xFFFF | |
| 协议标识符 | 2 byte | 固定值0x0000 | Modbus协议 |
| 数据长度 | 2 byte | 0x0003 至 0xFFFF | |
| 单元标识符(站号) | 1 byte | 0x01 至 0xFF | 默认值为1,一般无需变化 |
| 功能码 | 1 byte | 0x03 | |
| 起始地址 | 2 byte | 0x0000 至 0xFFFF | |
| 寄存器数量(N) | 2 byte | 0x0000 至 0x007D |
响应命令格式
| 大小 | 取值范围 | 备注 | |
|---|---|---|---|
| 事务元标识符 | 2 byte | 0x0000 至 0xFFFF | 复制自请求命令 |
| 协议标识符 | 2 byte | 固定值0x0000 | Modbus协议 |
| 数据长度 | 2 byte | 0x0003 至 0xFFFF | |
| 单元标识符(站号) | 1 byte | 0x01 至 0xFF | 默认值为1,一般无需变化 |
| 功能码 | 1 byte | 0x03 | |
| 字节数 | 1 byte | 2 * N | 寄存器值所包括的字节数 |
| 寄存器值 | 2 * N byte | 数据值 | 1个寄存器占用2个字节 |
注:N = 寄存器数量
错误响应格式
| 大小 | 取值范围 | 备注 | |
|---|---|---|---|
| 事务元标识符 | 2 byte | 0x0000 至 0xFFFF | 复制自请求命令 |
| 协议标识符 | 2 byte | 固定值0x0000 | Modbus协议 |
| 数据长度 | 2 byte | 0x0003 至 0xFFFF | |
| 单元标识符(站号) | 1 byte | 0x01 至 0xFF | 默认值为1,一般无需变化 |
| 错误码 | 1 byte | 0x83 | 0x80 + 0x03 |
| 异常码 | 1 byte | 0x01 至 0x04 | 0x01:该功能码不支持 0x02:起始地址不支持 或者 起始地址+寄存器数量不支持 0x03:读取寄存器数量不支持 0x04:读取寄存器失败 |
eg.使用 功能码0x03 读取 站号01 地址0x0000~0x0009 的数据
请求命令:SEND HEX>
(事务元标识符)00 01 (协议标识符)00 00 (数据长度)00 06 (站号)01 (功能码)03 (起始地址)00 00 (读取寄存器数量)00 0A
响应命令:RECV HEX>
(事务元标识符)00 01 (协议标识符)00 00 (数据长度)00 17 (站号)01 (功能码)03 (字节数)14 (读取的各个寄存器值)00 01 00 02 00 03 00 04 00 05 00 06 00 07 00 08 00 09 00 0A

功能码 0x10 命令格式解析
请求命令格式
| 大小 | 取值范围 | 备注 | |
|---|---|---|---|
| 事务元标识符 | 2 byte | 0x0000 至 0xFFFF | |
| 协议标识符 | 2 byte | 固定值0x0000 | Modbus协议 |
| 数据长度 | 2 byte | 0x0003 至 0xFFFF | |
| 单元标识符(站号) | 1 byte | 0x01 至 0xFF | 默认值为1,一般无需变化 |
| 功能码 | 1 byte | 0x10 | |
| 起始地址 | 2 byte | 0x0000 至 0xFFFF | |
| 寄存器数量(N) | 2 byte | 0x0000 至 0x007D | |
| 字节数 | 1 byte | 2 * N | 寄存器值所包括的字节数 |
| 寄存器值 | 2 * N byte | 数据值 | 1个寄存器占用2个字节 |
注:N = 寄存器数量
响应命令格式
| 大小 | 取值范围 | 备注 | |
|---|---|---|---|
| 事务元标识符 | 2 byte | 0x0000 至 0xFFFF | |
| 协议标识符 | 2 byte | 固定值0x0000 | Modbus协议 |
| 数据长度 | 2 byte | 0x0003 至 0xFFFF | |
| 单元标识符(站号) | 1 byte | 0x01 至 0xFF | 默认值为1,一般无需变化 |
| 功能码 | 1 byte | 0x03 | |
| 起始地址 | 2 byte | 0x0000 至 0xFFFF | |
| 寄存器数量(N) | 2 byte | 0x0000 至 0x007D |
错误响应格式
| 大小 | 取值范围 | 备注 | |
|---|---|---|---|
| 事务元标识符 | 2 byte | 0x0000 至 0xFFFF | |
| 协议标识符 | 2 byte | 固定值0x0000 | Modbus协议 |
| 数据长度 | 2 byte | 0x0003 至 0xFFFF | |
| 单元标识符(站号) | 1 byte | 0x01 至 0xFF | 默认值为1,一般无需变化 |
| 错误码 | 1 byte | 0x90 | 0x80 + 0x10 |
| 异常码 | 1 byte | 0x01 至 0x04 | 0x01:该功能码不支持 0x02:起始地址不支持 或者 起始地址+寄存器数量不支持 0x03:读取寄存器数量不支持 0x04:读取寄存器失败 |
eg.使用 功能码0x10 写入 站号01 地址0x0000~0x0009 的数据
请求命令:SEND HEX >
(事务元标识符)00 05 (协议标识符)00 00 (数据长度)00 1B (站号)01 (功能码)10 (起始地址)00 00 (写入寄存器数量)00 0A (字节数)14 00 (各个寄存器的写入值)0A 00 14 00 1E 00 28 00 32 00 3C 00 46 00 50 00 5A 00 64
响应命令:RECV HEX >
(事务元标识符)00 05 (协议标识符)00 00 (数据长度)00 06 (站号)01 10 (起始地址)00 00 (寄存器数量)00 0A
