Python pyserial库安装与基础串口读写教程

沈亦白
沈亦白
发布时间:2026-10-10 | 2人浏览过
原创

在嵌入式开发、工业设备调试以及单片机(如 STM32、Arduino、ESP32)交互项目中,计算机通过串口(UART/RS-232/RS-485)与外部硬件交换数据是极其普遍的需求。在 Python 生态中,pyserial 是用于串口访问的事实标准库。本文将围绕该库展开,完整讲解其安装要点、端口发现、配置连接以及底层二进制数据的读写处理流程,帮助读者建立稳定、规范的串口读写逻辑。

一、pyserial 库的正确安装与环境确认

在 Python 中使用串口前,第一步是安装驱动库。这里存在一个初学者极其容易踩中的安装陷阱:在 PyPI 仓库中,存在名为 serial 与 pyserial 的两个不同库。如果直接执行 pip install serial,安装的可能是一个长期未维护或功能不相关的包,导致代码中引入模块时发生属性缺失或运行时异常。

核心注意点:安装时包名为 pyserial,而在 Python 脚本中导入时模块名为 import serial。这一差异是由该库的历史命名规范决定的。
# 正确的安装命令(必须指定 pyserial)
pip install pyserial

# 如果在多 Python 环境下,建议使用模块方式调用 pip
python -m pip install pyserial

安装完成后,建议在终端中快速验证该库的版本信息,以确认环境就绪:

python -c "import serial; print(serial.__version__)"
# 预期输出类似:3.5
Python pyserial库安装与基础串口读写教程
图 1:pyserial 与 serial 包名辨析及安装检查流程

二、查找可用串口与设备识别

在编写串口读写逻辑前,必须明确当前主机连接的串口物理名称(Port)。不同操作系统的串口命名体系有所差异:

  • Windows 系统:通常命名为 COM1、COM3、COM8 等。
  • Linux 系统:USB 转串口设备一般显示为 /dev/ttyUSB0、/dev/ttyUSB1 或 /dev/ttyACM0。
  • macOS 系统:通常显示为 /dev/cu.usbserial-xxx 或 /dev/cu.usbmodemxxx。

pyserial 提供了方便的工具模块 serial.tools.list_ports,可以在不依赖外部设备管理器的情况下,直接用 Python 枚举当前系统中所有在线的串口设备:

import serial.tools.list_ports

def scan_ports():
    ports = serial.tools.list_ports.comports()
    if not ports:
        print("未检测到任何可用串口,请检查硬件连接或驱动。")
        return []
    
    print(f"检测到 {len(ports)} 个可用串口:")
    for port in ports:
        print(f"端口: {port.device}")
        print(f"  描述: {port.description}")
        print(f"  硬件ID: {port.hwid}")
    return ports

if __name__ == "__main__":
    scan_ports()

通过该脚本,可以准确获取目标硬件的端口字符串,避免硬编码端口导致程序在更换 USB 插槽或切换设备时发生 SerialException 无法连接。

三、串口连接基础配置与参数解析

串口通信是异步串行通信,通信双方必须约定完全相同的电气与协议参数,才能保证数据位被准确还原。核心参数包括端口号、波特率、数据位、校验位、停止位以及超时时间。

参数名 参数含义 常用推荐值 作用说明
port 串口名称 "COM3" 或 "/dev/ttyUSB0" 指定要打开的物理或虚拟串口路径。
baudrate 波特率(bps) 9600, 115200 每秒传输的符号数,通信双方必须保持严格一致。
bytesize 数据位 serial.EIGHTBITS (8) 每个数据帧的数据字节长度,默认通常为 8 位。
parity 校验位 serial.PARITY_NONE ('N') 用于检错,常用无校验 (N)、奇校验 (O) 或偶校验 (E)。
stopbits 停止位 serial.STOPBITS_ONE (1) 标识数据帧传输结束,常用 1 位或 2 位。
timeout 读超时(秒) 1.0(根据场景调整) 防止读取数据时线程永久阻塞,非常关键的保护参数。
Python pyserial库安装与基础串口读写教程
图 2:上位机与下位机参数协商及数据收发物理流

四、基础串口数据读写操作实战

1. 安全开启与上下文管理(with 语句)

串口属于系统独占型硬件资源。一旦某个进程打开了端口,其它程序将无法访问。如果在程序崩溃或异常退出时未执行 close(),端口可能会被系统占用锁死,导致后续必须拔插 USB 甚至重启电脑才能释放。因此,推荐使用 Python 的 with 上下文管理器来操作串口,确保退出作用域时自动释放资源:

import serial
import time

port_name = "COM3"  # Windows示例,Linux可替换为 "/dev/ttyUSB0"
baud_rate = 115200

# 使用上下文管理器,离开代码块时自动执行 ser.close()
with serial.Serial(port=port_name, baudrate=baud_rate, timeout=1.0) as ser:
    if ser.is_open:
        print(f"成功连接至串口: {ser.name}")
        # 部分单片机(如 Arduino)在打开串口时会触发自动复位,建议预留 1~2 秒就绪延时
        time.sleep(1.5)

2. 发送数据:必须显式使用 bytes 类型

在 Python 3 中,字符串(str)是 Unicode 格式,而串口物理线路上传输的是纯粹的二进制字节流(bytes)。因此,调用 ser.write() 时必须传入字节类型,不能直接传入字符串:

