2、Modbus协议原理:Modbus RTU vs Modbus TCP,数据帧结构深度解析

好,咱们进入正题。

Modbus协议,说白了就是工业设备之间的“通用语言”。你想想看,PLC、变频器、仪表,这些设备来自不同厂家,怎么让它们互相听懂?Modbus就是干这个的。我最早接触Modbus是在一个水处理项目里,当时要采集几十个液位计的数据,用的就是Modbus RTU。嗯,那会儿还踩了不少坑,后面慢慢给你们讲。

2.1 Modbus的两种“方言”:RTU vs TCP

Modbus主要有两个版本:RTU和TCP。很多人一开始搞不清区别,我简单说下。

Modbus RTU,走的是串口(RS-232/RS-485)。数据是二进制格式,紧凑、高效。适合短距离、低速场景。我在工厂里见过最多的就是RS-485总线,一根双绞线能挂几十个设备,成本低,抗干扰也不错。

Modbus TCP,走的是以太网。说白了就是把Modbus数据包塞进TCP/IP协议里。速度更快,距离更远,还能和IT系统对接。现在新项目基本都倾向用TCP,毕竟网线比串口线好找多了。

那怎么选?我个人习惯:现场总线用RTU,上位机通信用TCP。当然,现在也有网关能把RTU转成TCP,灵活很多。

核心区别一句话:

  • RTU:串口,二进制,无IP地址,靠设备地址区分
  • TCP:以太网,有IP地址,靠IP+端口区分

2.2 数据帧结构:RTU vs TCP 深度拆解

搞懂帧结构,才算真正入门Modbus。我见过太多人拿着工具抓包,却看不懂每一段是什么意思。咱们一个一个来拆。

2.2.1 Modbus RTU 帧结构

RTU的帧结构很简单,就四部分:

地址域 功能码 数据域 CRC校验
1字节 1字节 N字节 2字节

地址域:就是设备地址,范围1-247。0是广播地址,一般不用。我记得有一次项目里,有人把地址设成了0,结果所有设备都响应了,总线直接乱套。嗯,这个坑我替你们踩过了。

功能码:告诉设备要干什么。比如03是读保持寄存器,06是写单个寄存器。常用的就那几个,后面章节会细讲。

数据域:具体的数据内容。比如读寄存器时,这里放起始地址和读取数量。

CRC校验:16位循环冗余校验。这是RTU的保命符,数据传错了能检测出来。我曾经遇到过一条线缆老化,偶尔丢包,就是靠CRC发现异常的。

小技巧:RTU帧之间要有至少3.5个字符时间的间隔,用来区分不同帧。这个时间在9600波特率下大约是3.5ms。别小看这个间隔,我见过有人把间隔设太短,导致帧粘连,数据全乱。

2.2.2 Modbus TCP 帧结构

TCP的帧结构稍微复杂一点,因为它多了个MBAP头:

MBAP头 功能码 数据域
7字节 1字节 N字节

MBAP头包含:

  • 事务处理标识符(2字节):用来匹配请求和响应。我习惯用递增的计数器,方便调试时追踪。
  • 协议标识符(2字节):Modbus协议固定为0x0000。
  • 长度(2字节):后面数据的字节数。
  • 单元标识符(1字节):相当于RTU的设备地址。注意,TCP本身没有地址,这个字段是为了兼容RTU的网关设备。

你发现没有?TCP帧没有CRC校验。为什么?因为TCP/IP协议栈自己会做校验,Modbus就不重复了。说白了,TCP是“搭便车”,省掉了CRC。

注意:TCP帧的长度字段不包括MBAP头自身,只算功能码+数据域。很多人刚学时在这里算错,导致解析失败。我曾经调试一个网关,折腾了两小时,最后发现是长度算多了2字节。

2.3 实战对比:RTU vs TCP 的抓包分析

光讲理论没意思,咱们看个实际例子。假设我们要读取设备地址为1的PLC里,从地址0开始的10个保持寄存器。

RTU请求帧(十六进制):

01 03 00 00 00 0A C5 CD

解释:

  • 01:设备地址
  • 03:读保持寄存器
  • 00 00:起始地址(0)
  • 00 0A:读取数量(10)
  • C5 CD:CRC校验

TCP请求帧(十六进制):

00 01 00 00 00 06 01 03 00 00 00 0A

解释:

  • 00 01:事务标识符(第1次请求)
  • 00 00:协议标识符
  • 00 06:长度(后面6个字节)
  • 01:单元标识符(设备地址)
  • 03:功能码
  • 00 00:起始地址
  • 00 0A:读取数量

你看,TCP比RTU多了7个字节的MBAP头,但少了2个字节的CRC。实际传输效率差不多,但TCP的灵活性更高。

2.4 避坑指南:我踩过的那些坑

做Modbus开发,有几个坑是绕不开的。我给你们列一下:

  • 字节序问题:Modbus默认是大端模式(高位在前)。但有些设备厂家不按套路出牌,用的小端。我曾经被一个国产仪表坑过,读出来的数据死活不对,最后发现是字节序反了。
  • 寄存器地址偏移:有些PLC的寄存器地址从0开始,有些从1开始。比如你要读地址40001,实际发送时可能要用0x0000。这个一定要看设备手册。
  • 超时设置:RTU的响应时间一般设100-200ms,TCP可以设短一点。但别设太短,有些老设备反应慢。我习惯先设500ms,调试稳定后再优化。
  • 多设备冲突:RS-485总线上多个设备同时发送数据会冲突。所以Modbus是主从模式,只有主机能发起请求,从机只能响应。这个设计很巧妙,但也限制了灵活性。

总结一下:

Modbus RTU和TCP,本质上是同一套协议,只是传输层不同。RTU适合现场总线,TCP适合网络通信。搞懂帧结构,你就能自己写驱动、抓包分析、排查故障。下一章咱们讲功能码的详细用法,到时候我会带你们写一个Python的Modbus客户端,真正跑起来。

嗯,今天就到这儿。记住,协议是死的,人是活的。多动手抓包,多读手册,慢慢就有感觉了。

推荐工具:Modbus Poll(模拟主机)、Modbus Slave(模拟从机)、Wireshark(抓TCP包)。这三个工具我用了十年,够用。

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