Python使用pyserial库通过串口发送数据的基础代码示例

山间有风
山间有风
发布时间:2026-11-09 | 10人浏览过
原创

详细讲解如何使用Python的pyserial库通过串口(Serial Port)发送数据,涵盖环境准备、端口检测、字符串与字节编码转换、基础发送代码示例、关键参数设置及常见问题排错。

Python使用pyserial库通过串口发送数据的基础代码示例
图 0:Python 使用 pyserial 发送数据的核心链路概览图

在嵌入式开发、仪器控制与物联网设备调试过程中,串口(Serial Port/UART)是计算机与外部硬件交换数据最直接的通道。Python 官方标准库中并未包含串口通信模块,通常采用第三方社区标准库 pyserial 来完成硬件通信。利用 pyserial 发送数据的基本流程可以概括为三步:识别并打开指定串口、将文本或十六进制数据编码为字节流(bytes)、通过 write() 方法写入发送缓冲区。本文将通过简洁完整的代码示例,指导读者完成从环境配置、代码编写到数据发送的全流程,并逐一分析核心参数与常见陷阱。

一、操作准备:安装库与确认串口号

在编写代码前,必须确保安装了正确的模块,并获取到目标硬件在操作系统中分配的串口设备名称。

1. 正确安装 pyserial

请在终端(Terminal 或命令提示符)中运行以下命令安装:

pip install pyserial
特别提醒:不要执行 pip install serial。PyPI 上的 serial 是另一个已废弃的早期包,安装后可能导致命名冲突。如果不小心安装了,请先执行 pip uninstall serial,再安装 pyserial。在代码中引入时,统一使用 import serial。

2. 查询当前可用的串口号

不同操作系统对串口的命名格式不同:

  • Windows:通常为 COM1、COM3、COM4 等,可在“设备管理器 ➔ 端口 (COM 和 LPT)”中查看。
  • macOS:通常形如 /dev/cu.usbserial-xxx 或 /dev/cu.usbmodemxxx。
  • Linux:通常为 /dev/ttyUSB0 或 /dev/ttyACM0。

你也可以直接使用 pyserial 自带的工具函数,用一段简短的脚本列出本机当前所有已识别的串口:

import serial.tools.list_ports

ports = serial.tools.list_ports.comports()
for p in ports:
    print(f"设备名: {p.device} | 描述: {p.description}")

二、基础代码示例:发送字符串与十六进制数据

串口通信本质上只能传输原始二进制字节(bytes),不能直接传递 Python 3 的 Unicode 字符串。因此,发送文本时必须显式调用 .encode() 进行编码;若发送控制指令(如 Modbus RTU 或特定传感器协议),则需构造原始的十六进制字节串。

1. 基础发送代码(推荐使用 with 上下文管理器)

以下代码以波特率 9600 为例,展示了打开串口、发送换行结尾的字符串、发送 Hex 十六进制数据,并安全关闭串口的完整过程:

import serial
import time

# 1. 定义串口配置参数(请根据实际连接的设备修改端口名称与波特率)
PORT_NAME = 'COM3'      # Windows 为 'COMx';Linux/macOS 为 '/dev/ttyUSB0' 等
BAUD_RATE = 9600        # 通信波特率,须与下位机保持完全一致
TIMEOUT = 1.0           # 读超时时间,单位秒

try:
    # 2. 打开串口,推荐使用 with 语句确保使用完毕后串口自动释放
    with serial.Serial(PORT_NAME, BAUD_RATE, timeout=TIMEOUT) as ser:
        print(f"成功打开串口: {ser.name}")

        # 部分单片机(如 Arduino)在串口打开时会发生复位,等待 1.5 秒确保设备就绪
        time.sleep(1.5)

        # 3. 示例一:发送普通文本字符串(必须转换为 bytes 格式)
        message_str = "HELLO_DEVICE\r\n"
        bytes_sent = ser.write(message_str.encode('utf-8'))
        print(f"已发送字符串数据,实际写入字节数: {bytes_sent}")

        # 4. 示例二:发送原始十六进制指令(例如传感器读数帧:01 03 00 00 00 02 C4 0B)
        hex_command = bytes.fromhex("01 03 00 00 00 02 C4 0B")
        ser.write(hex_command)
        print(f"已发送十六进制指令: {hex_command.hex()}")

        # 5. 等待发送缓冲区中的数据完全排空输出
        ser.flush()

except serial.SerialException as e:
    print(f"串口打开或通信失败,错误信息: {e}")
运行结果示例:
成功打开串口: COM3
已发送字符串数据,实际写入字节数: 14
已发送十六进制指令: 010300000002c40b
Python使用pyserial库通过串口发送数据的基础代码示例
图 1:Python 通过 pyserial 发送数据的标准操作步骤流

三、关键参数详解与底层数据流转

串口是点对点的异步通信方式,收发双方没有统一的时钟线,因此双方必须在物理层事先约定相同的波特率、数据位、校验位和停止位配置(通常简称为串口参数格式,如“9600, 8, N, 1”)。

1. 关键初始化参数对照

