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

USB断开重连后Python无法识别RFID设备的诊断与修复问询

问题描述

树莓派OS环境下,部分模拟键盘的RFID阅读器会出现USB短暂断开(仅约300ms)后立即重连的情况,导致基于Python 3.9+EVDEV开发的应用崩溃并丢失状态:

  • 已能捕获[Errno 19] No such device异常,但即使设备重新出现在/dev目录中,Python仍无法识别它,等待10秒重试也无效
  • 不想通过保存状态重启应用(操作繁琐),需要找到无需重启即可让Python重新识别设备的方法

异常信息:

2025-10-22 09:21:06 User: {'authorized': True, 'id': '0009444691'}
2025-10-22 09:21:33 Exception: [Errno 19] No such device
Traceback (most recent call last):
  File "drivers/RFID/KeyboardRFID.py", line 28, in run
    for event in dev.read_loop():
  File "/usr/local/lib/python3.9/dist-packages/evdev/eventio.py", line 46, in read_loop
    for event in self.read():
  File "/usr/local/lib/python3.9/dist-packages/evdev/eventio.py", line 71, in read
    events = _input.device_read_many(self.fd)

核心问题代码块:

for event in dev.read_loop():
    # If key event and key up (0)
    if event.type == ecodes.EV_KEY and event.value == 0:
        key = scancodes.get(event.code)
        if key == u'\n':  # if enter
            self.notifyScanObservers(rfid_code)
            rfid_code = ""
        else:
            rfid_code = rfid_code + key

except Exception as e:
    logging.error("Exception: %s" % str(e), exc_info=1)
    os._exit(42) # Make sure entire application exits
解决方案

1. 移除硬退出逻辑,改为设备重连循环

当前代码在捕获异常后直接强制退出应用,这是必须重启的根本原因。修改异常处理逻辑,改为循环检测设备并重新初始化:

import time
import logging
from evdev import ecodes

def run(self):
    while True:
        try:
            # 循环内重新查找并初始化设备
            dev = self.find_rfid_device()
            if not dev:
                logging.warning("RFID设备未找到,1秒后重试...")
                time.sleep(1)
                continue
            
            logging.info("已连接到RFID设备")
            rfid_code = ""
            for event in dev.read_loop():
                if event.type == ecodes.EV_KEY and event.value == 0:
                    key = scancodes.get(event.code)
                    if key == u'\n':
                        self.notifyScanObservers(rfid_code)
                        rfid_code = ""
                    else:
                        rfid_code = rfid_code + key
        except OSError as e:
            if e.errno == 19:  # 捕获"设备不存在"错误
                logging.error("RFID设备断开,开始重连...")
                # 关闭旧设备句柄释放资源
                if 'dev' in locals():
                    dev.close()
                time.sleep(0.5)  # 等待设备完成重新枚举
        except Exception as e:
            logging.error("未知异常: %s" % str(e), exc_info=1)
            time.sleep(1)

2. 用硬件ID动态查找设备,避免依赖固定路径

不要硬编码/dev/input/eventX路径,通过设备的vendor ID和product ID精准匹配目标阅读器(可通过lsusb命令查看对应ID):

import evdev

def find_rfid_device(self):
    # 替换为你的RFID阅读器的vendor_id和product_id
    target_vid = 0x1234
    target_pid = 0x5678
    
    for path in evdev.list_devices():
        dev = evdev.InputDevice(path)
        if dev.info.vendor == target_vid and dev.info.product == target_pid:
            return dev
    return None

3. 及时失效文件句柄

设备断开后,原dev对象的文件句柄已失效,必须在捕获异常后主动调用dev.close(),否则无法复用旧对象连接新设备。

4. 用udev监听设备插拔(进阶)

如果需要更及时的重连响应,可结合pyudev库监听USB设备事件,替代轮询:

import pyudev

def setup_device_monitor(self):
    context = pyudev.Context()
    monitor = pyudev.Monitor.from_netlink(context)
    monitor.filter_by(subsystem='input')
    
    # 启动线程监听事件
    def monitor_thread():
        for device in iter(monitor.poll, None):
            if device.action == 'add':
                # 匹配目标设备的ID
                if device.get('ID_VENDOR_ID') == '1234' and device.get('ID_MODEL_ID') == '5678':
                    logging.info("RFID设备已重新连接")
                    # 触发设备初始化逻辑
    import threading
    threading.Thread(target=monitor_thread, daemon=True).start()

关键注意事项

  • 禁止在异常处理中直接退出应用,保持主循环持续运行
  • 每次重连必须重新创建InputDevice实例,不能复用旧对象
  • 用vendor/product ID定位设备,避免设备节点编号变化导致匹配失败

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 03:42:42