Modbus RTU 报文结构详解

调试 Modbus 时最耗时间的往往不是协议本身,而是地址对不上、CRC 字节序写反、异常码看不懂。这篇把一帧报文拆到字节级别,配一个真实例子。

一帧报文由四部分组成

从站地址
1 字节
功能码
1 字节
数据区
N 字节
CRC 校验
2 字节 · 低字节在前
字段长度含义
从站地址1 字节0 为广播(从站不回复);1–247 为从站设备地址。地址重复是"多台设备一起动"的常见原因
功能码1 字节告诉从站要做什么操作,见下表
数据区N 字节请求里是起始地址和数量,响应里是实际读到的数据
CRC 校验2 字节对前面所有字节计算 CRC-16/MODBUS,低字节先发

逐字节拆解一个读寄存器请求

主机要读 1 号从站、起始地址 0、共 10 个保持寄存器,完整报文是:

01 03 00 00 00 0A C5 CD
字节值含义
101从站地址 1
203功能码 03 = 读保持寄存器
3–400 00起始地址 0x0000
5–600 0A读 10 个寄存器(0x000A = 10)
7–8C5 CDCRC = 0xCDC5,按 Modbus 规定低字节在前写成 C5 CD

从站正常响应:

01 03 14 00 64 00 C8 ... xx xx
字节值含义
101从站地址原样返回
203功能码原样返回
314数据字节数 0x14 = 20
4–2300 64 00 C8 …20 字节数据(10 个寄存器 × 2 字节),每个寄存器 16 位、高位在前
末 2xx xxCRC,低字节在前
注意寄存器是 16 位的。每个寄存器占 2 字节,高位在前(大端)。0x0064 就是十进制 100,不是两个独立字节。很多"数值差 256 倍"的问题就出在这里。

常见功能码

功能码名称作用数据区含义
01读线圈读开关量输出状态起始地址(2) + 数量(2)
02读离散输入读开关量输入状态起始地址(2) + 数量(2)
03读保持寄存器读可读写的参数、设定值起始地址(2) + 数量(2)
04读输入寄存器读只读的测量值起始地址(2) + 数量(2)
05写单个线圈控制一个开关量地址(2) + 0xFF00/0x0000
06写单个寄存器改一个设定值地址(2) + 数值(2)
0F写多个线圈批量控制开关量地址(2) + 数量(2) + 字节数(1) + 数据
10写多个寄存器批量写参数地址(2) + 数量(2) + 字节数(1) + 数据

注意 0F 和 10 是十六进制写法,对应十进制的 15 和 16。文档里常写"功能码 15""功能码 16",指的就是这两个。

40001 到底对应哪个地址

这是现场最高频的困惑。Modbus 有两套地址体系,必须分清:

换算关系:协议地址 = PLC 地址 − 40001。所以 40001 → 0x0000、40002 → 0x0001、40100 → 0x0063。

常见前缀规则:0xxxx 线圈(功能码 01/05)、1xxxx 离散输入(02)、3xxxx 输入寄存器(04)、4xxxx 保持寄存器(03/06/10)。 但注意——有些厂家的手册直接给的就是协议地址(从 0 开始),有些给的是 PLC 地址(从 1 开始),还有的干脆自己定义。**当数值整体差 1 的时候,先怀疑这个偏移。**

异常响应怎么读

从站出错时,会把功能码最高位置 1(即加 0x80),后面跟一个异常码。例如功能码 03 执行失败:

01 83 02 C0 F1
字节值含义
101从站地址
2830x83 = 0x03 + 0x80,说明功能码 03 执行失败
302异常码 02 = 非法数据地址
4–5C0 F1CRC
异常码名称通常原因
01非法功能从站不支持这个功能码,或该功能被禁用
02非法数据地址地址超出范围,或地址偏移算错了(最常见)
03非法数据值写入的值超出允许范围,或数量字段超限
04从站设备故障从站内部异常,检查设备本身
05确认从站已接受请求,但需要较长时间处理
06从站忙从站正在处理上一条命令,稍后重试

如果完全没响应,通常是这几类:从站地址错、波特率/校验位不一致、A/B 线接反、CRC 字节序写反、终端电阻缺失导致信号反射。

算一下你手上的报文

把报文粘进计算器,选 CRC-16/MODBUS,会自动算出校验值并检查末尾两字节对不对。

在线工具
CRC16 计算器
支持 9 种算法模型,纯本地运算,不上传数据
立即使用 需要定制采集?