python 基于modbus_tk库实现modbusTCP 主站和从站[非常详细]

这篇具有很好参考价值的文章主要介绍了python 基于modbus_tk库实现modbusTCP 主站和从站[非常详细]。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


最近做了一个modbus tcp 传输浮点数的项目,参考了一些CSDN大佬的文章,这里做一个 整合和记录

modbus 协议

modbus 通信过程

  • 摘自详解Modbus通信协议—清晰易懂
  • 一主多从的通信协议:Modbus 通信中只有主机可以发送请求。其他从设备接收主机发送的数据来进行响应——处理信息和使用 Modbus 将其数据发送给主站。从机不会主动发送消息给主站。
  • Modbus 不能同步进行通信,主机在同一时间内只能向一个从机发送请求,总线上每次只有一个数据进行传输,即主机发送,从机应答,主机不发送,总线上就没有数据通信。
  • Modbus没有忙机制判断,比方说主机给从机发送命令, 从机没有收到或者正在处理其他东西,这时候就不能响应主机,因为 modbus 的总线只是传输数据,没有其他仲裁机制,所以需要通过软件的方式来判断是否正常接收。

Modbus 数据传输的方式,可以简单地理解成打电话。并且是单向通信的打电话

主机发送数据,首先需要从机的电话号码(区分每个从机,每个地址必须唯一),告诉从机打电话要干什么事情,然后是需要发送的内容,最后再问问从机,我说的话你都听清楚了没有呀,没有听错吧?

然后从机这里,得到了主机打过来的电话,从机回复主机需要的内容,主机得到从机数据,这样就是一个主机到从机的通信过程

modbus 存储区

  • 忘记从哪找的了hhh

  • 从机存储数据 -> 存储区

    • 文件操作 -> 只读(-r)和读写(-wr)
    • 数据类型 -> 布尔量 和 16 位寄存器
      • 布尔量比如 IO 口的电平高低,灯的开关状态等。
      • 16 位寄存器比如 传感器的温度数据,存储的密码等。
  • Modbus 协议规定了 4 个存储区,分别是 0 1 3 4 区 其中 1 区和 4 区是可读可写,1 区和 3 区是只读

    区号 名称 读写 地址范围
    0 区 输出线圈 可读可写布尔量 00001-09999
    1 区 输入线圈 只读布尔量 10001-19999
    3 区 输入寄存器 只读寄存器 30001-39999
    4 区 保持寄存器 可读可写寄存器 40001-49999
  • Modbus 给每个区都划分了地址范围,主机向从机获取数据时,只需要告诉从机数据的起始地址,还有获取多少字节的数据,从机就可以发送数据给主机

  • 每一个从机,都有实际的物理存储,跟 modbus 的存储区相对应,主机读写从机的存储区,实际上就是对从机设备对应的实际存储空间进行读写

Modbus-TCP 协议

Modbus-TCP 报文帧结构

  • 摘自ModbusTCP协议报文详细分析

  • 报文帧结构

    > > > MBAP 报文头(7 bytes) > 协议数据单元(PDU)
    事务处理标识符 协议标识符 长度 单元标识符 功能码 数据
    2 bytes 2 bytes 2 bytes 1 byte 1 byte N bytes
  • MBAP 报文头

    长度 说明 客户机 服务器
    事务处理标识符 2 字节 Modbus 请求/响应事务处理的标识 客户机启动 复制响应
    协议标识符 2 字节 0=Modbus 协议 客户机启动 复制响应
    长度 2 字节 长度之后的字节总数 客户机启动 服务器启动
    单元标识符 1 字节 串行链路或其它总线的从站识别 客户端启动 复制响应

    事务处理标识符 and 协议标识符 正常使用设置为 0 即可,长度为 4 个字节 -> 0x00000000

  • 功能码

    功能码 功能说明
    01H 读取输出线圈
    02H 读取输入线圈
    03H 读取保持寄存器
    04H 读取输入寄存器
    05H 写入单线圈
    06H 写入单寄存器
    0FH 写入多线圈
    10H 写入多寄存器
  • 每次可读数据最长长度(260 字节 - 9 字节 = 251 字节)

    • Modbus TCP 报文帧最长为 260 字节 -> MBAP 7 字节 + PDU 253 字节
    • 返回报文的 PDU 中: 功能码 1 字节 + 字节计数 1 字节 + 数据 251 字节
    • 一个寄存器 2 字节
    • 当数据为 32 位浮点数,一个数据占用两个寄存器 -> 一个数据 4 字节
    • 每次最多可读取:数据 251 字节 -> 125 个寄存器 -> 62 个 32 位浮点数(读取 124 个寄存器)
    • 从站返回报文格式详解
    > > 返回报文编码格式详解
    字节位 结构 编码格式
    前 6 个字节 事务/协议和数据长度 ‘>HHH’
    第 7 个字节 单元标识 ‘>B’
    第 8 个字节 功能码 ‘>B’
    第 9 个字节 数据长度 ‘>B’
    剩余字节 数据 data_format(default or 自定义)

