ESP32-S3实现远程虚拟的USB有线鼠标键盘

ESP32-S3实现远程虚拟的USB有线鼠标键盘

想要用ESP32-S3实现远程虚拟USB有线鼠标键盘,让ESP32-S3同时具备两个关键能力:一是模拟成电脑可识别的USB有线HID设备(鼠标+键盘),二是通过远程通信(WiFi/BLE)接收控制指令,最终让电脑像接了真实USB键鼠一样,响应远程的操作。这个方案无需额外USB转串口模块,直接利用ESP32-S3的USB-OTG功能实现"有线键鼠模拟",远程端通过WiFi(推荐,传输距离远)或BLE控制。

一、核心实现原理

ESP32-S3的USB-OTG接口支持USB Device模式,可配置为HID(人机接口设备)类,向电脑发送标准的鼠标/键盘HID报告,电脑会自动识别为"USB有线键鼠";同时ESP32-S3通过WiFi建立TCP/UDP连接,接收远程端(比如手机/另一台电脑)的控制指令,解析后转换成HID报告发送给电脑,完成远程操作。

简单来说:
远程控制端WiFi(TCP)ESP32-S3(解析指令)USB HID模拟电脑(识别为有线键鼠)

二、硬件准备(极简,新手友好)

组件 规格/要求 作用说明
ESP32-S3开发板 带USB-OTG口(如ESP32-S3-DevKitC-1) 核心,同时实现WiFi通信和USB HID模拟
USB Type-C数据线 数据款(非仅充电) 连接ESP32-S3的USB-OTG口到电脑
电脑(Windows/Linux) 任意版本 识别ESP32-S3为USB键鼠并响应操作
远程控制端 手机/另一台电脑(带网络调试助手) 发送键鼠控制指令(如"移动鼠标""按键盘A")

三、软件环境准备

  1. Arduino IDE配置 (新手首选,无需复杂编译):
    • 安装Arduino IDE,在「文件→首选项」中添加ESP32开发板地址:https://dl.espressif.com/dl/package_esp32_index.json
    • 在「工具→开发板→ESP32 Arduino」中选择ESP32-S3 Dev Module
    • 关键配置:USB ModeUSB-OTG (HID)CPU Frequency80MHz(降频降功耗),Flash Size4MB

四、完整实现代码(WiFi+USB HID键鼠,开箱即用)

以下代码实现核心功能:

  • ESP32-S3连接指定WiFi,作为TCP服务器等待远程指令;
  • 模拟USB有线鼠标(移动、左键点击)+ 键盘(单键、组合键);
  • 自定义简单指令协议,远程端发送指令即可控制。
cpp 复制代码
#include <Arduino.h>
#include <WiFi.h>
#include <USBHID.h>
#include <HIDReports.h>
#include <HIDTypes.h>

// ===================== 配置项(修改为你的信息)=====================
#define WIFI_SSID "你的WiFi名称"
#define WIFI_PWD  "你的WiFi密码"
#define TCP_PORT  8888       // TCP通信端口
#define BAUD_RATE 115200     // 串口波特率(调试用)

