杰瑞科技汇

Python Serial API如何实现串口通信?

Python Serial API完全指南:从零开始掌握串口通信,轻松实现设备交互

详解pyserial库使用、高级技巧与实战案例,解决你的串口编程难题)**

Python Serial API如何实现串口通信?-图1
(图片来源网络,侵删)

(Meta Description)

想用Python控制串口设备?本文手把手教你使用Python Serial API(pyserial库),从环境搭建、基础配置到高级参数设置、多线程通信,再到完整实战案例,一文带你精通串口编程,解决硬件交互难题,提升项目开发效率。


引言:为什么Python Serial API是硬件开发者的“利器”?

在物联网、嵌入式系统开发、工业自动化以及各类硬件原型验证项目中,计算机与外部设备(如单片机、传感器、GPS模块、调制解调器等)之间的通信是至关重要的一环,在这些通信方式中,串口通信因其协议简单、连接方便、稳定性高,至今仍被广泛应用。

而Python,凭借其简洁的语法、强大的生态和丰富的库支持,成为了连接软件世界与硬件世界的绝佳桥梁。pyserial 库(即我们常说的Python Serial API)是Python进行串口通信事实上的标准,它封装了复杂的底层操作,让我们只需几行代码就能实现与串口设备的双向数据交换。

无论你是刚入门的爱好者,还是经验丰富的工程师,本文都将为你提供一份详尽的、可操作的pyserial使用手册,助你攻克串口编程的难关。


准备工作:安装与配置你的Python Serial环境

在开始编码之前,我们需要确保开发环境已准备就绪。

1 安装pyserial库

pyserial库可以通过Python的包管理器pip轻松安装,打开你的终端或命令提示符,运行以下命令:

pip install pyserial

为了验证安装是否成功,可以在Python交互式环境中尝试导入该库:

import serial
print(serial.__version__)  # 打印版本号,若无报错则安装成功

2 硬件准备:找到你的COM端口

你的设备需要通过串口线(或USB转串口模块)连接到计算机,在编写代码前,你必须知道设备在计算机上被识别为哪个COM端口

  • Windows系统
    1. 打开“设备管理器”。
    2. 展开“端口 (COM和LPT)”。
    3. 查看你的设备对应的COM端口号(如 COM3, COM4 等)。
  • Linux/macOS系统
    • 通常设备会被识别为 /dev/ttyUSB0, /dev/ttyACM0/dev/ttyS0 等文件名,你可以使用 ls /dev/tty* 命令来列出所有串口设备。

提示:记下这个端口号,它将在我们的代码中频繁使用。


核心入门:5分钟掌握Python Serial API基础用法

pyserial的使用非常直观,核心流程可以概括为:打开串口 -> 配置参数 -> 读写数据 -> 关闭串口

1 打开与关闭串口

serial.Serial() 是创建串口对象的核心函数。

import serial
# --- 打开串口 ---
# 假设我们的设备在Windows上是COM3,在Linux上是/dev/ttyUSB0
ser = serial.Serial(
    port='COM3',          # 串口号
    baudrate=9600,        # 波特率,必须与设备端一致
    bytesize=serial.EIGHTBITS,   # 数据位
    parity=serial.PARITY_NONE,   # 校验位
    stopbits=serial.STOPBITS_ONE, # 停止位
    timeout=1             # 超时时间(秒)
)
# 检查串口是否成功打开
if ser.is_open:
    print(f"串口 {ser.name} 打开成功!")
    print(f"当前设置: {ser}")
else:
    print("串口打开失败!")
# --- 关闭串口 ---
ser.close()
if not ser.is_open:
    print("串口已关闭。")

关键参数解析

  • port: 串口号。
  • baudrate: 波特率(如 9600, 115200),通信双方必须完全一致。
  • timeout: 超时设置,这是非常重要的一个参数,它决定了read()方法等待数据的最长时间。
    • timeout=0:立即返回,不阻塞。
    • timeout=1:等待最多1秒,1秒后若无数据则返回已读取的数据(若无则返回空)。
    • timeout=None:无限期等待,直到有数据到达。

2 写数据到串口

使用write()方法向设备发送数据。注意write()方法接收的是字节流,因此字符串需要先编码。

