ModbusRTU通信协议报文剖析
前言
大家好!我是付工。前面给大家介绍了Modbus协议的应用层面。终于有人把Modbus说明白了那么,今天跟大家聊聊关于Modbus协议报文的那些事。
一、真实案例
前段时间有个粉丝朋友,让我帮他解决一个问题。
这个粉丝朋友是负责Modbus主站调试的。
项目背景:这是一个船舶的项目,主站是一个贝加莱PLC,带4个从站,从站是西门子S7-1200PLC,分别是右配电板PLC、右发电机PMS、岸电PMS和岸电柜,触摸屏使用的是北尔的iXDeveloper。
故障现象:触摸屏上提示部分通信中断报警,并且读写设备速度很慢。
因为跟老外沟通不畅,必须找到绝对的证据。
我让他将RS485的AB端子,通过485转USB转换器接到一台电脑上,然后通过抓包软件来观察报文的异常情况。
发送:02 03 00 00 00 00 45 F9返回:02 83 03 F1 31
这是2号从站的一个错误报文帧,读取长度为0,因为有重发机制,所以会连续重发多次,导致通信延时。
发送:03 0F 00 04 00 00 00 29 CF
返回:03 8F 03 A5 F1
这是3号从站的一个错误报文帧,预置多线圈功能码返回错误,这里可能是起始地址错误,这个错误导致连续重发,引起通信延时。
通过报文分析最终找到原因,解决了这个现场问题。
前面说过,学习Modbus有两个层面,第一个是应用层面,第二个是报文层面。对于PLC工程师来说,掌握应用层面一般就够用了,但是如果遇到一些特殊的场景,掌握报文层面,会更有利于编写出优质的程序和快速定位问题。
二、通信协议
如何理解通信协议呢?
通信的目的是数据交互。如果我们把通信的过程比作公司需要传达一个消息。
其中会涉及到三个部分,简称通信三要素。第一是通信介质,它决定了途径,微信还是打电话。第二是通信协议,它决定了内容,具体是什么消息。第三是通信角色,它决定了谁主动发出,谁被动接收。通信协议的本质就是设备之间沟通的语言。设备与人不同,设备是固定的,只要双方提前约定好协议就行了。比如我们对接机器人系统时,约定好发送“XYZ”,对方就会回复XYZ的坐标值,这就是一种协议。
很多时候,我们认为这是一种自定义协议。我认为,通信协议有三种类型:一种是标准协议,比如Modbus协议等。一种是品牌协议,比如西门子S7、三菱MC协议等。还有一种是自定义协议,比如上面那个“XYZ”协议。从本质来说,所有的协议都是自定义协议,只是使用的人多了,慢慢形成了标准。
世上本没有路,走的人多了,便形成了路。
虽然协议种类很多,但并不需要都学,见招拆招,才是最重要的。
学习的时候只要搞懂1-2种,就能触类旁通。
我认为最适合学习的是ModbusRTU与ModbusTCP,至于ModbusASCII,一般很少用。
而且这两个协议,也不需要学两次,先学会ModbusRTU,ModbusTCP就能轻松驾驭。
三、通用格式
学习通信协议,首先要弄清楚通用报文格式。
所谓通用报文格式,其实就是一个公式规范。所有的通信报文,必须是符合这个公式规范的。ModbusRTU的通用报文格式如下:
【1】从站地址:这个报文发送给谁或来自于谁。
【2】功能码:要干什么,读/写/线圈/寄存器。
【3】数据部分:配合功能码提供对应的参数。
【4】校验部分:保证报文的正确性和完整性。
下面围绕这个公式,针对每个功能码进行阐述。
四、读取输出线圈
在通用格式基础上,针对功能码,我们进行细化。
读取输出线圈发送报文格式如下:
对比通用格式来看:就是将数据部分细化成了起始线圈地址和数量。
我们来分析一下这段发送报文:
【1】从站地址:0x01表示读取1号从站的数据。
【2】功能码:0x01表示读取的是输出线圈存储区。
【3】起始地址:十六进制0x00 0x13对应十进制为19,表示从19号线圈开始读取,这个19对应的Modbus地址为00020。
【4】线圈数量:十六进制0x00 0x1B对应十进制的27,表示读取线圈数量为27。
【5】CRC校验:0x8D 0x4C是对前面所有数据进行CRC校验后的结果。
注意:报文中的起始地址,就是我们前面说过的相对地址。对于任意一个存储区,相对地址都是从0开始的,所有的通信报文中使用的都是相对地址。
主站发送这段报文是想要读取1号从站输出线圈存储区,Modbus地址从 00020-00046,共27个线圈的状态值。当1号从站收到这段报文后,知道了主站的意图,便会响应,响应报文如下:
我们再来分析一下这段响应报文:
【1】从站地址:0x01表示由1号从站响应的报文。
【2】功能码:0x01表示响应的是0x01功能码报文。
【3】字节计数:0x04表示返回的数据共有4个字节。
【4】字节1至字节4:返回的具体数据分别为0xCD、0x6B、0xB2、0x05,这4个字节对 应32个线圈值,前面的27个线圈值即对应00020-00046的值。
【5】CRC校验:0x00 0x02是对前面所有数据进行CRC校验后的结果。
主站收到响应报文便获取到了自己要的数据,具体对应关系如下:
0xCD=1100 1101 对应 00027-00020
0x6B=0110 1011 对应 00035-00028
0xB2=1011 0010 对应 00043-00036
0x05=0000 0101 对应 00051-00044
至此,就完成了一次Modbus通信交互。至于读取输入线圈,与读取输出线圈几乎一致,唯一的区别就是功能码从0x01变成了0x02,这里就不做过多赘述了。
五、读取保持型寄存器
接下来我们对03功能码读取保持型寄存器进行说明,这个也是我们经常使用的一个功能码。
读取保持型寄存器发送报文格式如下:
这个与读取输出线圈是相似的,只不过这里的起始地址是寄存器地址。
我们来分析一下这段发送报文:
【1】从站地址:0x01表示读取1号从站的数据。
【2】功能码:0x03表示读取保持型寄存器存储区。
【3】起始地址:十六进制0x00 0x6B对应十进制为107,表示从107号寄存器开始读取,这个107对应的Modbus地址为40108。
【4】寄存器数量:0x00 0x02对应十进制的2,表示读取寄存器数量为2。
【5】CRC校验:0xB5 0xD7是对前面所有数据进行CRC校验后的结果。
主站发送这段报文是想要读取1号从站保持型寄存器存储区,Modbus地址 从40108-40109,共2个寄存器的数据值。
从站响应报文格式如下:
我们再来分析一下这段响应报文:
【1】从站地址:0x01表示1号从站响应的报文。
【2】功能码:0x03表示响应的是0x03功能码的报文。
【3】字节计数:0x04表示返回的数据共有4个字节。
【4】字节1至字节4:返回的具体数据分别为0x00、0xC8、0x01、0x2C,这4个字节对 应2个保持型寄存器的值,即对应40108-40109的值。
【5】CRC校验:0x7B、0x80是对前面所有数据进行CRC校验后的结果。
主站收到响应报文便获取到了自己要的数据,具体对应关系如下:
0x00 0xC8对应40108的值,16进制的0x00 0xC8对应十进制的200
0x01 0x2C对应40109的值,16进制的0x01 0x2C对应十进制的300
读取输入寄存器,与读取保持型寄存器报文格式几乎一致,唯一的区别就是功能码从0x03变成了0x04,这里就不做过多赘述了。
六、预置单线圈
我们对0x05功能码预置单线圈进行说明,发送报文格式如下:
对比通用格式来看:将数据部分细化成了线圈地址和断通标志,如果是置位,则断通标志为0xFF 0x00,如果是复位,断通标志为0x00 0x00。
我们来分析一下这段发送报文:
【1】从站地址:0x01表示写入1号从站的数据。
【2】功能码:0x05表示写入单个输出线圈。
【3】线圈地址:十六进制0x00 0x1A对应十进制为26,表示写入26号线圈,这个26对应的Modbus地址为00027。
【4】断通标志:0xFF 0x00表示置位,要将该线圈状态设置为True。
【5】CRC校验:0xAD 0xFD是对前面所有数据进行CRC校验后的结果。
这段发送报文表示的含义是主站想要将1号从站输出线圈存储区,Modbus地址00027这个线圈置为True。
接收报文格式如下:
预置单线圈接收报文与发送报文一致,原报文返回。
七、预置单寄存器
我们对0x06功能码预置单寄存器进行说明,发送报文格式如下:
对比通用格式来看:将数据部分细化成了寄存器地址和写入值,单寄存器表示16位整数,占用2个字节。
我们来分析一下这段发送报文:
【1】从站地址:0x01表示写入1号从站的数据。
【2】功能码:0x06表示写入单个保持型寄存器。
【3】线圈地址:十六进制0x00 0x10对应十进制为16,表示写入16号寄存器,这个16对应的Modbus地址为40017。
【4】写入值:16进制0x03 0x00对应十进制768,就是将该寄存器的值设置为768。
【5】CRC校验:0x88 0xFF是对前面所有数据进行CRC校验后的结果。
这段发送报文表示的含义是主站想要将1号从站保持型寄存器存储区,Modbus地址 40017这个寄存器的值修改为768。
接收报文格式如下:
预置单寄存器接收报文与发送报文一致,原报文返回。
八、预置多线圈
0x0F功能码预置多线圈发送报文格式如下:
对比通用格式来看:将数据部分细化成了起始线圈地址、数量、字节计数和写入值。
我们来分析一下这段发送报文:
【1】从站地址:0x01表示写入1号从站的数据。
【2】功能码:0x0F表示写入多个输出线圈。
【3】起始地址:十六进制0x00 0x13对应十进制为19,表示从19号线圈开始写入,这个 19对应的Modbus地址为00020。
【4】数量:十六进制0x00 0x0A对应十进制为10,表示连续写入10个线圈,Modbus地 址从00020到00029。
【5】字节数:0x02表示2个字节,因为10个线圈至少要用2个字节来表示。
【6】写入值:0x0F 0x03表示写入的两个字节,第一个字节对应前8个线圈,第二个字节对应后面的线圈。
【7】CRC校验:0xA2 0x6A是对前面所有数据进行CRC校验后的结果。
这段发送报文表示主站想要对1号从站输出线圈存储区,从Modbus地址 00020开始的10个线圈值进行修改,0x0F对应二进制00001111,对应前8个线圈,就是将00020-00027写入11110000,0x03对应二进制00000011,对应后面的线圈,也就是将00028-00029写入11。
接收报文格式如下:
预置多输出线圈接收报文是在发送报文基础上除去字节数及写入值。
九、预置多寄存器
0x10功能码预置多寄存器发送报文格式如下:
对比通用格式来看:将数据部分细化成了起始寄存器地址、数量、字节计数和写入值。
我们来分析一下这段发送报文:
【1】从站地址:0x01表示写入1号从站的数据。
【2】功能码:0x10表示写入多个保持型寄存器。
【3】起始地址:十六进制0x00 0x10对应十进制为16,表示从16号寄存器开始写入,这个16对应的Modbus地址为40017。
【4】数量:十六进制0x00 0x02对应十进制为2,表示连续写入2个寄存器,Modbus地 址从40017到40018。
【5】字节数:0x04表示4个字节,因为1个寄存器对应2个字节,2个寄存器对应4个字节。
【6】写入值:0x01 0x0A 0x01 0x10表示写入的4个字节,前2个字节对应第1个寄存器, 后2个字节对应第2个寄存器。
【7】CRC校验:0xD3 0x01是对前面所有数据进行CRC校验后的结果。
这段发送报文表示的含义是主站想要对1号从站保持型存储区,从Modbus地址40017 开始的2个寄存器值进行修改,0x01 0x0A对应十进制266,对应第1个寄存器,也就是 将40017写入266,0x01 0x10对应十进制272,对应第2个寄存器,也就是将40018写入272。
接收报文格式如下:
预置多寄存器接收报文是在发送报文基础上除去字节数及写入值。
ModbusRTU通信协议报文剖析的更多相关文章
- 普通PC通过USB转485串口 ModBus-RTU通信协议控制伺服电机
一.RS485通信 RS485 是半双工通信(2 线制),可以一点对多点进行组网,而且 RS485 是用缆线两端的电压差值来表示传递信号,这与 RS232 电气特性大不一样.RS485 仅仅规定了接收 ...
- ModbusRtu通信报文详解【二】
这里接着上一篇内容对ModbusRtu的通信报文做个详细描述: [1]强制单个线圈 功能码:05H [2]预置单个寄存器 功能码:06H [3]强制多个线圈 功能码;0FH [4]预置多个寄存器 功能 ...
- ModbusRtu通信报文详解【一】
Modbus协议可谓是工业控制领域应用最广泛的协议之一.根据不同的电气接口,包括Modbus Rtu/ASCII,Modbus TCP/UDP,从学习的角度来说,只要学会其中一种,剩余的都是大同小异的 ...
- 物联网常见通信协议RFID、NFC、Bluetooth、ZigBee等梳理
1 概述 在上一篇文章<物联网常见通信协议与通讯协议梳理[上]-通讯协议>中,对物联网常用通信协议和通讯协议作了区分,并对通讯协议进行了分享:本文将对常用的通信协议进行剖析,重点面向市场 ...
- 欧姆龙PLC HostLink协议整理
欧姆龙PLC HostLink协议整理 1.常用的存储器功能区 CIO: 输入继电器 272 点(17 CH) 0.00-16.15 输出继电器 272 点(17 CH) 100.00-116.1 ...
- IP 层收发报文简要剖析6--ip_forward 报文转发
//在函数ip_route_input_slow->ip_mkroute_input注册, /* * IP数据包的转发是由ip_forward()处理,该函数在ip_rcv_finish() * ...
- IP 层收发报文简要剖析6--ip报文输出3 ip_push_pending_frames
L4层的协议会把数据通过ip_append_data或ip_append_page把数据线放在缓冲区,然后再显示调用ip_push_pending_frames传送数据. 把数据放在缓冲区有两个优点, ...
- IP 层收发报文简要剖析5--ip报文发送2
udp 发送ip段报文接口ip_append_data ip_append_data 函数主要用来udp 套接字以及raw套接字发送报文的接口.在tcp中发送ack 以及rest段的ip_send_u ...
- IP 层收发报文简要剖析3--ip输入报文分片重组
在ip_local_deliver中,如果检测到是分片包,则需要将报文进行重组.其所有的分片被重新组合后才能提交到上层协议,每一个被重新组合的数据包文用ipq结构实例来表示 struct ipq { ...
- IP 层收发报文简要剖析4--ip 报文发送
无论是从本地输出的数据还是转发的数据报文,经过路由后都要输出到网络设备,而输出到网络设备的接口就是dst_output(output)函数 路由的时候,dst_output函数设置为ip_output ...
随机推荐
- Python按条件筛选、剔除表格数据并绘制剔除前后的直方图
本文介绍基于Python语言,读取Excel表格文件数据,以其中某一列数据的值为标准,对于这一列数据处于指定范围的所有行,再用其他几列数据的数值,加以数据筛选与剔除:同时,对筛选前.后的数据分别绘 ...
- vue小知识:多层数据双向相应之向上派发和向下派发($dispatch和$broadcast)
注意:这两个实例已经在vue3中弃用啦!!!(所以不详细说了,封装知道怎么用就行了,作为了解) 都是在vue实例配置(main.js) 向上派发:$dispatch 注意,在相应后代组件中使用 thi ...
- JMeter 配置元件之按条件读取CSV Data Set Config
实践环境 win10 JMeter 5.4.1 需求描述 需求是这样的,需要压测某个接口(取消分配接口),请求这个接口之前,需要先登录系统(物流WMS系统),并在登录后,选择并进入需要操作的仓库,然后 ...
- R语言基于表格文件的数据绘制具有多个系列的柱状图与直方图
本文介绍基于R语言中的readxl包与ggplot2包,读取Excel表格文件数据,并绘制具有多个系列的柱状图.条形图的方法. 首先,我们配置一下所需用到的R语言readxl包与ggplot2 ...
- C# 推荐一种开机自启动的方式
概述(Overview) 网上多数搜索结果以注册表设置为优先,这个方法需要管理员权限,实际工作中可能并不适用.这个方法是直接写到用户开机自启动目录里,系统开机会带着一起启动.(Most search ...
- 如何自动实现本地AD中禁用的用户从地址列表中隐藏掉?
我的博客园:https://www.cnblogs.com/CQman/ 如何自动实现本地AD中禁用的用户从地址列表中隐藏掉? 需求信息: 用户本地AD用户通过ADConnect同步到O365,用户想 ...
- 【Java】Reflection 反射机制 03调用
调用属性,方法,构造器 属性调用 @Test public void fieldCall() throws NoSuchFieldException, IllegalAccessException, ...
- 【Uni-App】关于获取手机系统信息的项目实践
原因是这里APP下载方式的问题 安卓 和 IOS都可以写A标签跳转访问附件资源 但是甲方对这种下载方式并8满意[安卓行 苹果8行, 苹果行,安卓又8行] 通过 uni.getSystemInfo来判断 ...
- 【SpringBoot】01 快速上手
环境搭建: JDK8 + IDEA 2018 + SpringBoot + Maven 3.0 + 创建Boot项目 2020.6.1更新补充: 最近才发现SpringBoot用IDEA构建项目会发生 ...
- 【Java-GUI】07 Swing01 入门案例
Swing是Java自己开发出的一套GUI组件,不同于AWT去调用操作系统的GUI 正是因为非系统平台的GUI,所以程序运行的要慢一些 涉及的设计模式:MVC模式 Model(组件对象状态) View ...