// ===================== USB HID 配置(鼠标+键盘)=====================
USBHID hid;
// 鼠标HID报告描述符(标准格式:X/Y移动、滚轮、按键)
uint8_t mouseReportDesc[] = {
  0x05, 0x01,        // USAGE_PAGE (Generic Desktop)
  0x09, 0x02,        // USAGE (Mouse)
  0xA1, 0x01,        // COLLECTION (Application)
  0x09, 0x01,        //   USAGE (Pointer)
  0xA1, 0x00,        //   COLLECTION (Physical)
  0x05, 0x09,        //     USAGE_PAGE (Button)
  0x19, 0x01,        //     USAGE_MINIMUM (Button 1)
  0x29, 0x03,        //     USAGE_MAXIMUM (Button 3)
  0x15, 0x00,        //     LOGICAL_MINIMUM (0)
  0x25, 0x01,        //     LOGICAL_MAXIMUM (1)
  0x95, 0x03,        //     REPORT_COUNT (3)
  0x75, 0x01,        //     REPORT_SIZE (1)
  0x81, 0x02,        //     INPUT (Data,Var,Abs)
  0x95, 0x01,        //     REPORT_COUNT (1)
  0x75, 0x05,        //     REPORT_SIZE (5)
  0x81, 0x01,        //     INPUT (Cnst,Var,Abs)
  0x05, 0x01,        //     USAGE_PAGE (Generic Desktop)
  0x09, 0x30,        //     USAGE (X)
  0x09, 0x31,        //     USAGE (Y)
  0x09, 0x38,        //     USAGE (Wheel)
  0x15, 0x81,        //     LOGICAL_MINIMUM (-127)
  0x25, 0x7F,        //     LOGICAL_MAXIMUM (127)
  0x75, 0x08,        //     REPORT_SIZE (8)
  0x95, 0x03,        //     REPORT_COUNT (3)
  0x81, 0x06,        //     INPUT (Data,Var,Rel)
  0xC0,              //   END_COLLECTION
  0xC0               // END_COLLECTION
};
// 键盘HID报告描述符(标准104键+修饰键)
uint8_t keyboardReportDesc[] = {
  0x05, 0x01,        // USAGE_PAGE (Generic Desktop)
  0x09, 0x06,        // USAGE (Keyboard)
  0xA1, 0x01,        // COLLECTION (Application)
  0x05, 0x07,        //   USAGE_PAGE (Keyboard/Keypad)
  0x19, 0xE0,        //   USAGE_MINIMUM (Keyboard Left Control)
  0x29, 0xE7,        //   USAGE_MAXIMUM (Keyboard Right GUI)
  0x15, 0x00,        //   LOGICAL_MINIMUM (0)
  0x25, 0x01,        //   LOGICAL_MAXIMUM (1)
  0x95, 0x08,        //   REPORT_COUNT (8)
  0x75, 0x01,        //   REPORT_SIZE (1)
  0x81, 0x02,        //   INPUT (Data,Var,Abs)
  0x95, 0x01,        //   REPORT_COUNT (1)
  0x75, 0x08,        //   REPORT_SIZE (8)
  0x81, 0x03,        //   INPUT (Cnst,Var,Abs)
  0x95, 0x06,        //   REPORT_COUNT (6)
  0x75, 0x08,        //   REPORT_SIZE (8)
  0x15, 0x00,        //   LOGICAL_MINIMUM (0)
  0x25, 0x65,        //   LOGICAL_MAXIMUM (101)
  0x05, 0x07,        //   USAGE_PAGE (Keyboard/Keypad)
  0x19, 0x00,        //   USAGE_MINIMUM (Reserved)
  0x29, 0x65,        //   USAGE_MAXIMUM (Keyboard Application)
  0x81, 0x00,        //   INPUT (Data,Array)
  0xC0               // END_COLLECTION
};
// 定义HID报告结构
HIDReport mouseReport = {mouseReportDesc, sizeof(mouseReportDesc)};
HIDReport keyboardReport = {keyboardReportDesc, sizeof(keyboardReportDesc)};

// ===================== TCP通信相关 =====================
WiFiServer tcpServer(TCP_PORT);
WiFiClient tcpClient;
String recvData = "";

// ===================== 键鼠控制函数(核心)=====================
// 鼠标控制:x/y为移动偏移(-127~127),btn为按键(1=左键,2=右键,4=中键),wheel为滚轮(-127~127)
void mouseControl(int8_t x, int8_t y, uint8_t btn, int8_t wheel = 0) {
  uint8_t report[4] = {btn, x, y, wheel}; // 鼠标报告:按键、X、Y、滚轮
  hid.sendReport(&mouseReport, report, sizeof(report));
}

