
本教程旨在解决使用pyserial进行串口通信时,发送命令后无法接收到设备返回数据的问题。核心在于理解设备默认不进行回显,需发送触发响应的命令,并采用如`readline()`等恰当的数据读取方法。同时,文章将探讨其他串口工具“看似”正常工作的原理,并提供优化后的代码示例和注意事项,确保可靠的串口数据交互。
当使用Python的pySerial库与串口设备(如热电偶温度计)进行通信时,开发者常遇到一个困惑:代码似乎成功打开了串口并发送了命令,但尝试读取数据时却发现缓冲区为空,没有任何响应。这与在其他串口终端工具(如Termite)中能够正常获取响应的情况形成鲜明对比。本文将深入探讨这一现象背后的原因,并提供一套行之有效的解决方案。
许多串口设备在接收到命令后,并不会默认将其“回显”给发送方。这意味着,即使你成功发送了字符,设备也只是默默地接收并处理,而不会将收到的内容原封不动地发回。
为什么其他工具“看似”正常? 在Termite或Minicom这类串口终端软件中,用户可能会看到自己发送的命令立即出现在终端上,随后才是设备的实际响应。这通常是因为这些终端软件内置了“本地回显(Local Echo)”功能。当本地回显启用时,终端会将你输入并发送的字符立即显示在屏幕上,让你误以为是设备回传了数据。实际上,这只是软件为了方便用户查看发送内容而进行的一种模拟。当本地回显关闭时,你将只看到设备实际发回的数据。
pySerial的默认行为 pySerial库本身不会提供本地回显功能。因此,当你通过ser.write(b'K')发送命令后,如果设备没有被编程为回传接收到的字符,那么ser.in_waiting自然会返回0,因为设备端没有向串口发送任何数据。
为了成功从串口设备获取数据,我们需要采取两个关键策略:
发送触发响应的命令: 确保你发送的命令是设备协议中明确规定会产生某种响应的“查询”或“操作”命令。例如,发送一个请求设备型号或当前读数的命令,而不是仅仅发送一个控制字符。在示例中,如果字符'K'被预期会返回型号"0309",那么这个命令就是有效的。
采用合适的读取方法:ser.in_waiting仅指示输入缓冲区中等待读取的字节数。它并不能保证在ser.write()之后立即有数据。对于大多数基于行的串口通信,ser.readline()是一个更可靠的选择,它会阻塞直到读取到换行符或达到超时。
立即学习“Python免费学习笔记(深入)”;
以下是一个优化后的pySerial通信示例,展示了如何正确地发送命令并读取设备响应:
import serial
import time
def establish_serial_connection(port='COM4', baudrate=9600):
"""
建立并配置串口连接。
:param port: 串口名称,例如'COM4' (Windows) 或 '/dev/ttyUSB0' (Linux)
:param baudrate: 波特率
:return: 串口对象或None(如果连接失败)
"""
ser = serial.Serial()
ser.port = port
ser.baudrate = baudrate
ser.bytesize = serial.EIGHTBITS
ser.stopbits = serial.STOPBITS_ONE
ser.parity = serial.PARITY_NONE
ser.xonxoff = False
ser.rtscts = True # 根据设备要求设置流控制,示例中为True
ser.dsrdtr = False
ser.timeout = 1 # 设置读取超时时间(秒),非常重要
try:
ser.open()
print(f"成功打开串口 {port}")
return ser
except serial.SerialException as e:
print(f"打开串口 {port} 失败: {e}")
return None
def send_and_receive(ser_connection, command_bytes, delay=0.1):
"""
向串口发送命令并尝试读取响应。
:param ser_connection: 已建立的串口对象
:param command_bytes: 要发送的命令(字节串)
:param delay: 发送命令后等待设备响应的短暂延时(秒)
:return: 包含所有解码后响应行的列表,或None(如果通信失败)
"""
if not ser_connection or not ser_connection.is_open:
print("串口未打开或连接无效。")
return None
try:
# 清空输入缓冲区,确保读取的是最新数据
ser_connection.flushInput()
print(f"发送命令: {command_bytes.decode('utf-8', errors='ignore').strip()}")
ser_connection.write(command_bytes)
time.sleep(delay) # 短暂延时,等待设备处理并发送响应
response_lines = []
# 循环读取所有可用的行,直到超时或无数据
while True:
line = ser_connection.readline() # 读取一行数据,直到换行符或超时
if not line:
break # 读取到空行或超时,表示没有更多数据
try:
decoded_line = line.decode('utf-8').strip()
response_lines.append(decoded_line)
print(f"收到响应: {decoded_line}")
except UnicodeDecodeError:
# 如果解码失败,打印十六进制表示,方便调试
print(f"收到原始响应(Hex): {line.hex()}")
response_lines.append(line.hex())
return response_lines
except Exception as e:
print(f"通信过程中发生错误: {e}")
return None
if __name__ == "__main__":
serial_port = 'COM4' # 请根据实际连接的串口修改
baud_rate = 9600
# 1. 建立串口连接
ser = establish_serial_connection(serial_port, baud_rate)
if ser:
try:
# 2. 发送查询命令并接收响应
# 假设 'K' 命令用于查询设备型号,并期望返回 "0309"
# 注意:某些设备可能需要回车换行符作为命令终止符
command_to_send = b'K\r\n'
responses = send_and_receive(ser, command_to_send)
if responses:
print("\n所有收到的响应:")
for res in responses:
print(res)
else:
print("未收到任何响应。")
finally:
# 3. 关闭串口连接
ser.close()
print(f"串口 {serial_port} 已关闭。")
else:
print("无法建立串口连接,请检查端口和配置。")
通过理解串口设备的通信特性(特别是回显行为),并结合pySerial提供的强大功能,我们可以构建稳定可靠的串口通信程序。关键在于发送能够触发设备响应的命令,并使用readline()等阻塞式、带超时机制的读取方法来有效捕获设备回传的数据。遵循这些原则,将大大提高pySerial串口通信的成功率和健壮性。
以上就是Python pySerial串口通信:解决发送命令后无数据返回的疑难杂症的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号