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

10 KiB
Raw Blame 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 使用 xtensariscv32 target。如果环境配置不当会导致 cargo 命令冲突。

解决方案:

推荐:使用 VS Code + esp-idf extension完全隔离

  1. 安装 esp-idf VS Code 插件

    • 搜索 "Espressif IDF" 安装
    • 自动管理 ESP-IDF 和工具链,不影响系统 Rust
  2. 配置专用 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

参考资源