探究Modbus TCP:工业自动化中的关键通信协议

2024-05-30 20:29:36 浏览数 (2)

1、简介

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在大端模式下的存储顺序是:

代码语言:javascript复制
Memory Address -> | 12 | 34 |

其中,12是高位,存储在较低的地址。

小端(Little Endian)

在小端字节序中,多字节数据的最低有效字节(LSB)存储在最低的内存地址,其余字节按照重要性递增的顺序存储。这意味着数据的最后一个字节是最重要的一个字节。

例如,16位整数0x1234在小端模式下的存储顺序是:

代码语言:javascript复制
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

确认消息中涉及的第一个寄存器的地址。

确认消息中写入的寄存器数量。

0 人点赞