汇川PLC-Python与汇川easy521plc进行Modbustcp通讯

📌 前言

在工业自动化领域,上位机与PLC的通信是不可或缺的环节。汇川Easy 521作为一款高性能小型PLC,广泛应用于各类自动化设备中。本文将详细介绍如何使用Python通过Modbus TCP协议与汇川Easy 521 PLC进行通信,实现X、Y、M、D等区域的读写操作。

🎯 为什么选择Modbus TCP?

Modbus TCP是一种开放、简单、可靠的工业通信协议,具有以下优势:

  • 跨平台性:支持Windows、Linux、嵌入式系统

  • 易于实现:协议简单,开发周期短

  • 网络化:基于以太网,支持远程控制

  • 实时性:响应速度快,适合工业控制场景

🛠️ 环境准备

硬件准备

  • 汇川Easy 521 PLC(带以太网口)

  • 网线(直连或通过交换机)

  • 上位机(Windows/Linux均可)

软件环境

python 复制代码
# 安装pymodbus库
pip install pymodbus

PLC端配置(使用AutoShop软件)

  1. 设置PLC的IP地址(如:192.168.1.88)

  2. 确保Modbus TCP服务器使能(默认端口502)

  3. 确认防火墙允许502端口通信

📊 地址映射关系

汇川Easy系列PLC的元件地址与Modbus地址有固定的映射关系:

PLC元件 Modbus类型 起始地址(Hex) 起始地址(Dec) 功能码 说明
Y0-Y1777 线圈(Coil) 0xFC00 64512 01/05/15 输出继电器,八进制地址
X0-X1777 线圈(Coil) 0xF800 63488 01/05/15 输入继电器,八进制地址
M0-M7999 线圈(Coil) 0x0000 0 01/05/15 辅助继电器
D0-D7999 保持寄存器 0x0000 0 03/06/16 16位数据寄存器

⚠️ 重要提示:X和Y元件的地址是八进制表示法,例如Y10(八进制)实际对应十进制地址8。

💻 完整代码实现

python 复制代码
from pymodbus.client import ModbusTcpClient

# ==================== 配置区 ====================
PLC_IP = "192.168.1.88"      # 汇川521 PLC的IP地址
PLC_PORT = 502                # Modbus TCP默认端口

# ==================== 地址映射表 ====================
ADDR_OFFSET = {
    'Y': 0xFC00,    # Y区起始地址: 64512 (0xFC00)
    'X': 0xF800,    # X区起始地址: 63488 (0xF800)
    'M': 0x0000,    # M区起始地址: 0
    'D': 0x0000,    # D寄存器起始地址: 0
}

# ==================== 建立连接 ====================
def connect_plc():
    """建立Modbus TCP连接"""
    client = ModbusTcpClient(PLC_IP, port=PLC_PORT)
    if client.connect():
        print(f"[成功] 已连接到 {PLC_IP}:{PLC_PORT}")
        return client
    else:
        print(f"[失败] 无法连接到 {PLC_IP}:{PLC_PORT}")
        return None

# ==================== 线圈操作(Y, X, M区域)====================
def write_coil(client, area, address, value):
    """写入单个线圈"""
    if area.upper() not in ADDR_OFFSET:
        print(f"[错误] 不支持的区域: {area}")
        return False
    
    modbus_addr = ADDR_OFFSET[area.upper()] + address
    result = client.write_coil(modbus_addr, value)
    
    if result.isError():
        print(f"[失败] 写入 {area}{address} = {value}")
        return False
    else:
        print(f"[成功] 写入 {area}{address} = {value}")
        return True

def write_coils(client, area, start_address, values):
    """批量写入线圈"""
    if area.upper() not in ADDR_OFFSET:
        print(f"[错误] 不支持的区域: {area}")
        return False
    
    modbus_addr = ADDR_OFFSET[area.upper()] + start_address
    result = client.write_coils(modbus_addr, values)
    
    if result.isError():
        print(f"[失败] 批量写入失败")
        return False
    else:
        print(f"[成功] 批量写入 {area}{start_address}开始: {values}")
        return True

def read_coil(client, area, address):
    """读取单个线圈"""
    if area.upper() not in ADDR_OFFSET:
        print(f"[错误] 不支持的区域: {area}")
        return None
    
    modbus_addr = ADDR_OFFSET[area.upper()] + address
    result = client.read_coils(address=modbus_addr, count=1)
    
    if result.isError():
        print(f"[失败] 读取 {area}{address} 失败")
        return None
    else:
        value = result.bits[0]
        print(f"[读取] {area}{address} = {value}")
        return value

def read_coils(client, area, start_address, count):
    """读取多个线圈"""
    if area.upper() not in ADDR_OFFSET:
        print(f"[错误] 不支持的区域: {area}")
        return None
    
    modbus_addr = ADDR_OFFSET[area.upper()] + start_address
    result = client.read_coils(address=modbus_addr, count=count)
    
    if result.isError():
        print(f"[失败] 批量读取失败")
        return None
    else:
        values = result.bits[:count]
        print(f"[读取] {area}{start_address}-{start_address+count-1}: {values}")
        return values

