Python使用pyserial实现串口通信基础教程

阿麦
阿麦
发布时间:2026-10-22| 6人浏览过
原创

一、串口通信基本链路与核心概念

串口(Serial Port)通常指串行通信接口。现代计算机大多不再配备原生的DB9物理串口,而是通过USB转串口芯片(如CH340、CP2102、FT232)与外部设备的UART引脚相连。

在软件层面,操作系统会把接入的USB串口设备识别为一个虚拟串口节点。Python应用程序通过pyserial与系统底层串口驱动对接,向其输入或读取连续的二进制字节流(bytes)。硬件连接中,通常要求发送端(TX)连接接收端(RX),接收端(RX)连接发送端(TX),并且两端必须共地(GND连接GND)以维持统一电平基准。

Python使用pyserial实现串口通信基础教程
图 1:Python主机与下位机串口通信硬件与软件分层链路

二、环境安装与可用端口探测

在编写串口读写逻辑前,首先需要安装第三方库并确定目标设备连接在哪一个端口。

1. 安装正确安装包

在终端使用pip进行安装。请注意,包名必须是pyserial,不能误装为serial。如果以前误装了serial,应先卸载它,否则会引起命名空间覆盖和方法缺失报错。

安装命令
pip install pyserial
避坑提示:区分 pyserial 与 serial 模块

PyPI上存在名为serial的历史弃用包。代码中虽然书写import serial,但安装命令必须严格执行pip install pyserial。若出现AttributeError: module 'serial' has no attribute 'Serial',说明安装了错误的包。

2. 自动枚举系统可用端口

硬编码端口名(例如直接写死"COM3")在不同电脑或设备重新插拔后容易失效。借助serial.tools.list_ports模块,可以遍历当前系统在线的所有串口设备及其硬件ID信息。

示例代码:扫描并打印当前系统所有串口
import serial.tools.list_ports

def scan_serial_ports():
    ports = serial.tools.list_ports.comports()
    if not ports:
        print("未检测到可用串口设备,请检查数据线连接与驱动状态。")
        return []

    available_ports = []
    print(f"检测到 {len(ports)} 个串口设备:")
    for port, desc, hwid in sorted(ports):
        print(f"  - 端口: {port}")
        print(f"    描述: {desc}")
        print(f"    硬件ID: {hwid}")
        available_ports.append(port)
    return available_ports

if __name__ == "__main__":
    scan_serial_ports()
终端运行示例输出
检测到 2 个串口设备:
  - 端口: COM3
    描述: USB-SERIAL CH340 (COM3)
    硬件ID: USB VID:PID=1A86:7523 SER=5 LOCATION=1-1
  - 端口: COM5
    描述: Silicon Labs CP210x USB to UART Bridge (COM5)
    硬件ID: USB VID:PID=10C4:EA60 SER=0001 LOCATION=1-2

三、串口通信标准流程

使用pyserial进行开发时,应遵循固定的操作生命周期:发现端口、参数配置、建立连接、交互收发、安全关闭。

Python使用pyserial实现串口通信基础教程
图 2:使用pyserial进行通信的标准操作与生命周期流程

四、核心参数配置与帧结构

串口通信属于异步串行通信,收发双方没有共享的时钟信号线。为了保证双方正确还原每一个位(Bit),收发双方必须预先约定完全相同的通信波特率与数据帧格式。

Python使用pyserial实现串口通信基础教程
图 3:UART串行通信数据帧结构与pyserial关键参数对应关系

在上述参数中,timeout(读超时)是最容易被忽略却至关重要的参数:

  • timeout=None(默认值):完全阻塞模式。调用read()或readline()时,如果下位机没有发送足够的数据或换行符,程序将永久卡死在当前调用处。
  • timeout=0:非阻塞模式。无论缓冲区是否有数据,调用读操作立即返回,缓冲区为空则返回b''。
  • timeout=1.0(推荐值):设置浮点数秒数。程序最多等待设定的秒数,一旦收到足够数据或超时立即返回,兼顾等待响应与防止程序死锁。

五、数据收发实战与代码实现

串口在物理层传输的是连续的原始字节。在Python 3中,所有发送和接收的数据都必须显式使用bytes类型,而不能直接传递str对象。

