2. Modbus协议原理与数据模型:Modbus起源、主从架构、四种数据模型(DI、DO、AI、AO)、功能码分类
各位同学,咱们今天聊聊Modbus。说实话,这玩意儿在工业现场太常见了。我做了十几年边缘计算,几乎每个项目都离不开它。你想想看,从PLC到变频器,从温控表到智能电表,Modbus就像工业界的“普通话”。不懂它,你根本没法跟设备对话。
2.1 Modbus的起源:一个“老古董”为何至今活跃?
Modbus是1979年由Modicon公司(就是现在的施耐德电气)发明的。那时候还没有以太网,串口通信是主流。我记得第一次接触Modbus是在一个老旧的水处理厂,那台PLC还是90年代的,但Modbus RTU跑得稳稳的。
为什么这个“老古董”到现在还没被淘汰?说白了,就三个字:简单、开放、可靠。Modbus协议帧结构非常简洁,一个请求帧也就几个字节。我在项目中见过最极端的案例——用一对双绞线,通过Modbus RTU拉了1200米,照样稳定通信。换成其他协议,早丢包丢到姥姥家了。
核心要点:Modbus是应用层协议,它不关心底层物理层是RS-232、RS-485还是以太网。这种分层设计让它能“活”到现在。
2.2 主从架构:谁说了算?
Modbus采用主从(Master/Slave)架构。嗯,这里要注意,现在有些文档改叫“客户端/服务器”了,但工业现场的老工程师还是习惯叫主从。
主从架构的规则很简单:
- 主站(Master):只有一个,负责发起所有通信。它问,从站才能答。
- 从站(Slave):可以有多个(最多247个),每个有唯一地址(1-247)。从站不能主动说话,只能等主站点名。
我曾经在一个项目中踩过坑。现场有台变频器,怎么都通信不上。查了半天,发现是有人把从站地址设成了0。Modbus规定地址0是广播地址,所有从站都会接收但不会回复。嗯,这个坑我记了好几年。
避坑指南:从站地址范围是1-247,地址0用于广播,248-255保留。我曾经见过有人把地址设成255,结果设备直接“装死”。
2.3 四种数据模型:DI、DO、AI、AO
Modbus把数据分成四种类型。这个分类很巧妙,你想想看,工业现场无非就是开关量和模拟量,输入和输出。Modbus用四个表格就把它们全装下了。
| 数据模型 | 对象类型 | 访问类型 | 数据大小 | 地址范围 |
|---|---|---|---|---|
| DI(离散量输入) | 只读开关量 | 只读 | 1 bit | 10001-19999 |
| DO(离散量输出/线圈) | 读写开关量 | 读写 | 1 bit | 00001-09999 |
| AI(模拟量输入) | 只读寄存器 | 只读 | 16 bit | 30001-39999 |
| AO(模拟量输出/保持寄存器) | 读写寄存器 | 读写 | 16 bit | 40001-49999 |
我个人习惯这样记:
- DI:按钮、限位开关、光电传感器——只能读,不能写
- DO:继电器、指示灯、电磁阀——能读也能写
- AI:温度、压力、流量传感器——4-20mA或0-10V信号,只读
- AO:变频器频率设定、阀门开度控制——能读也能写
实战技巧:很多新手搞混地址。记住一个口诀:“0开头是线圈,1开头是输入,3开头是AI,4开头是AO”。我在调试时经常用这个口诀快速定位问题。
2.4 功能码分类:Modbus的“指令集”
功能码就是Modbus的指令。主站发一个功能码,从站就知道要干什么。常用的功能码就那么几个,我列个表给你看:
| 功能码 | 名称 | 操作对象 | 说明 |
|---|---|---|---|
| 01 (0x01) | 读线圈 | DO | 读取DO的状态(ON/OFF) |
| 02 (0x02) | 读离散量输入 | DI | 读取DI的状态 |
| 03 (0x03) | 读保持寄存器 | AO | 最常用的功能码,读取AO数据 |
| 04 (0x04) | 读输入寄存器 | AI | 读取AI数据 |
| 05 (0x05) | 写单个线圈 | DO | 控制单个DO |
| 06 (0x06) | 写单个寄存器 | AO | 写入单个AO数据 |
| 15 (0x0F) | 写多个线圈 | DO | 批量控制DO |
| 16 (0x10) | 写多个寄存器 | AO | 批量写入AO数据 |
为什么功能码这么重要?我给你举个例子。有一次我在调试一个边缘网关,读取温湿度数据时一直返回错误。后来发现,我把功能码03(读保持寄存器)用成了功能码04(读输入寄存器)。那台温湿度传感器用的是AI数据模型,只能用04读取。就这么一个码的区别,折腾了我两个小时。
记住:功能码03和04是最常用的。03读AO(保持寄存器),04读AI(输入寄存器)。搞反了,数据就读不出来。
2.5 一个完整的Modbus请求示例
光说不练假把式。咱们看一个实际的Modbus RTU请求帧。假设主站要读取从站地址为1的设备,从地址40001开始读2个保持寄存器:
请求帧(主站→从站):
01 03 00 00 00 02 C4 0B
解析:
01 - 从站地址
03 - 功能码(读保持寄存器)
00 00 - 起始地址(40001对应协议地址0x0000)
00 02 - 读取数量(2个寄存器)
C4 0B - CRC校验
响应帧(从站→主站):
01 03 04 00 0A 00 14 7A 9E
解析:
01 - 从站地址
03 - 功能码
04 - 数据字节数(2个寄存器=4字节)
00 0A - 第一个寄存器值(10)
00 14 - 第二个寄存器值(20)
7A 9E - CRC校验
你看,就这么简单。一个请求帧才8个字节,响应帧也才9个字节。这就是Modbus能在低速串口上跑得飞快的原因。
调试技巧:我建议你准备一个Modbus调试工具,比如ModScan或Modbus Poll。刚开始学的时候,用工具发几个功能码试试,看看设备怎么响应。比看文档管用多了。
2.6 边缘计算中的Modbus应用
在边缘计算场景下,Modbus通常扮演“数据采集”的角色。边缘网关作为Modbus主站,轮询各个从站设备,把数据采集上来,再通过MQTT或OPC UA上传到云端。
我个人习惯的做法是:
- 先搞清楚数据模型:哪些是DI,哪些是AI,哪些是AO。别搞混了。
- 规划地址映射:把Modbus地址映射到边缘网关的内部变量。
- 设置轮询周期:DI和DO可以快一点(100ms),AI和AO可以慢一点(1s)。
- 处理异常:从站掉线、超时、CRC错误,都要有处理机制。
重要提醒:Modbus RTU是半双工通信,主站发完请求后必须等待从站回复。如果从站没回复,主站不能立即发下一个请求。我见过有人把超时时间设成10ms,结果在长距离通信时频繁超时。建议至少设200ms以上。
好了,这一章的内容就到这里。Modbus协议原理其实不复杂,但细节很多。你只要把主从架构、四种数据模型、功能码分类这三个核心概念搞明白,后面实战部分就会轻松很多。下一章咱们聊聊Modbus RTU和Modbus TCP的区别,以及在实际项目中怎么选型。