# 方式一:直接传递字节字面量(仅限 ASCII 字符)
ser.write(b"HELLO_MCU\n")

# 方式二:字符串显式编码(通用推荐,支持中文及复杂协议字符)
msg = "设置温度:25.5\n"
ser.write(msg.encode('utf-8'))

# 立即刷新硬件/系统缓冲区,确保数据即时发出
ser.flush()
提示:ser.flush() 的作用是等待所有输出缓冲区的数据全部写出到物理层,避免数据滞留在底层缓冲区未及时发送。

3. 读取数据:三种常用模式与超时控制

根据通信协议的不同,pyserial 提供了不同的读取方法:

  • 按行读取 (readline()):最适用于以换行符(\n 或 \r\n)结尾的 ASCII 文本协议。读取直到遇到换行符或 timeout 超时到达。
  • 指定字节数读取 (read(size)):最适用于固定长度帧的二进制协议(例如 Modbus RTU)。读取指定数量的字节,若超时前未收齐则返回已读到的部分。
  • 等待全部缓冲区 (read_all()):一次性读取当前操作系统接收缓冲区中已有的所有字节,不阻塞等待。
# 示例:以文本行方式读取响应
if ser.in_waiting > 0:  # 检查当前缓冲区是否有待接收字节
    raw_data = ser.readline()
    if raw_data:
        try:
            # 将接收到的 bytes 解码为可读字符串,并剔除首尾空白符
            text = raw_data.decode('utf-8').strip()
            print(f"收到设备响应: {text}")
        except UnicodeDecodeError:
            print(f"无法以 UTF-8 解码,原始十六进制: {raw_data.hex()}")
Python pyserial库安装与基础串口读写教程
图 3:串口读写生命周期时序与处理步骤

五、常见问题排查与关键避坑指南

1. SerialException: could not open port(端口无法打开/拒绝访问)

该错误通常由两个原因引起:

  • 端口被占用:串口调试助手、其他运行中的脚本或终端仍在占用该 COM 口。请关闭其他调试软件后再运行脚本。
  • Linux/macOS 权限问题:在 Linux 下,串口设备默认归属于 dialout 组。普通用户若无权限读写 /dev/ttyUSB0,可通过以下命令将当前用户加入串口组后注销重新登录:
    sudo usermod -aG dialout $USER

2. 读出乱码或乱七八糟的十六进制字节

绝大多数乱码问题都源于波特率不匹配。例如下位机配置为 9600 bps,而 Python 脚本中配置了 115200 bps,采样时钟偏差会导致数据位识别错误。遇到乱码时,首先核对双方的波特率、数据位和停止位设置。

3. 打开串口后单片机反复复位

许多开发板(如 Arduino Uno、Nano、NodeMCU)将 USB 芯片的 DTR 或 RTS 引脚连接到了单片机的复位(RESET)引脚,以便实现程序下载时的自动复位。在 pyserial 打开串口时,默认可能会拉低 DTR 引脚引发复位。如果无需自动复位,可以在初始化时明确禁用该信号:

ser = serial.Serial()
ser.port = "COM3"
ser.baudrate = 115200
ser.dtr = False  # 禁用 DTR 自动拉低
ser.rts = False  # 禁用 RTS 自动拉低
ser.open()
Python pyserial库安装与基础串口读写教程
图 4:pyserial 核心开发规范与常见问题排查体系全景

六、总结与完整通信代码范式

掌握 pyserial 的核心在于理解异步串行通信的物理特性:严格匹配波特率、将字符串转换为字节、设置读超时以防死锁,并在退出时可靠释放端口资源。以下提供一份可直接运行的完整通信模板,集成了端口发现、超时防护、异常处理与数据读写功能:

import serial
import serial.tools.list_ports
import time

def run_serial_demo(target_port="COM3", baud=115200):
    # 1. 检查端口是否存在
    available = [p.device for p in serial.tools.list_ports.comports()]
    if target_port not in available:
        print(f"错误: 端口 {target_port} 未找到。当前可用端口: {available}")
        return

    # 2. 安全建立连接
    try:
        with serial.Serial(port=target_port, baudrate=baud, timeout=1.0) as ser:
            print(f"成功打开串口: {ser.name}")
            
            # 延时等待设备启动稳定(针对部分单片机)
            time.sleep(1.0)
            
            # 清除旧的未读取缓存
            ser.reset_input_buffer()
            
            # 3. 发送数据 (注意必须编码为 bytes)
            send_text = "PING\n"
            ser.write(send_text.encode('utf-8'))
            ser.flush()
            print(f"发送成功: {send_text.strip()}")
            
            # 4. 阻塞等待或读取响应(受 timeout 保护)
            response = ser.readline()
            if response:
                try:
                    decoded = response.decode('utf-8').strip()
                    print(f"接收成功: {decoded}")
                except UnicodeDecodeError:
                    print(f"接收到十六进制原始数据: {response.hex()}")
            else:
                print("在设定的超时时间内未收到设备响应。")
                
    except serial.SerialException as e:
        print(f"串口通信发生异常: {e}")

if __name__ == "__main__":
    # 执行示例
    run_serial_demo()

通过遵循上述规范,开发者可以在各类物联网、工控采集及单片机控制项目中构建出稳定、容错性高的串口通信交互逻辑。

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表