1. 基本发送与接收模式

以下是一个使用with上下文管理器的完整示例。使用上下文管理器可以确保无论程序正常运行还是出现异常,串口对象都会被安全关闭。

完整示例:单次请求与响应收发
import time
import serial

PORT = "COM3"       # Windows上形如 "COM3",Linux上形如 "/dev/ttyUSB0"
BAUDRATE = 115200

def send_and_receive():
    try:
        # 打开串口,设置读写超时均为 1.0 秒
        with serial.Serial(port=PORT, baudrate=BAUDRATE, timeout=1.0, write_timeout=1.0) as ser:
            print(f"成功打开串口: {ser.name}")

            # 建议给硬件留出一定的上电初始化时间 (特别是Arduino等带有DTR复位机制的板卡)
            time.sleep(1.5)

            # 清空历史残留缓冲区
            ser.reset_input_buffer()
            ser.reset_output_buffer()

            # 准备发送指令:必须将 str 编码为 bytes,通常追加换行符 \n 作为帧结尾
            command_str = "GET_SENSOR_DATA\n"
            ser.write(command_str.encode("utf-8"))
            print(f"已发送指令: {command_str.strip()}")

            # 接收响应:readline() 持续读取直到遇见 \n 或超时
            response_bytes = ser.readline()
            if response_bytes:
                # 将接收到的原始 bytes 解码为可读文本
                response_str = response_bytes.decode("utf-8", errors="replace").strip()
                print(f"收到下位机回复: {response_str}")
            else:
                print("在设定的 1.0 秒超时内未收到回复。")

    except serial.SerialException as err:
        print(f"串口通信错误: {err}")

if __name__ == "__main__":
    send_and_receive()

2. 持续循环监听模式

在许多实际应用(如采集传感器遥测数据)中,下位机会主动以固定频率不断向主机推送数据。主机端通常采用循环监听方式读取:

持续监听接收循环
import serial

def continuous_monitor(port="COM3", baudrate=115200):
    with serial.Serial(port=port, baudrate=baudrate, timeout=0.5) as ser:
        print(f"开始监听 {port},按 Ctrl+C 退出...")
        ser.reset_input_buffer()

        while True:
            try:
                # in_waiting 属性表示当前硬件接收缓冲区中已到达的未读字节数
                if ser.in_waiting > 0:
                    raw_data = ser.readline()
                    text = raw_data.decode("utf-8", errors="ignore").strip()
                    if text:
                        print(f"[RX] {text}")
            except KeyboardInterrupt:
                print("\n用户手动中断,退出监听。")
                break

六、常见问题排查与工程实践总结

串口编程涉及硬件与操作系统的交互,容易受到驱动、权限、硬件引脚等多方面因素影响。掌握以下典型问题的排查路径,可大幅减少调试成本。

现象 / 报错信息 根本原因 解决方法
PermissionError: [Errno 13] Permission denied (Linux) 当前操作系统用户不在 dialout 用户组中,无权读写串口设备节点。 在终端执行 sudo usermod -a -G dialout $USER,注销重新登录生效。
SerialException: could not open port 'COMx': Access is denied (Win) 串口已经被其他软件(如串口调试助手、Arduino IDE串口监视器、另一个Python进程)占用。 关闭冲突软件;在任务管理器中结束占用COM口的残留Python进程。
读取出来全是乱码或 \xff\xfe 波特率不匹配,或通信双方的校验位/数据位设置不一致。 核查下位机代码中的 Serial.begin(...) 数值,确保与Python端配置严格一致。
Python脚本一连接,下位机立刻重启 USB转串口芯片的 DTR/RTS 引脚在串口打开时会拉低,触发单片机硬件复位。 在打开串口后设置 ser.dtr = False,或在硬件复位电容处加跳线旁路。
程序莫名卡住不向下执行 未配置 timeout,或下位机发送的数据末尾缺少换行符导致 readline() 无法结束。 显式传递 timeout=1.0;检查下位机发送是否带上了 \n。
Python使用pyserial实现串口通信基础教程
图 4:串口通信全流程工程实践与排查自检总结图
相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。