import serial
ser = serial.Serial('COM3', 9600, timeout=1)
ser.open()
if ser.is_open:
    # 发送字符串,需要编码为字节
    message_to_send = "Hello, Device!"
    ser.write(message_to_send.encode('utf-8'))
    print(f"已发送: {message_to_send}")
    # 也可以直接发送字节
    # ser.write(b'\x01\x02\x03') # 发送十六进制数据
ser.close()

3 从串口读取数据

使用read()方法从设备读取数据。

import serial
ser = serial.Serial('COM3', 9600, timeout=1)
ser.open()
if ser.is_open:
    # read(size=1): 读取指定大小的字节,如果timeout超时则返回已读取的部分
    data = ser.read(10)  # 读取10个字节
    print(f"读取到数据: {data}")
    # readline(): 读取一行,以换行符(\n)为结束
    # ser.reset_input_buffer() # 清空输入缓冲区
    # line = ser.readline()
    # print(f"读取到一行: {line.decode('utf-8').strip()}")
    # readall(): 读取缓冲区中所有可用数据
    # all_data = ser.readall()
    # print(f"读取到所有数据: {all_data}")
ser.close()

进阶技巧:玩转串口通信的“高级操作”

掌握了基础操作后,让我们来探索一些能让你的程序更健壮、更高效的技巧。

1 正确处理数据:编码与解码

串口通信的本质是字节流,当你发送文本时,需要用.encode()将其转换为字节;接收到字节后,需要用.decode()将其转换回字符串。

# 发送
text = "你好,世界!"
ser.write(text.encode('gbk')) # 使用与设备端一致的编码,如utf-8, gbk等
# 接收
received_bytes = ser.read(10)
text = received_bytes.decode('gbk', errors='ignore') # 使用errors='ignore'解码错误字节

2 智能等待:让程序“听话”

直接使用read()可能会读到不完整的数据,一个常见的模式是:先发送一个指令,然后等待设备的响应数据。

def send_and_receive(command, expected_bytes=10):
    ser.write(command.encode('utf-8'))
    # 等待一小段时间,让设备有时间处理并发送数据
    import time
    time.sleep(0.5) # 延时时间需要根据设备响应速度调整
    response = ser.read(expected_bytes)
    return response
# 使用示例
ser = serial.Serial('COM3', 9600)
ser.open()
response = send_and_receive("GET_DATA")
print(f"设备响应: {response}")
ser.close()

3 多线程串口通信:避免程序卡死

如果你的主程序需要同时处理其他任务(如GUI界面更新),而串口读取又是阻塞的,那么使用多线程是最佳选择。

我们将创建一个专门的线程来负责监听和读取串口数据,主线程则可以自由执行其他操作。

import serial
import threading
import time
class SerialReader:
    def __init__(self, port, baudrate):
        self.ser = serial.Serial(port, baudrate, timeout=1)
        self.running = False
        self.thread = None
    def start(self):
        if not self.ser.is_open:
            self.ser.open()
        self.running = True
        self.thread = threading.Thread(target=self._read_loop)
        self.thread.daemon = True # 设置为守护线程,主线程退出时自动结束
        self.thread.start()
        print("串口监听线程已启动。")
    def stop(self):
        self.running = False
        if self.thread and self.thread.is_alive():
            self.thread.join()
        if self.ser.is_open:
            self.ser.close()
        print("串口监听线程已停止。")
    def _read_loop(self):
        while self.running:
            if self.ser.in_waiting > 0: # 检查缓冲区是否有数据
                data = self.ser.readline()
                print(f"接收到数据: {data.decode('utf-8').strip()}")
            time.sleep(0.1) # 避免CPU空转
# --- 使用示例 ---
if __name__ == '__main__':
    reader = SerialReader('COM3', 9600)
    reader.start()
    try:
        # 主线程可以做其他事情,比如模拟用户输入
        while True:
            cmd = input("输入指令发送 (输入 'exit' 退出): ")
            if cmd.lower() == 'exit':
                break
            reader.ser.write(cmd.encode('utf-8'))
    except KeyboardInterrupt:
        pass
    finally:
        reader.stop()

实战案例:用Python和Arduino实现一个简易温度监控

让我们通过一个完整的案例,将所学知识融会贯通。

