Files
JoyD/ESP32/ESP32_虚拟键鼠方案.md
zqm cdf64fa31f feat(XCamera): 实现异步抓图功能并优化图像处理
fix(nginx): 调整企业微信回调代理路径

feat(gateway): 添加企业微信消息处理功能

docs: 更新项目规划文档和企业微信配置详情

refactor(XCamera): 重构LED检测和图像处理逻辑

test: 添加ONVIF抓图测试功能
2026-03-31 11:04:43 +08:00

441 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 库**TinyUSBESP-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 设备模式
---
### 问题2Rust 环境隔离(关键!)
**问题描述:**
用户同时进行普通 Rust 应用开发和 ESP32 开发,普通 Rust 使用 `std` targetESP32 使用 `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