这个项目使用两块ESP32-S3开发板构建了一套完整的无线空鼠系统,其中一块作为发送端(空鼠本体),另一块作为接收端(USB适配器)。系统采用ESPNOW协议进行无线通信,完美解决了老旧电视、台式机等无蓝牙设备的鼠标控制问题。
我选择ESP32-S3主要基于三点考虑:首先它集成了WiFi和蓝牙双模,为后续功能扩展预留空间;其次S3系列原生支持USB-OTG,这是实现HID设备的关键;最后我手头正好有几片S3-zero开发板,其紧凑的尺寸(仅21x17.6mm)非常适合做成便携设备。虽然用S3做这么简单的功能确实有点"杀鸡用牛刀",但考虑到开发效率和原型验证,这是最合适的选择。
发送端组件:
接收端组件:
特别注意:经实测ESP32-C3/C6因缺少USB-OTG功能无法作为接收端,只能用作发送端。如果考虑成本,发送端可以用C3,但接收端必须用S3系列。
发送端电路设计:
接收端电路设计:
code复制https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
接收端通过USB HID协议模拟标准鼠标设备,核心代码如下:
cpp复制#include <USB.h>
#include <USBHIDMouse.h>
USBHIDMouse Mouse;
void setup() {
Mouse.begin(); // 初始化USB HID
while(!USB.ready()) delay(100); // 等待USB枚举完成
}
关键点:
cpp复制#include <esp_now.h>
#include <WiFi.h>
typedef struct {
int16_t moveX, moveY;
uint8_t buttons;
} mouse_data_t;
void setup() {
WiFi.mode(WIFI_STA);
esp_now_init();
esp_now_register_recv_cb([](const uint8_t *mac, const uint8_t *data, int len) {
// 数据处理回调
});
}
注意事项:
核心算法流程:
cpp复制MPU6050 mpu;
void loop() {
if(mpu.dmpGetCurrentFIFOPacket(fifoBuffer)) {
mpu.dmpGetQuaternion(&q, fifoBuffer);
mpu.dmpGetGravity(&gravity, &q);
mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
float yawDiff = (ypr[0] - lastYaw) * 180/M_PI;
float rollDiff = (ypr[2] - lastRoll) * 180/M_PI;
if(abs(yawDiff)>0.1) mouseData.moveX = yawDiff*2.8;
if(abs(rollDiff)>0.1) mouseData.moveY = -rollDiff*2.8;
}
}
校准技巧:
mpu.CalibrateAccel(6)和mpu.CalibrateGyro(6)重新校准采用状态机方式检测按键,避免抖动:
cpp复制void checkButtons() {
static uint32_t lastCheck = 0;
if(millis()-lastCheck < 20) return; // 20ms防抖
leftPressed = !digitalRead(PIN_LEFT);
// 其他按键同理...
lastCheck = millis();
}
灵敏度调节:
MOUSE_SENSITIVITY系数(建议2.5-3.5)ANGLE_THRESHOLD影响微小移动抑制(建议0.05-0.15弧度)功耗优化:
WiFi.setSleep(true)启用WiFi睡眠运动平滑处理:
cpp复制// 添加移动平均滤波
const int filterSize = 3;
static int16_t xBuf[filterSize], yBuf[filterSize];
xBuf[filterIndex] = rawX;
yBuf[filterIndex] = rawY;
mouseData.moveX = (xBuf[0]+xBuf[1]+xBuf[2])/3;
问题1:接收端无法识别为HID设备
问题2:光标移动不流畅
cpp复制mpu.setDMPEnabled(true);
mpu.setRate(4); // 0=1kHz, 4=200Hz
问题3:按键响应延迟
通过以下流程实现免配置配对:
关键代码片段:
cpp复制// 接收端广播配对信标
void startPairing() {
WiFi.softAP("ESP-AirMouse");
esp_now_add_peer(broadcastMac, ESP_NOW_ROLE_COMBO, 1, NULL, 0);
}
// 发送端扫描处理
void scanDone(int found) {
for(int i=0; i<found; i++) {
if(WiFi.SSID(i)=="ESP-AirMouse") {
esp_now_send(targetMac, pairingData, sizeof(pairingData));
}
}
}
通过拨码开关选择工作模式:
硬件改进:
发送端:
接收端:
实测功耗对比:
| 模式 | 电流消耗 | 续航时间(500mAh) |
|---|---|---|
| 常规模式 | 45mA | 11小时 |
| 优化后 | 18mA | 27小时 |
经过两个月的迭代开发,这个无线空鼠系统已经可以稳定运行。实测在智能电视、Windows电脑和树莓派上都能完美兼容。以下是一些实战经验:
外壳设计建议:
量产成本控制:
用户体验优化:
这个项目的独特价值在于:
对于想复现项目的开发者,建议从接收端开始验证USB HID功能,再逐步添加发送端功能。遇到MPU6050校准问题时,可以先用现成的校准数据(网上有很多分享),等系统能跑了再深入研究自动校准算法。