ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Python跨平台HID设备底层控制:绕过系统驱动直连USB硬件

Python跨平台HID设备底层控制:绕过系统驱动直连USB硬件 简介本资源是一套跨平台Python HID设备控制脚本面向嵌入式开发、硬件调试及自动化测试领域的初学者与工程师解决LinuxUbuntu与Windows环境下Python直接读写HID设备的兼容性难题。压缩包共4个文件含3个核心Python脚本分别实现Ubuntu/Linux下基于pyusb的HID通信、Windows下基于pywinusb的驱动控制以及屏幕点击模拟功能和1份详细说明文本总大小仅4KB轻量易集成。已有1525人学习下载脚本经Ubuntu 20.04Python 2.7、WindowsPython 3.9双平台实测可用并附权限配置、模块安装路径差异等关键排错提示——尤其针对多Python版本共存时模块误装问题给出明确规避方案帮助用户绕过常见环境陷阱快速启动HID设备调试。1. Python跨平台HID设备控制从USB协议层直连硬件绕过驱动封装实现稳定读写你有没有遇到过这样的场景手头有个定制的USB HID设备比如工业传感器、加密UKey、带按键的LED面板厂商只提供了Windows DLLLinux下连lsusb -v都看不到端点描述符或者用现成的hidapi绑定总在权限、内核模块、Python版本间反复踩坑这个脚本包不是调用高层API而是直接基于pyusb和pywinusb在用户态解析HID报告描述符、构造原始数据包、通过控制传输/中断传输与设备通信。它不依赖hidraw节点或hid-generic驱动Ubuntu下用sudo python2 hid_ubuntu.py就能发0x09 Report ID指令Windows下用python hid_win.py可捕获键盘类设备的原始输入流。适合嵌入式测试工程师、硬件联调人员、需要绕过系统HID栈做底层调试的开发者——尤其当你发现dmesg | grep hid里全是device reset failed时这套方案反而更稳。2. Linux平台HID控制原理与Ubuntu实操USB描述符解析与中断传输实战2.1 为什么不用hidraw而选pyusb协议层差异决定稳定性Linux内核的hidraw接口虽方便但存在三个硬伤一是设备拔插后/dev/hidraw*节点编号会变脚本需动态探测二是某些HID设备如Report ID为0的复合设备在hidraw中无法区分不同逻辑单元三是内核hid-core模块对自定义Report Descriptor解析有缓存修改固件后需modprobe -r usbhid modprobe usbhid才能生效。而pyusb直接操作USB设备通过get_descriptor()读取原始HID描述符用ctrl_transfer()发送Set_Report请求用interrupt_read()监听IN端点——这正是USB HID Class Spec 1.11第6.2节定义的标准流程。hid_ubuntu.py中find_hid_device()函数先遍历所有USB设备用bInterfaceClass 0x03筛选HID类再通过get_string()验证厂商名避免误匹配摄像头等伪HID设备。提示Ubuntu 20.04默认内核为5.4usbhid模块已启用ignore_other_devices1参数若设备被hid-generic接管需在/etc/modprobe.d/blacklist.conf中添加blacklist hid_generic并执行sudo update-initramfs -u2.2 安装与权限配置解决“Operation not permitted”核心报错# 确认Python2环境Ubuntu 20.04默认无python2需手动安装 sudo apt update sudo apt install python2.7 python2.7-dev curl https://bootstrap.pypa.io/pip/2.7/get-pip.py --output get-pip.py sudo python2.7 get-pip.py # 安装pyusb必须指定python2.7解释器否则pip3会装到Python3环境 sudo python2.7 -m pip install pyusb1.2.1 # 创建udev规则避免每次sudo运行 echo SUBSYSTEMusb, ATTRS{idVendor}04d8, ATTRS{idProduct}003f, MODE0664, GROUPplugdev | sudo tee /etc/udev/rules.d/99-hid-device.rules sudo usermod -a -G plugdev $USER sudo udevadm control --reload-rules sudo udevadm triggeridVendor/idProduct需替换为你的设备实际值用lsusb查看如Bus 001 Device 005: ID 04d8:003f Microchip Technology, Inc.MODE0664赋予读写权限GROUPplugdev将当前用户加入设备组执行后需重新插拔设备或重启udevsudo systemctl restart systemd-udevd2.3 hid_ubuntu.py核心代码解析从枚举到数据收发#!/usr/bin/env python2.7 import usb.core import usb.util import sys def find_hid_device(): # 查找VID0x04d8, PID0x003f的HID设备Microchip示例 dev usb.core.find(idVendor0x04d8, idProduct0x003f) if dev is None: raise ValueError(Device not found) # 检查是否已配置避免ConfigurationNotFound异常 if dev.is_kernel_driver_active(0): dev.detach_kernel_driver(0) # 强制接管接口 # 设置配置多数HID设备仅1个配置 dev.set_configuration() # 获取HID接口通常为Interface 0Alternate Setting 0 cfg dev.get_active_configuration() intf cfg[(0,0)] # 查找中断IN端点bEndpointAddress 0x80 0x80表示IN方向 ep_in usb.util.find_descriptor( intf, custom_matchlambda e: usb.util.endpoint_direction(e.bEndpointAddress) usb.util.ENDPOINT_IN ) # 查找中断OUT端点用于发送数据 ep_out usb.util.find_descriptor( intf, custom_matchlambda e: usb.util.endpoint_direction(e.bEndpointAddress) usb.util.ENDPOINT_OUT ) return dev, ep_in, ep_out def send_report(dev, ep_out, report_data): 发送HID Report数据包支持Report ID # 若设备要求Report ID需在数据前加1字节ID如report_data b\x01\x00\x01 try: # 控制传输方式标准HID Set_Report请求 dev.ctrl_transfer( bmRequestType0x21, # Host-to-Device, Class, Interface bRequest0x09, # SET_REPORT wValue0x0200, # HID Report Type (Output) 8 | Report ID (0x00) wIndex0x0000, # Interface Number data_or_wLengthreport_data ) except usb.core.USBError as e: print(Control transfer failed:, e) def read_report(ep_in, timeout1000): 读取中断IN端点数据 try: data ep_in.read(ep_in.wMaxPacketSize, timeout) return bytes(data) # 转为Python2.7兼容的str类型 except usb.core.USBError as e: if e.errno 110: # ETIMEDOUT return None raise e if __name__ __main__: dev, ep_in, ep_out find_hid_device() print(HID device connected: %s % dev) # 发送输出报告模拟按键按下 send_report(dev, ep_out, b\x01\x01\x00) # Report ID1, Key Code0x01 (KEY_ESC) # 循环读取输入报告 while True: data read_report(ep_in) if data: print(Received:, data.encode(hex)) # Python2.7中str的encode(hex)ctrl_transfer()参数详解bmRequestType0x21二进制00100001表示Host-to-Device0、Class0x01、Interface0x01bRequest0x09HID Class Spec定义的SET_REPORT请求码wValue0x0200高8位为Report Type0x02Output低8位为Report ID0x00wIndex0x0000目标接口号HID设备通常为0read_report()中ep_in.wMaxPacketSize需与设备描述符中wMaxPacketSize一致常见为64字节超时设为1000ms避免阻塞2.4 常见故障排查表从设备识别到数据校验现象根本原因解决方案usb.core.NoBackendError系统缺少libusb-1.0库sudo apt install libusb-1.0-0-devusb.core.USBError: Resource busy内核hid-generic已接管设备sudo modprobe -r hid_generic sudo modprobe usbhidusb.core.USBError: Operation not permittedudev规则未生效或用户未加入plugdev组执行groups确认含plugdev重插设备read_report()返回空数据设备未配置中断IN端点或Report Descriptor错误用sudo lsusb -v -d 04d8:003f检查bEndpointAddress和wMaxPacketSizesend_report()后设备无响应Report ID不匹配或wValue中Report Type错误用逻辑分析仪抓包比对标准HID Set_Report请求格式3. Windows平台HID控制实现pywinusb底层通信与事件回调机制3.1 pywinusb替代hidapi的技术动因Windows 10 RS5的驱动兼容性问题Windows平台传统方案是hidapi通过hid.dll调用但在Windows 10 1903版本中微软强化了HID驱动签名策略导致未签名的第三方hid.dll加载失败。pywinusb则直接调用Windows原生SetupAPI和HidD_GetPreparsedData绕过用户态DLL依赖。其核心优势在于支持HidD_GetFeature()/HidD_SetFeature()访问Feature Report常用于设备配置提供register_raw_data_handler()回调函数实时处理输入事件且无需管理员权限即可访问大多数HID设备除需FILE_DEVICE_SECURE_OPEN的加密设备外。hid_win.py中HidDevice类封装了设备打开、报告描述符解析、异步读取全流程比直接调用ctypes.windll.hid更健壮。3.2 安装与环境适配Python3.9下pywinusb的编译陷阱# PowerShell中以管理员身份运行确保能写注册表 # 先卸载可能存在的旧版本 pip uninstall pywinusb # 安装预编译wheel避免VS编译失败 pip install --only-binaryall pywinusb0.4.2 # 验证安装 python -c import pywinusb; print(pywinusb.__version__)pywinusb0.4.2是最后一个支持Python3.9的版本后续版本已停止维护若提示Microsoft Visual C 14.0 is required需安装 Build Tools for Visual Studio关键配置在脚本开头添加import pywinusb.hid否则HidDevice类无法正确初始化3.3 hid_win.py事件驱动模型从轮询到回调的性能跃迁#!/usr/bin/env python # -*- coding: utf-8 -*- import pywinusb.hid as hid import time class HidDevice: def __init__(self, vendor_id0x04d8, product_id0x003f): self.vendor_id vendor_id self.product_id product_id self.device None self.handler None def find_device(self): # 枚举所有HID设备按VID/PID匹配 all_devices hid.find_all_hid_devices() for device in all_devices: if (device.vendor_id self.vendor_id and device.product_id self.product_id): self.device device break if not self.device: raise RuntimeError(HID device not found) def open_device(self): if not self.device: self.find_device() self.device.open() # 注册输入报告回调自动触发非轮询 self.device.set_raw_data_handler(self.on_input_report) # 获取设备信息 print(Connected to: %s % self.device.product_name) print(Firmware: %s % self.device.firmware_revision) def on_input_report(self, data): 输入报告回调函数data为bytearray类型 # data[0]为Report IDdata[1:]为有效载荷 report_id data[0] payload bytes(data[1:]) print(IN Report ID%d, Data%s % (report_id, payload.hex())) # 可在此处添加业务逻辑如解析传感器数据 if report_id 0x01: temp (payload[0] 8) | payload[1] # 16-bit temperature print(Temperature: %d°C % temp) def send_output_report(self, report_id, data): 发送输出报告 # 构造完整报告包[Report ID] data report_buffer bytearray([report_id]) bytearray(data) # 获取输出报告特征需设备支持 try: output_report self.device.find_output_reports()[0] output_report.set_raw_data(report_buffer) output_report.send() except (IndexError, AttributeError): # 若无Output Report尝试Feature Report try: feature_report self.device.find_feature_reports()[0] feature_report.set_raw_data(report_buffer) feature_report.send() except Exception as e: print(Failed to send report:, e) def close_device(self): if self.device: self.device.close() if __name__ __main__: hid_dev HidDevice() try: hid_dev.open_device() # 发送输出报告如点亮LED hid_dev.send_output_report(0x02, b\x01) # Report ID2, LED ON # 保持程序运行以接收回调 print(Press CtrlC to exit...) while True: time.sleep(1) except KeyboardInterrupt: print(\nExiting...) finally: hid_dev.close_device()set_raw_data_handler()注册的回调函数在新数据到达时由系统线程自动调用避免while True: read()的CPU空转find_output_reports()返回列表索引[0]取第一个Output Report多数设备仅1个send()方法内部调用HidD_SetOutputReport()失败时降级到HidD_SetFeatureReport()3.4 Windows HID报告类型深度解析Input/Output/Feature Report的应用边界Report类型传输方向典型用途Windows API对应Input ReportDevice → Host按键、鼠标移动、传感器数据HidD_GetInputReport()Output ReportHost → DeviceLED控制、蜂鸣器、电机启停HidD_SetOutputReport()Feature Report双向设备配置灵敏度、采样率、固件升级HidD_GetFeature()/HidD_SetFeature()screen_click.py示例利用Input Report捕获触摸屏坐标通过data[2:4]提取X轴小端序data[4:6]提取Y轴再调用pyautogui.click(x,y)模拟点击若设备无Output Reportfind_output_reports()返回空列表必须使用Feature Report进行控制此时wValue参数需设为0x0300Feature Report Type4. 跨平台调试技巧与HID协议级验证方法4.1 使用USB协议分析仪验证数据包结构避免“看似成功实则无效”当脚本运行无报错但设备无响应时最可靠的方法是用USB协议分析仪如Total Phase Beagle USB 480抓包。重点比对三类数据HID描述符请求GET_DESCRIPTOR请求中wValue0x2200HID Descriptor确认bDescriptorType0x22且wDescriptorLength与设备手册一致Set_Report请求检查bmRequestType0x21、bRequest0x09、wValue高位是否匹配Report Type0x01Input, 0x02Output, 0x03Feature中断传输数据对比ep_in收到的数据长度是否等于wMaxPacketSize内容是否含预期Report ID注意Linux下pyusb的ctrl_transfer()默认使用bmRequestType0x21而某些设备要求0xA1Device-to-Host此时需改用dev.ctrl_transfer(0xA1, 0x01, 0x0100, 0, 8)读取Input Report4.2 报告描述符Report Descriptor逆向工程速查表HID设备行为由二进制Report Descriptor决定01说明.txt中应包含此字段。常用Item解析Item含义示例值说明0x05, 0x01Usage Page (Generic Desktop)0x05, 0x01后续Usage基于此页0x09, 0x06Usage (Keyboard)0x09, 0x06定义设备功能类别0x15, 0x00Logical Minimum0x15, 0x00数据最小值00x25, 0xFFLogical Maximum0x25, 0xFF数据最大值2550x75, 0x08Report Size0x75, 0x08每个字段8位0x95, 0x06Report Count0x95, 0x06共6个字段6字节数据0x81, 0x02Input (Data, Variable, Absolute)0x81, 0x02输入报告字段若01说明.txt缺失可用sudo lsusb -v -d VID:PID 2/dev/null | grep -A 5 HID Device提取原始Descriptor在线解析工具推荐 HID Descriptor Tool 粘贴十六进制字符串即可生成C结构体4.3 Ubuntu与Windows环境变量隔离实践避免Python版本冲突生产环境中常需同时运行Python2.7旧脚本和Python3.9新服务通过环境变量隔离# Ubuntu下创建专用环境 mkdir ~/hid_env cd ~/hid_env python2.7 -m virtualenv venv27 source venv27/bin/activate pip install pyusb1.2.1 # Windows下PowerShell脚本启动 $env:PYTHONPATHC:\hid_env\Lib\site-packages C:\Python27\python.exe C:\hid_control\hid_ubuntu.py # 或使用shebang指定解释器Linux #!/usr/bin/env python2.7 # 第一行确保调用python2.7而非系统默认pythonUbuntu中/usr/bin/python指向python2.7时#!/usr/bin/env python才安全否则必须写死#!/usr/bin/env python2.7Windows中.py文件关联到Python3.9时需在命令行显式调用python2.7 script.py4.4 实时监控HID设备状态的Bash/PowerShell脚本# Ubuntu实时监控保存为monitor_hid.sh #!/bin/bash DEVICE_PATH/sys/bus/usb/devices/*/idVendor while true; do echo $(date): $(lsusb | grep 04d8:003f | wc -l) devices found # 检查hidraw节点是否存在 if ls /dev/hidraw* 2/dev/null | grep -q hidraw; then echo hidraw nodes: $(ls /dev/hidraw*) fi sleep 2 done# Windows PowerShell监控monitor_hid.ps1 while ($true) { $devices Get-PnpDevice -Class HID | Where-Object {$_.InstanceId -match 04D8003F} Write-Host $(Get-Date): Found $($devices.Count) HID devices if ($devices.Count -gt 0) { $devices | ForEach-Object { Write-Host $($_.Name) - $($_.Status) } } Start-Sleep -Seconds 2 }Ubuntu脚本通过lsusb和/dev/hidraw*双重验证设备在线状态Windows脚本用Get-PnpDevice获取即插即用设备状态Status为OK表示正常本文还有配套的精品资源点击获取
返回列表