使用PySerial/PyVisa经RS232连接McPherson 747设备通信报错问题
问题背景
- 对接设备:McPherson 747滤光轮控制器,连接方式为RS232转USB
- 通信逻辑:读请求需要先发查询指令+报文头,设备返回ACK应答后才会返回业务数据
- 协议规则:全ASCII格式交互,3字节查询指令十六进制为
4E2105(对应N!<ENQ>),应答指令十六进制为4E2106(对应N!<ACK>),报文头根据读写目标不同由多组十六进制ASCII编码拼接组成 - 现存问题:发送查询指令始终报通信错误,不确定查询指令和报文头发送方式,PySerial、PyVisa实现均报错
第一步:先解决端口找不到的基础错误
你现在贴出的报错和指令逻辑没有关系,是最基础的端口配置错误:
- PySerial硬编码
COM1、PyVisa硬编码ASRL1::INSTR是完全错误的,RS232转USB线接入系统后会动态分配串口号,不会固定为COM1。Windows下去设备管理器的「端口(COM和LPT)」分类下找对应USB转串口设备的COM编号;Linux下查看/dev/ttyUSB*设备名即可。 - PyVisa代码里虽然调用了
rm.list_resources(),但没有打印返回结果就直接硬写资源地址,先把这个方法的返回值打印出来,选中对应串口的资源地址再连接。
第二步:修正现有代码的逻辑错误
PySerial代码问题
- 初始化
serial.Serial()对象时默认就会打开端口,你后面先ser.close()再ser.open()属于多余操作,反而可能触发端口占用错误 ser.read_until(size=None)用法错误,这个方法必须指定预期的终止字节,要读取所有缓冲区数据直接用ser.read(ser.in_waiting)即可- 超时设为
None会导致程序永久卡在读操作,改成1-2秒的超时值适配设备响应速度
PyVisa代码问题
- 没有导入
pyvisa.constants模块,直接调用visa.constants会触发名称错误 - 重复发送查询指令:先调用
inst.write(enquire)发了一次,又调用inst.query("N!<ENQ>")再发一次,query方法本身就包含发送指令+读取返回的逻辑,重复发报文会导致设备判定通信异常 - 手动加了
\r作为读写终止符是多余操作,手册没有要求报文末尾加回车,额外加字符会导致报文格式不符合协议要求
第三步:指令发送规则说明
查询指令和报文头必须拼接为连续的单条字节流一次性发送,绝对不能拆分多次发送。
手册标注的十六进制ASCII编码,指的是每个十六进制值对应一个ASCII字符:0x4E对应字符N,0x21对应字符!,0x05是ASCII控制符ENQ,0x06是ASCII控制符ACK。如果拆分两次发送,设备会把两段报文分别判定为无效格式,直接返回通信错误。
修正后可运行参考代码
PySerial版本
import serial import time # 替换为设备管理器中查到的实际串口号 PORT = "COM3" # 4E2105是查询指令,后面拼接你实际要使用的报文头十六进制字符串 send_hex = "4E2105" + "替换为实际报文头十六进制内容" send_bytes = bytes.fromhex(send_hex) if __name__ == "__main__": try: ser = serial.Serial( port=PORT, baudrate=9600, timeout=2, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, bytesize=serial.EIGHTBITS, xonxoff=False ) # 清空收发缓冲区残留数据 ser.reset_input_buffer() ser.reset_output_buffer() # 一次性发送完整报文 ser.write(send_bytes) # 留100ms等待设备返回 time.sleep(0.1) # 读取所有返回数据 recv_data = ser.read(ser.in_waiting) print(f"接收数据(十六进制): {recv_data.hex(' ')}") # 校验ACK应答 if recv_data.hex().startswith("4e2106"): print("设备ACK应答正常,可继续后续交互") else: print("设备应答异常,检查报文格式或串口参数") ser.close() except Exception as e: print(f"通信异常: {str(e)}")
PyVisa版本
import pyvisa from pyvisa import constants rm = pyvisa.ResourceManager() # 打印所有可用资源,找到对应串口的资源地址 all_res = rm.list_resources() print(all_res) # 替换为上面打印结果中对应串口的资源地址,例如"ASRL3::INSTR" inst = rm.open_resource("ASRL3::INSTR") # 串口参数配置 inst.baud_rate = 9600 inst.data_bits = 8 inst.parity = constants.Parity.none inst.stop_bits = constants.StopBits.one inst.flow_control = constants.VI_ASRL_FLOW_NONE # 清空默认终止符,不额外添加字符 inst.write_termination = "" inst.read_termination = "" inst.timeout = 2000 send_hex = "4E2105" + "替换为实际报文头十六进制内容" send_bytes = bytes.fromhex(send_hex) # 用write_raw发送原始字节,避免编码问题 inst.write_raw(send_bytes) recv_data = inst.read_raw() print(f"接收数据(十六进制): {recv_data.hex(' ')}") rm.close()
调试建议:正式接设备前,可以先用串口调试助手自发自收(短接串口TX、RX引脚),确认代码发送的字节流是连续的
4E 21 05加报文头内容,没有多余的回车、换行或者其他乱码字符,排除发送端问题后再接设备测试。
内容的提问来源于stack exchange,提问作者Olivia Jones
相关产品推荐
相关产品推荐

