2. Python串口通信:pySerial库安装、串口参数配置、读写操作

好,咱们进入正题。串口通信,说白了就是让电脑跟仪器「说上话」。做分析仪器的人,这关必须过。我当年第一次调一台气相色谱仪,折腾了一整天没反应,最后发现是波特率配错了——嗯,这种坑我踩过不少,今天一并告诉你。

2.1 pySerial库安装

Python操作串口,最常用的就是pySerial。这玩意儿轻量、稳定,我几乎所有项目都用它。

安装很简单,一行命令搞定:

pip install pyserial

如果你用的是Anaconda环境,也可以:

conda install pyserial

我个人习惯用pip,因为版本更新更快。装完之后,在Python里试试导入:

import serial
print(serial.__version__)

能打印出版本号,说明装好了。如果报错,大概率是pip没装对——检查一下你的Python环境是不是激活了。

小提示: 我建议在虚拟环境里装pySerial,避免跟其他项目冲突。用 python -m venv myenv 创建环境,再装库,干净又省心。

2.2 串口参数配置

串口通信不是插上线就能聊的。你得告诉电脑:用什么速度、多少数据位、有没有校验。这些参数必须跟仪器端完全一致,否则数据就是一堆乱码。

核心参数有这几个:

参数 说明 常见值
波特率 每秒传输的比特数 9600, 19200, 115200
数据位 每个字节的数据位数 7, 8
停止位 每个字节后的停止信号 1, 1.5, 2
校验位 错误检测方式 None, Even, Odd
超时 读取等待时间(秒) 0.5, 1, None

配置代码长这样:

import serial

ser = serial.Serial(
    port='COM3',        # Windows下是COM口,Linux下是/dev/ttyUSB0
    baudrate=9600,      # 波特率
    bytesize=8,         # 数据位
    parity='N',         # 无校验
    stopbits=1,         # 1位停止位
    timeout=1           # 超时1秒
)

这里有个坑——端口号。Windows上你可以在设备管理器里查,Linux上用 ls /dev/tty*。我遇到过好几次,插拔USB后端口号变了,代码里写死的COM3就报错。后来我学乖了,用循环自动检测:

import serial.tools.list_ports

ports = serial.tools.list_ports.comports()
for port in ports:
    print(f"发现端口: {port.device} - {port.description}")
注意: 波特率必须跟仪器设置一致。我曾经调一台pH计,仪器说明书写的是9600,结果实际是19200——折腾了俩小时才发现。所以,拿到仪器先看说明书,或者用示波器量一下实际波特率。

2.3 读写操作

配置好了,就该收发数据了。读写操作是串口通信的核心,也是坑最多的地方。

2.3.1 写数据

写数据用 write() 方法。注意,它只接受字节类型,不能直接传字符串:

# 发送字符串
ser.write(b'AT\r\n')          # 字节串
ser.write('AT\r\n'.encode())  # 字符串转字节

# 发送十六进制数据
ser.write(bytes([0x01, 0x03, 0x00, 0x00, 0x00, 0x01]))

我个人习惯用 encode(),因为代码可读性更好。但如果你跟仪器通信,很多仪器用的是Modbus协议,那就得用十六进制方式发送。

2.3.2 读数据

读数据有几种方式,看你的场景:

# 读取固定字节数
data = ser.read(10)           # 读10个字节

# 读取一行(直到换行符)
line = ser.readline()         # 读到 \n 为止

# 读取所有可用数据
data = ser.read_all()         # 非阻塞,读缓冲区所有数据

# 循环读取
while True:
    if ser.in_waiting > 0:    # 检查是否有数据
        data = ser.read(ser.in_waiting)
        print(data.hex())     # 打印十六进制

这里有个关键点——超时设置。如果你设了timeout=1,read()最多等1秒。如果不设超时,read()会一直等下去,程序就卡死了。我刚开始做项目时就犯过这错,程序跑起来没反应,还以为是仪器坏了。

实战经验: 我建议用 readline() 配合超时,这是最稳妥的方式。大多数仪器返回的数据都以换行符结尾,一行一行读,逻辑清晰,不容易丢数据。

2.3.3 完整示例

来个完整的读写示例,模拟跟一台分析仪器通信:

import serial
import time

# 配置串口
ser = serial.Serial(
    port='COM3',
    baudrate=9600,
    bytesize=8,
    parity='N',
    stopbits=1,
    timeout=1
)

# 发送查询命令
command = b'READ_DATA\r\n'
ser.write(command)
print(f"发送: {command}")

# 等待仪器响应
time.sleep(0.5)

# 读取响应
response = ser.readline()
print(f"收到: {response.decode().strip()}")

# 关闭串口
ser.close()

你想想看,这个流程是不是很清晰?发送命令、等待、读取、关闭。实际项目中,我会把这段逻辑封装成函数,方便反复调用。

2.4 常见问题与避坑指南

做串口通信,问题千奇百怪。我挑几个最常见的说说:

  • 端口被占用: 程序崩溃后没关串口,下次运行就报错。解决办法:用 try...finally 确保关闭,或者重启电脑。
  • 数据乱码: 多半是波特率或数据位不匹配。我曾经遇到过,仪器用7位数据位,我设成8位,结果读出来的数据全是错的。
  • 读取超时: 仪器没发数据,或者发的格式不对。建议先用串口调试助手(比如Putty)手动发命令,确认仪器正常再写代码。
  • 粘包问题: 仪器连续发多条数据,readline()可能一次读到多条。解决办法:用固定帧头帧尾来分包,或者加时间戳判断。
重要提醒: 串口通信没有「握手」机制,你发命令后仪器可能没准备好。我建议每次写操作后加个 time.sleep(0.1),给仪器一点反应时间。别问我怎么知道的——都是血泪教训。

2.5 小结

好了,串口通信这块就讲这么多。总结一下:

  • 安装pySerial用pip,简单直接
  • 配置参数要跟仪器完全一致,尤其是波特率
  • 写数据用write(),读数据用readline()最稳
  • 别忘了设超时,别忘了关串口

下一章咱们讲数据记录——怎么把串口收到的数据存到文件里。嗯,那又是另一番天地了。