1️⃣ 核心思想:Modbus RTU 报文 = 一封结构化的“电报”
想象一下,你要给朋友发一封电报:
- 你得先告诉邮局:“这封信是给谁的?” → 对应 从站地址
- 然后告诉邮局:“这封信里说了什么?” → 对应 功能码 + 数据部分
- 最后,为了确保信件没有被篡改,你还要加一个“验证码” → 对应 校验部分
Modbus RTU报文就是这样一个结构化的“电报”,它由三个主要部分组成:
✅ Modbus RTU报文结构:
[从站地址] + [功能码] + [数据部分] + [校验部分]
我们下面会逐一分解每一个部分。
2️⃣ 第一部分:从站地址(Slave Address)
🔹 定义
- 这是报文的第一字节,用来指定这封“电报”是发给哪个设备的。
- 在Modbus网络中,通常有一个主站(Master,如电脑、PLC)和多个从站(Slave,如传感器、变频器)。
- 主站通过“从站地址”来区分不同的从站设备。
🔹 取值范围
- 1 ~ 255(十进制)
- 其中,
0是广播地址(不常用),255是保留地址。 - 实际应用中,常用的地址是
1 ~ 247。
🔹 举例说明
假设你的网络中有3个设备:
- 设备A:地址
01 - 设备B:地址
02 - 设备C:地址
03
当你想读取设备B的数据时,你发送的报文第一个字节就是 02。
✅ 一句话总结:
从站地址 = 收件人地址,告诉设备“这封信是给你的”。
3️⃣ 第二部分:功能码(Function Code)
🔹 定义
- 这是报文的第二字节,用来告诉从站“你想干什么”。
- 功能码决定了接下来的数据部分应该包含哪些内容。
🔹 取值范围
- 1 ~ 8(实际上有更多,但常用的是这8个)
- 每个功能码对应一个特定的操作,比如“读取线圈”、“写入寄存器”等。
🔹 常用功能码速查表
| 功能码 (Hex) | 功能描述 | 操作对象 |
|---|---|---|
01 |
读取输出线圈 | 输出线圈 (0区) |
02 |
读取输入线圈 | 输入线圈 (1区) |
03 |
读取输出寄存器 | 输出寄存器 (4区) |
04 |
读取输入寄存器 | 输入寄存器 (3区) |
05 |
写入单个线圈 | 输出线圈 (0区) |
06 |
写入单个寄存器 | 输出寄存器 (4区) |
0F |
写入多个线圈 | 输出线圈 (0区) |
10 |
写入多个寄存器 | 输出寄存器 (4区) |
💡 记忆口诀:
- 读取功能码:
01,02,03,04→ 数字顺序排列,很好记!- 写入功能码:
05,06是单个;0F,10是多个。
🔹 举例说明
你想读取设备 01 的输出寄存器(地址 40001),那么功能码就是 03。
所以,报文的前两个字节是:01 03
✅ 一句话总结:
功能码 = 你想做什么,告诉设备“我要读/写什么数据”。
4️⃣ 第三部分:数据部分(Data Part)
这是报文的核心部分,包含了具体的“操作参数”。数据部分的长度不是固定的,它取决于功能码。
🔹 数据部分的结构
数据部分可以分为两种情况:
📌 情况1:配合功能码(用于读取操作)
当功能码是“读取”类(01, 02, 03, 04)时,数据部分包含:
- 起始地址:2个字节(高字节+低字节)
- 长度:2个字节(高字节+低字节)
✅ 总长度:
2 + 2 = 4字节
🧩 举例说明
你想读取设备 01 的输出寄存器,从地址 40001 开始,读取1个寄存器:
- 起始地址:
40001的相对地址是0(因为地址从1开始,偏移量从0开始),所以是00 00 - 长度:读1个寄存器,所以是
00 01
数据部分 = 00 00 00 01
完整报文前6个字节 = 01 03 00 00 00 01
📌 情况2:配合功能码(用于写入操作)
当功能码是“写入”类(05, 06, 0F, 10)时,数据部分包含:
- 写入地址:2个字节(高字节+低字节)
- 数据:N个字节(N取决于要写入多少数据)
✅ 总长度:
2 + N字节
🧩 举例说明
你想写入设备 01 的输出寄存器,地址 40001,写入值 1234(十六进制 04D2):
- 写入地址:
40001的相对地址是0,所以是00 00 - 数据:
04 D2(2个字节)
数据部分 = 00 00 04 D2
完整报文前6个字节 = 01 06 00 00 04 D2
✅ 一句话总结:
数据部分 = 操作的具体参数,告诉设备“从哪里开始”、“写入什么数据”。
5️⃣ 第四部分:校验部分(Checksum)
🔹 定义
- 这是报文的最后两个字节,用来保证报文的准确性和完整性。
- 如果在传输过程中数据被干扰或篡改,校验码就会不匹配,接收方会丢弃这个报文。
🔹 校验方式
- Modbus RTU 使用 CRC(循环冗余校验),这是一种非常可靠的校验算法。
- CRC校验码是根据前面所有字节(从站地址到数据部分)计算出来的。
🔹 计算方法(简单理解)
虽然CRC的计算公式很复杂,但你不需要手动计算!现代的Modbus软件或库都会自动帮你生成和验证CRC校验码。
📌 举例说明
我们继续上面的例子:
- 报文前6个字节:
01 03 00 00 00 01 - 计算CRC校验码:
84 0A(这是一个固定值,由算法计算得出)
完整报文 = 01 03 00 00 00 01 84 0A
✅ 一句话总结:
校验部分 = 数据的“验证码”,确保报文在传输过程中没有出错。
6️⃣ 完整报文示例解析
下面我们用一个完整的例子,把前面所有的知识点串起来。
🎯 场景:读取设备 01 的输出寄存器,从地址 40001 开始,读取1个寄存器。
📌 步骤1:确定从站地址
- 设备地址 =
01
📌 步骤2:确定功能码
- 读取输出寄存器 =
03
📌 步骤3:确定数据部分
- 起始地址:
40001的相对地址是0→00 00 - 长度:读1个寄存器 →
00 01 - 数据部分 =
00 00 00 01
📌 步骤4:计算校验部分
- CRC校验码 =
84 0A(由软件自动计算)
📌 最终报文(十六进制):
01 03 00 00 00 01 84 0A
📌 报文结构分解:
| 部分 | 字节位置 | 内容 | 说明 |
|---|---|---|---|
| 从站地址 | 第1字节 | 01 |
发送给设备1 |
| 功能码 | 第2字节 | 03 |
读取输出寄存器 |
| 数据部分 | 第3-6字节 | 00 00 00 01 |
起始地址0,读1个寄存器 |
| 校验部分 | 第7-8字节 | 84 0A |
CRC校验码 |
✅ 恭喜你!你已经掌握了Modbus RTU报文的完整结构!
7️⃣ 常见问题与避坑指南
❓ 问题1:为什么我发送的报文没有响应?
- 可能原因:
- 从站地址错误 → 检查设备地址是否正确。
- 功能码错误 → 检查功能码是否匹配存储区。
- 数据部分错误 → 检查起始地址和长度是否正确。
- 校验码错误 → 使用正确的CRC算法计算校验码。
- 通信参数错误 → 检查波特率、数据位、停止位、校验位是否一致。
❓ 问题2:CRC校验码怎么计算?
- 不要手动计算! 使用Modbus调试软件或编程库(如Python的
pymodbus)自动生成。 - 如果你一定要手动计算,可以使用在线CRC计算器,选择“Modbus RTU CRC16”。
❓ 问题3:报文长度是多少?
- 报文长度不是固定的,它取决于功能码和数据部分。
- 最短报文:
从站地址(1) + 功能码(1) + 校验(2)= 4字节(如写入单个线圈) - 最长报文:理论上可达256字节(受协议限制)
🚨 重要提醒:
Modbus RTU协议规定,报文之间必须有3.5个字符时间的静默间隔,否则会被认为是同一个报文。这个细节在调试时很重要!
8️⃣ 实战模拟:手把手教你构造报文
假设你现在有一个任务:
“我要写入设备
02的输出线圈,地址00001,设置为‘开’(值为1)。”
📌 步骤1:确定从站地址
- 设备地址 =
02
📌 步骤2:确定功能码
- 写入单个线圈 =
05
📌 步骤3:确定数据部分
- 写入地址:
00001的相对地址是0→00 00 - 数据:设置为“开” →
FF 00(Modbus规定,开=FF00,关=0000) - 数据部分 =
00 00 FF 00
📌 步骤4:计算校验部分
- CRC校验码 =
8C 4B(由软件自动计算)
📌 最终报文(十六进制):
02 05 00 00 FF 00 8C 4B
✅ 你已经成功构造了一个写入报文!
🎯 下一步学习建议
- 动手实践:使用Modbus调试软件(如Modbus Poll, QModBus),输入不同的地址、功能码和数据,观察返回结果。
- 学习CRC校验:虽然不需要手动计算,但了解其原理有助于深入理解协议。
- 阅读设备手册:任何Modbus设备都会在手册中列出支持的功能码、地址范围和通信参数,一定要学会看手册!
- 学习Modbus TCP:这是以太网上的Modbus协议,结构更简单,适合网络通信。
希望这篇超详细的笔记能帮你彻底搞懂Modbus RTU通信格式!记住,Modbus RTU报文就像一封结构化的“电报”,每个字节都有它的特定含义。只要掌握了这套规则,你就能轻松应对各种工业通信场景。加油,你已经迈出了成为工业通信高手的关键一步!💪
看似与人为善、心肠柔软之人,必然有一块坚硬如铁的心境土壤,在苦难人生中,死死支撑着那份看似愚蠢的善意