目标:Arduino连接一个温度传感器(如LM35),通过串口每秒发送一次温度数据,Python程序读取这些数据,并在终端实时显示。

1 Arduino端代码

将以下代码上传到你的Arduino开发板:

void setup() {
  // 初始化串口,波特率设置为9600
  Serial.begin(9600);
}
void loop() {
  // 读取模拟引脚A0的值 (0-1023)
  int sensorValue = analogRead(A0);
  // 将模拟值转换为电压值 (假设是5V供电)
  float voltage = sensorValue * (5.0 / 1023.0);
  // 将电压值转换为摄氏温度 (LM35: 10mV/°C)
  float temperatureC = voltage * 100.0;
  // 将温度数据通过串口发送
  // 为了方便Python解析,我们使用逗号作为分隔符,并加上换行符
  Serial.println(temperatureC);
  // 等待1秒
  delay(1000);
}

2 Python端代码

创建一个Python脚本(temperature_monitor.py)来接收并显示数据:

import serial
import time
def monitor_temperature(port, baudrate):
    try:
        # 创建串口对象
        ser = serial.Serial(port, baudrate, timeout=1)
        print(f"正在连接 {port}...")
        if ser.is_open:
            print("连接成功!开始监控温度...")
            while True:
                # 检查是否有数据到达
                if ser.in_waiting > 0:
                    # 读取一行数据
                    line = ser.readline()
                    try:
                        # 尝试解码并转换为浮点数
                        temperature = float(line.decode('utf-8').strip())
                        print(f"当前温度: {temperature:.2f} °C")
                    except ValueError:
                        print(f"接收到无效数据: {line}")
                # 短暂休眠,避免CPU占用过高
                time.sleep(0.1)
    except serial.SerialException as e:
        print(f"串口错误: {e}")
    except KeyboardInterrupt:
        print("\n监控已停止。")
    finally:
        if 'ser' in locals() and ser.is_open:
            ser.close()
            print("串口已关闭。")
if __name__ == '__main__':
    # 根据你的系统修改端口号
    PORT = 'COM3'  # Windows
    # PORT = '/dev/ttyUSB0' # Linux
    BAUDRATE = 9600
    monitor_temperature(PORT, BAUDRATE)

运行结果: 当你运行Python脚本后,终端将每秒打印一次从Arduino传来的温度数据,实现了一个实时的温度监控系统。


常见问题与解决方案 (FAQ)

Q1: SerialException: could not open port 'COM3': Permission denied 怎么办? A: 这通常是权限问题,在Linux/macOS上,普通用户可能没有访问串口设备的权限,你需要将用户添加到dialout组(sudo usermod -a -G dialout $USER),然后注销并重新登录,在Windows上,尝试以管理员身份运行你的Python IDE或脚本。

Q2: 为什么我读取到的数据是空的或者不完整? A: 最常见的原因是timeout设置过短,或者设备发送数据的速度比你预期的慢,尝试增加timeout的值,或者在发送指令后加入一个短暂的time.sleep()来等待设备响应。

Q3: readline()为什么一直卡住? A: readline()会一直读取,直到遇到换行符\n,如果你的设备没有发送换行符,或者数据中不包含换行符,它就会一直等待,确保你的设备端代码在发送数据后使用了println()(Arduino)或类似的函数来添加换行符。

Q4: 如何发送十六进制数据? A: Python中,0x开头的整数表示十六进制,你可以使用bytes()函数将其转换为字节流。 发送 0x01, 0x02, 0x03ser.write(bytes([0x01, 0x02, 0x03]))


总结与展望

本文系统地介绍了Python Serial API(pyserial库)的使用方法,从环境搭建、基础读写,到多线程编程和实战项目,为你提供了从入门到精通的完整路径,掌握串口通信,你将能够自由地与各种硬件设备进行交互,极大地拓展你的项目边界。

随着技术的发展,USB、蓝牙、Wi-Fi等通信方式日益普及,但串口通信因其简单可靠,在特定领域依然不可替代,希望本文能成为你探索硬件世界的坚实第一步。就动手连接你的第一个串口设备,开始你的创作之旅吧!


延伸阅读与资源


(文章结束)

分享:
扫描分享到社交APP
上一篇
下一篇