modbus-TCP协议详解

程序员小x大约 13 分钟network

modbus-TCP协议详解

1996年施耐德公司推出基于以太网TCP/IP的modbus协议:modbus-TCP。

MODBUS-TCP使MODBUS-RTU协议运行于以太网,MODBUS-TCP使用TCP/IP以太网在站点间传送MODBUS报文,MODBUS-TCP结合了以太网物理网络和网络标准TCP/IP以及以MODBUS作为应用协议标准的数据表示方法。MODBUS-TCP通信报文包在以太网TCP/IP数据包中。与传统的串口方式,MODBUS-TCP插入一个标准的MODBUS报文到TCP报文中,不再带有数据校验和地址。

MODBUS报文解析

| MBAP Header | Function code | Data | | Header | PDU |

MBAP header包含下面几个部分:

  • Transaction ID
  • Protocol ID
  • Length
  • UnitID
id名称长度说明
1Transaction ID 事务处理标识2字节报文的序列号,一般每次通讯加1,用于区别不同的报文
2Protocol ID 协议标识2字节00 00 代表modbus-Tcp
3Length2字节Unit长度 + PDU的长度
4UnitID单元标识符1

下面的这一串MBAP header|00 01| 00 00|00 06| 01 |, 其含义如下:

事务标识为1,协议是modbus-tcp协议,数据长度是:6,从站号是1。

需要注意的是MODBUS协议是一个大端的协议,前两个byte 00 01代表0x1 , 因此Transaction ID=1。而长度字段00 06代表0x6, 即UnitID和PDU的长度总和为6。

PDU部分相对复杂一些,主要是对一些寄存器进行读写操作。

modbus的操作对象有四种:线圈寄存器离散输入寄存器输入寄存器保持寄存器

寄存器种类数据类型访问类型功能码
线圈寄存器bit读写01H 05H 0FH
离散输入寄存器bit只读02H
输入寄存器2 bytes(word)只读04H
保持寄存器2 bytes(word)读写03H 06H 10H

线圈寄存器和离散输入寄存器是以bit为单位的寄存器,只能存储开关量,线圈寄存器可读可写,而离散输入寄存器只可读。

输入寄存器和保持寄存器以为2个byte为单位的寄存器,可以存储离散的变量, 保持寄存器可读可写, 输入寄存器只读。

常用的功能码作用如下:

功能码功能
0x01读单个或者多个线圈寄存器
0x02读离散量输入寄存器
0x03读保持寄存器
0x04读输入寄存器
0x05写单个线圈寄存器
0x06写单个保持寄存器
0x10写多个保持寄存器
0x0F写多个线圈寄存器

常用功能码详解

功能码0x1:读线圈寄存器

每个线圈寄存器可以存储一个bit的信息, 功能码0x01就是用于读取slave中线圈寄存器的状态,可以是单个线圈寄存器,也可以是多个连续的线圈寄存器

发送报文

发送报文由下面几个部分组成,总共12字节:

MBAP header(7字节) + 功能码(1字节) + 线圈寄存器起始地址的高位(1字节) + 线圈寄存器起始地址的低位(1字节) + 线圈寄存器数量的高位(1字节) + 线圈寄存器数量的低位(1字节)

下面是一个用Modbus-Poll和Modbus-Slave测试的实际的例子,其含义是读取线圈寄存器的起始地址是0x0, 读取数量为 0x0a(十进制10)个。

0x1-request
0x1-request
MBAP header功能码起始地址高字节起始地址低字节寄存器数量的高位寄存器数量的低位
01 66 00 00 00 06 01010000000a

其中:

TransanctionID = 358, Length = 6。

功能码为0x1,代表读取线圈寄存器。

读取的线圈寄存器的起始地址为0。

读取的线圈寄存器的数量为0xa(十进制10)个。

响应报文

响应报文的长度不是固定的,长度和用户请求的数据长度有关,由下面几个部分组成:

MBAP header(7字节) + 功能码(1字节) + 线圈寄存器的值

下面是一个实际的响应报文的内容:

0x1-request
0x1-request
MBAP header功能码字节数请求的数据1请求的数据2
01 66 00 00 00 05 0101022102

返回的第一个字节21,转化为二进制为00100001, 其中bit0代表00寄存器,bit7代表07寄存器。

0x70x60x50x40x30x20x10x0
00100001

返回的第二个字节02,转化为二进制为00000010, 其中bit0代表08寄存器,bit1代表09寄存器。

0x90x8
10

功能码0x5: 写单个线圈寄存器0x05:

