You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Python控制RS-485模块Modbus RTU继电器的通断

Python基于Modbus RTU协议控制RS-485继电器实现方案

你当前编写的代码不符合Modbus RTU协议规范,无法实现继电器控制,核心问题有三个:

  • 仅发送单字节b'\x08'不满足Modbus RTU帧结构要求,完整有效帧必须包含从站地址、功能码、数据段、CRC校验四个部分,不符合规范的指令会被继电器直接丢弃
  • 调用readline()读取Modbus数据逻辑错误,Modbus RTU是二进制传输协议,不存在换行符作为帧结束标识,用该方法会出现读空、读残帧的问题
  • 未做收发缓存清理,串口残留的历史数据会干扰响应判断

继电器控制的核心逻辑

RS-485继电器的通断控制本质是对Modbus线圈寄存器执行写操作,常用两个功能码:

  • 0x05:写单个线圈,用于控制单路继电器的通断
  • 0x0F:写多个线圈,用于批量控制多路继电器的通断

以最常用的0x05写单个线圈功能码为例,标准指令帧结构如下(以从站地址1、第1路继电器吸合为例):

帧字段取值示例说明
从站地址0x01继电器模块自身的Modbus地址,可通过设备手册查询、修改
功能码0x05标识当前帧为写单个线圈操作
线圈地址高8位0x00继电器通道对应的寄存器地址,多数模块第1路默认地址为0x0000
线圈地址低8位0x00
写入值高8位0xFF写入0xFF00表示继电器吸合通电,写入0x0000表示继电器断开断电
写入值低8位0x00
CRC校验低8位计算得到Modbus RTU要求CRC校验值低字节在前、高字节在后传输
CRC校验高8位计算得到

可直接运行的实现代码

代码仅依赖你已经在用的pyserial库,不需要额外安装第三方依赖,内置了Modbus CRC16校验计算逻辑:

import serial
import time

def modbus_crc16(data: bytes) -> bytes:
    """计算Modbus RTU标准CRC16校验值,返回符合传输要求的小端序2字节结果"""
    crc = 0xFFFF
    for byte in data:
        crc ^= byte
        for _ in range(8):
            if crc & 0x0001:
                crc >>= 1
                crc ^= 0xA001
            else:
                crc >>= 1
    return crc.to_bytes(2, byteorder='little')

# 串口初始化,参数需和继电器模块配置完全一致,否则无法通信
conn = serial.Serial(
    port='/dev/ttyUSB0',
    baudrate=9600,
    parity=serial.PARITY_NONE,
    stopbits=serial.STOPBITS_ONE,
    bytesize=serial.EIGHTBITS,
    timeout=1,
    rtscts=False, 
    dsrdtr=False
)

def control_single_relay(slave_addr: int, channel: int, is_on: bool) -> bytes:
    """
    单路继电器控制函数
    :param slave_addr: 继电器模块从站地址(十进制)
    :param channel: 继电器通道号,从0开始计数,第1路传0、第2路传1,以此类推
    :param is_on: True为吸合通电,False为断开断电
    :return: 模块返回的响应帧
    """
    # 清空接收缓存,避免历史残留数据干扰
    conn.reset_input_buffer()
    frame = bytearray()
    # 拼接从站地址、功能码
    frame.append(slave_addr)
    frame.append(0x05)
    # 拼接线圈地址(大端序2字节)
    frame.extend(channel.to_bytes(2, byteorder='big'))
    # 拼接通断控制值
    frame.extend(b'\xFF\x00' if is_on else b'\x00\x00')
    # 拼接CRC校验值
    frame.extend(modbus_crc16(frame))
    # 发送指令
    conn.write(bytes(frame))
    # 读取8字节固定长度响应(0x05功能码响应固定为8字节)
    resp = conn.read(8)
    return resp

# 功能测试
if __name__ == "__main__":
    # 控制地址为1的模块第1路吸合
    resp_on = control_single_relay(slave_addr=1, channel=0, is_on=True)
    print(f"继电器吸合响应帧: {resp_on.hex(' ')}")
    time.sleep(2)
    # 等待2秒后控制第1路断开
    resp_off = control_single_relay(slave_addr=1, channel=0, is_on=False)
    print(f"继电器断开响应帧: {resp_off.hex(' ')}")
    # 用完关闭串口连接
    conn.close()

调试注意事项

  • 首次使用务必对照继电器手册确认四个核心参数:从站地址、串口通信参数(波特率/校验位/停止位)、通道对应的线圈起始地址,部分厂商模块第1路线圈地址从0x0001开始,对应修改channel参数即可
  • 接线时注意RS-485总线A、B线不要接反,模块供电要符合额定要求,否则会出现通信失败的问题
  • 不要使用readline()方法读取Modbus数据,需根据功能码对应的响应帧固定长度读取,避免帧解析错误
  • 如果你使用的是非自动收发型USB转RS-485模块,需要在发送指令前拉低RTS引脚切换为发送模式,发送完成后等待1-2个字符传输时间再拉高RTS切回接收模式,自动收发模块不需要额外操作

内容的提问来源于stack exchange,提问作者Murugesan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 02:51:22