如何通过PC端HIDAPI控制RP2040键鼠复合设备并接收指令
问题描述
参考Adafruit_TinyUSB_Arduino的HID复合设备示例,计划通过PC端Python的hidapi发送指令,让RP2040实现键鼠操作。使用Raspberry Pi Pico Arduino core v4.7.1,设备能被PC正确识别为键鼠,但RP2040无法接收PC发送的指令。
现有代码
Python发送端代码
import hid import time # 打开HID设备(基于设备的VID和PID) VID = 0x239A # 设置对应的厂商ID PID = 0xCAFE # 设置对应的产品ID # 打开HID设备 device = hid.device() device.open(VID, PID) # 发送鼠标移动报告 def send_mouse_move(dx, dy): # 限制dx和dy在-127到127范围内 dx = max(min(dx, 127), -127) dy = max(min(dy, 127), -127) # 转换为无符号8位整数(0-255) dx = (dx + 128) & 0xFF dy = (dy + 128) & 0xFF # 构造鼠标报告 report = [0x00, dx, dy, 0x00, 0x00, 0x00, 0x00, 0x00] # 鼠标报告格式 print("发送鼠标报告:", report) device.write(report) time.sleep(0.1) # 发送键盘按键报告 def send_key_press(keycode): report = [ keycode, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ] # 键盘报告格式 print("发送键盘报告:", report) device.write(report) time.sleep(0.1) # 发送释放报告 report = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] print("发送键盘释放报告:", report) device.write(report) # 控制指令映射 def send_command(command): if command == "MOVE_LEFT": send_mouse_move(-10, 0) elif command == "MOVE_RIGHT": send_mouse_move(10, 0) elif command == "KEY_A": send_key_press(0x04) # 按下A键 elif command == "KEY_ENTER": send_key_press(0x28) # 按下回车键 # 发送测试指令 commands = ["MOVE_LEFT", "MOVE_RIGHT", "KEY_A", "KEY_ENTER"] for command in commands: send_command(command) time.sleep(1) device.close()
RP2040接收端代码
#include "Adafruit_TinyUSB.h" // 定义报告ID enum { RID_KEYBOARD = 1, RID_MOUSE, }; // HID报告描述符 uint8_t const desc_hid_report[] = { TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(RID_KEYBOARD)), TUD_HID_REPORT_DESC_MOUSE(HID_REPORT_ID(RID_MOUSE)), }; // USB HID对象 Adafruit_USBD_HID usb_hid; // 处理接收的HID报告(Get Report回调) uint16_t get_report(uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t bufsize) { Serial.print("收到Get Report: "); for (uint16_t i = 0; i < bufsize; i++) { Serial.print(buffer[i], HEX); Serial.print(" "); } Serial.println(); return bufsize; } // 设置报告回调(Set Report回调) void set_report(uint8_t report_id, hid_report_type_t report_type, const uint8_t *buffer, uint16_t bufsize) { Serial.print("收到Set Report: "); for (uint16_t i = 0; i < bufsize; i++) { Serial.print(buffer[i], HEX); Serial.print(" "); } Serial.println(); } void setup() { // 初始化串口调试 Serial.begin(115200); while (!Serial); // 初始化TinyUSB if (!TinyUSBDevice.isInitialized()) { TinyUSBDevice.begin(0); } // 配置HID设备 usb_hid.setReportDescriptor(desc_hid_report, sizeof(desc_hid_report)); usb_hid.setPollInterval(2); usb_hid.setStringDescriptor("TinyUSB HID Composite"); // 设置报告回调函数 usb_hid.setReportCallback(get_report, set_report); usb_hid.begin(); // 确保设备正确挂载 if (TinyUSBDevice.mounted()) { TinyUSBDevice.detach(); delay(10); TinyUSBDevice.attach(); } Serial.println("Adafruit TinyUSB HID复合设备示例启动"); } void loop() { #ifdef TINYUSB_NEED_POLLING_TASK // 运行TinyUSB任务处理 TinyUSBDevice.task(); #endif if (!TinyUSBDevice.mounted()) { return; } }
问题排查与修复方案
1. 报告ID不匹配
Arduino端的HID描述符定义了带报告ID的键鼠报告(RID_KEYBOARD=1、RID_MOUSE=2),但Python代码发送的报告未携带报告ID,导致TinyUSB无法识别并触发回调。
修复Python代码:
- 鼠标报告添加第一个字节
0x02(对应RID_MOUSE) - 键盘报告添加第一个字节
0x01(对应RID_KEYBOARD)
修改后的鼠标移动函数:
def send_mouse_move(dx, dy): dx = max(min(dx, 127), -127) dy = max(min(dy, 127), -127) dx = (dx + 128) & 0xFF dy = (dy + 128) & 0xFF # 第一个字节为报告ID(0x02=鼠标) report = [0x02, dx, dy, 0x00, 0x00, 0x00, 0x00, 0x00] print("发送鼠标报告:", report) device.write(report) time.sleep(0.1)
修改后的键盘按键函数:
def send_key_press(keycode): # 标准带报告ID的键盘报告格式:[报告ID, 修饰键, 保留, 按键1, 按键2, ..., 按键6] report = [ 0x01, # 报告ID(0x01=键盘) 0x00, # 修饰键(无) 0x00, # 保留位 keycode, 0x00, 0x00, 0x00, 0x00, 0x00 ] print("发送键盘报告:", report) device.write(report) time.sleep(0.1) # 释放报告 report = [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] print("发送键盘释放报告:", report) device.write(report)
2. 确保打开正确的HID接口
部分复合设备会有多个HID接口,需确认Python代码打开的是支持输出报告的接口。可通过枚举设备接口验证:
devices = hid.enumerate(VID, PID) for d in devices: print(f"接口路径: {d['path']}, Usage Page: {d['usage_page']}, Usage: {d['usage']}") # 选择Usage Page=1(通用桌面设备)、Usage=6(键盘)或Usage=2(鼠标)的接口 device.open_path(d['path'])
3. 验证回调触发
Python的device.write()发送的是输出报告,会触发Arduino端的set_report回调。修复报告ID后,打开RP2040的串口监视器(115200波特率),运行Python代码即可看到接收的报告数据。
内容的提问来源于stack exchange,提问作者kongbai
相关产品推荐
相关产品推荐

