python实现ModBusTCP协议的client

python实现ModBusTCP协议的client是一件简单的事情,只要通过pymodbus或pyModbusTCP任意模块就可以实现,本文采用pymodbus。

一、ModBusTCP协议

1、了解ModBusTCP协议

Modbus TCP 是一种基于 TCP/IP 协议栈的 Modbus 通信协议,它用于在工业自动化系统中进行设备之间的通信。Modbus TCP 将 Modbus 协议封装在 TCP/IP 协议之上,通过网络连接设备,实现数据的读取和写入。

以下是 Modbus TCP 的基本特点:

(1)基于 TCP/IP 协议:Modbus TCP 使用 TCP/IP 网络进行通信,可以通过以太网、互联网等方式进行远程通信。

(2)实时性:Modbus TCP 具有较高的实时性,适用于需要快速响应的控制系统。

(3)异步通信:Modbus TCP 支持异步通信,允许设备之间的非同步数据交换。

(4)客户端-服务器模型:Modbus TCP 通信采用客户端-服务器模型。客户端(通常是控制系统或监控系统)向服务器(设备或传感器)发出请求,服务器返回响应数据。

(5)支持多种数据类型:Modbus TCP 支持不同数据类型的读写操作,包括线圈(Coil)、离散输入(Discrete Input)、保持寄存器(Holding Register)和输入寄存器(Input Register)等。

(6)数据传输格式:Modbus TCP 使用 Modbus 协议的格式进行数据传输,包括设备地址、功能码、数据域等。

(7)安全性:由于 Modbus TCP 通信是基于 TCP/IP 的,因此可以通过网络安全措施(例如 VPN、防火墙等)提供数据传输的安全性。

总的来说,Modbus TCP 提供了一种可靠的、灵活的工业通信解决方案,广泛用于自动化领域中的各种设备之间的数据交换。

2、ModBusTCP协议的client与TCPclient的区别?

Modbus TCP 是一种特定的应用层通信协议,用于在工业自动化系统中设备之间进行数据交换。它是在 TCP/IP 协议栈上运行的 Modbus 协议的变种。Modbus TCP 协议的数据包是通过 TCP/IP 协议进行传输的。

TCP client 是一种通用的网络通信模式,它指的是通过 TCP/IP 协议与远程服务器建立连接,并向服务器发送请求并接收响应的程序。TCP client 可以用于与任何支持 TCP/IP 协议的服务器进行通信,不限于 Modbus 协议。

区别主要在于:

(1)用途不同:Modbus TCP 是一种特定的工业自动化通信协议,用于工业设备之间的数据交换;而 TCP client 是一种通用的网络通信模式,可以与各种服务器进行通信,不限于 Modbus 协议。

(2)协议不同:Modbus TCP 使用 Modbus 协议进行数据传输,而 TCP client 没有固定的协议限制,可以与各种应用层协议进行通信。

(3)功能不同:Modbus TCP 协议定义了特定的功能码和数据格式,用于读写线圈、离散输入、保持寄存器等;TCP client 则没有固定的功能码和数据格式,可以根据具体需求自定义通信内容。

(4)适用场景不同:Modbus TCP 主要用于工业自动化控制系统中,用于实时数据交换;TCP client 可以用于各种通信场景,包括 Web 客户端、数据库客户端、文件传输等。

综上所述,Modbus TCP 是一种特定协议的 TCP client,用于在工业自动化领域实现设备之间的数据交换。TCP client 则是一个更通用的概念,可以与各种服务器进行通信,不受特定协议限制。

3、ModBusTCP协议的数据帧格式是怎样的?

大体如上图红色部分所描述,Modbus TCP 协议的数据帧格式如下:

(1)MBAP 头部(Modbus Application Protocol Header):

Transaction Identifier(事务标识符):2 字节,用于标识事务,通常是递增的序号。

Protocol Identifier(协议标识符):2 字节,固定为0,表示 Modbus 协议。

Length(数据长度):2 字节,表示 MBAP 后面数据的长度,包括单元标识符(Unit Identifier)和数据字段。

Unit Identifier(单元标识符):1 字节,用于标识 Modbus 设备,通常为 1。

(2)PDU(Protocol Data Unit):

Function Code(功能码):1 字节,表示 Modbus 操作的类型,如读取保持寄存器、写入线圈等。

Data(数据):根据功能码的不同,数据的格式和长度会有所变化。

数据帧的格式可以根据不同的功能码和操作类型而变化,例如:

(1)对于读取保持寄存器(Function Code 0x03):

起始地址:2 字节,表示要读取的寄存器的起始地址。

寄存器数量:2 字节,表示要读取的寄存器的数量。

(2)对于写入单个保持寄存器(Function Code 0x06):

