3. 常见通信协议(上):Modbus RTU/TCP协议原理、寄存器映射、功能码详解、报文解析实战

3.1 为什么能源系统离不开Modbus?

做能源管理系统这些年,我接触过的设备没有一千也有八百。电表、逆变器、温控器、PLC……你猜它们共同支持什么协议?

答案就是Modbus。

为什么?说白了,Modbus太实用了。它诞生于1979年,比我还大几岁。但你别看它老,在工业现场,老家伙往往最可靠。Modbus协议简单、开放、不收费,几乎所有的工业设备都支持它。

我个人习惯把Modbus比作「工业界的普通话」。不管设备是哪国造的,只要会说Modbus,就能跟你的EMS系统对话。

核心要点:Modbus是主从架构。一个主站(你的EMS服务器)可以带最多247个从站(现场设备)。主站问,从站答,规矩得很。

3.2 Modbus的两副面孔:RTU vs TCP

Modbus主要有两种变体:RTU和TCP。我刚开始做项目时也搞混过,后来发现记住一句话就行——RTU走串口,TCP走网口

特性 Modbus RTU Modbus TCP
物理层 RS-232 / RS-485 以太网
传输距离 RS-485可达1200米 100米(交换机可扩展)
从站数量 最多247个 理论上无限制
报文格式 带CRC校验 带MBAP头,无CRC
典型场景 工厂、配电房 楼宇、数据中心

嗯,这里要注意:RTU报文末尾有2字节的CRC校验,TCP没有。为什么?因为TCP/IP协议栈自己会做校验,Modbus就不重复劳动了。

3.3 寄存器映射——设备的数据字典

你想想看,Modbus协议本身只定义了「怎么传」,没定义「传什么」。那设备的数据怎么组织?靠寄存器映射表。

Modbus定义了四种数据对象:

  • 线圈(Coil):1位,可读写。比如开关状态。
  • 离散输入(Discrete Input):1位,只读。比如报警信号。
  • 输入寄存器(Input Register):16位,只读。比如电压、电流。
  • 保持寄存器(Holding Register):16位,可读写。比如设定值、参数。

我在项目中遇到过最坑的事:某厂家把电压值放在保持寄存器里,但文档写的是输入寄存器。我查了三天才找到原因。所以拿到设备第一件事——先看寄存器映射表,别信经验主义。

我的习惯:拿到新设备,先读一遍所有保持寄存器的值,再读一遍输入寄存器。对比两份数据,基本能发现映射关系。

3.4 功能码详解——Modbus的指令集

Modbus功能码就是主站发给从站的「命令」。常用的其实就几个:

功能码 名称 操作对象 说明
0x01 读线圈 线圈 读一组开关量
0x02 读离散输入 离散输入 读一组只读开关量
0x03 读保持寄存器 保持寄存器 最常用,读参数
0x04 读输入寄存器 输入寄存器 读测量值
0x05 写单个线圈 线圈 控制单个开关
0x06 写单个寄存器 保持寄存器 设置单个参数
0x0F 写多个线圈 线圈 批量控制
0x10 写多个寄存器 保持寄存器 批量设置参数

你看,是不是很简单?实际项目中,80%的通信只用0x03和0x04这两个功能码。读数据嘛,够用了。

3.5 报文解析实战——手撕Modbus报文

光说不练假把式。我们来拆一个真实的Modbus RTU报文。

假设主站发送:01 03 00 00 00 02 C4 0B

我来逐字节拆解:

  • 01:从站地址,1号设备
  • 03:功能码,读保持寄存器
  • 00 00:起始寄存器地址,从0号开始
  • 00 02:读取数量,读2个寄存器
  • C4 0B:CRC校验,低字节在前

从站回复:01 03 04 00 0A 00 14 7A 1E

  • 01:从站地址
  • 03:功能码回显
  • 04:数据字节数,4字节
  • 00 0A:寄存器0的值,10(十进制)
  • 00 14:寄存器1的值,20(十进制)
  • 7A 1E:CRC校验

我曾经用Wireshark抓一个TCP报文,发现设备回复的数据总是少两个字节。折腾半天,原来是设备固件有bug,把CRC当数据发了。嗯,现场调试就是这样,什么怪事都能遇到。

避坑指南:我曾经在调试时发现,有些国产设备对功能码0x03和0x04的实现有差异。明明文档写的是输入寄存器,但用0x04读不到数据,换0x03反而能读到。遇到这种情况,别纠结,哪个能用用哪个。

3.6 知识体系总览

下面这张图是我自己整理的Modbus知识框架,帮你快速建立全局认知:

Modbus通信协议知识体系 Modbus RTU Modbus TCP 线圈 (Coil) 离散输入 输入寄存器 保持寄存器 0x01 读 / 0x05 写 0x02 读 0x04 读 0x03 读 / 0x06 写 报文结构:地址 + 功能码 + 数据 + CRC(RTU)/ MBAP(TCP) 应用场景:电表采集、逆变器监控、PLC通信 寄存器映射表 → 数据解析 → 存入数据库

3.7 实战小贴士

最后分享几个我踩过的坑:

  1. 字节序问题:Modbus默认大端序(高字节在前),但有些设备用的小端序。我遇到过读出来的电压值变成65535,其实就是字节序搞反了。
  2. 浮点数处理:32位浮点数占2个寄存器。IEEE754标准,但有些厂家自己定义格式。建议先读一个已知值验证。
  3. 超时设置:RTU模式下,报文间隔不能超过3.5个字符时间。TCP模式下,建议超时设500ms以上,网络抖动时不会误判。

我的调试三板斧:第一,用Modbus Poll模拟主站;第二,用串口助手看原始报文;第三,对照寄存器映射表逐字节核对。这三招下来,90%的问题都能定位。

好了,Modbus RTU和TCP的核心内容就这些。下一节我们会聊Modbus的进阶话题,包括批量读取策略、异常处理、以及如何用Python写一个Modbus采集程序。到时候我会分享一个我实际项目中用过的代码框架。


公众号:蓝海资料掘金营,微信deep3321