杰瑞科技汇

Python串口inwaiting如何获取当前待读数据量?

in_waiting 是串口通信中一个非常核心和常用的属性,尤其是在处理异步数据时。

Python串口inwaiting如何获取当前待读数据量?-图1
(图片来源网络,侵删)

什么是 in_waiting

in_waiting 是一个只读属性,它返回当前串口输入缓冲区中等待读取的字节数

它就像一个“计数器”,告诉你串口硬件缓冲区里已经堆积了多少个数据字节,正等着你的程序去读取。

为什么需要 in_waiting

串口通信是异步的,这意味着发送方和接收方没有共享的时钟,数据是一个字节一个字节地、以不确定的时间间隔到达的。

当你调用 ser.read() 时,它会:

Python串口inwaiting如何获取当前待读数据量?-图2
(图片来源网络,侵删)
  • 立即返回缓冲区中已有的所有数据。
  • 如果缓冲区是空的,它会阻塞(等待)直到有数据到达,或者直到你指定的超时时间结束。

这种“阻塞”行为在很多场景下是不理想的,

  • 你只想读取一帧完整的数据:比如一个以 0xAA 开头,以 0xBB 结尾的数据包。read() 阻塞了,你可能读到一半数据就被卡住了,永远等不到结束符。
  • 你需要同时处理其他任务:比如在一个 GUI 程序中,你不能让主线程因为等待串口数据而卡死。

in_waiting 的出现就是为了解决这个问题,它允许你在读取数据之前,先检查一下缓冲区里有多少数据,从而做出更智能的决策。

如何使用 in_waiting?(核心用法)

最常见的用法是配合 read()readline(),来一次性读取缓冲区中所有的数据。

基本语法

import serial
# 假设 ser 已经是一个打开的 serial.Serial 对象
# num_bytes = ser.in_waiting
# data = ser.read(num_bytes)

示例代码

下面是一个完整的、可运行的示例,你需要准备一个 USB 转串口模块(如 CH340/FT232),并连接一个设备(比如另一个 Arduino)不断发送数据,或者使用一个串口调试助手来模拟发送数据。

Python串口inwaiting如何获取当前待读数据量?-图3
(图片来源网络,侵删)
import serial
import time
# --- 配置串口 ---
# 请根据你的实际情况修改以下参数
SERIAL_PORT = 'COM3'  # Windows: 'COMx', Linux: '/dev/ttyUSBx', Mac: '/dev/cu.usbserial-x'
BAUD_RATE = 9600
TIMEOUT = 1  # 设置超时,防止无限等待
try:
    # 打开串口
    ser = serial.Serial(
        port=SERIAL_PORT,
        baudrate=BAUD_RATE,
        timeout=TIMEOUT
    )
    print(f"串口 {ser.name} 打开成功")
    # 主循环
    while True:
        # 1. 检查串口缓冲区中是否有数据
        if ser.in_waiting > 0:
            print(f"检测到 {ser.in_waiting} 个字节等待读取...")
            # 2. 读取缓冲区中所有的数据
            #    in_waiting 会告诉你该读多少字节,避免阻塞
            received_data = ser.read(ser.in_waiting)
            # 3. 处理数据
            #    received_data 是 bytes 类型,通常需要解码成字符串
            try:
                decoded_data = received_data.decode('utf-8').strip()
                print(f"接收到数据: '{decoded_data}'")
                # 在这里可以添加你的数据处理逻辑
                # 查找特定的数据帧格式
            except UnicodeDecodeError:
                print(f"接收到无法解码的数据 (原始字节): {received_data}")
        # 4. 做其他事情...
        #    即使串口没有数据,程序也不会卡在这里,可以继续执行其他任务
        print("执行其他任务...")
        time.sleep(1) # 模拟程序其他部分的耗时操作
except serial.SerialException as e:
    print(f"无法打开串口 {SERIAL_PORT}: {e}")
except KeyboardInterrupt:
    print("程序被用户中断")
finally:
    if 'ser' in locals() and ser.is_open:
        ser.close()
        print("串口已关闭")

in_waiting vs. readline()

很多初学者会混淆 in_waitingser.readline()

  • ser.readline():

    • 它会持续读取,直到遇到一个换行符 \n(或者超时)。
    • 如果发送方没有发送换行符,readline() 会一直阻塞,直到超时。
    • 它读取的是一行逻辑数据,而不是所有物理字节。
  • ser.read(ser.in_waiting):

    • 它会一次性读取缓冲区中所有的字节,无论这些字节是否构成一个完整的逻辑行。
    • 它读取的是所有等待的物理数据
  • 如果你的协议是基于换行符的(比如很多调试信息),readline() 更方便。
  • 如果你的协议是自定义的、二进制的,或者你需要确保不丢失任何数据包,in_waiting + read() 是更可靠的选择。

注意事项和常见问题

  1. 数据丢失风险in_waiting 只告诉你缓冲区里有多少数据,如果你的程序处理速度比数据到达速度慢,新的数据可能会覆盖旧的数据(如果缓冲区满了),导致数据丢失,增大串口缓冲区大小或加快数据处理速度可以缓解这个问题。

  2. timeout 的重要性:在打开串口时设置一个合理的 timeout 是一个好习惯,这可以防止你的程序在意外情况下(如设备断开)无限期地阻塞。

  3. read(size)size 参数ser.read(ser.in_waiting) 是一个很常见的模式,但要注意,in_waiting 的值在你调用 read() 的瞬间和 read() 执行期间可能会改变(如果新数据到达),对于大多数应用场景,这种影响可以忽略不计。

  4. 跨平台性in_waitingpyserial 库的标准属性,在 Windows, Linux, macOS 上的行为是一致的。

实际应用场景举例

假设你要接收一个以 0xAA 开头,0x55 结尾的数据包,长度不定。

# 在主循环中
while True:
    if ser.in_waiting > 0:
        # 先读取一个字节,看看是不是包头
        first_byte = ser.read(1)
        if first_byte == b'\xAA':
            print("检测到包头 0xAA")
            # 继续等待,直到收到至少一个字节的结束符
            while ser.in_waiting < 1:
                time.sleep(0.01) # 短暂休眠,避免空转CPU
            # 现在缓冲区里至少有1个字节,我们把它读完
            # 假设数据包在结束符之前到达
            data = ser.read(ser.in_waiting)
            if data.endswith(b'\x55'):
                print(f"收到完整数据包: {data}")
            else:
                print("收到数据包,但格式不正确(缺少结束符)")
分享:
扫描分享到社交APP
上一篇
下一篇