Python串口通信实战:PySerial库应用与物联网开发
1. Python串口通信基础与核心概念
串口通信作为嵌入式开发和硬件交互的基础手段,在物联网设备调试、工业控制等领域有着广泛应用。Python凭借其简洁语法和丰富生态,成为快速实现串口通信的理想选择。与C/C++等传统语言相比,Python的串口编程可以节省大量底层代码,让开发者更专注于业务逻辑实现。
PySerial是当前Python生态中最成熟的串口通信库,支持Windows、Linux和macOS三大平台。其核心优势在于:
- 统一的API设计屏蔽了不同操作系统的底层差异
- 支持同步和异步I/O操作模式
- 提供完善的异常处理机制
- 兼容Python 2.7和Python 3.x系列
典型的串口通信参数包括:
- 波特率(Baud Rate):9600、115200等
- 数据位(Data Bits):5、6、7、8
- 停止位(Stop Bits):1、1.5、2
- 校验位(Parity):None、Even、Odd、Mark、Space
注意:实际使用中必须确保通信双方的参数完全一致,否则会出现乱码或通信失败。工业设备常用9600或115200波特率,8位数据位,无校验位,1位停止位(简称8N1配置)。
2. 开发环境搭建与PySerial安装
2.1 Python环境准备
推荐使用Python 3.7+版本,可通过以下命令检查当前Python环境:
python --version pip --version对于开发工具的选择:
- VSCode + Python插件:轻量级,适合快速开发
- PyCharm Professional:提供完整的专业开发功能
- Jupyter Notebook:适合交互式调试和数据分析
2.2 PySerial安装与验证
通过pip安装最新版PySerial:
pip install pyserial安装完成后可执行以下验证测试:
import serial print(serial.__version__) # 应输出如'3.5'等版本号 print(serial.tools.list_ports.comports()) # 列出可用串口常见安装问题解决方案:
- 权限问题:Linux/macOS下需要sudo或添加用户到dialout组
- 驱动缺失:某些USB转串口设备需要单独安装驱动(如CH340、CP2102等)
- 虚拟环境:确保在激活的虚拟环境中执行安装
3. 串口通信基础实现
3.1 串口对象初始化
基本串口初始化示例:
import serial ser = serial.Serial( port='/dev/ttyUSB0', # Windows下为'COM3'等形式 baudrate=115200, bytesize=serial.EIGHTBITS, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, timeout=1 # 读超时设置(秒) ) print(f"串口状态: {'开启' if ser.is_open else '关闭'}") print(f"串口配置: {ser.get_settings()}")关键参数说明:
- timeout:读操作超时时间,None表示阻塞模式,0表示非阻塞
- write_timeout:写操作超时时间
- xonxoff:软件流控制开关
- rtscts:硬件(RTS/CTS)流控制开关
- dsrdtr:硬件(DSR/DTR)流控制开关
3.2 基本读写操作
数据发送示例:
data_to_send = "Hello UART\r\n" # 注意添加换行符 bytes_sent = ser.write(data_to_send.encode('utf-8')) print(f"已发送 {bytes_sent} 字节")数据接收的三种模式:
- 按指定字节数读取:
received = ser.read(10) # 读取10个字节 print(received.decode('utf-8'))- 读取直到遇到特定字符:
received = ser.read_until(b'\n') # 读取直到换行符 print(received.decode('utf-8').strip())- 读取缓冲区所有数据:
received = ser.read_all() # 非阻塞读取当前所有可用数据 print(received.decode('utf-8'))3.3 资源释放与异常处理
正确的资源管理方式:
try: ser = serial.Serial('COM3', 115200) # 通信操作... except serial.SerialException as e: print(f"串口错误: {e}") finally: if 'ser' in locals() and ser.is_open: ser.close() print("串口已安全关闭")常见异常类型:
- SerialException:基础异常类
- SerialTimeoutException:超时异常
- PortNotOpenError:端口未打开错误
4. 高级应用与实战技巧
4.1 二进制数据处理
实际项目中经常需要处理二进制协议,示例:
# 发送二进制数据 frame = bytes([0xAA, 0xBB, 0x01, 0x02, 0xCC]) ser.write(frame) # 接收二进制数据并解析 while True: header = ser.read(1) if header == b'\xAA': payload = ser.read(4) checksum = ser.read(1) # 解析处理逻辑... break4.2 多线程串口通信
对于需要同时进行读写操作的场景:
import threading def read_from_port(ser): while True: if ser.in_waiting > 0: data = ser.read_all() print(f"收到: {data.decode('utf-8')}") ser = serial.Serial('COM3', 115200) thread = threading.Thread(target=read_from_port, args=(ser,)) thread.daemon = True thread.start() # 主线程可以继续执行其他任务或发送数据 while True: message = input("输入要发送的消息: ") ser.write((message + '\r\n').encode('utf-8'))4.3 与常见硬件设备的交互
与STM32通信示例:
# STM32通常使用简单的文本协议 def send_stm32_command(ser, cmd, param=None): command_str = f"${cmd} {param}\r\n" if param else f"${cmd}\r\n" ser.write(command_str.encode('ascii')) return ser.read_until(b'\r\n').decode('ascii').strip() response = send_stm32_command(ser, "LED", "ON") print(f"设备响应: {response}")与树莓派通信的特殊考虑:
- 需要禁用控制台功能:
sudo raspi-config→ Interface Options → Serial - 可能需要修改/boot/config.txt文件
- GPIO引脚电压电平匹配(3.3V vs 5V)
5. 调试技巧与性能优化
5.1 常用调试工具链
硬件工具:
- USB转TTL模块(如CH340、CP2102)
- 逻辑分析仪(Saleae等)
- 示波器(检查信号质量)
软件工具:
- 串口调试助手(Windows)
- screen/minicom(Linux/macOS)
- Wireshark(高级协议分析)
5.2 性能优化策略
- 缓冲区设置:
ser = serial.Serial('COM3', 115200, write_buffer_size=1024, # 写缓冲区 read_buffer_size=2048) # 读缓冲区- 批量写入减少IO操作:
# 不推荐:多次小数据写入 for i in range(100): ser.write(b'A') # 推荐:单次批量写入 ser.write(b'A'*100)- 选择性读取:
while True: if ser.in_waiting >= expected_packet_size: data = ser.read(expected_packet_size) process_data(data)5.3 常见问题排查指南
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 无法打开端口 | 端口被占用/不存在 | 检查设备管理器,重启设备 |
| 接收乱码 | 波特率不匹配 | 确认双方波特率一致 |
| 数据丢失 | 流控未启用 | 启用硬件(RTS/CTS)流控 |
| 通信不稳定 | 线路干扰 | 检查接线,缩短线缆长度 |
| 部分数据缺失 | 超时设置过短 | 适当增加timeout参数 |
6. 项目实战:温度监控系统
6.1 系统架构设计
实现一个通过串口采集温度数据并可视化的系统:
- 硬件端(如STM32):采集温度→通过串口发送
- Python端:接收数据→存储→可视化
6.2 完整实现代码
import serial import time import matplotlib.pyplot as plt from collections import deque # 初始化 ser = serial.Serial('COM3', 115200) max_points = 100 temp_history = deque(maxlen=max_points) timestamps = deque(maxlen=max_points) # 实时绘图设置 plt.ion() fig, ax = plt.subplots() line, = ax.plot([], []) ax.set_ylim(0, 50) # 假设温度范围0-50℃ def update_plot(): line.set_data(timestamps, temp_history) ax.relim() ax.autoscale_view() fig.canvas.draw() fig.canvas.flush_events() # 主循环 start_time = time.time() try: while True: if ser.in_waiting: data = ser.read_until(b'\n').decode('utf-8').strip() try: temp = float(data) temp_history.append(temp) timestamps.append(time.time() - start_time) update_plot() except ValueError: print(f"无效数据: {data}") time.sleep(0.1) finally: ser.close() plt.ioff() plt.show()6.3 扩展功能建议
- 数据持久化:添加SQLite数据库存储
- 报警功能:超过阈值触发邮件通知
- 远程监控:集成WebSocket实现网页展示
- 协议加密:增加简单的数据加密传输
- 多设备支持:扩展为多串口监控系统
在实际部署中发现,USB转串口模块在长时间工作时可能出现连接不稳定的情况。我的解决方案是:
- 使用优质USB转串口模块(如FTDI芯片)
- 在代码中添加自动重连机制
- 定期发送心跳包检测连接状态
- 对关键数据添加校验和重传机制
