# ESP32 虚拟键鼠实现方案 ## 功能目标 通过 ESP32-S3 模拟键盘按键,实现虚拟键鼠功能 --- ## 技术方案 | 方案 | 通信方式 | 延迟 | 适用场景 | 复杂度 | |------|----------|------|----------|--------| | **USB HID** | USB OTG 有线 | 极低 | 桌面设备、即插即用 | 中等 | | **蓝牙 HID** | Bluetooth | 中等 | 移动设备、无线场景 | 较低 | | **WiFi HID** | WiFi/UDP/HTTP | 中等 | 远程控制、IoT集成 | 较低 | --- ## 采用方案:ESP-IDF C + TinyUSB ### 开发环境 - **开发板**:ESP32-S3(支持 USB OTG) - **框架**:ESP-IDF(乐鑫官方 C 框架) - **USB 库**:TinyUSB(ESP-IDF 内置支持) ### 硬件要求 - ESP32-S3 开发板 - USB 数据线(ESP32-S3 的 USB 接口) --- ## ⚠️ 重要注意事项 ### 问题1:键鼠 + WiFi + 蓝牙同时使用 ESP32-S3 支持 **WiFi 和蓝牙同时运行**,但需要注意: | 模式 | WiFi | 蓝牙 | USB HID | 说明 | |------|------|------|----------|------| | **三模** | ✅ | ✅ | ✅ | 同时运行 | | 双模 | ✅ | ❌ | ✅ | WiFi + USB HID | | 双模 | ❌ | ✅ | ✅ | 蓝牙 + USB HID | **配置要求:** - 开启 WiFi Station 模式 - 开启 蓝牙 BLE 模式 - 开启 USB HID 设备模式 --- ### 问题2:Rust 环境隔离(关键!) **问题描述:** 用户同时进行普通 Rust 应用开发和 ESP32 开发,普通 Rust 使用 `std` target,ESP32 使用 `xtensa` 或 `riscv32` target。如果环境配置不当,会导致 `cargo` 命令冲突。 **解决方案:** #### ✅ 推荐:使用 VS Code + esp-idf extension(完全隔离) 1. **安装 esp-idf VS Code 插件** - 搜索 "Espressif IDF" 安装 - 自动管理 ESP-IDF 和工具链,不影响系统 Rust 2. **配置专用 VS Code 工作区** - 为 ESP32 项目创建独立工作区 - 插件会自动激活 ESP 环境 #### ✅ 替代:使用 `espup` 创建独立工具链 ```bash # 安装到指定目录,不污染系统环境 cargo install espup # 安装 ESP 工具链到指定目录 espup install -t ~/esp/esp-idf # 在 ESP32 项目中激活(不影响全局) source ~/esp/esp-idf/export.sh ``` #### ❌ 避免:不要修改系统默认 Rust ```bash # 错误做法 - 会影响普通 Rust 开发 rustup default esp # 正确做法 - 项目级指定 target rustup target add xtensa-esp32s3-elf cargo build --release -target xtensa-esp32s3-elf ``` #### 推荐的项目结构 ``` ESP32 项目/ ├── esp32_hid_keyboard/ │ ├── CMakeLists.txt │ ├── main/ │ │ ├── CMakeLists.txt │ │ └── main.c │ └── sdkconfig └── .vscode/ └── settings.json # esp-idf 插件配置 ``` --- ## ESP-IDF C 方案优势 ✅ **官方成熟例程,直接可用** ✅ **功能丰富,一站式解决** ✅ **文档完善,调试方便** | 功能 | 支持情况 | |------|----------| | USB 虚拟键盘 | ✅ 官方例程 | | USB 虚拟鼠标 | ✅ 官方例程 | | USB 虚拟网卡 | ✅ 官方例程 | | USB 虚拟 U 盘 | ✅ 官方例程 | | 蓝牙配网 (BLE) | ✅ 官方例程 | | WiFi + 蓝牙同时 | ✅ 支持 | --- ## 实现步骤 ### 1. 环境准备 **安装 ESP-IDF(独立安装,不影响 Rust):** ```bash # Windows (PowerShell) .\install.ps1 # 激活环境(每次开发 ESP32 时执行) .\env.ps1 # 确认 ESP-IDF 版本 idf.py --version # 推荐 ESP-IDF v5.0+ ``` **或者使用 esp-idf VS Code 插件(推荐):** - 安装 Espressif IDF 插件 - 在插件中配置 ESP-IDF 路径 - 打开项目自动切换环境 ### 2. 创建项目 ```bash # 从官方示例创建 USB HID 项目 cp -r $IDF_PATH/examples/peripherals/usb/device/hid_device ./esp32_hid_keyboard cd esp32_hid_keyboard ``` ### 3. ESP-IDF 配置(关键!) ```bash # 进入配置菜单 idf.py menuconfig ``` #### 3.1 开启 USB 支持 **→ Hardware Settings → USB CONFIG** - [x] Enable USB OTG - [x] Enable USB CDC - [x] Force USB OTG mode as device #### 3.2 开启 TinyUSB **→ Component config → TinyUSB** - [x] Enable TinyUSB - [x] Support CDC (Serial) - [x] Support HID (Keyboard/Mouse/Gamepad) - [x] HID keyboard - [x] HID mouse - [x] Support MSC (Mass Storage) - [x] Support Ethernet (RNDIS/ECM) #### 3.3 开启 WiFi + 蓝牙(重点!) **→ Component config → Wi-Fi** - [x] Enable WiFi - [x] WiFi Station - [x] WiFi AP (可选) **→ Component config → Bluetooth** - [x] Enable Bluetooth - [x] Bluetooth LE (BLE) - [x] BLE GATT Server #### 3.4 配置 Flash 和 RAM **→ Serial flasher config** - [x] Default flash size: 8MB - [x] Partition table: Factory app, two OTA definitions --- ## 核心代码 ### 1. WiFi + 蓝牙同时初始化 ```c #include "esp_bt.h" #include "esp_wifi.h" #include "esp_blufi_api.h" #include "nvs_flash.h" static void wifi_init(void) { ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); esp_netif_create_default_wifi_sta(); wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_wifi_init(&cfg)); wifi_config_t wifi_config = { .sta = { .threshold.authmode = WIFI_AUTH_WPA2_PSK, }, }; // 设置 WiFi 名称和密码(可从 NVS 或配网获取) memcpy(wifi_config.sta.ssid, "YourSSID", 7); memcpy(wifi_config.sta.password, "YourPassword", 12); ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config)); ESP_ERROR_CHECK(esp_wifi_start()); ESP_LOGI(TAG, "WiFi started"); } static void ble_init(void) { // 初始化 NVS esp_err_t ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { ESP_ERROR_CHECK(nvs_flash_erase()); ret = nvs_flash_init(); } ESP_ERROR_CHECK(ret); // 释放经典蓝牙内存(只用 BLE) ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT)); // 初始化蓝牙控制器 esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_bt_controller_init(&bt_cfg)); // 使能 BLE 模式 ESP_ERROR_CHECK(esp_bt_controller_enable(ESP_BT_MODE_BLE)); // 初始化 Bluedroid ESP_ERROR_CHECK(esp_bluedroid_init()); ESP_ERROR_CHECK(esp_bluedroid_enable()); ESP_LOGI(TAG, "BLE started"); } ``` ### 2. TinyUSB 初始化 ```c #include "tusb.h" static const tusb_desc_device_t dev_desc = { .bLength = sizeof(tusb_desc_device_t), .bDescriptorType = TUSB_DESC_DEVICE, .bcdUSB = 0x0200, .bDeviceClass = 0x00, .bDeviceSubClass = 0x00, .bDeviceProtocol = 0x00, .bMaxPacketSize0 = 64, .idVendor = 0x303A, // Espressif VID .idProduct = 0x4002, // USB HID Keyboard PID .bcdDevice = 0x0100, .iManufacturer = 0x01, .iProduct = 0x02, .iSerialNumber = 0x03, .bNumConfigurations = 0x01 }; static void tinyusb_init(void) { const tinyusb_config_t tusb_cfg = { .device_descriptor = &dev_desc, .string_descriptor = NULL, .external_phy = false, }; ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg)); ESP_LOGI(TAG, "TinyUSB initialized"); } ``` ### 3. 键盘发送函数 ```c // 键盘修饰键 #define HID_MOD_LEFTCTRL (1 << 0) #define HID_MOD_LEFTSHIFT (1 << 1) #define HID_MOD_LEFTALT (1 << 2) #define HID_MOD_LEFTGUI (1 << 3) // 常用键码 #define HID_KEY_A 0x04 #define HID_KEY_C 0x06 #define HID_KEY_ENTER 0x28 void hid_keyboard_send(uint8_t key, uint8_t modifier) { uint8_t report[8] = { modifier, 0, key, 0, 0, 0, 0, 0 }; tud_hid_n_report(0, 0, report, sizeof(report)); } void hid_keyboard_release(void) { uint8_t report[8] = {0}; tud_hid_n_report(0, 0, report, sizeof(report)); } void hid_keyboard_send_string(const char *str) { while (*str) { if (*str >= 0x20 && *str <= 0x7E) { hid_keyboard_send(*str, 0); vTaskDelay(pdMS_TO_TICKS(10)); hid_keyboard_release(); vTaskDelay(pdMS_TO_TICKS(5)); } str++; } } void hid_keyboard_send_ctrl_c(void) { hid_keyboard_send(HID_KEY_C, HID_MOD_LEFTCTRL); vTaskDelay(pdMS_TO_TICKS(10)); hid_keyboard_release(); } ``` ### 4. 主程序 ```c #include "esp_log.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "tusb.h" static const char *TAG = "USB_HID"; void app_main(void) { ESP_LOGI(TAG, "ESP32-S3 USB HID + WiFi + BLE Starting..."); // 1. 初始化 WiFi wifi_init(); ESP_LOGI(TAG, "WiFi initialized"); // 2. 初始化蓝牙 ble_init(); ESP_LOGI(TAG, "BLE initialized"); // 3. 初始化 TinyUSB tinyusb_init(); ESP_LOGI(TAG, "USB HID initialized, waiting for host..."); while (1) { if (tud_mounted()) { int c = getchar(); if (c != EOF) { switch (c) { case 'a': hid_keyboard_send(0x04, 0); break; case 'c': hid_keyboard_send_ctrl_c(); break; case '\n': hid_keyboard_send(0x28, 0); break; default: break; } vTaskDelay(pdMS_TO_TICKS(10)); hid_keyboard_release(); } } vTaskDelay(pdMS_TO_TICKS(10)); } } ``` --- ## 常用键码参考 | 按键 | 键码 | |------|------| | A-Z | 0x04-0x1D | | 0-9 | 0x1E-0x27 | | Enter | 0x28 | | ESC | 0x29 | | Backspace | 0x2A | | Tab | 0x2B | | Space | 0x2C | | F1-F12 | 0x3A-0x45 | | ↑↓←→ | 0x52-0x4F | --- ## 官方例程参考 | 示例路径 | 功能 | |----------|------| | `examples/peripherals/usb/device/hid_device` | USB HID 键鼠 | | `examples/peripherals/usb/device/cdc_serial` | USB 虚拟串口 | | `examples/peripherals/usb/device/msc_disk` | USB 虚拟 U 盘 | | `examples/wifi/ble_esp` | 蓝牙配网 | | `examples/wifi/wps` | WiFi 配网 | | `examples/bluetooth/blufi` | 蓝牙配网 + WiFi | --- ## 快速启动命令 ```bash # 进入 HID 示例 cd $IDF_PATH/examples/peripherals/usb/device/hid_device # 配置(开启 WiFi + 蓝牙) idf.py menuconfig # 编译烧录 idf.py -p COMX flash monitor ``` --- ## 参考资源 - TinyUSB 官方文档:https://github.com/hathach/tinyusb - ESP-IDF USB 示例:https://github.com/espressif/esp-idf/tree/master/examples/peripherals/usb/device - ESP-IDF 蓝牙文档:https://docs.espressif.com/projects/esp-idf/zh_CN/latest/esp32s3/api-reference/bluetooth/index.html - ESP-IDF WiFi 文档:https://docs.espressif.com/projects/esp-idf/zh_CN/latest/esp32s3/api-reference/network/esp_wifi.html - esp-idf VS Code 插件:https://marketplace.visualstudio.com/items?itemName=espressif.esp-idf-extension