寄存器地址:2 字节,表示要写入的寄存器的地址。

寄存器值:2 字节,表示要写入的寄存器的值。

总体来说,Modbus TCP 协议的数据帧格式是固定的,但是具体的数据内容和长度会根据功能码的不同而有所变化。详细的数据帧格式需要根据具体的功能码和操作类型来确定。

二、一个Demo及其引发的问题

1、一个Demo

复制代码
from pymodbus.client import ModbusTcpClient


if __name__ == "__main__":
    # Modbus TCP服务器的IP地址和端口号
    server_ip = "192.168.1.189"
    port = 502
    station = 1

    # 创建Modbus TCP客户端
    MDclient = ModbusTcpClient(server_ip, port)
    if MDclient.connect():
        # 读取保持寄存器的示例
        address = 0  # 起始寄存器地址
        count = 10   # 要读取的寄存器数量
        MDclient.write_registers(address, 115, slave=1)
        time.sleep(2)
        response = MDclient.read_holding_registers(10, 10, slave=station)
        print(response.registers[0])
        MDclient.close()

2、pymodbus.client.ModbusTcpClient 都实现了哪些功能码

pymodbus.client.ModbusTcpClient 类是 PyModbus 库中用于 Modbus TCP 客户端通信的类。它支持以下常用的 Modbus 功能码:

(1)读取线圈状态(Read Coils):功能码 0x01,用于读取输出线圈的状态。

read_coils(address, count=1, slave=0)

  • address: 起始线圈的地址
  • count: 要读取的线圈数量
  • slave: Modbus 单元标识符(站号)

(2)读取离散输入状态(Read Discrete Inputs):功能码 0x02,用于读取输入线圈的状态。

read_discrete_inputs(address, count=1, slave=0)

  • address: 起始输入线圈的地址
  • count: 要读取的输入线圈数量
  • slave: Modbus 单元标识符

(3)读取保持寄存器(Read Holding Registers):功能码 0x03,用于读取保持寄存器的值。

read_holding_registers(address, count=1, slave=0)

  • address: 起始保持寄存器的地址
  • count: 要读取的保持寄存器数量
  • slave: Modbus 单元标识符

(4)读取输入寄存器(Read Input Registers):功能码 0x04,用于读取输入寄存器的值。

read_input_registers(address, count=1, slave=0)

  • address: 起始输入寄存器的地址
  • count: 要读取的输入寄存器数量
  • slave: Modbus 单元标识符

(5)写单个线圈(Write Single Coil):功能码 0x05,用于写入一个输出线圈的状态。

write_coil(address, value, slave=0)

  • address: 线圈的地址
  • value: 要写入的值,True 表示 ON,False 表示 OFF
  • slave: Modbus 单元标识符

(6)写单个寄存器(Write Single Register):功能码 0x06,用于写入一个保持寄存器的值。

write_register(address, value, slave=0)

  • address: 寄存器的地址
  • value: 要写入的值
  • slave: Modbus 单元标识符

(7)写多个线圈(Write Multiple Coils):功能码 0x0F,用于写入多个输出线圈的状态。

write_coils(address, values, slave=0)

  • address: 起始线圈的地址
  • values: 要写入的线圈状态,是一个布尔值列表
  • slave: Modbus 单元标识符

(8)写多个寄存器(Write Multiple Registers):功能码 0x10,用于写入多个保持寄存器的值。

write_registers(address, values, slave=0)

  • address: 起始寄存器的地址
  • values: 要写入的寄存器值,是一个整数列表
  • slave: Modbus 单元标识符

3、读到的结果与写入的值为什么是反的

只能说有可能是反的。

比如我用 response = MDclient.read_holding_registers(10, 10, slave=station) print(response.registers[0])打印了一个寄存器,能读到数值为17217,转化成16进制再转化成字符串为CA,但实际的字符串应该为AC,这是为什么呢?

当你期望的结果是AC而不是CA,说明你读取的数值可能被解释为了大端字节序(big-endian)而不是你期望的小端字节序(little-endian)。在大端字节序中,高位字节保存在低地址,而在小端字节序中,高位字节保存在高地址。

这里为什么说可能呢?原因很简单,通信的两端如果内存存储方式不一样,读到的数据就是反的。比如PC是小端序,嵌入式设备是大端序,存储在嵌入式设备的值为AC,高位是A,低位是C,那么PC在以小端序存储时,会把对方高位的A存储低位,把对方低位的C存储到高位,就变成了CA。

如果二者都是大端序或小端序的存储方式,则不会有该问题,因为他们的读取和存储方式都是一样的。

4、大端序与小端序之间的传输