mosbus_tk库

介绍

  • 安装: pip install modbus_tk
  • github主页
  • 主站示例代码
  • 从站示例代码

从站记录的数据格式

timestamp lon lat cog
1651924800 108.6030967 20.57094167 169.9
1651925400 108.6061227 20.56535943 170.7
1651926000 108.6091487 20.5597772 171.7
1651926600 108.6121746 20.55419496 143.8
1651927200 108.6152006 20.54861273 203.8
1651927800 108.6182266 20.5430305 207.8
1651928400 108.6212526 20.53744826 181.4
1651929000 108.6242786 20.53186603 180.6
1651929600 108.6273046 20.52628379 179.2
1651930200 108.6303306 20.52070156 172.9
1651930800 108.6333566 20.51511933 172.6
1651931400 108.6363826 20.50953709 173

主站

  • 根据示例改的,直接上代码
import modbus_tk
import modbus_tk.defines as cst
from modbus_tk import modbus_tcp, hooks
import numpy as np
import pandas as pd

master = modbus_tcp.TcpMaster()
master.set_timeout(5.0)
print("connected")

# 连接从站读取数据,一次最多读取125个寄存器,由于2个寄存器为一个数据,故 size 设置为124
data = []  # 存放读取的数据
data += master.execute(  # 向从站发报文读取[0,123]区间的寄存器数据
    1,  # 从站标识符
    cst.READ_HOLDING_REGISTERS,  # 功能码
    0,  # 起始寄存器地址
    124,  # 读取的寄存器数量
    data_format='62f',  # 数据解码格式
)
data += master.execute( # 向从站发送报文读取[124,247]区间的寄存器数据
    1, cst.READ_HOLDING_REGISTERS, 124, 124, data_format='62f'
)

# 将数据保存到csv文件中
if len(data) % 4 != 0:  # 如果数据长度不是4的倍数,则用 0 补齐
    for i in range(0, 4 - len(data) % 4):
        data.append(0)
data = np.reshape(data, (-1, 4))  # 将数据重新转为4列的二维数组
# print(data, data.shape) # 打印数据
df = pd.DataFrame(
    data, columns=['timestamp', 'lon', 'lat', 'cog']
)  # 将数据转为DataFrame,设置列名
df.to_csv('data_recv.csv', index=False)  # 将数据保存到csv文件中
  • 常见问题:
    • Modbus Error: Exception code = 3: 看看是不是接收的数据超出最大长度了

    • struct.error: unpack requires a buffer of xx bytes: 如果在master.execute()时设置了data_format,注意data_format必须与接收到的数据长度匹配!

      • 例如传输的数据为32位float,每个数据为4个字节,收到24字节的数据,那么收到了6个数据,那么data_format必须为'6f'

从站

  • 从站负责接收主站的请求并返回数据,modbut_tk库已经集成好了这些功能,所有从站的代码非常简单。
  • 当我们给从站存入数据时,一个寄存器只有2个字节,那么怎么存入4个字节的数据呢?
    1. 当量缩放:量程固定时不传输具体数据,只传输百分比0-100%
    2. 用两个寄存器存一个数据:把32位浮点数分为两部分(代码中就是这种方法)
pi_bytes = [int(a_byte) for a_byte in struct.pack("f", num)]
pi_register1 = pi_bytes[0] * 256 + pi_bytes[1]
pi_register2 = pi_bytes[2] * 256 + pi_bytes[3]
registers_list.append(pi_register1)
registers_list.append(pi_register2)
  • 上完整代码!
import sys