参数名称 默认值 说明与常用选值
port None 串口设备名称,例如 'COM3'、'/dev/ttyUSB0'。
baudrate 9600 通信波特率。常用 9600、19200、38400、57600、115200。数值代表每秒传输的符号位(bit)数。
bytesize serial.EIGHTBITS (8) 数据位长度,通常为 8 位,也可选 FIVEBITS、SIXBITS、SEVENBITS。
parity serial.PARITY_NONE ('N') 奇偶校验位,通常无校验。可选 PARITY_EVEN(偶校验)、PARITY_ODD(奇校验)。
stopbits serial.STOPBITS_ONE (1) 停止位长度,常用 1 位,也可选 STOPBITS_TWO (2)。
timeout None (阻塞) 读操作的超时时间(秒)。设为 0 表示非阻塞立即返回;设为具体数值(如 1.0)表示最多等待 1 秒。
write_timeout None (阻塞) 写操作的超时时间(秒)。如果串口发送缓冲区已满且无法排出,超时后将抛出异常。
Python使用pyserial库通过串口发送数据的基础代码示例
图 2:串口通信协议帧结构与 pyserial 参数对应图

2. 从 Python 对象到硬件引脚的数据流转

当在 Python 中执行 ser.write(data) 时,并不是瞬间直接作用在硬件引脚上的。数据需要先经过操作系统的内核驱动,再复制到 USB 转串口芯片(或主板 UART 控制器)的硬件缓冲区中,最后按照指定的波特率时钟逐位转换成 TX 引脚的高低电平。

Python使用pyserial库通过串口发送数据的基础代码示例
图 3:数据从 Python 内存到物理硬件引脚的流转过程图

四、发送并接收验证:双向交互完整示例

在实际工程中,发送指令后往往需要验证下位机的应答(即“发-收”闭环模式)。以下是一个通用的发送请求并等待响应的完整工程代码模板:

import serial
import time

def send_command_and_get_reply(port, baudrate, cmd, timeout=2.0):
    """
    向串口发送一条字符串指令,并读取单行回复。
    """
    try:
        with serial.Serial(port=port, baudrate=baudrate, timeout=timeout) as ser:
            # 避免 Arduino / ESP32 复位初期丢失字符
            time.sleep(1.5)

            # 规范:指令末尾附加回车换行符,便于下位机检测结束
            full_cmd = cmd.strip() + "\r\n"
            ser.write(full_cmd.encode('utf-8'))
            ser.flush()
            print(f"已发送: {full_cmd.strip()}")

            # 读取下位机回复(readline 会等待直到读到 \n 或超时)
            response_bytes = ser.readline()
            
            if response_bytes:
                response_str = response_bytes.decode('utf-8', errors='replace').strip()
                print(f"下位机回复: {response_str}")
                return response_str
            else:
                print("等待回复超时,未收到下位机响应。")
                return None

    except serial.SerialException as err:
        print(f"通信异常: {err}")
        return None

# 示例调用
if __name__ == "__main__":
    send_command_and_get_reply('COM3', 9600, 'PING')

五、四大核心注意事项与排错矩阵

很多初学者在编写串口发送代码时,经常遇到“代码执行成功但硬件没有任何反应”或“报错无法打开端口”的问题。以下为四个最关键的踩坑点及解决方法。

Python使用pyserial库通过串口发送数据的基础代码示例
图 4:串口发送常见异常与排查修复矩阵
  1. 串口打开与 Arduino 自动复位现象:许多基于 USB 串口芯片的微控制器(如 Arduino Uno/Nano)将 DTR(数据终端就绪)引脚连接到芯片的 RESET 线上。当 Python 打开串口时,驱动通常会默认拉低 DTR,触发单片机重启(Bootloader 启动运行通常需要 1~2 秒)。如果代码在 serial.Serial() 之后立刻发送数据,微控制器此时还在执行启动引导程序,无法接收用户数据,从而导致数据丢失。因此,打开串口后加入 time.sleep(1.5) 是一种常见且稳妥的习惯。
  2. 显式调用 ser.flush() 保证发送完整:ser.write() 仅仅负责把数据从 Python 进程拷贝进操作系统的发送缓冲区,并立即返回写入的字节数。如果程序在发送后立刻退出或关闭串口,操作系统可能尚未将缓冲区内容完全串行移出。调用 ser.flush() 会阻塞等待直到所有缓冲区数据物理发送完毕。
  3. 硬件连线常识(交叉连接):若使用两根线进行串口直连,计算机的 TXD(发送引脚)必须连接目标设备的 RXD(接收引脚),计算机的 RXD 连接设备的 TXD,并且双方的 GND(地线)必须共地,否则无法形成电平回路。
  4. 操作权限问题(Linux 与 macOS):在 Linux 系统下,普通用户通常不在 dialout 权限组,尝试打开 /dev/ttyUSB0 会触发 PermissionError。可通过终端执行 sudo usermod -a -G dialout $USER 并重新登录解决。

六、总结

掌握 Python 通过 pyserial 发送数据的核心在于理清三个环节:端口名与波特率的正确匹配、Python 字符串与二进制 bytes 的严格转换,以及合理的生命周期管理(推荐使用 with 语句配合 flush 确保数据发毕与句柄释放)。在此基础代码之上,你可以进一步拓展数据包校验(如 CRC16)、帧头帧尾封装以及异步多线程收发架构,构建稳定高效的上位机通信系统。

相关专题:

本站声明

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

Python 相关产品和应用

全部下载

专题列表