当数据在大端字节序(big-endian)和小端字节序(little-endian)之间传送时,通常需要进行大小端转换。这是因为不同的处理器和计算机体系结构可能使用不同的字节序,如果发送方和接收方的字节序不同,就需要进行转换。

  • 大端字节序 :在大端字节序中,高位字节保存在低地址,低位字节保存在高地址。例如,整数0x12345678在大端字节序中存储为12 34 56 78

  • 小端字节序 :在小端字节序中,低位字节保存在低地址,高位字节保存在高地址。同样的整数0x12345678在小端字节序中存储为78 56 34 12

如果数据在不同字节序的系统之间传递,发送方需要将数据按照目标系统的字节序进行转换,接收方再将接收到的数据进行反向转换,以确保数据的正确传递。在很多网络通信和文件传输的场景下,大小端转换是非常常见的操作。

5、线圈、输入寄存器、保持寄存器、离散寄存器分别占用多少bit位

在Modbus协议中,不同类型的数据(线圈、输入寄存器、保持寄存器、离散寄存器)占用的位数如下:

(1)线圈(Coils):线圈是只能读写的二进制输出,每个线圈占用1位。这意味着一个线圈的状态只能是开(1)或者关(0)。

(2)输入寄存器(Input Registers):输入寄存器是只读的,每个输入寄存器占用16位(2个字节)。

(3)保持寄存器(Holding Registers):保持寄存器是读写的,每个保持寄存器也占用16位(2个字节)。

(4)离散输入(Discrete Inputs):离散输入是只读的二进制输入,每个离散输入占用1位。类似于线圈,一个离散输入的状态只能是开(1)或者关(0)。

总结:

  • 线圈:1位
  • 输入寄存器:16位(2字节)
  • 保持寄存器:16位(2字节)
  • 离散输入:1位

请注意,这些大小是Modbus协议规定的标准大小,不同的设备可能有不同的实现,因此在实际应用中,你应该查阅设备的文档以确认具体的数据大小。

6、一个保持寄存器能存2个字母,用两个保持寄存器存储ACK1,第一个寄存器存储AC,第二个存储K1,大端序会怎么存储?

在大端序(Little Endian)下,字节的存储顺序是低位字节在前,高位字节在后。如果每个保持寄存器能存储两个字母,要存储字符串"ACK1",它将被拆分为两个部分:'AC' 和 'K1'。以AC为例,A为高位,C为低位。

在大端序下,存储顺序如下:

  • 第一个保持寄存器(低位字节在前(高位),高位字节在后(低位)):存储67(0x43)和65(0x41),即存储为0x4341。
  • 第二个保持寄存器(低位字节在前(高位),高位字节在后(低位)):存储49(0x31)和75(0x4B),即存储为0x314B。

所以,在小端序下,字符串"ACK1"会按照上述方式存储到两个保持寄存器中。

7、如何判断win10存储是大端序还是小端序?

在通用的个人计算机(包括Windows 10系统)中,主流处理器(如x86和x86-64架构的处理器)使用的是小端序(Little Endian)存储。这意味着在存储多字节数据时,低位字节存储在内存的低地址处,高位字节存储在内存的高地址处。

如果你想要确认你的Windows 10系统是使用小端序还是大端序,可以使用Python来进行测试。以下是一个简单的Python代码片段,它可以帮助你判断系统的字节序:

复制代码
import sys

print(sys.byteorder)

运行这段代码,如果输出结果是'little',说明你的系统是小端序;如果输出结果是'big',则表示系统是大端序。在大部分个人计算机上,特别是使用x86和x86-64架构的系统,输出应该是'little'

相关推荐
LetsonH7 分钟前
⭐CVPR2025 MatAnyone:稳定且精细的视频抠图新框架
人工智能·python·深度学习·计算机视觉·音视频
jie*23 分钟前
小杰数据结构——题库——拂衣便欲沧海去,但许明月随吾身
数据结构·windows·python
zhangfeng11331 小时前
把“距离过近”的节点(或端点)合并成一个,避免重复。机器学习 python
人工智能·python·机器学习
白应穷奇2 小时前
编写高性能数据处理代码 02
后端·python
用户11906708018322 小时前
掌控python的属性,描述符钩子函数
python
阿赵3D2 小时前
selenium自动化收集资料
python·selenium·测试工具·自动化
AI手记叨叨2 小时前
Python分块读取大型Excel文件
python·excel
无限大.3 小时前
《计算机“十万个为什么”》之 面向对象 vs 面向过程:编程世界的积木与流水线
网络·人工智能·python
白葵新3 小时前
C#案例实战
c++·python·算法·计算机视觉·c#
Cosolar3 小时前
下一代 Python Web 框架?FastAPI 全面解析与实战对比
后端·python