import modbus_tk
import modbus_tk.defines as cst
from modbus_tk import modbus_tcp, hooks
import struct
import pandas as pd
'''
可使用的函数:
创建从站: server.add_slave(slave_id)
    slave_id(int):从站id
为从站添加存储区: slave.add_block(block_name, block_type, starting_address, size)
    block_name(str):block名
    block_type(int):block类型,COILS = 1,DISCRETE_INPUTS = 2,HOLDING_REGISTERS = 3,ANALOG_INPUTS = 4
    starting_address(int):起始地址
    size(int):block大小
设置block值:slave.set_values(block_name, address, values)
    block_name(str):block名
    address(int):开始修改的地址
    values(a list or a tuple or a number):要修改的一个(a number)或多个(a list or a tuple)值
获取block值:slave.get_values(block_name, address, size)
    block_name(str):block名
    address(int):开始获取的地址
    size(int):要获取的值的数量
'''
# 创建从站总服务器
server = modbus_tcp.TcpServer(address='127.0.0.1')  # address必须设置,port默认为502
print("running...")
print("enter 'quit' for closing the server")
server.start()

# 创建从站
slave_1 = server.add_slave(1)  # slave_id = 1
# 为从站添加存储区
slave_1.add_block(
    '0', cst.HOLDING_REGISTERS, 0, 1200
)  # block_name = '0', block_type = cst.HOLDING_REGISTERS, starting_address = 0, size = 1200

# 将数据存入寄存器
data = pd.read_csv('modbus_tcp.csv').values  # 读取数据data
num_array = data.flatten()  # 将data压为一维数组

registers_list = []  # 要存入寄存器的数据

# 将数据转化为32位float格式,每个数据4个字节 -> 占2个寄存器
for num in num_array:
    pi_bytes = [int(a_byte) for a_byte in struct.pack("f", num)]
    pi_register1 = pi_bytes[0] * 256 + pi_bytes[1]
    pi_register2 = pi_bytes[2] * 256 + pi_bytes[3]
    registers_list.append(pi_register1)
    registers_list.append(pi_register2)
slave_1.set_values(
    '0', 0, registers_list
)  # 将数据存入寄存器, block_name = '0', address = 0, values = registers_list

while True:
    cmd = sys.stdin.readline()  # input
    args = cmd.split(' ')  # 按空格分割输入

    if cmd.find('quit') == 0:  # 指令 quit -> 退出服务器
        print('bye-bye')
        break
  • 可以看到,从站的代码非常简单!从站只需要一直开着(while True),当主站发送请求时,从站就会自动处理请求

hook函数

  • 主站从站都可以设置多个hook函数,实现自定义的功能
def on_before_connect(args):
    '''
    钩子函数,连接前输出主站信息

    Args:
        args (tuple): (self(主站对象),)
    '''
    master = args[0]
    print("host: {0},port: {1}".format(master._host, master._port))

hooks.install_hook("modbus_tcp.TcpMaster.before_connect", on_before_connect)

def on_after_recv(args):
    '''
    钩子函数,在收到报文后输出报文总长度

    Args:
        args (tuple): (self(主站对象), response(从站返回的数据))
    '''
    response = args[1]
    print("{0} bytes received".format(len(response)))

hooks.install_hook("modbus_tcp.TcpMaster.after_recv", on_after_recv)
  • 设置hook函数分三步
    • 定义函数
      • 参数args是一个元组,是主站/从站在执行完某个操作,如发送数据,后使用hook函数时传入的参数,元祖中包含的参数不固定,可以自己查看源码
    • 载入函数: hooks.install_hook('触发hook函数的操作名',函数名)
    • 愉快的使用!
  • modbus_tk.hooks中定义了哪些操作可以触发hook,具体请自行查看源码!

有问题随时留言!文章来源地址https://www.toymoban.com/news/detail-780546.html

