汇川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() 一次性读取多个连续寄存器

📚 参考资料

相关推荐
代码小书生2 小时前
Matplotlib,Python 数据可视化核心库!
python·信息可视化·matplotlib
lzhdim2 小时前
SharpCompress:跨平台的 C# 压缩与解压库
开发语言·c#
嘿嘿嘿x33 小时前
Linux记录过程
linux·开发语言
默 语3 小时前
Records、Sealed Classes这些新特性:Java真的变简单了吗?
java·开发语言·python
架构师老Y3 小时前
013、数据库性能优化:索引、查询与连接池
数据库·python·oracle·性能优化·架构
止观止3 小时前
拥抱 ESNext:从 TC39 提案到生产环境中的现代 JS
开发语言·javascript·ecmascript·esnext
Kel3 小时前
PydanticAI 源码深潜:类型安全依赖注入与图执行引擎的双核架构解析
人工智能·python·架构
卷心菜狗3 小时前
Python进阶-深浅拷贝辨析
开发语言·python
时寒的笔记3 小时前
js逆向7_案例惠nong网
android·开发语言·javascript