树莓派Python控制USB RFID读卡器异常问题排查
问题分析与解决方案
你的核心问题在于USB读卡器的输入缓冲区在音频播放期间堆积了未读取的数据,导致后续读取到混乱的数据包,触发读卡器异常蜂鸣,甚至因为数据解析逻辑错误导致程序挂起。虽然你用了线程播放音频,但thread_zwei.join()会阻塞RFID读取线程,这段时间内读卡器的刷卡数据会积压在USB缓冲区,后续一次性读取时会破坏ID解析逻辑,同时触发设备的错误蜂鸣。
关键修复点
- 取消RFID线程对音频线程的阻塞:让音频在后台独立播放,RFID线程持续监听读卡器,避免缓冲区堆积。
- 添加USB缓冲区清理逻辑:在每次处理完ID后,主动读取并丢弃缓冲区中残留的数据,防止后续读取到旧数据。
- 优化ID解析逻辑:避免硬编码字符串匹配的冗余写法,同时增加异常处理防止程序挂起。
- 添加设备重置机制:当出现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
相关产品推荐
相关产品推荐

