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'

相关推荐
魔道不误砍柴功38 分钟前
Java 中如何巧妙应用 Function 让方法复用性更强
java·开发语言·python
_.Switch1 小时前
高级Python自动化运维:容器安全与网络策略的深度解析
运维·网络·python·安全·自动化·devops
测开小菜鸟2 小时前
使用python向钉钉群聊发送消息
java·python·钉钉
萧鼎3 小时前
Python并发编程库:Asyncio的异步编程实战
开发语言·数据库·python·异步
学地理的小胖砸3 小时前
【一些关于Python的信息和帮助】
开发语言·python
疯一样的码农3 小时前
Python 继承、多态、封装、抽象
开发语言·python
Python大数据分析@4 小时前
python操作CSV和excel,如何来做?
开发语言·python·excel
黑叶白树4 小时前
简单的签到程序 python笔记
笔记·python
Shy9604184 小时前
Bert完形填空
python·深度学习·bert
上海_彭彭4 小时前
【提效工具开发】Python功能模块执行和 SQL 执行 需求整理
开发语言·python·sql·测试工具·element