# ==================== 寄存器操作(D区域)====================
def write_register(client, address, value):
    """写入单个D寄存器(16位)"""
    modbus_addr = ADDR_OFFSET['D'] + address
    result = client.write_register(modbus_addr, value)
    
    if result.isError():
        print(f"[失败] 写入 D{address} = {value}")
        return False
    else:
        print(f"[成功] 写入 D{address} = {value}")
        return True

def read_register(client, address):
    """读取单个D寄存器"""
    modbus_addr = ADDR_OFFSET['D'] + address
    result = client.read_holding_registers(address=modbus_addr, count=1)
    
    if result.isError():
        print(f"[失败] 读取 D{address} 失败")
        return None
    else:
        value = result.registers[0]
        print(f"[读取] D{address} = {value}")
        return value

def read_registers(client, start_address, count):
    """读取多个D寄存器"""
    modbus_addr = ADDR_OFFSET['D'] + start_address
    result = client.read_holding_registers(address=modbus_addr, count=count)
    
    if result.isError():
        print(f"[失败] 批量读取失败")
        return None
    else:
        values = result.registers[:count]
        print(f"[读取] D{start_address}-D{start_address+count-1}: {values}")
        return values

def read_32bit_int(client, start_address):
    """读取32位整数(占用D和D+1两个寄存器)"""
    modbus_addr = ADDR_OFFSET['D'] + start_address
    result = client.read_holding_registers(address=modbus_addr, count=2)
    
    if result.isError():
        print(f"[失败] 读取32位整数失败")
        return None
    else:
        low_word = result.registers[0]
        high_word = result.registers[1]
        value = (high_word << 16) | low_word
        # 处理负数
        if value & 0x80000000:
            value = value - 0x100000000
        print(f"[读取] D{start_address}组合32位整数 = {value}")
        return value

# ==================== 主程序示例 ====================
if __name__ == "__main__":
    # 建立连接
    client = connect_plc()
    if client is None:
        exit(1)

    print("\n" + "=" * 50)
    print("开始执行读写示例")
    print("=" * 50 + "\n")

    # 示例1: 写入Y0输出点
    write_coil(client, 'Y', 0, True)

    # 示例2: 写入M100辅助继电器
    write_coil(client, 'M', 100, True)

    # 示例3: 写入D100数据寄存器
    write_register(client, 100, 250)

    # 示例4: 验证读取
    print("\n" + "-" * 50)
    print("验证读取")
    print("-" * 50)
    read_coil(client, 'Y', 0)
    read_coil(client, 'M', 100)
    read_register(client, 100)

    # 示例5: 批量写入操作
    print("\n" + "-" * 50)
    print("批量写入")
    print("-" * 50)
    write_coils(client, 'M', 0, [True, False, True])
    read_coils(client, 'M', 0, 3)

    # 关闭连接
    client.close()
    print("\n[完成] 连接已关闭")

📖 代码详解

1. 连接管理

connect_plc()函数创建Modbus TCP客户端并建立连接。建议在程序结束时显式关闭连接,释放资源。

2. 线圈操作(位元件)

  • 写入write_coil() 控制单个Y、X、M点的通断

  • 批量写入write_coils() 可一次性控制多个点,提高通信效率

  • 读取read_coil() / read_coils() 获取位元件的当前状态

3. 寄存器操作(字元件)

  • 16位数据write_register() / read_register() 处理0-65535的整数

  • 32位数据read_32bit_int() 读取占用两个连续寄存器的32位整数

  • 批量操作read_registers() 一次性读取多个连续寄存器

📚 参考资料

相关推荐
phltxy3 小时前
深度解析:Spring Cloud Gateway 从入门到实战
java·开发语言
无敌昊哥战神3 小时前
【LeetCode 37】解数独 (Sudoku Solver) —— 回溯法详解 (Python/C/C++)
c语言·c++·python·算法·leetcode
AI进化营-智能译站3 小时前
ROS2 C++开发系列08-传感器数据缓存与指令解析方式之数组、向量与字符串实战
开发语言·c++·缓存·ai
风流 少年3 小时前
Python Web框架:FastAPI
前端·python·fastapi
shjita3 小时前
记录java执行中的一个错误细节
java·开发语言
Qres8213 小时前
Rabrg/artificial-life test
python·模拟
AI进化营-智能译站3 小时前
ROS2 C++开发系列14-Lambda表达式处理传感器数据流|文件IO保存机器人实验日志
开发语言·c++·ai·机器人
itzixiao3 小时前
L1-067 洛希极限(10分)[java][python]
java·开发语言·算法
财经资讯数据_灵砚智能3 小时前
基于全球经济类多源新闻的NLP情感分析与数据可视化(夜间-次晨)2026年5月1日
大数据·人工智能·python·信息可视化·自然语言处理
l1t4 小时前
DeepSeek总结的DuckDB anofox-forecast季节调整时间序列预测插件功能
开发语言·数据库