Files
JoyD/ESP32/ESP32_虚拟键鼠方案.md

441 lines
10 KiB
Markdown
Raw Normal View History

# 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