到了这里,关于python 基于modbus_tk库实现modbusTCP 主站和从站[非常详细]的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 汇川AM403PLC作为ModbusTCP主站通讯实例

    一、通讯方案实例  1.客户端/服务端   客户端和服务端是计算机网络中常见的两种网络设备类型,在工业通信中也称之为主站/从站,它们在网络通信过程中扮演着不同的角色。简单来说,客户端通常在网络中发起请求,而服务端则为客户端提供资源或数据。本例中使用主

    2024年02月03日
    浏览(58)
  • 西门子S7-1200建立ModbusTCP通讯数据交互实例(从站+主站)

    协议:Modbus TCP 设备类型:S7-1200 CPU 1215C 组态软件:TIA Portal V17 测试工具:Modbus Slave Modbus Poll 插入CPU设备,并分配IP地址 PLC_1:192.168.2.11 记录PLC网卡硬件标识符,默认为64 新建一个DB块(DB_ConnPara_1),在DB块中创建一个\\\"TCON_IP_v4\\\"类型的变量,修改其参数值如下: 新建一个DB块

    2024年03月08日
    浏览(66)
  • PMAC与Modbus主站进行Modbus Tcp通讯

    在项目的PMAC Script LanguageGlobal Includes下创建一个名为00_Modbus_Para.pmh的pmh文件。 具体的参数查看手册,样例使用的是本机的回环地址。 在C LanguageBackground Programs下添加一个后台C应用程序,名为capp1。在capp1文件夹下创建一个capp1.c的source文件。 在PMAC Script LanguagePLC Programs文件夹

    2024年02月11日
    浏览(34)
  • STM32开发之Modbus协议(主站RTU)

    在单片机方面,针对于通讯常用的协议之一modbus,这里将modbus协议和硬件之间的关系完全独立出来,硬件和协议之间的联系采用的是回调的方式进行一个关联。 1、此协议可直接移植,并不需要关心硬件相关的。 2、modbus相关协议概念自行查找,本文只做代码的实现。 宏定义(

    2024年02月12日
    浏览(44)
  • Modbus/TCP:主站、从站、客户端和服务端关系

    主站主动找从站读写数据 客户端主动找服务端读写数据 所以当使用Modbus/TCP时,主站一般作为客户端,从站一般作为服务端 当使用Modbus/TCP时,modbus poll一般模拟客户端,modbus slave一般模拟服务端

    2024年02月12日
    浏览(61)
  • Modbus RTU(Remote Terminal Unit)与RS-485协议(rs485)介绍(主站设备(Master)、从站设备(Slave))Modbus TCP、Modbus ASCII

    参考文章:ModBus协议 参考文章:一篇文章了解 RS485 和 MODBUS 的区别,它们有什么不同? Modbus RTU和RS-485是工业通信中常用的两种协议。Modbus RTU 是一种数据表示协议,而 RS-485 则是物理传输标准。尽管这两者有时被并列讨论,但它们在通信系统中的角色却大不相同。以下内容将

    2024年02月05日
    浏览(68)
  • Modbus PLC攻击分析:从Modbus Poll Slave到M340_intouch modbustcp 读取 m340

    先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7 深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前! 因此收集整理了一份《2024年最新网络安全全套学习资料》

    2024年04月28日
    浏览(36)
  • Modbus通信从入门到精通_2_Modbus TCP通信详解及仿真(搭建ModbusTCP仿真环境:创建虚拟PLC并进行ModbusTCP通讯;寄存器与PLC中映射关系;适合理解如何编写上位机)

    本篇将会以西门子PLC软件搭建ModbusTCP仿真环境,并通过仿真环境,介绍基础知识及模拟实际应用中写一个简单的通信读取PLC数据方法,并简介了编写上位机的方法。 由于具有TCP/IP栈协议,通常在Modbus TCP通讯的上位机开发中, 上位机是作为客户端,控制器作为服务器 。Modbu

    2024年02月08日
    浏览(179)
  • 用IoTClient模拟ModbusTCP从站,用Modbus Poll软件进行模拟测试的方法及软件下载

    IoTClient是一个物联网设备通讯协议实现客户端,将包括主流PLC通信读取、ModBus协议、Bacnet协议等常用工业通讯协议。本组件基于.NET Standard 2.0,可用于.Net的跨平台开发,如Windows、Linux甚至可运行于树莓派上。 1、打开IoTClient软件  ,选择modbus tcp  ,设置IP 127.0.0.1 端口502  ,

    2024年04月10日
    浏览(71)
  • 运动控制器设计——基于FreeModbus在STM32F4平台实现ModbusTCP和ModbusRTU

    本文笔者最近的项目是设计一款运动控制器,MCU使用的是STM32F429,要求是通过Modbus TCP协议实现与示教器通讯,并通过ModbusRTU实现与触摸屏通讯。 本文将介绍在STM32F4上实现 ModbusTCP和ModbusRTU通讯 的过程。笔者才疏学浅,如有错误还请指正。 Modbus协议是典型的主-从通讯结构,链

    2024年02月05日
    浏览(54)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包