【Flask-10】modbus tcp通信

Modbus TCP数据包由两部分组成:

  • MBAP头(Modbus Application Protocol Header) - 7字节

  • PDU(Protocol Data Unit) - 可变长度

完整数据帧结构:

事务标识符 \]\[ 协议标识符 \]\[ 长度字段 \]\[ 单元标识符 \]\[ 功能码 \]\[ 数据

2字节 2字节 2字节 1字节 1字节 N字节

逐字节解析:

字节位置 值(十六进制) 说明
0-1 00 01 事务标识符(Transaction ID):0x0001,用于匹配请求和响应
2-3 00 00 协议标识符(Protocol ID):0x0000,Modbus协议固定值
4-5 00 06 长度字段:0x0006 = 6,表示后面还有6个字节
6 01 单元标识符(Unit ID/设备地址):0x01 = 设备地址1
7 03 功能码(Function Code):0x03 = 读保持寄存器
8-9 01 90 起始地址:0x0190 = 十进制400(Modbus地址从0开始,对应40001)
10-11 00 0C 寄存器数量:0x000C = 十进制12

逐字节解析响应:

字节位置 值(十六进制) 说明
0-1 00 01 事务标识符:与请求匹配,0x0001
2-3 00 00 协议标识符:0x0000
4-5 00 19 长度字段:0x0019 = 25,后面有25个字节
6 01 单元标识符:设备地址1
7 03 功能码:0x03
8 18 字节计数:0x18 = 24字节(12个寄存器 × 2字节/寄存器)
9-32 00 00 ... 寄存器数据:每个寄存器2字节,共12个寄存器

代码解析:

功能码已包含在各方法中,不需要另外指定。

复制代码
# 读取保持寄存器 - 功能码 0x03,不需要指定function_code=0x03,方法名已包含
result = self.client.read_holding_registers(...)

# 读取输入寄存器 - 功能码 0x04
result = self.client.read_input_registers(...)

# 读取线圈状态 - 功能码 0x01
result = self.client.read_coils(...)

# 读取离散输入 - 功能码 0x02
result = self.client.read_discrete_inputs(...)

real_vol_int = (registers[0] << 16) | registers[1]
1、registers[0] << 16:将第一个寄存器的值左移16位
假设 registers[0] = 0x1234(16位)
左移16位后变成:0x12340000(32位)
2、registers[1]:与第二个寄存器值进行按位或操作
假设 registers[1] = 0x5678(16位)
0x12340000 | 0x5678 = 0x12345678
3、最终结果:0x12345678(32位整数)

完整示例代码:

复制代码
from pymodbus.client import ModbusTcpClient
import struct

class ModbusdeviceClient:
    def __init__(self, host='*.*.*.*', port=7890, device_address=1):
        self.host = host
        self.port = port
        self.device_address = device_address
        self.client = None
        self.connect()
    
    def connect(self):
        """连接Modbus设备"""
        try:
            self.client = ModbusTcpClient(self.host, port=self.port)
            if self.client.connect():
                print(f"Modbus连接成功 - 主机: {self.host}:{self.port}, 设备地址: {self.device_address}")
            else:
                print("Modbus连接失败")
        except Exception as e:
            print(f"Modbus连接错误: {e}")
  

    def read_device_data(self):
        try:
            # 使用read_holding_registers读取保持寄存器
            result = self.client.read_holding_registers(
                address=0x0190,  # 开始地址,使用偏移地址400
                count=12,         # 寄存器数量
                # unit=self.device_address  # 设备地址
                device_id = self.device_address
            )

            if not result.isError():
                registers = result.registers
                # 解析数据
                parsed_data = self._parse_device_data(registers)
                return parsed_data
            else:
                print(f"读取数据错误: {result}")
                return None
                
        except Exception as e:
            print(f"读取设备数据错误: {e}")
            # 尝试重新连接
            self.connect()
            return None
        
    def _parse_device_data(self, registers):
        """
        正确解析流量数据
        registers: 包含12个WORD值的列表
        """
        if len(registers) < 12:
            return None
        
        results = {}
        
        # 1. 实时流量(体积) - 寄存器0,1
        real_vol_int = (registers[0] << 16) | registers[1]
        results['instant_flow_volume'] = real_vol_int / 100.0 # m³/h
        
        。。。
        
        return results

    
    
    def build_request_message(self):
        print("\n请求报文结构分析:")
        print("00 01 - 事务标识符 (Transaction ID)")
        print("00 00 - 协议标识符 (Protocol ID)")
        print("00 06 - 长度 (Length)")
        print(f"{self.device_address:02X} - 单元标识符 (Unit ID/设备地址)")  # {:02X} 是 Python 的 格式化字符串
        print("03 - 功能码 (Function Code - 读保持寄存器)")
        print("9D D0 - 起始地址 (Start Address - 0x000A)")
        print("00 0C - 寄存器数量 (Number of Registers)")
        
        # 返回报文结构字典
        return {
            'transaction_id': 0x0001,
            'protocol_id': 0x0000,
            'length': 0x0006,
            'unit_id': self.device_address,
            'function_code': 0x03,
            'start_address': 0x9DD0,
            'register_count': 0x000C
        }
    
    
    def is_connected(self):
        """检查连接状态"""
        return self.client.is_socket_open() if self.client else False
    
    def close(self):
        """关闭连接"""
        if self.client:
            self.client.close()
            print("Modbus连接已关闭")


def main():
    print("=" * 70)
    print("Modbus设备2数据读取演示")
    print("=" * 70)
    
    # 创建Modbus客户端
    modbus_client = ModbusdeviceClient('*.*.*.*', 503, 1)
    
    if modbus_client.is_connected():
        print("\n" + "=" * 70)
        print("实际数据读取")
        print("=" * 70)
        
        # 实际读取设备数据
        device_data = modbus_client.read_device_data()
        
        if device_data:
            print("\n设备数据解析结果:")
            print(f"实时流量(体积): {device_data['instant_flow_volume']}m³/h")
        else:
            print("实际数据读取失败")

        
        # 关闭连接
        modbus_client.close()
    else:
        print("无法连接到Modbus设备")
     

if __name__ == "__main__":
    
    # 运行主演示
    main()
相关推荐
Java后端的Ai之路3 小时前
【Python 教程15】-Python和Web
python
冬奇Lab4 小时前
一天一个开源项目(第15篇):MapToPoster - 用代码将城市地图转换为精美的海报设计
python·开源
二十雨辰6 小时前
[python]-AI大模型
开发语言·人工智能·python
Yvonne爱编码7 小时前
JAVA数据结构 DAY6-栈和队列
java·开发语言·数据结构·python
前端摸鱼匠7 小时前
YOLOv8 环境配置全攻略:Python、PyTorch 与 CUDA 的和谐共生
人工智能·pytorch·python·yolo·目标检测
WangYaolove13148 小时前
基于python的在线水果销售系统(源码+文档)
python·mysql·django·毕业设计·源码
AALoveTouch8 小时前
大麦网协议分析
javascript·python
ZH15455891318 小时前
Flutter for OpenHarmony Python学习助手实战:自动化脚本开发的实现
python·学习·flutter
xcLeigh8 小时前
Python入门:Python3 requests模块全面学习教程
开发语言·python·学习·模块·python3·requests
xcLeigh8 小时前
Python入门:Python3 statistics模块全面学习教程
开发语言·python·学习·模块·python3·statistics