Modbus这样学,没有理由学不会
2026/8/27 11:10:34 网站建设 项目流程

刚入行那会儿,看Modbus协议跟看天书似的。寄存器、线圈、CRC、MBAP头,一个个名词堆上来,脑子全是浆糊。后来发现,不是这玩意儿难,是市面上教程写得忒绕。今天按我自己的理解路子,把Modbus从头捋一遍,争取让刚接触的人看完就能上手抓包。

一、先别碰代码,搞清楚Modbus到底是个啥

很多人一上来就翻代码,看NModbus源码,两眼一抹黑。正确姿势是先搞清楚三件事:

第一,Modbus是应用层协议。它不管数据怎么从A传到B,那是串口或者TCP的事。Modbus只规定数据长什么样、怎么问、怎么答。

第二,Modbus是主从模式。主站发问,从站回答。从站从来不主动说话,你问一句它答一句。这个设计简单粗暴,但也意味着从站有问题主站不知道,只能靠超时判断。

第三,Modbus只传16位整数。什么浮点数、字符串、32位整数,全是后来人们想出来的歪招。Modbus底层只认0x0000到0xFFFF,别的都不认。

这三条搞明白,心里就有底了。剩下的全是技术细节,一个坑一个坑填就行。

二、从一帧最简单的报文开始

别上来就搞什么01读线圈、03读寄存器,先看一帧最简单的请求长什么样。

假设你要读从站地址为1的设备,从地址0开始读2个保持寄存器。

RTU发出去的报文是这样的:

text

01 03 00 00 00 02 C4 0B

拆开看:

  • 01:从站地址

  • 03:功能码,读保持寄存器

  • 00 00:起始地址,0x0000

  • 00 02:读2个寄存器

  • C4 0B:CRC校验

就这么8个字节。你要是能对着这个报文把每一字节的含义说清楚,Modbus就算入门了。

设备回复的报文大概长这样:

text

01 03 04 12 34 56 78 9A 3F

拆开:

  • 01:从站地址

  • 03:功能码

  • 04:数据字节数,4个字节表示2个寄存器

  • 12 34:第一个寄存器的值

  • 56 78:第二个寄存器的值

  • 9A 3F:CRC

看明白了吧?主站问"把0号和1号寄存器的值告诉我",从站答"0号是0x1234,1号是0x5678"。就这么个对话,简单到令人发指。

三、功能码:就记四个常用的

Modbus功能码几十个,实际干活就四个。

功能码名称干啥用
01读线圈读开关量输出,比如继电器状态
02读离散输入读开关量输入,比如按钮状态
03读保持寄存器读16位数据,可读可写
04读输入寄存器读16位数据,只读

03和04的区别:03读的是PLC程序能改的数据,04读的是传感器直接采上来的数据。温度变送器用04读温度值,变频器用03读运行频率也写目标频率。

另外两个写的功能码:

  • 05:写单个线圈(开关)

  • 06:写单个寄存器(16位)

  • 0F:写多个线圈

  • 10:写多个寄存器

够用了,别的用上的概率极低。

四、地址到底怎么算?这事儿我专门拎出来说

新手最头疼的问题:手册说温度存在40001地址,你发报文时起始地址填多少?

答案是填0x0000

"40001"是PLC厂家为了让人看懂搞的映射规则,跟协议本身没有半毛钱关系。Modbus协议里地址就是0~65535,没有前缀。

具体的对应关系:

  • 线圈地址对应PLC地址:00001开始,协议地址从0开始

  • 离散输入:10001开始,协议地址从0开始

  • 输入寄存器:30001开始,协议地址从0开始

  • 保持寄存器:40001开始,协议地址从0开始

所以你看到手册写"温度值存放在40001",意思就是功能码用03,协议起始地址填0x0000,读1个寄存器。手册写"40003",那协议起始地址就是0x0002。

协议地址 = PLC地址 - 40001。就这么简单。

有人非要杠"为什么手册写40001而不是0",别问,问就是历史遗留问题,记住换算规则就行。

五、RTU和TCP到底啥关系

很多人分不清RTU和TCP,以为这俩是两套完全不同的协议。其实它们的关系就一句话:

TCP是RTU去掉了CRC、前面加了个MBAP头

看对比:

RTU请求:

text

[地址][功能码][数据][CRC]

TCP请求:

text

