4. Modbus协议详解:Modbus RTU、Modbus TCP、功能码、报文结构
好,咱们今天来啃一块硬骨头——Modbus协议。
说实话,Modbus这玩意儿在工业现场太常见了。我做了十几年SCADA系统集成,几乎每个项目都离不开它。有人觉得它老,1980年代的东西了。但我想说,老不代表不好用。你想想看,一个协议能活40多年,一定有它的道理。
4.1 Modbus的两种“方言”:RTU vs TCP
Modbus其实分两种主流形态:RTU和TCP。很多人搞混,我简单说说区别。
Modbus RTU跑在串口上,RS-232或者RS-485。它的特点是紧凑、高效。每个字节都压缩成16进制,所以同样波特率下,RTU比ASCII模式快一倍。我在一个水处理项目中用过RTU,现场环境恶劣,电磁干扰大,但RTU愣是扛住了,数据没丢过。
Modbus TCP则是跑在以太网上。说白了就是把RTU的报文封装到TCP/IP包里。端口号固定用502。好处是啥?速度快,距离远,还能和办公网共用线路。但注意——别把工业网和办公网直接怼一起,我吃过这个亏,后面讲。
核心区别一句话:RTU是串口上的二进制协议,TCP是以太网上的封装版。RTU有CRC校验,TCP靠TCP/IP协议栈保证可靠性。
4.2 报文结构:拆开看看里面长啥样
Modbus的报文结构其实不复杂。我习惯把它分成三块:
- 地址域:1个字节,告诉从站“我在叫你”
- 功能码:1个字节,告诉从站“你要干啥”
- 数据域:N个字节,具体参数和数值
RTU报文还要加两个字节的CRC校验。TCP报文则加一个MBAP头(7个字节),代替了地址和CRC。
举个例子,读保持寄存器的RTU报文:
从站地址: 0x01
功能码: 0x03
起始地址: 0x00 0x6B
寄存器数: 0x00 0x03
CRC校验: 0x?? 0x??
嗯,这里要注意。CRC计算很多人搞错。我建议直接用查表法,别自己算,容易翻车。
4.3 功能码:Modbus的“指令集”
功能码就是Modbus的指令。常用的就那么几个:
| 功能码 | 名称 | 作用 |
|---|---|---|
| 0x01 | 读线圈 | 读取DO(数字量输出) |
| 0x02 | 读离散输入 | 读取DI(数字量输入) |
| 0x03 | 读保持寄存器 | 读取AO(模拟量输出)或参数 |
| 0x04 | 读输入寄存器 | 读取AI(模拟量输入) |
| 0x05 | 写单个线圈 | 控制单个DO |
| 0x06 | 写单个寄存器 | 设置单个AO或参数 |
| 0x0F | 写多个线圈 | 批量控制DO |
| 0x10 | 写多个寄存器 | 批量设置AO或参数 |
我个人习惯,项目里80%的通信都用0x03和0x06。读参数、写参数,搞定。但有一次在光伏电站项目中,逆变器非要我用0x10写多个寄存器,否则不响应。嗯,设备厂商的“个性”你得适应。
小技巧:调试时先用0x03读几个寄存器,确认通信正常,再尝试写操作。这样能快速定位问题。
4.4 异常响应:设备说“不”的时候
Modbus从站如果收到非法请求,会返回异常响应。格式是:功能码 + 0x80,再加一个异常码。
常见异常码:
- 0x01:非法功能码——从站不支持这个指令
- 0x02:非法数据地址——你读的地址不存在
- 0x03:非法数据值——参数超出范围
- 0x04:从站设备故障——设备内部出问题了
我曾经遇到一个情况,PLC一直返回0x02。查了半天,发现是地址偏移搞错了。Modbus地址从0开始,但有些设备手册写的是1开始。你想想看,差一个数,全盘皆输。
避坑指南:我曾经在调试时忽略了异常响应,以为设备没反应就是坏了。其实设备在说“你发的东西我不懂”。所以,一定要在代码里处理异常码,别闷头重试。
4.5 实战中的几个坑
讲几个我踩过的坑,你们记一下:
- 字节序问题:Modbus默认大端序(高字节在前),但有些设备用的小端序。我遇到过变频器,高低字节反着来,读出来的数值完全不对。
- 寄存器映射:同一个地址,不同厂商可能映射不同的物理量。一定要看设备手册,别想当然。
- 通信超时:RTU模式下,报文之间要有3.5个字符的静默时间。太短了,设备会认为报文没结束;太长了,设备会超时。我一般设10ms,稳妥。
- TCP连接管理:Modbus TCP是短连接还是长连接?看场景。频繁读写建议长连接,省去握手开销。但记得加心跳,防止连接假死。
嗯,差不多就这些。Modbus协议看着简单,但用好了能解决很多实际问题。下一节咱们讲怎么在SCADA里集成Modbus设备,到时候我会拿一个真实项目案例来拆解。