fix(nginx): 调整企业微信回调代理路径 feat(gateway): 添加企业微信消息处理功能 docs: 更新项目规划文档和企业微信配置详情 refactor(XCamera): 重构LED检测和图像处理逻辑 test: 添加ONVIF抓图测试功能
10 KiB
10 KiB
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(完全隔离)
-
安装 esp-idf VS Code 插件
- 搜索 "Espressif IDF" 安装
- 自动管理 ESP-IDF 和工具链,不影响系统 Rust
-
配置专用 VS Code 工作区
- 为 ESP32 项目创建独立工作区
- 插件会自动激活 ESP 环境
✅ 替代:使用 espup 创建独立工具链
# 安装到指定目录,不污染系统环境
cargo install espup
# 安装 ESP 工具链到指定目录
espup install -t ~/esp/esp-idf
# 在 ESP32 项目中激活(不影响全局)
source ~/esp/esp-idf/export.sh
❌ 避免:不要修改系统默认 Rust
# 错误做法 - 会影响普通 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):
# 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. 创建项目
# 从官方示例创建 USB HID 项目
cp -r $IDF_PATH/examples/peripherals/usb/device/hid_device ./esp32_hid_keyboard
cd esp32_hid_keyboard
3. ESP-IDF 配置(关键!)
# 进入配置菜单
idf.py menuconfig
3.1 开启 USB 支持
→ Hardware Settings → USB CONFIG
- Enable USB OTG
- Enable USB CDC
- Force USB OTG mode as device
3.2 开启 TinyUSB
→ Component config → TinyUSB
- Enable TinyUSB
- Support CDC (Serial)
- Support HID (Keyboard/Mouse/Gamepad)
- HID keyboard
- HID mouse
- Support MSC (Mass Storage)
- Support Ethernet (RNDIS/ECM)
3.3 开启 WiFi + 蓝牙(重点!)
→ Component config → Wi-Fi
- Enable WiFi
- WiFi Station
- WiFi AP (可选)
→ Component config → Bluetooth
- Enable Bluetooth
- Bluetooth LE (BLE)
- BLE GATT Server
3.4 配置 Flash 和 RAM
→ Serial flasher config
- Default flash size: 8MB
- Partition table: Factory app, two OTA definitions
核心代码
1. WiFi + 蓝牙同时初始化
#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 初始化
#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. 键盘发送函数
// 键盘修饰键
#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. 主程序
#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 |
快速启动命令
# 进入 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