ModBus TCP协议总结

本文深入解析Modbus协议中常见的功能码,包括0x01、0x02、0x03、0x05、0x06、0x0F、0x10,涵盖线圈和寄存器的读写操作。详细介绍了每种功能码的使用场景、数据解析规则及报文结构。

本篇文章是我看了以下博客之后的总结,以便于自己日后的复习和巩固,这里贴出源文章的出处,以表示尊重知识产权。
https://www.cnblogs.com/dathlin/p/8007297.html
https://blog.youkuaiyun.com/thebestleo/article/details/52269999

对于modbus来说,涉及的功能码也就是0x01,0x02,0x03,0x05,0x06,0x0F,0x10了,其实分类来说,就只有两种,线圈和寄存器,也就是位读写和字读写,首先需要清楚的是功能码不一样,对应数据的解析规则也不一样,下面就针对不同的情况来说。
对于位操作来说(各种线圈和离散量),一个地址代表了一个bool变量,即 0 和 1,要么通要么断。
对于寄存器来说,一个地址代表了2个byte,共有65536种方式,可以满足大多数日常使用了,比如我们读取地址0的寄存器,返回 00 00 及代表寄存器0数据为0,如果返回 01 00 ,那么代表寄存器0数据为 256

功能码0x01:
读线圈的通断,可以理解为读取某一位的值

发送:00 00 00 00 00 06 FF 01 00 00 00 01 下面将对这串消息进行解析

	byte[0]    byte[1]    byte[2]   byte[3]   byte[4]   byte[5]   byte[6]   byte[7]   byte[8]   				byte[9]   byte[10]   byte[11]  

	byte[0]    byte[1]             : 消息号---------随便指定,服务器返回的数据的前两个字和这个一样

	byte[2]   byte[3]              :modbus标识,强制为0即可

	byte[4]   byte[5]               :指示排在byte[5]后面所有字节的个数,也就是总长度-6

	byte[6]:                       站号,对于TCP协议来说,不重要,可以随便指定,对于rtu及ascii来说,就需要选择设备的站号信息。

	byte[7] :                     功能码,这里就需要填入我们的真正的想法了

	byte[8]  byte[9]               :起始地址,比如我们想读取地址0的数据,就填 00 00 ,如果我们想读取地址1000的数据,怎么办,填入 03 E8 ,也就是将1000转化十六进制填进去。

	byte[10]  byte[11]              :指定想读取的数据长度,比如我们就想读取地址0的一个数据,这里就写 00 01,如果我们想读取地址0-999共计一个数据的长度,就写 03 E8。和起始地址是一样的。

意思就是读取地址为00的数据,读取的数据长度为1,这里的数据长度(对线圈的操作是按位来计算的)是按这个地址中的位来说的,即读取1位。

假设我们读取地址10开始的共10个线圈呢,那么会返回什么?所以我们发送 00 00 00 00 00 06 FF 01 00 0A 00 0A
06代表06后面有6位字节,FF是站号,01是功能码代表读取线圈,读取线圈的起始地址为10,读取的长度是10位。
然后接受到:00 00 00 00 00 05 FF 01 02 79 01
那么接受到的这一堆数据代表什么意思呢?

byte[0]  byte[1] :             消息号,我们之前写发送指令的时候,是多少,这里就是多少。

byte[2]  byte[3]:           必须都为0,代表这是modbus 通信

byte[4]  byte[5]:           指示byte[5]后面的所有字节数,你数数看是不是4个?所以这里是00 04,如果后面共有100个,那么这里就是 00 64

byte[6]:                        站号,之前我们写了FF,那么这里也就是FF

byte[7]:                        功能码,我们之前写了01的功能码,这里也是01,和我们发送的指令是一致的

byte[8]:                        指示byte[8]后面跟随的字节数量,因为跟在byte[8]后面的就是真实的数据,我们最终想要的结果就在byte[8]后面

byte[9]:                        真实的数据,哈哈,这肯定就是我们真正想要的东西了,我们知道一个byte有8位,但是我们只读取了一个位数据,所有这里的有效值只是byte[9]的最低位,二进制为 0000 0000 我们看到最低位为0,所以最终我们读取的地址0的线圈为断。

功能码之前的解析和发送的解析都是一样的,05代表后面的字节数,FF代表站号,01是功能码表示读取线圈,02代表后面跟随的字节数量是2个字节,我们前面不是要读取10位线圈吗,跟随的字节数量也就是两个字节(10位数据,不满两个字节,但是一个字节又不够,所以只能用两个字节来表示,但是里面只有10位数据是有效的)啦,这两个字节是0x79 01,转换成二进制就是0111 1001 0000 0001,然后以字节为单位将得到的二进制数据的顺序进行颠倒就是 1001 1110 1000 0000
所以我们读取到的从地址10开始的10个线圈1001 1110 1,也就是 通 断 断 通 通 通 通 断 通。

