串口连接不上或打开失败的常见原因:90%为设备未插、权限不足或端口号错误;linux/macos需加用户至dialout/uucp组,windows需检查驱动及com端口格式。

串口连接不上或打开失败的常见原因
PySerial 打开串口失败,90% 是设备路径、权限或占用问题。Windows 上串口号通常是 COM3、COM7 这类;macOS/Linux 则是 /dev/tty.usbserial-XXXX 或 /dev/ttyUSB0。别凭经验硬写,先用 serial.tools.list_ports.comports() 实时枚举:
from serial.tools import list_ports
for port in list_ports.comports():
print(port.device, port.description)
Linux/macOS 下还常因用户没加入 dialout(Linux)或 accessibility(macOS)组而被拒绝访问,需手动添加用户;Windows 则注意驱动是否安装完整(尤其 CH340、CP2102 类芯片),设备管理器里看到黄色感叹号就说明驱动异常。
read() 和 readline() 的行为差异与选择依据
read() 默认阻塞等待指定字节数,readline() 阻塞直到收到换行符(\n 或 \r\n)。如果设备发的是无分隔符的二进制流(如传感器原始 ADC 值),用 read(8) 指定长度更稳妥;如果是带换行的文本协议(如 AT 指令响应、Modbus ASCII),readline() 更自然。但要注意:
-
readline()可能永远卡住——设备没发换行符,或发送间隔超时,必须配timeout=参数 -
read(1)循环逐字节读效率极低,避免在高频场景中使用 - 默认
timeout=None是完全阻塞,生产环境务必设为非零值(如timeout=0.1)
如何稳定解析不定长、含粘包的串口数据
设备连续发多帧数据时,read() 可能一次读到半帧+整帧+半帧,即“粘包”。不能直接按行或固定长度切分。推荐做法是:用 in_waiting 检查缓冲区字节数,再批量读取,最后用状态机或正则提取完整帧。例如设备以 0x02 开头、0x03 结尾:
while True:
if ser.in_waiting > 0:
data = ser.read(ser.in_waiting) # 一次性读完当前所有缓存
# 再用 bytes.find(b'\x02') + bytes.find(b'\x03') 提取合法帧
关键点:
- 不要依赖
readline()处理自定义帧头帧尾协议 -
in_waiting返回的是内核缓冲区字节数,不是已解析帧数 - 若设备波特率高、主机处理慢,仍可能丢字节——这时得调大系统串口缓冲区或降低波特率
长时间运行后数据错乱或连接中断怎么办
PySerial 对底层串口资源的异常恢复能力很弱。USB 转串口设备热插拔、供电不稳、线缆松动都可能导致 SerialException 或读出全 0x00。不能只靠 try/except 包一层就完事:
- 捕获
SerialException后,必须显式调用ser.close(),再重新Serial(...)初始化 - 定期用
ser.is_open检查连接状态,而非假设它一直有效 - 某些芯片(如 FT232)在空闲超时后会进入休眠,需在配置中加
inter_byte_timeout=0.1避免读取卡死 - Windows 下长时间运行后偶发
PermissionError: [Errno 13],本质是句柄泄漏,需确保每次异常后彻底释放资源
真正难的不是读数据,而是让读取逻辑在设备掉线、重启、干扰下还能自动续上——这需要把重连逻辑、帧同步恢复、超时熔断全写进主循环里,而不是当成边缘情况忽略。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











