Modbus 速查与实战笔记(功能码、帧结构、坑点)
文章目录
- Modbus 速查与实战笔记(功能码、帧结构、坑点与最佳实践)
- 1. Modbus 是啥(一句话版)
- 2. 数据模型:四类“地址空间”
- 3. 传输形态与帧结构
- 3.1 Modbus RTU(串口)
- 3.2 Modbus TCP(以太网)
- 4. 必背功能码(现场 90% 用这些)
- 5. 地址与数据编码的坑
- 6. 最小闭环:如何“正确把数读出来”
- 6.1 串口(RTU)最小步骤
- 6.2 以太网(TCP)最小步骤
- 7. 异常响应(错误码)速记
- 8. 小示例(思路级伪代码)
- 8.1 读保持寄存器(TCP)
- 8.2 写单个寄存器(RTU)
- 9. 现场调试清单(拎着就走)
- 10. 常见 FAQ
- 11. 你可能用得上的补充知识点
- 12. 一页速查卡(Cheat Sheet)
Modbus 速查与实战笔记(功能码、帧结构、坑点与最佳实践)
这篇当做一份能落地的备忘录:概念够用、步骤清晰、功能码一页记住,顺手补上常见坑和调试要点。少花时间踩雷,多花时间把数读对。
1. Modbus 是啥(一句话版)
工业现场最常见的主从式(Client/Server)通信协议之一。串口时代用 RTU/ASCII(走 RS-485/RS-232),以太网时代用 Modbus TCP(端口 502)。简单、稳定、跨厂商通用,但无认证、无遮挡——生产网请收口隔离。
2. 数据模型:四类“地址空间”
设备内部数据被抽象成 4 类寄存器/位,读写行为受功能码约束:
- Coils(线圈):开关量输出,布尔,可读写
- Discrete Inputs(离散输入):开关量输入,布尔,只读
- Holding Registers(保持寄存器):16 位寄存器,可读写(常用来存参数/输出)
- Input Registers(输入寄存器):16 位寄存器,只读(常用来读测量值)
传统文档里会看到 0xxxx/1xxxx/3xxxx/4xxxx 的“人类友好地址”,协议帧里没有这些前缀,只有零基地址。是否偏移 +1,各家实现不一——最容易踩坑,下文有对策。
3. 传输形态与帧结构
3.1 Modbus RTU(串口)
-
物理层:常见 RS-485 半双工,多点总线
-
帧格式(请求示例,读保持寄存器 0x03)
[SlaveID][Func][StartAddrHi][StartAddrLo][QtyHi][QtyLo][CRCLo][CRCHi]
响应:
[SlaveID][Func][ByteCount][Data(2*N bytes)][CRCLo][CRCHi]
-
CRC16:初值 0xFFFF,多项式 0xA001(低字节在前)
-
定时:帧间至少 3.5 字符时间静默;字符间不应超过 1.5 字符时间,否则被视为断帧
3.2 Modbus TCP(以太网)
-
端口 502,不带 CRC,在 TCP 里靠 MBAP 头
-
MBAP Header:
[TransactionID(2)][ProtocolID=0(2)][Length(2)][UnitID(1)]
随后是 PDU:
[Func][Data...]
-
UnitID:穿透网关时用来指明下游 RTU 的从站地址;直连纯 TCP 设备时通常固定为设备定义的值(常见 1)
术语对齐:
PDU(Protocol Data Unit)= 功能码 + 数据;
ADU(Application Data Unit)= 传输介质相关的完整帧(RTU=地址+PDU+CRC,TCP=MBAP+PDU)。
4. 必背功能码(现场 90% 用这些)
功能码 | 名称 | 读写 | 典型用途 |
---|---|---|---|
01 (0x01) | Read Coils | 读 | 开关量输出状态 |
02 (0x02) | Read Discrete Inputs | 读 | 开关量输入状态 |
03 (0x03) | Read Holding Registers | 读 | 读参数、模拟量、运算结果 |
04 (0x04) | Read Input Registers | 读 | 读传感器测量值 |
05 (0x05) | Write Single Coil | 写 | 置位/复位一个线圈 |
06 (0x06) | Write Single Register | 写 | 写入一个保持寄存器 |
0F (0x0F) | Write Multiple Coils | 写 | 批量写线圈 |
10 (0x10) | Write Multiple Registers | 写 | 批量写保持寄存器 |
17 (0x17) | Read/Write Multiple Registers | 读写 | 一次性读写寄存器(少见但高效) |
其他常见但非必背:
08 诊断、11 报告设备 ID、14/15 文件记录、16 掩码写寄存器、18 读 FIFO、2B/0E(设备识别,Modbus TCP 常见)。
广播地址 0(仅 RTU/ASCII):无响应,只能用于部分写操作。
5. 地址与数据编码的坑
-
偏移问题(0/1 基)
- 设备手册写“40001”的点,协议里通常发 0x0000。
- 有的厂商把“寄存器号 40001”直接当 1 用。
- 对策:先读一个已知值的寄存器,连着试 addr 和 addr-1,一眼就能确认。
-
字节序 & 字序
- 单个寄存器 16 位:**大端字节序(高字节在前)**是 Modbus 约定。
- 32 位/64 位:跨两个/多个寄存器时,不同厂商可能字序交换(word swap),甚至“字节+字都交换”。
- 对策:拿一个易识别常量(如 1.0f、1000 或时间戳)对照,快速判别组合方式。
-
布尔打包
- 01/02 返回的位是从 LSB 开始依序装在字节里;第 0 位对应请求的起始地址。
6. 最小闭环:如何“正确把数读出来”
6.1 串口(RTU)最小步骤
- 物理层:两线 RS-485,末端 120Ω 终端电阻,总线两端加,偏置电阻/Fail-safe建议到位。
- 接口参数:波特率/数据位/校验/停止位要与设备一致(常见 9600/19200,8E1 或 8N1)
- 从站地址:1–247,确定唯一
- 先用 功能码 03 读一个已知寄存器,确认地址偏移与字节/字序
- 写操作先在仿真/非产线环境验证,避免误动作
6.2 以太网(TCP)最小步骤
- 设备 IP、端口 502 可达;生产网请VLAN/ACL隔离
- 若通过网关转 RTU,UnitID 必须为下游 RTU 的从站地址
- 仍旧用 03 做握手验证,再扩展到批量读写
7. 异常响应(错误码)速记
当设备返回的功能码最高位被置 1(如请求 0x03,响应 0x83),随后的一个字节是异常码:
- 01:非法功能(该设备不支持这个功能码)
- 02:非法数据地址(寄存器不存在/不可达)
- 03:非法数据值(长度、范围不合法)
- 04:从站设备故障(内部错误)
- 05:已接收请求,处理需时间(少见)
- 06:从站忙
- 08:存储奇偶错误
- 0A/0B:网关路径不可达 / 目标设备无响应(TCP-RTU 网关场景)
读到这些,先检查功能码是否对、地址是否对、长度是否越界、权限是否允许。
8. 小示例(思路级伪代码)
8.1 读保持寄存器(TCP)
目标:读设备 10.0.0.5 上保持寄存器起始 0(40001)起的 2 个寄存器,UnitID=1请求PDU: [0x03][0x00 0x00][0x00 0x02]
MBAP: TxID任意; ProtID=0; Length = PDU长度 + UnitID(1)
完整: MBAP + [UnitID=0x01] + PDU
响应: [0x03][0x04][Data0Hi Data0Lo Data1Hi Data1Lo]
把两个寄存器按设备手册组合成 32 位(注意字序)
8.2 写单个寄存器(RTU)
[SlaveID][0x06][AddrHi][AddrLo][DataHi][DataLo][CRCLo][CRCHi]
响应会回显同样内容,CRC 校验一致
9. 现场调试清单(拎着就走)
- 物理层:A/B 极性各家标法不一(有厂商 A=–,B=+),不通就对调再测;总线只在两端加终端电阻
- 串口参数:波特率/校验必须匹配;RTU 帧间隔别被驱动层无意拉大
- 地址:先 03 读一个已知值;不通就试addr 与 addr-1
- 数据:确认字节序+字序;浮点/整型转换别混
- 网关:TCP 的 UnitID 与下游从站地址一致
- 安全:端口 502 不外曝;分区分域,必要时加网关白名单/隧道
- 抓包:以太网可用抓包器看 MBAP/PDU;串口用 USB-485 + 串口监视工具
10. 常见 FAQ
- 能广播写吗? RTU/ASCII 可用从站地址 0 做无应答写(仅部分写类功能支持),TCP 没有广播。
- 一次最多读多少? 标准上寄存器读常见上限 125(字),线圈 2000(位);很多设备更保守。
- 为什么读 32 位总是错位? 你和设备对“高字在前/低字在前”的理解不一致。先读一组已知值或文档示例校对字序。
- 能否混合长短帧? RTU 要求一个帧内连续发送,字符间隔不要超过 1.5 字符时间。
11. 你可能用得上的补充知识点
- ASCII 模式:以
:
开头、结尾\r\n
、校验 LRC,人眼可读但效率低,现代现场罕用 - 文件记录/设备识别:0x14/0x15/0x2B-0x0E 能读设备序列号、版本等,做资产盘点挺香
- 时间同步:无内建时钟同步,通常通过写寄存器实现(厂商自定义)
- 冗余:协议本身不管冗余,靠上层逻辑或网络结构实现
12. 一页速查卡(Cheat Sheet)
- 必用功能码:01/02/03/04/05/06/0F/10(+ 17 进阶)
- 地址:文档 40001 ⇄ 帧里 0x0000(小心 +1 偏移)
- 字节序:寄存器大端;跨寄存器字序不定,实测为准
- RTU 定时:帧间 ≥ 3.5 char;CRC16 初值 0xFFFF,多项式 0xA001
- TCP:端口 502;MBAP 有 TransactionID/Length/UnitID;无 CRC
- 异常码:01/02/03/04 最常见
- 安全:不暴露公网,网关/ACL 掐死