功能码0x5的作用是对单个线圈寄存器写值ON/OFF。

发送报文

发送报文由下面几个部分组成,总共12字节:

MBAP header(7字节) + 功能码(1字节) + 线圈寄存器起始地址的高位(1字节) + 线圈寄存器起始地址的低位(1字节) + 要写的值的高位(1字节) + 要写的值的低位(1字节)

将从站中的一个输出写成ON或OFF,0xFF00代表为ON,0x0000代表为OFF。

下面是一个实际的例子:

0x5-request
0x5-request
MBAP header功能码起始地址高字节起始地址低字节写的值的高位写的值的低位
00 6f 00 00 00 06 01050004ff00

在这个例子中,对从站01的0x4号线圈执行ON操作。

响应报文

可以看到,如果写单个线圈成功,返回报文的值和发送报文的值相同:

0x5-request
0x5-request

功能码0x0F:写多个线圈寄存器

将一个从站中的多个线圈寄存器的写为ON或OFF,数据域中置1的位请求相应输出位ON,置0的位请求响应输出为OFF。

发送报文

发送报文由下面几部分组成:

MBAP 功能码 + 起始地址H 起始地址L + 输出数量H 输出数量L + 字节长度 + 输出值H 输出值L

其总长度为 13 + (修改的线圈寄存器数量/8 + 1)。

下面是一个实际的例子

0xF-request
0xF-request

按照协议进行对应的结果如下:

MBAP header功能码线圈寄存器起始位置线圈寄存器的数量字节数第一个byte的值第二个byte的值
01 71 00 00 00 09 010f00 0000 0a027000

其中第一个byte的值是0x70, 转换为二进制是01110000,其中低位bit0代表00寄存器,bit7代表07寄存器。

0x70x60x50x40x30x20x10x0
01110000

第二个byte的值是0x0, 转换为二进制是00000000,其中低位bit0代表08寄存器,bit1代表09寄存器。

0x90x8
00

该请求的作用起始就是将04 05 06寄存器的状态改为ON。

返回报文

返回的报文相对比较简单,由下面几个部分组成:

MBAP header + 功能码 + 起始地址H 起始地址L + 输出数量H 输出数量L

下面是一个实际的例子:

0xF-response
0xF-response

按照协议对应各部分的内容如下:

MBAP header功能码线圈寄存器起始位置线圈寄存器的数量
01 71 00 00 00 06 010f00 0000 0a

功能码0x02:读离散量输入寄存器

功能码0x2和0x1是比较类似的。只是操作的离散量输入寄存器是只读的,没有写操作的接口。

每个离散量输入寄存器可以存储一个bit的信息, 功能码0x02就是用于读取slave中离散量输入寄存器的状态,可以是单个线圈寄存器,也可以是多个连续的线圈寄存器

发送报文

发送报文由下面几个部分组成,总共12字节:

MBAP header(7字节) + 功能码(1字节) + 离散量输入寄存器起始地址的高位(1字节) + 离散量输入寄存器起始地址的低位(1字节) + 离散量输入寄存器数量的高位(1字节) + 离散量输入寄存器数量的低位(1字节)

下面是一个用Modbus-Poll和Modbus-Slave测试的实际的例子,其含义是读取线圈寄存器的起始地址是0x0, 读取数量为 0x0a(十进制10)个。

0x2-request
0x2-request
MBAP header功能码起始地址高字节起始地址低字节寄存器数量的高位寄存器数量的低位
01 b9 00 00 00 06 01010000000a

其中:

TransanctionID = 441, Length = 6。

功能码为0x1,代表读取线圈寄存器。

读取的线圈寄存器的起始地址为0。

读取的线圈寄存器的数量为0xa(十进制10)个。

响应报文

响应报文的长度不是固定的,长度和用户请求的数据长度有关,由下面几个部分组成:

MBAP header(7字节) + 功能码(1字节) + 离散量输入寄存器的值

下面是一个实际的响应报文的内容:

0x2-request
0x2-request
MBAP header功能码字节数请求的数据1请求的数据2
01 b9 00 00 00 05 0101021202

返回的第一个字节12,转化为二进制为00010010, 其中bit0代表00寄存器,bit7代表07寄存器。

0x70x60x50x40x30x20x10x0
00010010

返回的第二个字节02,转化为二进制为00000010, 其中bit0代表08寄存器,bit1代表09寄存器。

0x90x8
10

功能码0x03:读保持寄存器

功能码0x03用于读取保持寄存器的值,从远程设备中读保持寄存器连续块的内容。