功能码0x02:
读离散量输入,

功能码0x05:
指定某一个线圈的通断,单个线圈的写入。

写入地址100为通: 00 00 00 00 00 06 FF 05 00 64 FF 00   
06代表后面有6个字节,FF是站号,05是功能码,00 64是地址,FF 00是让地址线圈通,返回的和发送的一摸一样。

写入地址1000为断:00 00 00 00 00 06 FF 05 03 E8 00 00

FF 00是规定的数据,如果你想地址线圈通,就填这个值,想指定线圈为断,就填 00 00 ,其他任何的值都对结果无效。

功能码0x0F:
这个功能码非常强大,可以实现任意线圈的通断,功能码0x05只能指定某一个线圈的通断,当我们要实现将地址0-999共计1000个线圈全部为off 或on或者任意设置时,怎么办?这里就用到了0x0F功能码,实现任意线圈的通断。

00 00         消息标识号,随便写什么,反正你写什么数据,服务器就复制一遍而已。

00 00         modbus标志号,都是00 就对了。

00 84         我们先转化为十进制,0x0084转化十进制就是132,也就是说,00 84(不包含00 84)后面跟了132个字节

FF              站号,其实也是随便写,反正服务器返回一样的

00 00         起始地址,此处就是0,如果起始地址为100,那么就写00 64,如果起始地址为1000,那么就写03 E8

03 E8         我们需要写的数据长度,因为我们需要写1000个线圈,就是03 E8,如果我们写999个线圈,那么就是03 E7。

7D             这个字节代表后面跟随的真实写入的数据的长度,为125个字节。125个字节  真实的数据,我们写1000个位,那么一个字节为8位,那么刚好125个字节	可以塞完数据

然后就是要写入的数据(经过转换之后的)。

比如我们写入地址10-地址19共计10个长度的线圈,要求的结果分别是,On,Off,Off,On,On,On,On,Off,On,Off,也就是 通,断,断,通,通,通,通,断,通,断,接下来转换0和1,如下:1001111010
然后以字节位单位进行分割得到:10011110 , 10
由于总共是10位,多出来的两位要放在下一个字节里,所以会有 1000 0000(不足八位的用0补齐1001 1110 , 1000 000),这里可能就有人有疑问了,不应该是
10 , 01111010吗?当然不是了,这里我们好好想一想,要写入10,有两位是要放到下一个字节里的是吧,所以依次写入前八位,所以剩下后两位10放到下一个字节,但是咱们的计算机是大端模式(高字节对应低地址是大端模式,高字节对应高地址低字节对应低地址是小端模式),所以还应该将字节里的数据的顺序颠倒过来,所以最终的数据是0111 1001 0000 0001,也就是0x79 01。
所以要发送: 00 00 00 00 00 09 FF 0F 00 0A 00 0A 02 79 01 ,这样也就实现了地址10-地址19 共10位线圈的写入。

写入之后,看看服务器返回了什么:
00 00 00 00 00 06 FF 0F 00 0A 00 0A :现在再来看这个数据就很简单了,就是返回了我们写入数据的前12个字节,然后把00 09长度更改为实际的长度 00 06。

功能码0x03:
读保持寄存器
就i是读取寄存器里的值,这个比较简单,比如读取寄存器地址为108-110三个寄存器里的值,发送00 01 00 00 00 06 FF 03 00 6C 00 03
前面都一样,从03开始解析,03功能码代表读取保持寄存器的值,寄存器的起始地址为0x006C,寄存器的个数为0x0003也就是三个寄存器,也就是读取108-110这三个寄存器的值。
然后服务器返回了:
00 01 00 00 00 09 FF 03 06 02 2B 00 00 00 64
03代表功能码,06代表字节长度,02 28 为第一个寄存器的值,00 00为第二个寄存器的值,00 64为第三个寄存器的值。

功能码0x06:
写单个寄存器
例如:往地址为2的寄存器里写入0x0003
发送00 01 00 00 00 06 FF 06 00 01 00 03
功能码之前和前面的解析一样,06代表功能码为06,向一个寄存器里写入某个值,这个寄存器的地址为0x0001,写入的数据为0x0003,转换为十进制为3。
接收到的是:00 01 00 00 00 06 FF 06 00 01 00 03

功能码0x10:
写多个寄存器。
将十六进制00 0A 和01 02 写入地址以2 开始的两个寄存器的实例
发送数据:
00 01 00 00 00 0B FF 10 00 02 00 02 04 00 0A 01 02
解析:向地址00 02(第一个00 02)为起始地址寄存器的数量为2,字节数为4,依次写入00 0A , 01 02。
接收到的数据:
00 01 00 00 00 06 FF 10 00 02 00 02
解析:功能码之前的都一样,10代表功能码0x10意思是写多个寄存器,第一个00 02代表寄存器的起始地址,第二个00 02 代表寄存器的数量。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值