// 键盘控制:modifier为修饰键(如0x01=左Ctrl,0x02=左Shift),key为按键(如0x04=A,0x05=B)
void keyboardControl(uint8_t modifier, uint8_t key) {
  uint8_t report[8] = {modifier, 0, key, 0, 0, 0, 0, 0}; // 键盘报告格式
  hid.sendReport(&keyboardReport, report, sizeof(report));
  delay(50); // 按键保持时间
  // 释放按键(避免长按)
  memset(report, 0, sizeof(report));
  hid.sendReport(&keyboardReport, report, sizeof(report));
}

// ===================== 指令解析(自定义简单协议)=====================
// 指令格式示例:
// 鼠标移动:M|x|y (如M|10|5 → 鼠标右移10,下移5)
// 鼠标左键点击:MB|1 (MB|0=释放,MB|1=按下)
// 键盘按键:K|mod|key (如K|0|4 → 按A键;K|1|4 → 按Ctrl+A)
void parseCmd(String cmd) {
  cmd.trim(); // 去除空格
  if (cmd.startsWith("M|")) { // 鼠标移动
    cmd = cmd.substring(2);
    int x = cmd.substring(0, cmd.indexOf("|")).toInt();
    int y = cmd.substring(cmd.indexOf("|")+1).toInt();
    mouseControl((int8_t)x, (int8_t)y, 0);
    Serial.printf("鼠标移动:X=%d, Y=%d\n", x, y);
  } else if (cmd.startsWith("MB|")) { // 鼠标按键
    int btn = cmd.substring(3).toInt();
    mouseControl(0, 0, btn);
    Serial.printf("鼠标按键:%d\n", btn);
  } else if (cmd.startsWith("K|")) { // 键盘按键
    cmd = cmd.substring(2);
    int mod = cmd.substring(0, cmd.indexOf("|")).toInt();
    int key = cmd.substring(cmd.indexOf("|")+1).toInt();
    keyboardControl((uint8_t)mod, (uint8_t)key);
    Serial.printf("键盘按键:修饰键=%d, 按键=%d\n", mod, key);
  }
}

// ===================== WiFi连接 =====================
void connectWiFi() {
  WiFi.begin(WIFI_SSID, WIFI_PWD);
  Serial.print("连接WiFi...");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("\nWiFi连接成功!");
  Serial.print("ESP32-S3 IP地址:");
  Serial.println(WiFi.localIP());
  tcpServer.begin(); // 启动TCP服务器
  Serial.printf("TCP服务器已启动,端口:%d\n", TCP_PORT);
}

// ===================== 初始化 =====================
void setup() {
  Serial.begin(BAUD_RATE);
  delay(1000);

  // 初始化USB HID
  hid.addReport(&mouseReport);
  hid.addReport(&keyboardReport);
  hid.begin();
  while (!hid.ready()) { // 等待HID初始化完成
    delay(100);
    Serial.print(".");
  }
  Serial.println("\nUSB HID初始化完成,电脑应识别为USB键鼠!");

  // 连接WiFi
  connectWiFi();
}

// ===================== 主循环 =====================
void loop() {
  // 检测TCP客户端连接
  if (!tcpClient.connected()) {
    tcpClient = tcpServer.available();
    if (tcpClient) {
      Serial.println("远程客户端已连接!");
    }
    delay(100);
    return;
  }

  // 读取远程指令
  while (tcpClient.available() > 0) {
    char c = tcpClient.read();
    if (c == '\n') { // 按换行符分割指令
      parseCmd(recvData);
      recvData = "";
    } else {
      recvData += c;
    }
  }

  delay(10);
}