[事务ID][协议ID][长度][单元ID][功能码][数据]

相同颜色的部分,功能码和数据完全一样。TCP把地址改叫单元ID,把CRC砍了,前面加7个字节的头部。

所以只要你学会了RTU的报文格式,TCP就是个加了个头的变种。反过来也一样,把TCP报文去掉前7个字节、尾部加上CRC再算一遍,就变成了RTU报文。

两者传输的内容一样,只是包装不同。理解了这个,RTU和TCP在你眼里就是同一种东西的不同马甲。

六、CRC到底要不要手算

CRC-16-Modbus,多项式0x8005,初值0xFFFF。这个东西不学行不行?

行,但要会用工具算

网上有在线CRC计算器,填上数据点一下就能出结果。调试阶段用这个完全够用。等你真的需要写代码实现通信了,直接从网上抄现成的CRC计算函数,哪个都行,别自己发明轮子。

但有一点必须清楚:CRC是低字节在前。算出来是0x1234,发的时候先发0x34再发0x12。这地方搞反了,CRC就算对了设备也不认,因为它按约定顺序接收。

七、动手抓一帧报文,比看十遍书都管用

这是最管用的一招。装个串口调试助手或者Modbus Poll,连上一个设备,发一个读寄存器的请求,把收到的原始数据复制出来。

然后对着每一字节分析:

  • 第1字节是谁?

  • 第2字节是什么功能码?

  • 数据部分是哪些字节?

  • CRC是哪两个字节?

对着手册看一遍,当场就通了。比你看一百页PDF来得都快。

Modbus Poll这个软件特别好用,作为主站可以发各种请求,作为从站可以模拟设备响应。调试时用它发命令,看设备回什么,所有报文都显示在界面上。初期学习阶段,花半小时玩这个软件比读三章协议文档都有用。

八、常见错误:我见过的翻车现场

错误一:串口参数没对上

这是新手第一坑。9600 8 N 1是默认配置,但不是所有设备都用这个。你设9600,设备跑19200,能通就见鬼了。拿到设备第一件事,翻手册查串口参数页。

错误二:A/B线接反

RS485有极性,A接A,B接B。接反了能收到乱码但CRC全错。示波器看一眼波形,反了就是反的,别侥幸。

错误三:从站地址填错

设备地址设的是2,你发请求填1,设备不会理你。如果地址填0,那是广播地址,所有从站都收但不回复。

错误四:读的地址超限

手册说保持寄存器范围40001~40010,你发请求从40011开始读,设备回异常码02(非法地址)。然后你怀疑程序写错了,调了半天才想起来查手册。别问我怎么知道的。

错误五:一次读太多

RTU单帧最多读125个寄存器,读126个有些设备直接不回。TCP也差不多,虽然没硬性限制但设备缓冲区就那么大。

九、学习路线图:按这个顺序来

第一天:搞明白Modbus是主从模式,主问从答。看一帧报文长什么样,能对着报文把每个字节的含义说清楚。

第二天:上手Modbus Poll软件,连一个实际设备或者虚拟设备,发读请求看回复。把功能码01、02、03、04都试一遍。

第三天:把RTU的CRC算一遍,不用手算,用工具算但要理解低字节在前。把RTU和TCP的报文格式对比着看,找出相同点和不同点。

第四天:写代码。用C#或者Python,串口通信把SerialPort整明白,TCP通信把Socket整明白。先实现读寄存器,再实现写寄存器,别的慢慢加。

第五天:抓包调试。把自己代码发出去的报文和Modbus Poll发出去的报文对比,看看哪里不一样。CRC对不上就调CRC,地址对不上就调地址。

按这个节奏走,一周时间足够把Modbus用到项目里。

十、不要被"协议"两个字吓住

说穿了,Modbus就是规定了对话格式。你问问题用一种格式,对方回答用另一种格式,来来回回就这么点事。

复杂的地方不在协议本身,在现场环境。干扰、接线、参数配置、设备故障、厂家不按标准来,这些才是真正消耗时间精力的地方。

所以学习阶段别有压力。会看报文、会算CRC、会发请求,这就叫学会了。至于现场那些幺蛾子,那是经验的活儿,遇到了再解决,解决一次就记住了。

抓包抓包抓包,重要的事情说三遍。你手里有Wireshark和串口监听工具,就没有查不出来的问题。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询