Modbus TCP是一种应用于工业自动化领域的通信协议,它是Modbus协议的一个变种,采用TCP/IP协议进行数据传输。Modbus TCP使得Modbus协议可以通过以太网网络运行,允许设备之间在IP网络上交换数据。
在Modbus TCP协议中,通信通常由一个客户端(通常称为Master)和一个或多个服务器(Slave)进行。客户端发出请求,服务器响应这些请求。这些请求和响应包括读取或写入服务器中存储的数据。这些数据可以是输入/输出状态、保持寄存器、模拟输入等。
Modbus TCP常用于工业环境中,用于连接控制系统和现场设备,如传感器、执行器、变频器等。它特别适用于那些需要在设备之间进行快速、可靠通信的场景。
2、modbus TCP常用功能码
功能码 | Hex表示 | 功能 | 备注 |
---|---|---|---|
1 | 0x01 | 读取线圈 | 可读写开关量 接受05、15写入 |
2 | 0x02 | 读取离散输入 | 只读开关量 |
3 | 0x03 | 读取保持寄存器 | 可读性模拟量 接受06、16写入 |
4 | 0x04 | 读取输入寄存器 | 只读模拟量 |
5 | 0x05 | 写入单线圈 | 0xFF00表示ON(True) 0x0000表示OFF(False) |
6 | 0x06 | 写入单个寄存器 | 只能操作1个寄存器(2字节) |
15 | 0x0F | 写入多个线圈 | 每个字节只能存储一个线圈的值 |
16 | 0x10 | 写入多个寄存器 | 操作的基本单位是字节 |
3、常用功能码及名词解释
线圈(Coils)
线圈在Modbus协议中可以类比为布尔类型的变量。每个线圈代表一个单一的二进制位,通常用于控制或指示某个功能的开/关状态。例如,在编程中,你可能会用一个布尔变量来控制一个循环是否继续执行或判断一个条件是否满足,类似地,在Modbus中,线圈可以用来控制一个电机的启停或检测某个开关是否被激活。
寄存器(Registers)
寄存器则可以类比为程序中的整数变量。在Modbus中,寄存器是16位的,这意味着它可以存储从0到65535的整数值。寄存器根据其用途可分为保持寄存器和输入寄存器:
- 保持寄存器(Holding Registers):这些寄存器可以读写,类似于普通的变量。它们可用于保存可更改的设置或中间计算结果。
- 输入寄存器(Input Registers):这些寄存器仅供读取,类似于常量或只读变量。它们常用于保存从设备如传感器获取的不可更改数据。
4、常用数据类型
英文描述 | 简易表示 | 转换过程 | 目标顺序 | 中文描述 | go类型 | 占用字节 | 字节序 | 字节交换 |
---|---|---|---|---|---|---|---|---|
Signed | int16 | - | 有符号整数 | int16 | 2 | - | - | |
Unsigned | uint16 | - | 无符号整数 | uint16 | 2 | - | - | |
Float little-endian byte swap | float32CDAB | ABCD->DCBA->CDAB | CDAB | 浮点数 | float32 | 4 | 小端 | √ |
Float big-endian | float32ABCD | ABCD | ABCD | 浮点数 | float32 | 4 | 大端 | × |
Float little-endian | float32DCBA | ABCD->DCBA | DCBA | 浮点数 | float32 | 4 | 小端 | × |
Float big-endian byte swap | float32BADC | ABCD->BADC | BADC | 浮点数 | float32 | 4 | 大端 | √ |
32-bit Signed big-endian byte swap | int32BADC | ABCD->BADC | BADC | 有符号整数 | int32 | 4 | 大端 | √ |
32-bit Unsigned big-endian | uint32ABCD | ABCD | ABCD | 无符号整数 | uint32 | 4 | 大端 | × |
32-bit Unsigned little-endian byte swap | uint32CDAB | ABCD->DCBA->CDAB | CDAB | 无符号整数 | uint32 | 4 | 小端 | √ |
32-bit Unsigned big-endian byte swap | uint32BADC | ABCD->BADC | BADC | 无符号整数 | uint32 | 4 | 大端 | √ |
32-bit Unsigned little-endian | uint32DCBA | ABCD->DCBA | DCBA | 无符号整数 | uint32 | 4 | 小端 | × |
32-bit Signed little-endian byte swap | uint32CDAB | ABCD->DCBA->CDAB | CDAB | 有符号整数 | uint32 | 4 | 小端 | √ |
32-bit Signed big-endian | int32ABCD | ABCD | ABCD | 有符号整数 | int32 | 4 | 大端 | × |
32-bit Signed little-endian | uint32DCBA | ABCD-DCBA | DCBA | 有符号整数 | uint32 | 4 | 小端 | × |
Double little-endian byte swap | float64GHEFCDAB | ABCDEFGH->HGFEDCBA->GHEFCDAB | GHEFCDAB | 双精度浮点数 | float64 | 8 | 小端 | √ |
Double big-endian | float64ABCDEFGH | ABCDEFGH | ABCDEFGH | 双精度浮点数 | float64 | 8 | 大端 | × |
Double little-endian | float64HGFEDCBA | ABCDEFGH->HGFEDCBA | HGFEDCBA | 双精度浮点数 | float64 | 8 | 小端 | × |
Double big-endian byte swap | float64BADCFEHG | ABCDEFGH->BADCFEHG | BADCFEHG | 双精度浮点数 | float64 | 8 | 大端 | √ |
64-bit Signed big-endian | int64ABCDEFGH | ABCDEFGH | ABCDEFGH | 有符号整数 | int64 | 8 | 大端 | × |
64-bit Signed little-endian byte swap | int64GHEFCDAB | ABCDEFGH->HGFEDCBA->GHEFCDAB | GHEFCDAB | 有符号整数 | int64 | 8 | 小端 | √ |
64-bit Signed big-endian byte swap | int64BADCFEHG | ABCDEFGH->BADCFEHG | BADCFEHG | 有符号整数 | int64 | 8 | 大端 | √ |
64-bit Signed little-endian | int64HGFEDCBA | ABCDEFGH->HGFEDCBA | HGFEDCBA | 有符号整数 | int64 | 8 | 小端 | × |
64-bit Unsigned big-endian | uint64ABCDEFGH | ABCDEFGH | ABCDEFGH | 无符号整数 | uint64 | 8 | 大端 | × |
64-bit Unsigned little-endian byte swap | uint64GHEFCDAB | ABCDEFGH->HGFEDCBA->GHEFCDAB | GHEFCDAB | 无符号整数 | uint64 | 8 | 小端 | √ |
64-bit Unsigned big-endian byte swap | uint64BADCFEHG | ABCDEFGH->BADCFEHG | BADCFEHG | 无符号整数 | uint64 | 8 | 大端 | √ |
64-bit Unsigned little-endian | uint64ABCDEFGH | ABCDEFGH | ABCDEFGH | 无符号整数 | uint64 | 8 | 小端 | × |
Hex | - | - | 十六进制 | - | - | - | - | |
Binary | bool | - | 二进制 | bool | 1/8 | - | - |
大端(Big Endian)
在大端字节序中,多字节数据的最高有效字节(MSB)存储在最低的内存地址,其余字节按照重要性递减的顺序存储。这意味着数据的第一个字节是最重要的一个字节。
例如,16位整数0x1234
在大端模式下的存储顺序是:
Memory Address -> | 12 | 34 |
其中,12
是高位,存储在较低的地址。
小端(Little Endian)
在小端字节序中,多字节数据的最低有效字节(LSB)存储在最低的内存地址,其余字节按照重要性递增的顺序存储。这意味着数据的最后一个字节是最重要的一个字节。
例如,16位整数0x1234
在小端模式下的存储顺序是:
Memory Address -> | 34 | 12 |
其中,34
是低位,存储在较低的地址。
字节交换(Byte Swap)
字节交换是指在大端和小端之间转换数据时改变字节的顺序的过程。这通常在不同的计算系统之间传输数据时需要考虑,因为不同系统可能采用不同的字节序。字节交换确保数据在一个系统中被正确解释和使用。
例如,如果一个小端系统发送0x1234
给一个大端系统,没有适当的字节交换,大端系统将会错误地解释这个数字为0x3412
。为了防止这种情况,发送前需要将字节顺序从小端转换为大端,或者接收方需要在接收后进行转换。
按照字节来解析,最主要还是需要看转换过程,比如ABCD->DCBA->CDAB 相当于在程序中表达的ABCD字节,在进行modbus传输的时候需要将数据字节顺序转换成CDAB,以此类推。
5、01功能码解析
01功能码中的数据能够接受05功能码(单个线圈的控制)和15功能码(多个线圈的控制)的写入
(1)请求
事务标识符 | 协议标识符 | 长度字段 | 单元标识符 | 功能码 | 起始地址 | 数量 |
---|---|---|---|---|---|---|
2字节 | 2字节 | 2字节 | 1字节 | 1字节 | 2字节 | 2字节 |
标识Modbus事务 | 固定值0x0000 | 这个字节之后剩余数据的长度 | 远程服务器地址,默认1 | 0x01 | 要读取的第一个线圈的地址 | 要读取的线圈数量 |
**事务标识符: **占用2字节,用来计数。例如客户端发送了0x00的事务标识,服务端接收到请求后也会带有0x00的事务标识进行返回,也就是说正常情况下单链路访问并发不能超过2字节(65535个)否则会导致接收到事务重复的回复数据。
长度字段: 这个字节之后剩余数据的长度
协议标识符: modbus 特有的协议标识,固定值
单元标识符: 服务器(Slave)的标记,一般一个网络中只有一个,所以默认为1,如果有多个就依次排序,不能重复。
功能码: 第一段中列出的功能码
起始地址: 在modbus中,每一个数据都有一个地址,在数据请求的时候,针对线圈(开关量)都是进行批量请求,所以在接收到服务端返回的数据的时候,需要用户自己来维护。我举一个例子:当你请求一个线圈(开关量)数据的时候,你应该设置其实地址为线圈实际地址,然后数量设置为1。但是在返回数据的时候是按照字节返回的,也就是说会直接返回8个开关量。这时候你只需要解析这个字节的第0位的数据状态就行,剩下的都是被服务端填充的。
数量: 要读取的线圈数量
(2)响应
事务标识符 | 协议标识符 | 长度字段 | 单元标识符 | 功能码 | 字节计数 | 状态 |
---|---|---|---|---|---|---|
2字节 | 2字节 | 2字节 | 1字节 | 1字节 | 1字节 | N字节 |
标识Modbus事务 | 固定值0x0000 | 这个字节之后剩余数据的长度 | 与请求包中的相同 | 0x01 | 表示后续线圈状态值的字节数 | 每个字节表示8个线圈的状态,1表示ON,0表示OFF |
6、02功能码解析
02功能码只能读取数据,不能写入数据
(1)请求
事务标识符 | 协议标识符 | 长度字段 | 单元标识符 | 功能码 | 起始地址 | 数量 |
---|---|---|---|---|---|---|
2字节 | 2字节 | 2字节 | 1字节 | 1字节 | 2字节 | 2字节 |
标识Modbus事务 | 固定值0x0000 | 这个字节之后剩余数据的长度 | 远程服务器地址,默认1 | 0x02 | 要读取的第一个线圈的地址 | 要读取的线圈数量 |
(2)响应
事务标识符 | 协议标识符 | 长度字段 | 单元标识符 | 功能码 | 字节计数 | 状态 |
---|---|---|---|---|---|---|
2字节 | 2字节 | 2字节 | 1字节 | 1字节 | 1字节 | N字节 |
标识Modbus事务 | 固定值0x0000 | 这个字节之后剩余数据的长度 | 与请求包中的相同 | 0x02 | 表示后续线圈状态值的字节数 | 每个字节表示8个线圈的状态,1表示ON,0表示OFF |
7、03功能码
(1)请求
事务标识符 | 协议标识符 | 长度字段 | 单元标识符 | 功能码 | 起始地址 | 寄存器数量 |
---|---|---|---|---|---|---|
2字节 | 2字节 | 2字节 | 1字节 | 1字节 | 2字节 | 2字节 |
标识Modbus事务 | 固定值0x0000 | 这个字节之后剩余数据的长度 | 远程服务器地址,默认1 | 0x03 | 表示请求的起始寄存器的地址 | 要读取的寄存器数量 |
(2)响应
事务标识符 | 协议标识符 | 长度字段 | 单元标识符 | 功能码 | 字节计数 | 数据 |
---|---|---|---|---|---|---|
2字节 | 2字节 | 2字节 | 1字节 | 1字节 | 1字节 | N字节 |
标识Modbus事务 | 固定值0x0000 | 这个字节之后剩余数据的长度 | 与请求包中的相同 | 0x03 | 表示接下来的数据字节的数量 | 实际的寄存器值,数量由字节计数确定 |
8、04功能码
(1)请求
事务标识符 | 协议标识符 | 长度字段 | 单元标识符 | 功能码 | 起始地址 | 寄存器数量 |
---|---|---|---|---|---|---|
2字节 | 2字节 | 2字节 | 1字节 | 1字节 | 2字节 | 2字节 |
标识Modbus事务 | 固定值0x0000 | 这个字节之后剩余数据的长度 | 远程服务器地址,默认1 | 0x04 | 表示请求的起始寄存器的地址 | 要读取的寄存器数量 |
(2)响应
事务标识符 | 协议标识符 | 长度字段 | 单元标识符 | 功能码 | 字节计数 | 数据 |
---|---|---|---|---|---|---|
2字节 | 2字节 | 2字节 | 1字节 | 1字节 | 1字节 | N字节 |
标识Modbus事务 | 固定值0x0000 | 这个字节之后剩余数据的长度 | 与请求包中的相同 | 0x04 | 表示接下来的数据字节的数量 | 实际的寄存器值,数量由字节计数确定 |
9、05功能码
(1)请求
事务标识符 | 协议标识符 | 长度字段 | 单元标识符 | 功能码 | 输出地址 | 输出值 |
---|---|---|---|---|---|---|
2字节 | 2字节 | 2字节 | 1字节 | 1字节 | 2字节 | 2字节 |
标识Modbus事务 | 固定值0x0000 | 这个字节之后剩余数据的长度 | 远程服务器地址,默认1 | 0x05 | 表示请求的起始寄存器的地址 | 将该线圈设置为ON或OFF。0x0000表示OFF,0xFF00表示ON |
(2)响应
事务标识符 | 协议标识符 | 长度字段 | 单元标识符 | 功能码 | 输出地址 | 输出值 |
---|---|---|---|---|---|---|
2字节 | 2字节 | 2字节 | 1字节 | 1字节 | 2字节 | 2字节 |
标识Modbus事务 | 固定值0x0000 | 这个字节之后剩余数据的长度 | 与请求包中的相同 | 0x05 | 表示接下来的数据字节的数量 | 实际的寄存器值,数量由字节计数确定 |
10、06功能码
(1)请求
事务标识符 | 协议标识符 | 长度字段 | 单元标识符 | 功能码 | 寄存器地址 | 寄存器值 |
---|---|---|---|---|---|---|
2字节 | 2字节 | 2字节 | 1字节 | 1字节 | 2字节 | 2字节 |
标识Modbus事务 | 固定值0x0000 | 这个字节之后剩余数据的长度 | 远程服务器地址,默认1 | 0x06 | 寄存器的地址 | 寄存器的数据 |
(2)响应
事务标识符 | 协议标识符 | 长度字段 | 单元标识符 | 功能码 | 寄存器地址 | 寄存器值 |
---|---|---|---|---|---|---|
2字节 | 2字节 | 2字节 | 1字节 | 1字节 | 2字节 | 2字节 |
标识Modbus事务 | 固定值0x0000 | 这个字节之后剩余数据的长度 | 与请求包中的相同 | 0x06 | 寄存器的地址 | 寄存器的数据 |
11、15功能码
(1)请求
事务标识符 | 协议标识符 | 长度字段 | 单元标识符 | 功能码 | 起始地址 | 输出数量 | 字节计数 | 数据 |
---|---|---|---|---|---|---|---|---|
2字节 | 2字节 | 2字节 | 1字节 | 1字节 | 2字节 | 2字节 | 1字节 | N字节 |
标识Modbus事务 | 固定值0x0000 | 这个字节之后剩余数据的长度 | 远程服务器地址,默认1 | 0x0F | 寄存器的地址 | 指示要写入的线圈数量 | 随后的字节数量 | 线圈的值,每个线圈占1位,字节间从左到右填充,不足一个字节的部分填充至右侧。 |
(2)响应
事务标识符 | 协议标识符 | 长度字段 | 单元标识符 | 功能码 | 起始地址 | 输出数量 |
---|---|---|---|---|---|---|
2字节 | 2字节 | 2字节 | 1字节 | 1字节 | 2字节 | 2字节 |
标识Modbus事务 | 固定值0x0000 | 这个字节之后剩余数据的长度 | 与请求包中的相同 | 0x0F | 寄存器的地址 | 指示要写入的线圈数量 |
12、16功能码
(1)请求
事务标识符 | 协议标识符 | 长度字段 | 单元标识符 | 功能码 | 起始地址 | 寄存器数量 | 字节计数 | 寄存器值 |
---|---|---|---|---|---|---|---|---|
2字节 | 2字节 | 2字节 | 1字节 | 1字节 | 2字节 | 2字节 | 1字节 | N字节 |
标识Modbus事务 | 固定值0x0000 | 这个字节之后剩余数据的长度 | 远程服务器地址,默认1 | 0x10 | 寄存器的地址 | 寄存器的数据 | 随后的字节数(应等于寄存器数量乘以2,因为每个寄存器是2字节) | 要写入的实际值,其中N是要写入的寄存器数量。 |
(2)响应
事务标识符 | 协议标识符 | 长度字段 | 单元标识符 | 功能码 | 起始地址 | 寄存器数量 |
---|---|---|---|---|---|---|
2字节 | 2字节 | 2字节 | 1字节 | 1字节 | 2字节 | 2字节 |
标识Modbus事务 | 固定值0x0000 | 这个字节之后剩余数据的长度 | 与请求包中的相同 | 0x10 | 确认消息中涉及的第一个寄存器的地址。 | 确认消息中写入的寄存器数量。 |