五、关键代码解析(新手必看)

  1. USB HID报告描述符

    • 是电脑识别"USB键鼠"的核心,上述代码用的是标准HID报告格式,无需修改,电脑会自动驱动;
    • 鼠标报告包含"3个按键(左/右/中)、X/Y移动、滚轮",键盘报告包含"8个修饰键(Ctrl/Shift/Alt等)+ 6个普通按键"。
  2. 指令协议

    • 自定义极简协议,远程端发送指令时按格式(如M|10|5),ESP32-S3解析后调用mouseControl/keyboardControl发送HID报告;
    • 键盘按键码参考标准HID值(如0x04=A、0x05=B、0x1E=0、0x44=Enter),修饰键码:0x01=左Ctrl、0x02=左Shift、0x04=左Alt、0x08=左Win。
  3. TCP通信

    • ESP32-S3作为TCP服务器,远程端(手机/电脑)用"网络调试助手"连接其IP+端口(8888),发送指令即可控制。

六、测试步骤(5分钟验证)

  1. 烧录代码 :修改WIFI_SSIDWIFI_PWD后,烧录到ESP32-S3,用USB Type-C线连接ESP32-S3的USB-OTG口到电脑;
  2. 识别设备:电脑会自动识别"USB Human Interface Device"(无需装驱动),在「设备管理器」可看到"鼠标""键盘";
  3. 远程控制
    • 打开串口监视器,查看ESP32-S3的IP地址(如192.168.1.100);
    • 远程端(如电脑)打开"网络调试助手",选择「TCP客户端」,输入IP+8888,连接成功后发送指令:
      • 发送M|10|5 → 鼠标向右移动10像素、向下移动5像素;
      • 发送MB|1 → 鼠标左键按下,发送MB|0 → 释放;
      • 发送K|0|4 → 按下并释放A键,发送K|1|4 → 按下并释放Ctrl+A。

七、避坑指南(新手常踩)

  1. USB线选数据款:仅充电的USB线会导致电脑无法识别HID设备,务必用带数据传输的Type-C线;
  2. HID初始化等待 :代码中while (!hid.ready())必须保留,否则HID未就绪会导致指令无响应;
  3. 指令格式严格 :远程指令需按M|x|y/MB|btn/K|mod|key格式,且以换行符结尾(网络调试助手勾选"发送新行");
  4. WiFi断连处理:代码可扩展"重连WiFi"逻辑,避免远程端断连后无法恢复。

总结

实现ESP32-S3远程虚拟USB有线键鼠的核心关键点:

  1. USB HID配置:用标准报告描述符让电脑识别为有线键鼠,无需额外驱动;
  2. 远程通信:WiFi TCP是最通用的远程方式,传输距离远、稳定性高;
  3. 指令解析:自定义简单协议,降低远程端开发成本,适配手机/电脑等控制端。

这套方案无需复杂硬件,代码开箱即用,既实现了"USB有线键鼠模拟"的稳定性,又具备"远程控制"的灵活性,适合远程办公、智能家居控制等场景。

相关推荐
呉師傅15 小时前
东芝3525AC彩色复印机CC219测试页打印方法【实际操作】
运维·网络·windows·计算机外设·电脑
弓.长.21 小时前
基础入门 React Native 鸿蒙跨平台开发:KeyboardAvoidingView 键盘避让视图
react native·计算机外设·harmonyos
Luminbox紫创测控1 天前
车载抬头显示器HUD阳光倒灌的检测
计算机外设
正方形的轮子1 天前
testmouse.com 一款免费的在线鼠标测试工具
测试工具·计算机外设
TESmart碲视1 天前
Mac多显示器支持:TESmart USB-C KVM(搭载DisplayLink技术)全面解析
macos·计算机外设·音视频·外设·kvm切换器·tesmart
LDR0062 天前
显示器 Type-C 口有哪些妙用呢?
计算机外设
特立独行的猫a3 天前
ESP32小智AI设备端接入后台流程详解
esp32·设备接入·小智ai
sanqima3 天前
设置鼠标的灵敏度
计算机外设·鼠标灵敏度
TESmart碲视4 天前
解锁多屏办公效率:2026年深度解析EDID技术与KVM切换器解决方案
macos·计算机外设·kvm切换器·tesmart·双屏kvm切换器·tesmart碲视