本文系统介绍 Modbus TCP 与 Modbus RTU 的帧格式、核心差异,并对常用功能码进行逐一举例说明,帮助开发者快速掌握这一工业通信领域最常用的协议之一。
一、Modbus 协议简介
Modbus 是一种应用于工业控制领域的串行通信协议,由 Modicon(现为施耐德电气)于 1979 年发布。因其协议简单、公开透明、无需授权费用,已成为工业设备之间事实上的通信标准。
常见的 Modbus 传输模式有三种:
| 模式 | 传输介质 | 特点 |
|---|---|---|
| Modbus RTU | RS-232 / RS-485 | 二进制编码,紧凑高效,带 CRC 校验 |
| Modbus ASCII | RS-232 / RS-485 | ASCII 字符编码,可读性强,效率较低 |
| Modbus TCP | 以太网 | 基于 TCP/IP,加入 MBAP 报文头,无 CRC |
本文重点讲解 Modbus TCP 与 Modbus RTU 两种模式。
二、Modbus TCP 数据帧格式
Modbus TCP 的数据帧由两部分组成:MBAP 报文头 + PDU 数据单元。
2.1 MBAP 报文头(7 字节)
MBAP(Modbus Application Protocol Header)是 Modbus TCP 特有的报文头:
| 字段 | 长度 | 说明 |
|---|---|---|
| 事务处理标识(Transaction ID) | 2 字节 | 报文序列号,每次通信后加 1,用于区分不同报文 |
| 协议标识(Protocol ID) | 2 字节 | 固定为 0x0000,表示 Modbus TCP 协议 |
| 长度(Length) | 2 字节 | 后续数据的长度(包括单元标识符 1 字节 + PDU) |
| 单元标识符(Unit ID) | 1 字节 | 从机设备地址(类似 RTU 中的从机地址) |
示例:一帧 Modbus TCP 报文如下:
00 01 00 00 00 06 01 06 08 00 12 34
│ ─┘│ ─┘│ ─┘│ ││ ─┘│ ─┘│ ─┘
│ │ │ │ ││ │ │ └── 写入值 0x1234
│ │ │ │ ││ │ └─────── 寄存器地址 0x0800
│ │ │ │ ││ └──────────── 功能码 0x06(写单个寄存器)
│ │ │ │ │└───────────────── 单元标识符(设备号 01)
│ │ │ │ └────────────────── 长度(6 字节)
│ │ │ └───────────────────── 协议标识(Modbus TCP)
│ │ └────────────────────────── 事务处理标识(序号 0001)
└──────────────────────────────────── 事务处理标识(序号 0001)
2.2 PDU 数据单元
PDU(Protocol Data Unit)由 1 字节功能码 + N 字节数据 组成,数据长度由具体功能决定。
Modbus 的操作对象有四种:
| 操作对象 | 类型 | 功能码范围 | 访问权限 |
|---|---|---|---|
| 线圈(Coils) | 布尔量 | 01、05、0F | 读写 |
| 离散输入(Discrete Inputs) | 布尔量 | 02 | 只读 |
| 保持寄存器(Holding Registers) | 16 位整型 | 03、06、10 | 读写 |
| 输入寄存器(Input Registers) | 16 位整型 | 04 | 只读 |
三、常用功能码详解与示例
3.1 0x01 ------ 读线圈状态(Read Coils)
读取从机中线圈的 ON/OFF 状态,属于位操作。
请求 :从地址 0x0002 开始,读 0x0008 个线圈状态。
00 01 00 00 00 06 01 01 00 02 00 08
| 字段 | 值 | 说明 |
|---|---|---|
| 事务处理标识 | 00 01 |
序号 |
| 协议标识 | 00 00 |
Modbus TCP |
| 长度 | 00 06 |
后续 6 字节 |
| 单元标识符 | 01 |
设备号 1 |
| 功能码 | 01 |
读线圈 |
| 起始地址 | 00 02 |
从地址 0x0002 开始 |
| 操作个数 | 00 08 |
读 8 个线圈 |
响应:
00 01 00 00 00 04 01 01 01 01
响应数据中,01 表示 8 个线圈的状态字节:0x01 = 0000 0001(二进制),从右往左数第 1 个线圈为 ON,其余为 OFF。
3.2 0x02 ------ 读离散量输入(Read Discrete Inputs)
读取离散量输入状态,只读,同样是位操作。
请求 :从地址 0x0000 开始,读 0x0012(18)个离散量输入。
00 01 00 00 00 06 01 02 00 00 00 12
响应:
00 01 00 00 00 06 01 02 03 01 04 00
响应数据为 01 04 00,对应二进制:
0x01=0000 0001→ 第 1 个输入为 ON0x04=0000 0100→ 第 11 个输入为 ON0x00=0000 0000→ 其余均为 OFF
3.3 0x03 ------ 读保持寄存器(Read Holding Registers)
读取保持寄存器的值,16 位整型,是最常用的功能码之一。
请求 :从地址 0x0000 开始,读 0x0003 个寄存器。
00 01 00 00 00 06 01 03 00 00 00 03
响应:
00 01 00 00 00 09 01 03 06 00 21 00 00 00 00
响应数据 06 表示后面有 6 字节数据(3 个寄存器 × 2 字节):
- 寄存器 0:
00 21=0x0021 - 寄存器 1:
00 00=0x0000 - 寄存器 2:
00 00=0x0000
3.4 0x04 ------ 读输入寄存器(Read Input Registers)
读取输入寄存器的值,只读,格式与 0x03 相同。
请求 :从地址 0x0002 开始,读 0x0005 个连续输入寄存器。
00 01 00 00 00 06 01 04 00 02 00 05
响应:
00 01 00 00 00 0D 01 04 0A 00 0C 00 00 00 00 00 00 00 00
响应数据 0A = 10 字节(5 个寄存器 × 2 字节):
- 寄存器 2:
00 0C=0x000C - 其余寄存器均为
0x0000
3.5 0x05 ------ 写单个线圈(Write Single Coil)
将指定地址的线圈设置为 ON 或 OFF。
请求 :将地址 0x0003 的线圈设置为 ON。
00 01 00 00 00 06 01 05 00 03 FF 00
写线圈时,
FF 00表示 ON,00 00表示 OFF。
响应:
00 01 00 00 00 06 01 05 00 03 FF 00
响应与请求完全一致,表示写入成功。
3.6 0x06 ------ 写单个保持寄存器(Write Single Register)
向指定保持寄存器写入一个 16 位值。
请求 :向地址 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
响应与请求一致,表示写入成功。
3.7 0x0F(15)------ 写多个线圈状态(Write Multiple Coils)
一次性写入多个线圈的 ON/OFF 状态。
请求 :从地址 0x0000 开始,写入前 5 个线圈状态为 ON。
00 01 00 00 00 08 01 0F 00 00 00 05 01 1F
| 字段 | 值 | 说明 |
|---|---|---|
| 起始地址 | 00 00 |
从地址 0x0000 开始 |
| 操作个数 | 00 05 |
写 5 个线圈 |
| 写入字节数 | 01 |
后续 1 字节数据 |
| 写入数据 | 1F = 0001 1111 |
前 5 个线圈均为 ON |
响应:
00 01 00 00 00 06 01 0F 00 00 00 05
响应返回起始地址和操作个数,表示写入成功。
3.8 0x10(16)------ 写多个保持寄存器(Write Multiple Registers)
一次性写入多个保持寄存器的值。
请求 :从地址 0x0000 开始,写入前 5 个保持寄存器,数据均为 0x0000。
00 01 00 00 00 11 01 10 00 00 00 05 0A 00 00 00 00 00 00 00 00 00 00
| 字段 | 值 | 说明 |
|---|---|---|
| 起始地址 | 00 00 |
从地址 0x0000 开始 |
| 操作个数 | 00 05 |
写 5 个寄存器 |
| 写入字节数 | 0A = 10 |
5 个寄存器 × 2 字节 |
| 写入数据 | 00 00 ... |
5 个寄存器均为 0x0000 |
响应:
00 01 00 00 00 06 01 10 00 00 00 05
响应返回起始地址和写入寄存器个数,表示写入成功。
四、Modbus RTU 与 Modbus TCP 的核心区别
| 对比项 | Modbus RTU | Modbus TCP |
|---|---|---|
| 传输介质 | RS-232 / RS-485 串口 | 以太网(TCP/IP) |
| 报文头 | 无 MBAP 头 | 7 字节 MBAP 头 |
| 地址字段 | 1 字节从机地址(帧首) | 1 字节单元标识符(MBAP 中) |
| 校验方式 | CRC16(帧尾 2 字节) | 无 CRC(TCP 自带校验) |
| 帧间隔 | 需满足 3.5 个字符时间的静默间隔 | 无需额外帧间隔,由 TCP 流控制 |
| 主从模式 | 一主多从 | 支持多主多从(基于 TCP 连接) |
实例对比 :向从机 01 的地址 0x0105 写入 0x0190
-
Modbus RTU 请求 :
01 06 01 05 01 90 99 CB01= 从机地址06= 功能码01 05= 寄存器地址01 90= 写入值99 CB= CRC16 校验码
-
Modbus TCP 请求 :
00 01 00 00 00 06 01 06 01 05 01 90- 前 7 字节为 MBAP 头
- 无 CRC 校验
五、功能码速查表
| 功能码 | 名称 | 操作对象 | 读写属性 |
|---|---|---|---|
0x01 |
读线圈状态 | 线圈 | 读 |
0x02 |
读离散量输入 | 离散输入 | 读 |
0x03 |
读保持寄存器 | 保持寄存器 | 读 |
0x04 |
读输入寄存器 | 输入寄存器 | 读 |
0x05 |
写单个线圈 | 线圈 | 写 |
0x06 |
写单个保持寄存器 | 保持寄存器 | 写 |
0x0F |
写多个线圈 | 线圈 | 写 |
0x10 |
写多个保持寄存器 | 保持寄存器 | 写 |
六、总结
Modbus 协议之所以在工业领域长盛不衰,核心在于其简单、开放、易实现。
- Modbus TCP 适合现代以太网环境,借助 MBAP 头实现多设备区分,无需担心校验问题;
- Modbus RTU 适合传统串口总线场景,帧结构紧凑,CRC 校验保证可靠性;
- 两者在 PDU 层面完全一致,仅帧头和校验方式不同,因此上层应用代码可以高度复用。
掌握上述 8 个常用功能码,即可覆盖绝大多数 Modbus 通信场景。希望本文对你有所帮助!
参考资料:Modbus_Application_Protocol_V1_1b.pdf(官方标准文档)