From 6975706f48b25b8049655369f39adb665b5b58e0 Mon Sep 17 00:00:00 2001 From: cuicuiV5 Date: Tue, 1 Sep 2026 14:19:22 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DEVELOPMENT.md | 298 +++++++++++++++++++++++++++++++------------------ 1 file changed, 189 insertions(+), 109 deletions(-) diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 9c836d1..3635e0e 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -1,8 +1,9 @@ # RemoteMapper-ESP32 开发者手册与技术全景文档 -## 小米蓝牙语音遥控器 → USB 复合设备(免驱物理麦克风 + 独立多媒体键盘 + Web 可视化后台) +## 小米蓝牙语音遥控器 2 Pro (RC003) ➔ USB 复合硬件桥接固件 -> **适用固件版本**:v1.0.0+ +> **适用固件版本**:v1.0.0+ (Production Release) > **目标主控硬件**:ESP32-S3-DevKitC-1 N16R8(16MB Octal SPI Flash + 8MB Octal SPI PSRAM) +> **适配遥控型号**:小米蓝牙语音遥控器 2 Pro(型号:RC003,铝合金金属机身款) > **文档受众**:嵌入式开发者、代码维护者与后续 AI Agent 协作开发 --- @@ -12,20 +13,21 @@ 2. [硬件选型与存储分区设计 (16MB Flash)](#二-硬件选型与存储分区设计-16mb-flash) 3. [BLE 与 ATVV 语音协议栈全流程解析](#三-ble-与-atvv-语音协议栈全流程解析) 4. [DSP 实时音频处理流水线算法设计](#四-dsp-实时音频处理流水线算法设计) -5. [按键映射手势状态机与键码转换](#五-按键映射手势状态机与键码转换) -6. [USB 复合设备架构 (UAC 1.0 + HID + CDC)](#六-usb-复合设备架构-uac-10--hid--cdc) -7. [Wi-Fi 双模网络与嵌入式 Web 控制台](#七-wi-fi-双模网络与嵌入式-web-控制台) -8. [RESTful API 完整接口规范](#八-restful-api-完整接口规范) -9. [源码文件索引与模块依赖关系](#九-源码文件索引与模块依赖关系) -10. [编译、烧录与测试指南](#十-编译烧录与测试指南) -11. [故障排查手册与常见问题 (Troubleshooting)](#十一-故障排查手册与常见问题-troubleshooting) -12. [未来扩展与演进路线 (Roadmap)](#十二-未来扩展与演进路线-roadmap) +5. [多触发按键映射状态机与键码引擎](#五-多触发按键映射状态机与键码引擎) +6. [Flash NVS 存储引擎与开机自愈容灾机制](#六-flash-nvs-存储引擎与开机自愈容灾机制) +7. [USB 复合设备架构 (UAC 1.0 + HID + CDC)](#七-usb-复合设备架构-uac-10--hid--cdc) +8. [Wi-Fi 双模网络与嵌入式 Web 控制台](#八-wi-fi-双模网络与嵌入式-web-控制台) +9. [PC 本地伴侣程序架构 (pc_companion)](#九-pc-本地伴侣程序架构-pc_companion) +10. [RESTful API 完整接口规范](#十-restful-api-完整接口规范) +11. [源码文件索引与模块依赖关系](#十一-源码文件索引与模块依赖关系) +12. [编译、烧录与测试指南](#十二-编译烧录与测试指南) +13. [故障排查手册与常见问题 (Troubleshooting)](#十三-故障排查手册与常见问题-troubleshooting) --- # 一. 系统整体架构与多核任务分配 -ESP32-S3 具有 Xtensa® 双核 32 位 LX7 微处理器(主频 240MHz)。为了保证音频实时解码不卡顿、USB Isochronous 报文不丢包、BLE 吞吐高响应,系统采用 **FreeRTOS 双核物理隔离调度架构**: +ESP32-S3 配备 Xtensa® 双核 32 位 LX7 微处理器(主频 240MHz)。为了保证实时音频解码 0 掉帧、USB Isochronous 报文 1000Hz 零抖动、BLE 协议栈秒级响应以及 Web 异步请求不卡顿,系统采用 **FreeRTOS 双核物理隔离调度架构**: ```text ┌────────────────────────────────────────────────────────────────────────────────────────┐ @@ -34,12 +36,13 @@ ESP32-S3 具有 Xtensa® 双核 32 位 LX7 微处理器(主频 240MHz)。为 │ Core 0 (专职蓝牙与 DSP) │ Core 1 (专职 USB、网络与业务) │ ├───────────────────────────────────────────┼────────────────────────────────────────────┤ │ • NimBLE Central 协议栈 (扫描/连接/绑定) │ • TinyUSB 复合设备 (UAC Mic + HID + CDC) │ -│ • HOGP 按键报文解析 (订阅 0x2A4D) │ • UAC 麦克风 1ms Isochronous 数据泵 (16kHz)│ -│ • ATVV 语音握手 (0x0A/0x0C/0x0E/0x00) │ • KeyState 映射手势状态机 (单击/长按/连发) │ -│ • 4-bit IMA-ADPCM 实时流式解码 │ • Wi-Fi 双模管理器 (AP 192.168.4.1 + STA) │ -│ • Declip 尖峰消除 + 3-Tap FIR 低通平滑 │ • Captive Portal DNS 强制门户 (Port 53) │ -│ • 动态 AGC 增益控制与软削波 │ • HTTP WebServer (Port 80 RESTful API) │ -│ • 音频数据注入 SPSC 无锁环形缓冲区 │ • Serial / CDC CLI 控制台指令调度 │ +│ • HOGP 原始报文解析 (订阅 0x2A4D) │ • UAC 麦克风 1ms Isochronous 数据泵 (16kHz)│ +│ • ATVV 语音握手 (0x0A/0x0C/0x0E/0x00) │ • Multi-Trigger 状态机 (单击/长按/双击) │ +│ • 4-bit IMA-ADPCM 实时流式解码 │ • Flash NVS 存储引擎 & 自愈容灾校验 │ +│ • Declip 尖峰消除 + 3-Tap FIR 低通滤波 │ • Wi-Fi 双模管理器 (AP 192.168.4.1 + STA) │ +│ • 动态 AGC 增益控制与软削波 │ • Captive Portal DNS 强制门户 (Port 53) │ +│ • 音频数据注入 SPSC 无锁环形缓冲区 │ • HTTP WebServer (Port 80 RESTful API) │ +│ • RGB 状态指示灯控制器 (WS2812 / GPIO48) │ • Serial / CDC CLI 控制台指令调度 │ └───────────────────────────────────────────┴────────────────────────────────────────────┘ │ ▲ └────── RingBuffer ───┘ @@ -51,12 +54,12 @@ ESP32-S3 具有 Xtensa® 双核 32 位 LX7 微处理器(主频 240MHz)。为 # 二. 硬件选型与存储分区设计 (16MB Flash) ### 1. 硬件连接规范 -* **USB 接口**:必须连接 ESP32-S3 开发板的 **`USB/OTG` 接口**(对应 GPIO 19: D-,GPIO 20: D+),**不可连接 UART 口**; -* **天线**:板载 PCB 天线或 IPEX 外接天线; -* **供电**:USB 5V 直接供电,典型功耗约 120mA。 +* **主控型号**:ESP32-S3-DevKitC-1 N16R8(16MB Flash + 8MB Octal PSRAM); +* **USB 接口**:必须连接 ESP32-S3 开发板的 **`USB/OTG` 接口**(对应内部硬件 GPIO 19: D-,GPIO 20: D+),**不可连接 UART 口**; +* **遥控器型号**:**小米蓝牙语音遥控器 2 Pro(型号:RC003)**。 +* **供电**:USB 5V 直接供电,典型运行功耗约 110~130mA。 ### 2. 16MB Flash 分区表 (`default_16MB.csv`) -为确保后续支持 OTA 无缝固件升级,Flash 分区规划如下: | 分区名 | 类型 | 子类型 | 起始偏移 (Offset) | 大小 (Size) | 用途说明 | | :--- | :--- | :--- | :--- | :--- | :--- | @@ -72,15 +75,15 @@ ESP32-S3 具有 Xtensa® 双核 32 位 LX7 微处理器(主频 240MHz)。为 # 三. BLE 与 ATVV 语音协议栈全流程解析 ### 1. GATT 服务与特征值定义 -* **HOGP 规范服务**:UUID `0x1812` ➔ HID Report 特征值 `0x2A4D`(Notify,订阅捕获按键字节); +* **HOGP 规范服务**:UUID `0x1812` ➔ HID Report 特征值 `0x2A4D`(Notify,捕获按键原始报文); * **ATVV 自定义服务**:UUID `ab5e0001-5a21-4f05-bc7d-af01f617b664` - * `ab5e0002-5a21-4f05-bc7d-af01f617b664` (**CMD 通道**,Write Without Response) - * `ab5e0003-5a21-4f05-bc7d-af01f617b664` (**AUDIO 通道**,Notify,接收 ADPCM 数据流) + * `ab5e0002-5a21-4f05-bc7d-af01f617b664` (**CMD 通道**,Write Without Response,下发握手指令) + * `ab5e0003-5a21-4f05-bc7d-af01f617b664` (**AUDIO 通道**,Notify,接收 120 字节 ADPCM 音频帧) * `ab5e0004-5a21-4f05-bc7d-af01f617b664` (**CTL 通道**,Notify,接收语音启停状态) ### 2. ATVV 握手与语音状态机时序 ```text -ESP32-S3 (Central) 小米遥控器 (Peripheral) +ESP32-S3 (Central) 小米遥控器 RC003 (Peripheral) │ │ │── 1. 扫描匹配 "MI RC" 或 MAC 前缀 "c0:5d:39" ─────────────►│ │◄─ 2. 建立 BLE GATT 连接并启用 Bonding ─────────────────────│ @@ -108,7 +111,7 @@ ESP32-S3 (Central) 小米遥控器 (Periphe │ ═════════════════ 用户松开遥控器语音键 ═════════════════ │ │◄─ 10. CTL 通道上报 AUDIO_STOP: [0x00, 0x02] ──────────────│ │ • 释放语音热键 │ - │ • 停止麦克风推流,清空缓冲区 │ + │ • 停止麦克风推流,清空环形缓冲区 │ │ • 重新发送 MIC_OPEN [0x0C, 0x00] 防御下次按键失效 │ ``` @@ -117,9 +120,9 @@ ESP32-S3 (Central) 小米遥控器 (Periphe # 四. DSP 实时音频处理流水线算法设计 ### 1. 4-bit IMA-ADPCM 流式解码 -- **采样参数**:16000 Hz 单声道 16-bit PCM; -- **帧规格**:BLE 传输每包 120 字节(Hi-nibble 高 4 位优先,低 4 位其次),对应解码出 240 个 PCM 采样点; -- **跨帧连续性**:Predictor(预测器)和 StepIndex(步长索引 [0, 88])在帧间保持连续,遇到 ATVV `0x0A` (`AUDIO_SYNC`) 指令时动态重置为同步点。 +- **采样规格**:16000 Hz 单声道 16-bit PCM; +- **帧解析**:BLE 每帧 120 字节(高 4 位优先,低 4 位其次),对应解出 240 个 PCM 采样点; +- **跨帧连续性**:Predictor 与 StepIndex 在帧间保持连续,收到 `AUDIO_SYNC`(`0x0A`)时重置同步点。 ### 2. Declip 单点尖峰消除 消除蓝牙丢包或电磁干扰引起的孤立单点脉冲毛刺: @@ -131,142 +134,222 @@ $$y_i = \frac{x_{i-1} + 2x_i + x_{i+1}}{4}$$ ### 4. 动态 AGC (自动增益控制) - **目标电平**:`28000.0` -- **包络衰减率**:`0.9997`(约 144ms 慢释放,避免呼吸底噪) -- **最大增益限制**:`30.0` 倍(避免空闲无声时放大本底噪声) -- **软削波**:防止波形硬切顶产生破音。 +- **包络衰减率**:`0.9997`(约 144ms 慢释放,避免呼吸底噪); +- **最大增益限制**:`30.0` 倍(避免空闲无声时放大本底噪声); +- **软削波**:防止波形硬切顶产生爆音。 --- -# 五. 按键映射手势状态机与键码转换 +# 五. 多触发按键映射状态机与键码引擎 ### 1. 遥控器全按键物理编码转换表 | 遥控器物理按键 | 小米原始 HID 报文 | ESP32 输出的 USB 信号 | 功能效果 | | :--- | :--- | :--- | :--- | -| **音量+** | `0x80` | `Consumer: Volume Up (0x00E9)` | Windows 系统原生主音量增加 (支持高速连发) | -| **音量-** | `0x81` | `Consumer: Volume Down (0x00EA)`| Windows 系统原生主音量减小 (支持高速连发) | +| **音量+** | `0x80` | `Consumer: Volume Up (0x00E9)` | Windows 原生系统全局音量增加 | +| **音量-** | `0x81` | `Consumer: Volume Down (0x00EA)`| Windows 原生系统全局音量减小 | | **返回键** | `0xF1` | `Consumer: AC Back (0x0224)` | 浏览器/文件管理器后退、播放器退出全屏 | | **语音键** | ATVV 事件 | **注入 `RAlt + ,` + UAC 麦克风** | 唤醒微信输入法语音并实时录音 | -| **电源键** | `0xFF` / `0x66` | **单击 `Alt + Tab` / 长按 `Sleep`** | 窗口切换 / 系统睡眠 | -| **主页键** | `0x24` / `0x4A` | `Keyboard: Win + D` | 一键返回桌面 | -| **菜单键** | `0x5D` / `0x65` | `Keyboard: Space` | 视频播放 / 暂停 | -| **直播/TV 键** | `0xC0` / `0x35` | `Keyboard: F8` | 自定义快捷键 | +| **电源键** | `0xFF` / `0x66` | **单击 `Alt + F4` / 长按 `Sleep (0x0032)`** | 关闭当前窗口 / 系统睡眠 | +| **主页键** | `0x24` | `Keyboard: Win + D` | 一键返回桌面 | +| **菜单键** | `0x5D` | `Keyboard: Space` | 视频播放 / 暂停 | +| **电视键 (TV)**| `0xC0` | `Keyboard: F8` | 自定义快捷键 / 触发伴侣脚本 | | **方向上/下/左/右** | `0x52/51/50/4F`| `Keyboard: Up/Down/Left/Right` | 播放器快进快退调进度 | | **确定键 (OK)** | `0x28` | `Keyboard: Return` | 确认 / 回车 | -### 2. 状态机手势逻辑 -- **单击 (Tap)**:按键按下后在 `long_ms` (600ms) 内释放,无双击需求时立即派发; -- **长按 (Hold)**:按键持续按下超过 `long_ms` (600ms),触发长按动作(如关屏/睡眠),释放时不重复触发单击; -- **连发 (Repeat)**:首次按下延时 `350ms` 后,以 `70ms` 间隔持续发送(专用于音量键流畅调节)。 +### 2. Multi-Trigger 三重触发状态机设计 (`key_state_machine.c`) +每个按键独立维护一个插槽状态结构体,支持 3 种互不干扰的手势: +* **单击(Click)**:瞬时点按或按下保持(Hold 直通); +* **长按(Long Press)**:按压时长超过 `long_ms`(默认 600ms,支持 200~2000ms 自定义),触发长按动作,松开时不触发短按; +* **双击(Double Click)**:按键在 `double_ms`(默认 250ms,支持 100~600ms 自定义)内再次按下,触发双击动作; +* **防卡键瞬时脉冲机制**:超时触发的长按操作,释放时会补发释放包,彻底杜绝键值卡死。 + +### 3. 全链路 16 进制 / 10 进制双模解析引擎 +- 键码统一支持 `0x...` 标准 16 进制与 10 进制无缝互通; +- `parse_u32_or_hex()` 智能识别数字或 `"0x2C"` 字符串格式。 --- -# 六. USB 复合设备架构 (UAC 1.0 + HID + CDC) +# 六. Flash NVS 存储引擎与开机自愈容灾机制 -ESP32-S3 原生 USB OTG 控制器在枚举时向 Windows 呈现一个**复合多接口设备 (Composite Device)**: +为确保在 ESP32-S3 Flash 单扇区(4KB)限制下可靠持久化,存储系统采用分层加固设计: -1. **Interface 0 & 1:USB Audio Class 1.0 (麦克风)** +```text +┌────────────────────────────────────────────────────────┐ +│ Flash NVS 存储安全防御体系 │ +├────────────────────────────────────────────────────────┤ +│ 1. 紧凑型 JSON 序列化 (未启用手势字段自动省略, < 1.4KB) │ +│ 2. 局部原子事务 (Preferences.begin -> putString -> end) │ +│ 3. 强制刷写提交 (nvs_flash_commit) │ +│ 4. 开机自愈容灾 (校验失败/坏块自动降级为 13 键出厂默认) │ +│ 5. 底层分区防误删 (phy / nimble_bond / net80211 只读保护)│ +└────────────────────────────────────────────────────────┘ +``` + +1. **紧凑型 JSON 压缩**:仅序列化启用的触发手势,13 键全部配置 3 触发时总数据量仅约 1.2KB(< 35% 扇区容量); +2. **开机自愈保护**:`key_config_storage_load()` 启动时严格校验 JSON 完整性,若异常断电损坏,自动恢复出厂默认值,杜绝系统变砖。 + +--- + +# 七. USB 复合设备架构 (UAC 1.0 + HID + CDC) + +ESP32-S3 原生 USB OTG 在枚举时向操作系统呈现标准**复合多接口设备 (Composite Device)**: + +1. **Interface 0 & 1:USB Audio Class 1.0 (物理麦克风)** - 格式:PCM 16-bit Mono, 16000Hz; - - 端点:Isochronous IN 端点(采用降频设计:端点大小 64 字节,每 2ms 轮询一次); - - 设备名:`RemoteMapper Wireless Mic`。 - > ⚠️ **开发踩坑警告 (The DWC2 Boundary Crossing Bug)**: - > 在 Arduino TinyUSB 预编译栈(无 SOF 中断)环境下开发 Isochronous 端点时,极易因任务相位漂移触发 DWC2 控制器的硬件 EONUM 奇偶边界跨越缺陷,导致 1000Hz 的物理帧降速为 500Hz 从而引发一半以上的音频丢包和重度锯齿音。 - > **本固件通过合规的 `bInterval=2` 和 `wMaxPacketSize=64` 实现了完美的降频打包修复。** - > 详细复盘分析请参阅根目录独立文档:[USB_UAC_BUG_POSTMORTEM.md](./USB_UAC_BUG_POSTMORTEM.md) + - 端点:Isochronous IN 端点; + - 独创 **bInterval=2 / wMaxPacketSize=64** 降频缓冲设计,完美绕过 DWC2 控制器无 SOF 中断引起的 500Hz 丢帧魔咒; + - **硬件 SOF / DSTS 状态感知**:电脑休眠唤醒时自动重置 UAC 状态机。 2. **Interface 2:USB HID Keyboard** - - 标准 6KRO 键盘报文,负责发送 `Win+D`, `Alt+Tab`, `RAlt+,` 等修饰组合键。 + - 标准 6KRO 键盘报文,负责发送 `Win+D`, `Alt+Tab`, `RAlt+,`, `F13~F24` 等修饰组合键。 + 3. **Interface 3:USB HID Consumer Control** - - 16-bit Usage 控制报文,负责发送系统音量与 AC 后退。 + - 16-bit Usage 控制报文,负责发送原生系统音量(`0x00E9`/`0x00EA`)、静音(`0x00E2`)与 AC 后退(`0x0224`)。 + 4. **Interface 4:USB CDC Serial** - 虚拟串口,用于串口监控与 JSON 命令行交互。 --- -# 七. Wi-Fi 双模网络与嵌入式 Web 控制台 +# 八. Wi-Fi 双模网络与嵌入式 Web 控制台 ### 1. 网络拓扑 -* **AP 模式(热点)**: - - SSID: `RemoteMapper-AP` (无密码) +* **AP 模式(配置热点)**: + - SSID: `RemoteMapper-AP` (开放免密) - IP: `192.168.4.1` (子网掩码 `255.255.255.0`) - - DNS Captive Portal: 拦截所有未识别域名的 DNS 查询并重定向到 `192.168.4.1`。 -* **STA 模式(局域网客户端)**: + - DNS Captive Portal: 自动拦截探测域名并重定向到 `192.168.4.1`。 +* **STA 模式(局域网接入)**: - 连接路由器 2.4GHz Wi-Fi; - - mDNS: 注册服务名 `remotemapper`,局域网访问地址为 **`http://remotemapper.local`**。 + - mDNS: 局域网访问地址为 **`http://remotemapper.local`**。 ### 2. Web 前端架构 (`src/web/web_ui.h`) -- **纯原生实现**:零外部 CDN、零 npm 依赖,HTML/CSS/JS 经 PROGMEM 嵌入 Flash; -- **响应式设计**:完美适配手机屏幕(触摸操作)与电脑宽屏浏览器; -- **暗黑科技主题**:高对比度仪表盘、实时 RSSI 状态指示条、自动滚动日志监视器。 +- **纯原生单页架构**:HTML/CSS/JS 经 PROGMEM 嵌入 Flash,零外部 CDN 依赖; +- **1:1 遥控器仿真面板**:实体按键按下时界面实时高光联动; +- **敲击即录制**:改键窗口直接按下键盘即可自动识别并填入 HID 键码; +- **NVS 纯文本高级编辑器**:内置语法校验、格式化与一键备份导出/导入。 --- -# 八. RESTful API 完整接口规范 +# 九. PC 本地伴侣程序架构 (`pc_companion`) + +为了弥补 USB HID 键盘无法直接执行本地程序/脚本的不足,随固件配备了独立的 Windows 伴侣守护进程: + +```text +┌────────────────────────┐ USB HID Keycode (F13 ~ F24) ┌────────────────────────┐ +│ │ ────────────────────────────────────► │ Windows 宿主机 PC │ +│ ESP32-S3 硬件桥接器 │ │ │ +│ (发送零冲突高阶 F 键) │ │ pc_companion 守护进程 │ +└────────────────────────┘ │ (Win32 RegisterHotKey)│ + └───────────┬────────────┘ + │ 异步派发 + ▼ + ┌────────────────────────┐ + │ • 打开网页 (抖音/Gemini)│ + │ • 批处理 (.bat 切 PS5) │ + │ • PowerShell / Python │ + │ • 启动应用程序 (Steam) │ + └────────────────────────┘ +``` + +* **零冲突暗号**:使用 Windows 系统预留但物理键盘没有的 `F13 ~ F24` 键码; +* **极速无感**:原生 Win32 `RegisterHotKey` 消息循环,待机 CPU 0.0%,内存仅几 MB; +* **热重载配置**:修改 `config.json` 自动生效无需重启。 + +--- + +# 十. RESTful API 完整接口规范 | 请求方法 | 路由 Path | 描述 | 请求体示例 | 响应体示例 | | :--- | :--- | :--- | :--- | :--- | -| `GET` | `/api/status` | 获取系统全量状态与遥测 | 无 | `{"version":"1.0.0","ble_state":3,"frames_decoded":1240,"free_heap":241500,"sta_ip":"192.168.1.108"}` | -| `GET` | `/api/logs` | 获取最近 60 条环形日志 | 无 | `{"logs":["[0012.340] [BLE] Connected","[0014.120] [VOICE] PRESSED"]}` | -| `POST` | `/api/logs/clear` | 清空内部日志环形缓冲 | 无 | `{"status":"cleared"}` | +| `GET` | `/api/status` | 获取系统全量运行状态与统计 | 无 | `{"version":"1.0.0","ble_state":3,"frames_decoded":1240,"free_heap":241500,"sta_ip":"192.168.2.179"}` | +| `GET` | `/api/logs` | 获取最近 250 行环形运行日志 | 无 | `{"logs":["[0012.340] [BLE] Connected","[0014.120] [VOICE] PRESSED"]}` | +| `POST`| `/api/logs/clear` | 清空运行日志缓冲区 | 无 | `{"status":"cleared"}` | | `GET` | `/api/wifi/scan` | 扫描周围 2.4GHz Wi-Fi | 无 | `{"networks":[{"ssid":"Home-WiFi","rssi":-58,"secure":true}]}` | | `POST`| `/api/wifi/config` | 配置并连接家庭 Wi-Fi | `{"ssid":"MyHome","pass":"12345678"}` | `{"status":"ok"}` | +| `GET` | `/api/keymap` | 获取当前按键映射 JSON | 无 | `{"ver":2,"bindings":[{"source_vk":102,"has_click":true,...}]}` | +| `POST`| `/api/keymap/save` | 保存按键映射到 Flash NVS | `{"ver":2,"bindings":[...]}` | `{"status":"saved"}` | | `POST`| `/api/keymap/reset` | 重置按键映射为出厂默认 | 无 | `{"status":"reset_ok"}` | -| `POST`| `/api/ble/reconnect` | 触发重新扫描与连接遥控器 | 无 | `{"status":"reconnecting"}` | -| `POST`| `/api/system/restart` | 软重启 ESP32 | 无 | `{"status":"rebooting"}` | +| `GET` | `/api/keymap/telemetry`| 获取当前按键实时触发遥测 | 无 | `{"source_vk":102,"is_pressed":true,"duration_ms":120,"key_code":44}` | +| `GET` | `/api/ble/scan` | 扫描周围可连接的蓝牙设备 | 无 | `{"devices":[{"name":"Xiaomi Voice Remote","mac":"c0:5d:39:...","rssi":-45}]}` | +| `POST`| `/api/ble/connect` | 连接并绑定指定 MAC 遥控器 | `{"mac":"c0:5d:39:c3:94:ab"}` | `{"status":"connected"}` | +| `POST`| `/api/ble/unpair` | 解绑当前遥控器并清空 NVS | 无 | `{"status":"unpaired"}` | +| `GET` | `/api/ble/info` | 获取当前绑定的遥控器信息 | 无 | `{"name":"Xiaomi Voice Remote","mac":"c0:5d:39:...","bound":true}` | +| `POST`| `/api/ble/reconnect` | 手动触发重新扫描与连接 | 无 | `{"status":"reconnecting"}` | +| `GET` | `/api/nvs` | 获取全量 NVS 命名空间纯文本 | 无 | `{"wifi_conf":{"ssid":"..."},"keymap_conf":{"cfg_json":{...}}}` | +| `POST`| `/api/nvs/save` | 将编辑后的 JSON 写入 NVS | `{"keymap_conf":{"cfg_json":{...}}}` | `{"status":"saved","message":"NVS写入成功"}` | +| `POST`| `/api/nvs/reset` | 清空 NVS 扇区恢复出厂重启 | 无 | `{"status":"erased","message":"NVS已清空..."}` | +| `POST`| `/api/system/restart` | 软重启 ESP32-S3 | 无 | `{"status":"rebooting"}` | --- -# 九. 源码文件索引与模块依赖关系 +# 十一. 源码文件索引与模块依赖关系 ```text D:\tool\RemoteMapper-ESP32/ -├── platformio.ini # PlatformIO 构建配置文件 (Flash/PSRAM/编译选项) +├── platformio.ini # PlatformIO 构建配置文件 (Flash/PSRAM/编译宏) ├── default_16MB.csv # 16MB Flash 分区表 +├── build.bat # 一键编译脚本 +├── flash.bat # 一键烧录脚本 +├── monitor.bat # 串口监视器脚本 +├── test.bat # 单元测试运行脚本 ├── include/ -│ ├── app_config.h # 全局配置 (采样率、引脚、UUID、看门狗超时) +│ ├── app_config.h # 全局配置 (引脚、UUID、缓冲区与看门狗宏) │ └── version.h # 固件版本号 ├── src/ -│ ├── main.cpp # 系统入口与 FreeRTOS 双核任务调度 +│ ├── main.cpp # 系统主入口与 FreeRTOS 双核任务调度 +│ ├── led_indicator.h/.cpp # WS2812 RGB 状态指示灯驱动 (蓝/绿/黄/红呼吸灯) │ ├── log/ -│ │ ├── app_log.h # 全局日志接口 -│ │ └── app_log.cpp # 环形日志缓冲实现 -│ ├── audio/ # DSP 音频流水线模块 -│ │ ├── adpcm_decoder.h/.c # IMA-ADPCM 4-bit 解码器 +│ │ ├── app_log.h # 全局格式化日志接口 +│ │ └── app_log.cpp # 250 行环形内存日志缓冲区实现 +│ ├── audio/ # DSP 实时音频处理流水线 +│ │ ├── adpcm_decoder.h/.c # 4-bit IMA-ADPCM 流式解码器 │ │ ├── audio_filter.h/.c # Declip 尖峰消除与 3-Tap FIR 低通平滑 -│ │ ├── audio_agc.h/.c # 动态 AGC 与软削波 +│ │ ├── audio_agc.h/.c # 动态 AGC 增益控制与软削波 │ │ ├── audio_ring_buffer.h/.c # SPSC 无锁环形缓冲区 -│ │ └── audio_pipeline.h/.c # 音频流水线协调器 +│ │ └── audio_pipeline.h/.c # 音频流水线主协调器 │ ├── ble/ # 蓝牙与协议栈模块 -│ │ ├── ble_remote_client.h # BLE Central 状态机 -│ │ └── ble_remote_client.cpp # NimBLE 驱动、HOGP 按键与 ATVV 语音处理 +│ │ ├── ble_remote_client.h # BLE Central 状态机接口 +│ │ └── ble_remote_client.cpp # NimBLE 驱动、HOGP 监听与 ATVV 握手客户端 │ ├── usb/ # USB 复合设备模块 -│ │ ├── usb_composite.h # USB 设备抽象接口 -│ │ └── usb_composite.cpp # TinyUSB UAC 麦克风与 HID 键盘/多媒体派发 -│ ├── keymap/ # 按键映射状态机 -│ │ ├── key_definitions.h # 遥控器与 USB HID 键码映射表 -│ │ ├── key_state_machine.h # 手势状态机接口 -│ │ └── key_state_machine.c # 单击/长按/连发状态机实现 +│ │ ├── usb_composite.h # TinyUSB 抽象接口 +│ │ └── usb_composite.cpp # UAC 1.0 麦克风 + HID 键盘 + Consumer 控制器 +│ ├── keymap/ # 按键映射与手势引擎 +│ │ ├── key_definitions.h # 遥控器物理键码与 USB HID 键码定义表 +│ │ ├── key_state_machine.h # Multi-Trigger 状态机接口 +│ │ ├── key_state_machine.c # 单击/长按/双击手势引擎实现 +│ │ ├── key_config_storage.h# 按键映射 Flash NVS 持久化接口 +│ │ └── key_config_storage.cpp# 紧凑 JSON 序列化、自愈容灾与十六进制解析 +│ ├── nvs/ # 底层 Flash 分区管理 +│ │ ├── nvs_manager.h # NVS 遍历、读写与重置接口 +│ │ └── nvs_manager.cpp # nvs_entry_find 动态扫描与安全防护 │ ├── wifi/ # 网络模块 │ │ ├── wifi_manager.h # Wi-Fi 管理接口 -│ │ └── wifi_manager.cpp # AP/STA 双模、DNS 强制门户与 mDNS -│ ├── web/ # 嵌入式 Web 模块 -│ │ ├── web_ui.h # 嵌入式 HTML/CSS/JS 单页应用 +│ │ └── wifi_manager.cpp # AP 192.168.4.1、DNS 强制门户与 mDNS +│ ├── web/ # 嵌入式 Web 控制台 +│ │ ├── web_ui.h # HTML/CSS/JS 单页应用 (PROGMEM 存储) │ │ ├── web_server.h # WebServer 接口 -│ │ └── web_server.cpp # RESTful API 路由处理 +│ │ └── web_server.cpp # 18+ 条 RESTful API 路由处理 │ └── cli/ # 串口诊断模块 │ ├── cli_manager.h # CDC CLI 接口 -│ └── cli_manager.cpp # JSON 格式命令行解析器 +│ └── cli_manager.cpp # JSON 命令行解析器 +├── pc_companion/ # PC 本地伴侣程序 +│ ├── remotemapper_host.py # Win32 RegisterHotKey 高性能守护进程 +│ ├── config.json # F13 ~ F24 本地脚本绑定配置文件 +│ ├── start_companion.bat # 前台调试启动脚本 +│ ├── silent_start.vbs # 后台静默启动脚本 +│ ├── enable_autostart.bat # 注册 Windows 开机自启 +│ └── build_exe.bat # PyInstaller 一键打包独立 EXE └── test/ - └── native/test_suite.py # Python 算法单元验证测试套件 + └── native/test_suite.py # 原生算法单元验证测试套件 ``` --- -# 十. 编译、烧录与测试指南 +# 十二. 编译、烧录与测试指南 ### 1. 编译固件 ```cmd -cd /d D:\tool\RemoteMapper-ESP32 build.bat ``` *(底层调用 `python -m platformio run -e esp32s3_n16r8`)* @@ -277,7 +360,7 @@ build.bat flash.bat ``` -### 3. 查看串口监控日志 +### 3. 查看实时串口调试日志 ```cmd monitor.bat ``` @@ -289,19 +372,19 @@ test.bat --- -# 十一. 故障排查手册与常见问题 (Troubleshooting) +# 十三. 故障排查手册与常见问题 (Troubleshooting) ### Q1: 遥控器按键没有反应,串口一直显示 `Scanning for Xiaomi Remote...`? - **排查步骤**: 1. 遥控器是否已被电脑原本的蓝牙占用?请在 Windows 蓝牙设置中先点击「删除设备/断开连接」; - 2. 同时按住遥控器的 **主页键 + 菜单键** 进行配对,直到遥控器指示灯闪烁并发出蜂鸣声; - 3. 观察串口日志,ESP32 扫描到广播名含 `MI RC` 或 MAC `C0:5D:39` 会自动完成配对与服务绑定。 + 2. 同时按住遥控器的 **主页键 + 菜单键** 进行配对,直到遥控器指示灯快闪; + 3. 网页后台进入「📡 蓝牙配对管理」,点击「🔍 扫描附近蓝牙设备」找到 `Xiaomi Voice Remote` 点击连接。 ### Q2: 语音键按下后输入法唤醒了,但没有声音录入? - **排查步骤**: - 1. 打开 Windows「声音设置」➔「录音设备」,查看是否存在 `RemoteMapper Wireless Mic`; - 2. 将微信输入法或讯飞输入法的麦克风直接选择为 `RemoteMapper Wireless Mic`; - 3. 在 Web 控制台查看「已解码音频帧」数字是否在说话时实时增长。 + 1. 打开 Windows「声音设置」➔「录制设备」,查看是否存在 `RemoteMapper Mic`; + 2. 将微信输入法或讯飞输入法的默认麦克风选择为 **`RemoteMapper Mic`**; + 3. 确认输入法快捷键与 Web 控制台中语音键绑定的热键一致(默认为 `右Alt + 逗号`)。 ### Q3: 手机连接 `RemoteMapper-AP` 后无法打开配置界面? - **排查步骤**: @@ -309,15 +392,12 @@ test.bat 2. 手动在手机浏览器地址栏输入 **`http://192.168.4.1`**; 3. 确保电脑/手机未开启全局代理或 VPN。 -### Q4: 固件编译报错找不到 `NimBLEDevice.h` 或 `USBHIDKeyboard.h`? +### Q4: 音量加减在 Windows 下变成打开搜索栏? - **排查步骤**: - 1. 确保 `platformio.ini` 中的 `framework = arduino` 且 `lib_deps` 包含 `NimBLE-Arduino`; - 2. 运行 `python -m platformio pkg install -e esp32s3_n16r8` 重新下载依赖库。 + 1. 检查 WebUI 下拉菜单或 NVS 中音量键的键码,音量加标准 USB Consumer 键码为 **`0x00E9` (233)**,音量减为 **`0x00EA` (234)**;切勿使用旧版错误的 `545`(`0x0221` 代表 AC Search 全局搜索)。 --- -# 十二. 未来扩展与演进路线 (Roadmap) +## 📄 开源许可证 +本项目遵循 [MIT License](./LICENSE) 开源许可协议。 -1. **Web 端按键完全图形化拖拽自定义**:支持用户在浏览器中点击任意按键,弹出下拉框直接挑选 Windows 快捷键(如 `Ctrl+Alt+A` 截图、`Win+Shift+S` 等)并持久化到 NVS。 -2. **WebSerial USB 离线免网改键**:通过浏览器 WebSerial API 直接走 USB 虚拟串口收发 JSON 改键指令,彻底无需 Wi-Fi。 -3. **多设备配对管理与一键切换**:在 Web 端保存多个小米遥控器 MAC 地址并支持一键切换绑定。