发送报文

请求报文的结构如下:

MBAP header + 功能码 + 起始地址H 起始地址L + 寄存器数量H 寄存器数量L(共12字节)

下面是一个实际的例子:

0x3-request
0x3-request

按照协议对照如下:

MBAP header功能码起始地址寄存器数量
05 f1 00 00 00 06 010300 0000 0a

响应报文

响应报文的结构由下面几个部分组成:

MBAP header + 功能码 + 数据长度 + 寄存器数据

数据总长度 = 9 + 寄存器数量 × 2

下面是一个response的结构:

0x3-response
0x3-response

从中我们可以提取出下面的对应关系:

MBAP header功能码字节数第0byte值第1byte值第2byte值第3byte值第4byte值第5byte值第6byte值第7byte值第8byte值第9byte值
05 f1 00 00 00 17 01031401 2c00 0000 0000 3700 0000 0000 6400 0000 3c00 00

由上面的对应关系,我们可以提取出我们想要读取的保持寄存器的值:

第0个寄存器第1个寄存器第2个寄存器第3个寄存器第4个寄存器第5个寄存器第6个寄存器第7个寄存器第8个寄存器第9个寄存器
3000055001000600

功能码0x04:读输入寄存器

功能码0x4用于读取输入寄存器的值,输入寄存器是只读的,因此没有功能码可以写输入寄存器。

发送报文

请求报文格式如下:

MBAP header + 功能码 + 起始地址H 起始地址L + 寄存器数量H 寄存器数量L(共12字节)

下面是一个实际的例子:

0x4-request
0x4-request
MBAP header功能码起始地址寄存器数量
0b f8 00 00 00 06 010400 0000 0a

该请求的含义是读取0-9号寄存器的值。

响应报文

响应报文的结构由下面几个部分组成:

MBAP header + 功能码 + 数据长度 + 寄存器数据

数据总长度 = 9 + 寄存器数量 × 2

下面是一个response的结构:

0x4-response
0x4-response

从中我们可以提取出下面的对应关系:

MBAP header功能码字节数第0byte值第1byte值第2byte值第3byte值第4byte值第5byte值第6byte值第7byte值第8byte值第9byte值
0b f8 00 00 00 17 01041400 0000 0000 c800 0001 2c00 0000 0000 0000 4200 00

由上面的对应关系,我们可以提取出我们想要读取的保持寄存器的值:

第0个寄存器第1个寄存器第2个寄存器第3个寄存器第4个寄存器第5个寄存器第6个寄存器第7个寄存器第8个寄存器第9个寄存器
002000300000660

功能码0x06:写单个保持寄存器

在一个远程设备中写一个保持寄存器。

发送报文

请求报文格式如下:

MBAP header + 功能码 + 寄存器地址H 寄存器地址L + 寄存器值H 寄存器值L(共12字节)

下面是一个实际的例子:

0x6-request
0x6-request
MBAP header功能码保持寄存器地址保持寄存器的值
1F 97 00 00 00 06 010600 0400 64

该请求的含义是向地址为0x4的寄存器写入100。

响应报文

响应报文如下:

MBAP header + 功能码 + 寄存器地址H 寄存器地址L + 寄存器值H 寄存器值L(共12字节)

成功响应的报文与发送报文格式相同。

0x6-response
0x6-response

功能码0x10:写多个保持寄存器

在一个远程设备中写连续寄存器块(1~123个寄存器)

发送报文

请求报文格式如下:

MBAP header + 功能码 + 起始地址H 起始地址L + 寄存器数量H 寄存器数量L + 字节长度 + 寄存器值(13+寄存器数量×2)

0x10-request
0x10-request
MBAP header功能码起始地址长度字节数字节0数据字节1数据字节2数据字节3数据字节4数据字节5数据字节6数据字节7数据字节8数据字节9数据
00 26 00 00 00 1b 011000 0000 0a14 0000 0000 0000 0000 0000 c800 0000 0000 6400 0000 64

响应报文

响应报文如下:

MBAP header + 功能码 + 起始地址H 起始地址L + 寄存器数量H 寄存器数量L(共12字节)

0x10-response
0x10-response
MBAP header功能码起始地址长度
00 26 00 00 00 1b 011000 0000 0a

实用调试工具

在研究modbus的过程中, 大量的使用了modbus poll和 modbus slave软件,这个软件可以很好的帮助理解modbus-tcp协议。

modbus poll: modbus客户端工具(主站)

modbus slave: modbus服务端工具(从站)

Loading...