You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

树莓派Python控制USB RFID读卡器异常问题排查

问题分析与解决方案

你的核心问题在于USB读卡器的输入缓冲区在音频播放期间堆积了未读取的数据,导致后续读取到混乱的数据包,触发读卡器异常蜂鸣,甚至因为数据解析逻辑错误导致程序挂起。虽然你用了线程播放音频,但thread_zwei.join()会阻塞RFID读取线程,这段时间内读卡器的刷卡数据会积压在USB缓冲区,后续一次性读取时会破坏ID解析逻辑,同时触发设备的错误蜂鸣。

关键修复点

  1. 取消RFID线程对音频线程的阻塞:让音频在后台独立播放,RFID线程持续监听读卡器,避免缓冲区堆积。
  2. 添加USB缓冲区清理逻辑:在每次处理完ID后,主动读取并丢弃缓冲区中残留的数据,防止后续读取到旧数据。
  3. 优化ID解析逻辑:避免硬编码字符串匹配的冗余写法,同时增加异常处理防止程序挂起。
  4. 添加设备重置机制:当出现USB读取异常时,重置读卡器连接,恢复正常通信。

修改后的代码

from __future__ import print_function
import sys
import usb.core
import usb.util
import wave
import getopt
import alsaaudio
from time import sleep
from threading import Thread

# 音频播放函数
def play(device_name, f):
    print('%d channels, %d sampling rate\n' % (f.getnchannels(), f.getframerate()))
    # 匹配音频格式
    if f.getsampwidth() == 1:
        format = alsaaudio.PCM_FORMAT_U8
    elif f.getsampwidth() == 2:
        format = alsaaudio.PCM_FORMAT_S16_LE
    elif f.getsampwidth() == 3:
        format = alsaaudio.PCM_FORMAT_S24_LE
    elif f.getsampwidth() == 4:
        format = alsaaudio.PCM_FORMAT_S32_LE
    else:
        raise ValueError('Unsupported format')

    periodsize = f.getframerate() // 8
    device = alsaaudio.PCM(channels=f.getnchannels(), 
                           rate=f.getframerate(), 
                           format=format, 
                           periodsize=periodsize,
                           device=device_name)
    data = f.readframes(periodsize)
    while data:
        device.write(data)
        data = f.readframes(periodsize)

def musik_spielen(p_id_rfid):
    if p_id_rfid == 'Status_1':
        try:
            f = wave.open('status_1.wav','rb')
            play(device, f)
            f.close()
        except Exception as e:
            print(f"音频播放出错: {str(e)}")

# 清理USB缓冲区残留数据
def clear_usb_buffer(dev, endpoint):
    try:
        while True:
            dev.read(endpoint.bEndpointAddress, endpoint.wMaxPacketSize, timeout=10)
    except usb.core.USBError as e:
        if e.args[0] != 'Operation timed out':
            raise e

# RFID读取主逻辑
def rfid_auslesen():
    dev = usb.core.find(idVendor=0xffff, idProduct=0x0035)
    if not dev:
        print("未找到RFID设备")
        return

    interface = 0
    endpoint = dev[0][(0,0)][0]

    try:
        if dev.is_kernel_driver_active(interface):
            dev.detach_kernel_driver(interface)
        usb.util.claim_interface(dev, interface)

        collected = 0
        attempts = 22
        rfid_id = ""
        while True:
            try:
                data = dev.read(endpoint.bEndpointAddress, endpoint.wMaxPacketSize, timeout=500)
                collected += 1
                rfid_id += str(data[2])

                if collected == attempts:
                    print(f"读取到RFID ID: {rfid_id}")
                    # 处理匹配逻辑
                    if rfid_id == "390390390320330300300350380300400":
                        print("程序结束")
                        break
                    elif rfid_id == "390390390320390330320320340370400":
                        # 后台启动音频线程,不阻塞RFID读取
                        Thread(target=musik_spielen, args=('Status_1',), daemon=True).start()
                    
                    # 重置计数器并清理缓冲区
                    rfid_id = ""
                    collected = 0
                    clear_usb_buffer(dev, endpoint)

            except usb.core.USBError as e:
                if e.args[0] == 'Operation timed out':
                    continue
                else:
                    print(f"USB读取错误: {str(e)}")
                    # 尝试重置设备
                    dev.reset()
                    # 重新初始化设备
                    if dev.is_kernel_driver_active(interface):
                        dev.detach_kernel_driver(interface)
                    usb.util.claim_interface(dev, interface)
                    rfid_id = ""
                    collected = 0

    finally:
        usb.util.release_interface(dev, interface)
        dev.attach_kernel_driver(interface)

if __name__ == '__main__':
    device = 'default'
    opts, args = getopt.getopt(sys.argv[1:], 'd:')
    for o, a in opts:
        if o == '-d':
            device = a

    # 启动RFID读取线程
    Thread(target=rfid_auslesen, daemon=True).start()
    # 保持主线程运行
    while True:
        sleep(1)

修复说明

  • 取消线程阻塞:用daemon=True创建音频线程,无需join(),RFID线程持续监听读卡器,避免缓冲区堆积。
  • 缓冲区清理:clear_usb_buffer函数主动读取并丢弃缓冲区残留数据,确保每次读取的都是最新的刷卡数据。
  • 设备重置:当出现USB异常时,调用dev.reset()重置读卡器,恢复正常通信,解决程序挂起问题。
  • 异常处理:给音频播放添加异常捕获,避免音频文件问题导致程序崩溃。

这些修改可以解决音频播放期间刷卡引发的长蜂鸣和程序挂起问题,同时保证读卡器通信的稳定性。

内容的提问来源于stack exchange,提问作者MmeBerry

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 07:54:18