现在网上有很多类似的文章、其实这一篇也借鉴了很多其他博主的文章。
写这篇文章的重点是在于解析功能和报文、对Modbus这个协议并不会做很多介绍。
好了,我们开始吧。
常用的功能码其实也没多少、我也就按照大小逐个介绍吧。
1、01X 读取一组逻辑线圈的当前状态(ON/OFF)
请求:MBAP 功能码 起始地址H 起始地址L 数量H 数量L(共12字节)
响应:MBAP 功能码 数据长度 数据(一个地址的数据为1位)
发送包
byte[0] byte[1] 00 02 为消息号,随便指定,服务器返回的数据的前两个字和这个一样
byte[2] byte[3] 00 00 为modbus标识,强制为0即可
byte[4] byte[5] 00 06 值在06 之后所有字节的个数,大家也可以数一数哈
byte[6] 01 为站号、随便指定。
byte[7] 01 为功能码(这个是决定了要干什么事)
byte[8] byte[9] 00 00为起始地址,比如我们我们想读地址0的数据就为00,读1000地址为03 E8
byte[10] byte[11] 00 80为指定读取数据的长度,跟地址规则一样
回包
注:bit是一直到127的 因为图片太大无法截到
byte[0] byte[1] 消息号,我们之前写发送指令的时候,是多少,这里就是多少。
byte[2] byte[3] 同上
byte[4] byte[5] 指后面的字节数
byte[6] 站号
byte[7] 功能码
byte[8] 指示在byte[8]后面的字节数量 在byte[8]后面就是真实数据
byte[9] 到结尾都是我们读取到的数据 因为字节是8位所以是16(0-127所占了128个bit 128/8 得出16)
列子:
读5个线圈状态
发送:00 00 00 00 00 06 01 01 00 00 00 05
接收:00 00 00 00 00 04 01 01 01 1F
2、0x02 读取一组开关输入的当前状态(ON/OFF)
请求:MBAP 功能码 起始地址H 起始地址L 数量H 数量L(共12字节)
响应:MBAP 功能码 数据长度 数据(长度:9+ceil(数量/8))
发送包
其实大家自己看与0x01是基本一样的。只是多了个Bit Count这一个
Bit Count所代表的意思就是 读取十个数据。
回包
跟0x01的回包也是一样的,可能大家伙会有些迷茫,我发包的时候是读取十个数据回包的时候Byte Count怎么会给我回复了个2呢?
细心的朋友可能也发现了、这两个单词是不一样的~一个是Bit Count 一个是Byte Count,哈哈哈哈小小的幽默 。
那这个Byte Count代表的是什么意思呢。还是字节有关。
一个字节是8位、但是10个位超过了1个字节但是不满2个字节所以占用2个字节。还有很多跟这个情况相同。
列子:
读5个输入状态
发送:00 00 00 00 00 06 01 02 00 00 00 05
接收:00 00 00 00 00 04 01 02 01 15
3、0x03 读取一个或多个保持寄存器的数值
请求:MBAP 功能码 起始地址H 起始地址L 寄存器数量H 寄存器数量L(共12字节)
响应:MBAP 功能码 数据长度 寄存器数据(长度:9+寄存器数量×2)
发包
这边Modbus/TCP我就不再展开了、都是一样的。
这个发包也很好去理解、根据Reference这一栏还有Word Count这一栏说明是起始位置是400读取6个保持寄存器数值。
一通百通、Modbus在功能报文上基本都是一致的。
回包
大家伙可能有迷茫了 怎么会是12呢 这个可怎么计算呢
我把这个Register这一栏专门的点开了,大家也都能看出来了吧。
这样就跟明了了吧,Register是占了两个位所以呢 读寄存器的个数6乘2 那不就是12了吗
列子:
读取连续三个保持寄存器的数值
发送:00 00 00 00 00 06 01 03 00 00 00 03
接收:00 00 00 00 00 09 01 03 06 03 E8 13 88 02 8A
4、0x04 读取一个或多个输入寄存器的数值
请求:MBAP 功能码 起始地址H 起始地址L 寄存器数量H 寄存器数量L(共12字节)
响应:MBAP 功能码 数据长度 寄存器数据(长度:9+寄存器数量×2)
发送
这个意思也就是 从0的位置读取10个输入寄存器的数值。
一通百通、有时候这些协议的原理我们没必要去搞明白,我们只要是知道这一块代表什么有什么用处我该如何去利用这一块这样就足够。
没必要去研究它如何工作如何交互(其实说白了 万变不离其宗 大部分的协议交互还是 挥手 握手)
看每个人的需求吧,可能有些工作是必须要吃透协议那只能硬着头皮去看了。
扯远了。
回包
这个也很好看明白了,还是跟0x03一样。没有变化。
列子:
读5个输入寄存器的数值
发送:00 00 00 00 00 06 01 04 00 00 00 05
接收:00 00 00 00 00 0D 01 04 0A 00 00 00 01 00 02 00 03 00 04
5、0x05 强置一个逻辑线圈的通断状态
请求:MBAP 功能码 输出地址H 输出地址L 输出值H 输出值L(共12字节)
响应:MBAP 功能码 输出地址H 输出地址L 输出值H 输出值L(共12字节)
发包
05 是功能码, 00 00是我们指定的地址,如果我们想写地址1000,那么就为 03 E8,后四位是规定线圈的通断状态。
那么上面发包的意思就是 在00 00这个位置 指定线圈通断状态为 断开。
举个例子:
写入地址100为通: 00 00 00 00 00 06 FF 05 00 64 FF 00
写入地址1000为断:00 00 00 00 00 06 FF 05 03 E8 00 00
回包
各位也发现了,回包和发包是一样的,因为在你写入的操作中,是不带读取数据的,所以服务器会直接复制一遍你的指令并返回。
例子:强置一个逻辑线圈的通断状态
发送:00 00 00 00 00 06 01 05 00 00 FF 00
接收:00 00 00 00 00 06 01 05 00 00 FF 00
6、0x06 写单个保持寄存器
请求:MBAP 功能码 寄存器地址H 寄存器地址L 寄存器值H 寄存器值L(共12字节)
响应:MBAP 功能码 寄存器地址H 寄存器地址L 寄存器值H 寄存器值L(共12字节)
发包
这个也不用过多的介绍了吧。在00 00位置 写入 00 00
回包
也是一样的,服务器会直接复制一遍你的指令并返回。
7、0x0f 强置多个逻辑线圈的通断状态
请求:MBAP 功能码 起始地址H 起始地址L 输出数量H 输出数量L 字节长度 输出值H 输出值L
响应:MBAP 功能码 起始地址H 起始地址L 输出数量H 输出数量L
发送
Bit Count 10是要写的个数
回包
都一样,直接说例子吧。
例子:强置一串连续逻辑线圈的通断
发送:00 00 00 00 00 0B 01 0F 00 00 00 02 04 FF 00 00 00
发送:00 00 00 00 00 06 01 0F 00 00 00 02
8、0x10 写多个保持寄存器
请求:MBAP 功能码 起始地址H 起始地址L 寄存器数量H 寄存器数量L 字节长度 寄存器值(13+寄存器数量×2)
响应:MBAP 功能码 起始地址H 起始地址L 寄存器数量H 寄存器数量L(共12字节)
发送
Word count为 要写的个数
Byte count 为 要写入的数目 1个word等于2个bytes
后面 00 00 每组为写入的数值
回包
也是一样的,写了十个那么就返回十。
列子:
向起始地址为0x0000,数量为0x0001的寄存器写入数据,数据长度为0x02,数据为0x000F
发送 00 01 00 00 00 09 01 10 00 00 00 01 02 00 0F
接收 00 01 00 00 00 06 01 10 00 00 00 01
9、0x2b 读取设备ID
发送
byte[0]byte[1] 消息号 随便指定
byte[2]byte[3] modbus的标识
byte[4]byte[5] 在此之后的长度
byte[6] 站号
byte[7] 功能码
byte[8] 功能类型 这里是读取设备ID
byte[9] 读什么 这里是读设备标识
byte[10] 设备名称.
回包
跟发包一样。
附录一:Modbus功能码表
功能码 |
名称 |
作用 |
01 |
读取线圈状态 |
取得一组逻辑线圈的当前状态(ON/OFF) |
02 |
读取输入状态 |
取得一组开关输入的当前状态(ON/OFF) |
03 |
读取保持寄存器 |
在一个或多个保持寄存器中取得当前的二进制值 |
04 |
读取输入寄存器 |
在一个或多个输入寄存器中取得当前的二进制值 |
05 |
强置单线圈 |
强置一个逻辑线圈的通断状态 |
06 |
预置单寄存器 |
把具体二进值装入一个保持寄存器 |
07 |
读取异常状态 |
取得8个内部线圈的通断状态,这8个线圈的地址由控制器决定,用户逻辑可以将这些线圈定义,以说明从机状态,短报文适宜于迅速读取状态 |
08 |
回送诊断校验 |
把诊断校验报文送从机,以对通信处理进行评鉴 |
09 |
编程(只用于484) |
使主机模拟编程器作用,修改PC从机逻辑 |
10 |
控询(只用于484) |
可使主机与一台正在执行长程序任务从机通信,探询该从机是否已完成其操作任务,仅在含有功能码9的报文发送后,本功能码才发送 |
11 |
读取事件计数 |
可使主机发出单询问,并随即判定操作是否成功,尤其是该命令或其他应答产生通信错误时 |
12 |
读取通信事件记录 |
可是主机检索每台从机的ModBus事务处理通信事件记录。如果某项事务处理完成,记录会给出有关错误 |
13 |
编程(184/384 484 584) |
可使主机模拟编程器功能修改PC从机逻辑 |
14 |
探询(184/384 484 584) |
可使主机与正在执行任务的从机通信,定期控询该从机是否已完成其程序操作,仅在含有功能13的报文发送后,本功能码才得发送 |
15 |
强置多线圈 |
强置一串连续逻辑线圈的通断 |
16 |
预置多寄存器 |
把具体的二进制值装入一串连续的保持寄存器 |
17 |
报告从机标识 |
可使主机判断编址从机的类型及该从机运行指示灯的状态 |
18 |
(884和MICRO 84) |
可使主机模拟编程功能,修改PC状态逻辑 |
19 |
重置通信链路 |
发生非可修改错误后,是从机复位于已知状态,可重置顺序字节 |
20 |
读取通用参数(584L) |
显示扩展存储器文件中的数据信息 |
21 |
写入通用参数(584L) |
把通用参数写入扩展存储文件,或修改之 |
22~64 |
保留作扩展功能备用 |
|
65~72 |
保留以备用户功能所用 |
留作用户功能的扩展编码 |
73~119 |
非法功能 |
|
120~127 |
保留 |
留作内部作用 |
128~255 |
保留 |
用于异常应答 |
好了,基本上也就这些比较常见,文章写得比较简陋也比较懒省事。
日后有时间了话再进行补充吧。
还有ModbusTCP一些抓的包还有模拟器也一并分享出来吧。
(模拟器是源自dathlin的作品)
链接:https://pan.baidu.com/s/1K7FjXhWOJrVyVMXWUhQCcQ
提取码:mjf3