python modbus RTU协议的主机和从机案例

该文章已生成可运行项目,

本身我不是做PLC领域的,但正好有一个项目需要提供一个modbus接口,查阅了很多资料,发现主要都是在讲原理和客户端的实现,我这里就给大家提供一个主机和从机的通信实验案例吧。

连接好两个COM口

为了方便实验,这里我们用一个VSPD 7.2软件先为电脑提供两个COM口,并让两个COM口相连,打开软件,操作的方法如下:

添加完成以后,点开设备管理器,能看到下面的COM2和COM5相连

下载对应的pip安装相关的库

pip install modbus_tk pyserial

首先是从机的代码,我这里是写了一个温湿度的案例,让主机可以访问解析数据。

import modbus_tk
import modbus_tk.defines as cst
from modbus_tk import modbus_rtu
import serial
import random
import time

# 串口设置
PORT = 'COM5'  # 根据实际情况修改串口名称 COM5是下位机


def get_temperature_and_humidity():
    """模拟获取温度和湿度的函数"""
    temperature = random.uniform(20.0, 30.0)  # 模拟温度数据
    humidity = random.uniform(30.0, 60.0)  # 模拟湿度数据
    return temperature, humidity


def main():
    logger = modbus_tk.utils.create_logger(name="console", record_format="%(message)s")

    # 创建 Modbus RTU 从机服务器
    server = modbus_rtu.RtuServer(serial.Serial(PORT, baudrate=9600, bytesize=8, parity='N', stopbits=1))

    try:
        logger.info("Modbus RTU server is running...")

        server.start()

        # 创建从机,地址为1
        slave_1 = server.add_slave(1)

        # 添加保持寄存器 (Holding Registers),假设温度和湿度分别映射到地址 0 和 1
        slave_1.add_block('0', cst.HOLDING_REGISTERS, 0, 2)  # 地址0开始,2个寄存器(一个存温度,一个存湿度)

        while True:
            # 获取当前温度和湿度
            temperature, humidity = get_temperature_and_humidity()

            # 将温度和湿度写入寄存器
            slave_1.set_values('0', 0, [int(temperature * 10), int(humidity * 10)])  # 存储时放大10倍(例如20.3 -> 203)

            # 每隔1秒更新一次数据
            time.sleep(1)

    except KeyboardInterrupt:
        pass
    finally:
        server.stop()


if __name__ == "__main__":
    main()

然后是主机的代码,负责向从机发送指令并解析出温度湿度。

import modbus_tk
from modbus_tk import modbus_rtu
import serial
import time

# 串口设置
PORT = 'COM2'  # 根据实际情况修改串口名称,COM2是上位机


def main():
    logger = modbus_tk.utils.create_logger(name="console", record_format="%(message)s")

    # 创建 Modbus RTU 主机客户端
    client = modbus_rtu.RtuMaster(serial.Serial(PORT, baudrate=9600, bytesize=8, parity='N', stopbits=1))

    client.set_timeout(1.0)  # 设置超时时间为1秒

    try:
        logger.info("Modbus RTU client is running...")

        while True:
            # 读取从机 1 的保持寄存器(地址 0 和 1,分别存储温度和湿度)
            response = client.execute(1, modbus_tk.defines.READ_HOLDING_REGISTERS, 0, 2)

            # 获取返回的数据
            temperature_raw = response[0]
            humidity_raw = response[1]

            # 将数据恢复为实际的温度和湿度
            temperature = temperature_raw / 10.0
            humidity = humidity_raw / 10.0

            print(f"Temperature: {temperature}°C, Humidity: {humidity}%")

            # 每隔1秒读取一次数据
            time.sleep(1)

    except KeyboardInterrupt:
        pass
    finally:
        client.close()


if __name__ == "__main__":
    main()

运行时要先运行从机的代码,在运行主机的代码,就可以看到结果了。

这是从机代码的运行结果:

这是主机代码的运行结果:

通过报文获取数据

也可以只运行从机代码,通过串口调试助手发送指令获取数据,我这里从机是使用的COM5,我用串口调试助手打开COM2,设置如下:

主机发送报文获取温度湿度01 03 00 00 00 02 C4 0B

  • 01: 从机地址 1
  • 03: 功能码,表示读取保持寄存器
  • 00 00: 寄存器起始地址 0
  • 00 02: 读取 2 个寄存器
  • C4 0B: CRC 校验码,确保数据的传输正确性。

可以收到从机返回的温度湿度报文01 03 04 01 1A 01 5F 9B A0:

  • 01: 从机地址 1
  • 03: 功能码,表示读取保持寄存器
  • 04: 字节数,表示数据部分为 4 字节
  • 01 1A: 第一个寄存器的数据,十进制值为 255
  • 01 5F: 第二个寄存器的数据,十进制值为 492
  • 9B A0: CRC 校验码,确保数据正确传输

表示当前的测量出的温度是25.5℃湿度是49.2%

本文章已经生成可运行项目
### Modbus RTU 通讯协议中的高位低位解释 在Modbus RTU通信协议中,数据传输通常按照特定的字节顺序进行。对于寄存器的数据表示,每个寄存器占用两个字节(16位),分为高位字节低位字节。 #### 数据存储方式 当涉及到寄存器的操作时,无论是读取还是写入操作,都需要考虑如何处理这两个字节。具体来说: - **高位字节 (High Byte)**:位于前一个字节位置,代表较高有效位。 - **低位字节 (Low Byte)**:位于后一个字节位置,代表较低有效位。 例如,在请求帧格式中,如果要设置某个寄存器值为 `0x3301`,则该值会被拆分成高低两位分别放入不同的字段内[^2]。 ```plaintext | 寄存器高 | 寄存器低 | |----------|-----------| | 0x33 | 0x01 | ``` 这种设计使得即使在网络上传输多字节数值时也能保持一致性兼容性。此外,由于Modbus采用的是大端序(big-endian),即最高有效字节先传送的方式,因此上述例子中的`0x33`会先于`0x01`被发送出去[^1]。 #### 实际应用案例 假设有一个预置单个寄存器的功能码(06)用于修改从站上的某寄存器值。此时,主机会构建如下结构的消息包来完成这项任务: ```python def create_modbus_request(slave_id, register_address, value_to_write): high_byte = (value_to_write >> 8) & 0xFF low_byte = value_to_write & 0xFF request_frame = [ slave_id, 0x06, # 功能码: 预置单个寄存器 (register_address >> 8), # 寄存器地址高字节 (register_address & 0xFF)# 寄存器地址低字节 high_byte, # 写入值高字节 low_byte # 写入值低字节 ] crc = calculate_crc(request_frame[:-2]) # CRC校验计算不包括CRC本身 request_frame.extend(crc) return bytes(request_frame) ``` 此函数展示了创建一条标准的Modbus RTU命令的过程,其中包含了对目标寄存器地址以及待写入数值按高低位分离后的编码过程。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值