441 lines
10 KiB
Markdown
441 lines
10 KiB
Markdown
|
|
# 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
|