# 按键版电子吧唧移植到 Baji_Rtc_Toy_Key 方案 ## 一、目标项目分析 ### 1.1 项目架构 `Baji_Rtc_Toy_Key` 是 AI 对话 + 电子吧唧双模式项目,通过 NVS 存储的 `device_mode` 切换: - **AI 模式** (`DEVICE_MODE_AI`): 火山引擎 RTC 语音对话 + GIF 表情 - **电子吧唧模式** (`DEVICE_MODE_BADGE`): 图片浏览 + BLE 传图(当前仍为触屏版) ### 1.2 目标项目已有的吧唧模块(`main/dzbj/` 目录) | 文件 | 功能 | 状态 | |------|------|------| | `dzbj_ble.c` | GATT Server(APP 传图) | ✅ 已有,仅 Server | | `dzbj_button.c` | ISR 轮询按键 | ⚠️ 需替换为 iot_button | | `dzbj_battery.c` | 电池 ADC 采样 | ✅ 已有 | | `sleep_mgr.c` | 休眠管理 | ✅ 已有 | | `fatfs.c` | FatFS 图片存储 | ✅ 已有(本项目用 SPIFFS) | | `pages.c` | 图片列表管理 | ✅ 已有 | | `lcd.c` | LCD 驱动 | ✅ 已有 | | UI 界面 | ScreenHome/Img/Set/Peiwang/Update | ✅ 已有(触屏版) | ### 1.3 目标项目缺失的模块 | 模块 | 说明 | |------|------| | **key_nav** | 按键导航管理器(上下文状态机 + 事件分发) | | **ble_transfer** | 设备间 BLE 图片传输(GATT Client 扫描/连接/发送) | | **4 个新界面** | ScreenImageShar / ScreenImageReception / ScreenSharing / ScreenReceiving | | **battery_ui** | 电池指示器 show-briefly + fade-out 动画 | | **两步删除确认** | ScreenImg 的 ContainerDle 重设计 + NAV_CTX_IMG_DELETE_CONFIRM | --- ## 二、架构差异与适配策略 ### 2.1 按键驱动差异 | 维度 | 本项目 (Dzbj_ESP32_S3_Key) | 目标项目 (Baji_Rtc_Toy_Key) | |------|---------------------------|---------------------------| | 组件 | iot_button (ESP Component Registry) | ISR 轮询(自定义 dzbj_button.c) | | 语言 | 纯 C | C++ (Application 框架) | | 事件 | 单击/双击/长按 × 2 键 | 仅按下/释放 | | 回调线程 | esp_timer 任务 | ISR 中断 | **适配策略**: - 在吧唧模式下添加 iot_button 依赖,替换 `dzbj_button.c` 的 ISR 实现 - 或在 `dzbj_button.c` 中增加双击/长按检测逻辑(不引入新依赖,但开发量大) - **推荐**: 引入 iot_button,仅在吧唧模式下初始化 ### 2.2 模式隔离 目标项目已有模式隔离框架: ```cpp // main/main.cc if (device_mode_is_badge()) { dzbj_app_main(); // 吧唧模式入口 } else { // AI 模式入口 } ``` **适配策略**: - key_nav 模块仅在 `dzbj_app_main()` 中初始化 - iot_button 的按键回调仅注册吧唧模式的处理函数 - AI 模式的按键处理保持不变(ISR 方式) ### 2.3 存储差异 | 维度 | 本项目 | 目标项目 | |------|--------|---------| | 文件系统 | SPIFFS | FatFS | | 图片管理 | `init_spiffs_image_list()` | `fatfs` 模块 | **适配策略**: - key_nav 中的 `init_spiffs_image_list()` 替换为目标项目的 FatFS 图片列表接口 - ble_transfer 中的图片保存路径适配 FatFS 挂载点 --- ## 三、移植步骤(共 7 步) ### 步骤 1: 添加 iot_button + 适配按键驱动 **操作**: 1. `main/idf_component.yml` 添加 `button: ">=3.2.0"` 2. 新建 `main/dzbj/dzbj_button_nav.c`(或修改 `dzbj_button.c`) 3. 实现 6 种事件回调接口,参数照搬本项目的 `button.c` **注意**: - AI 模式下不初始化 iot_button,避免资源浪费 - 确认 iot_button 和现有 ISR 按键不冲突(同一 GPIO 不要双重处理) ### 步骤 2: 移植 key_nav 模块 **操作**: 1. 复制 `main/key_nav/` 目录到目标项目 `main/dzbj/key_nav/` 2. 适配 include 路径(ui 头文件路径可能不同) 3. 适配 LVGL 锁:确认目标项目的 `lvgl_port_lock()` 接口一致 4. 替换 SPIFFS 相关调用为 FatFS 接口 **关键修改点**: ```c // 本项目 #include "ui/ui.h" #include "ui/screens/ui_ScreenHome.h" init_spiffs_image_list(); // 目标项目适配 #include "dzbj_ui.h" // 或目标项目实际的 UI 头文件路径 init_fatfs_image_list(); // 或目标项目的图片列表初始化函数 ``` ### 步骤 3: 移植 ble_transfer 模块 **操作**: 1. 复制 `main/ble/ble_transfer.c` 和 `main/ble/include/ble_transfer.h` 2. 适配 BLE 初始化:目标项目的 `dzbj_ble.c` 已有 GATT Server,需新增 GATT Client 能力 3. 适配图片保存:SPIFFS → FatFS 路径 4. 适配界面跳转:确认目标项目的 `_ui_screen_change()` 接口 **注意**: - 目标项目的 BLE 在 AI 模式下可能有不同用途,确保吧唧模式的 ble_transfer 不影响 AI 模式 - `ble_start()` / `ble_stop()` 需适配到 `dzbj_ble.c` 的广播控制 ### 步骤 4: 移植 4 个新 UI 界面 **操作**: 1. 复制以下文件到目标项目的 UI 目录: - `ui_ScreenImageShar.c/.h` - `ui_ScreenImageReception.c/.h` - `ui_ScreenSharing.c/.h` - `ui_ScreenReceiving.c/.h` 2. 复制 4 张图片资源文件(`ui_img_*_png.c`) 3. 在目标项目的 `ui.h` 中添加 include 和 `LV_IMG_DECLARE` 4. 在 CMakeLists.txt 中添加编译 ### 步骤 5: 移植 battery_ui 模块 **操作**: 1. 复制 `main/ui/battery_ui.c` 和 `main/ui/battery_ui.h` 2. 在 ScreenHome 和 ScreenImg 的 `screen_init` 中调用 `battery_ui_add_to_screen()` 3. 在 SCREEN_LOADED 事件中调用 `battery_ui_show_briefly()` 4. 在 `dzbj_battery.c` 的电量更新回调中调用 `battery_ui_update_level()` ### 步骤 6: 适配 ScreenImg 删除确认 UI **操作**: 1. 将目标项目的 `ui_ScreenImg.c` 中的 ContainerDle 改为 220×220 底部半圆设计 2. ui_ImageDel 改为 68×68 正圆容器 + S13 图片居中 3. 添加白色边框显示/隐藏函数 4. key_nav 中已包含两步删除逻辑,无需额外修改 ### 步骤 7: 适配 main 入口和手势移除 **操作**: 1. 在 `dzbj_app_main()` 中添加 `key_nav_init()` 调用 2. 移除吧唧模式下各界面的触摸手势事件回调 3. 保留 `LV_EVENT_SCREEN_LOADED` 延迟加载逻辑 4. 适配 sleep_mgr 的唤醒回调(由 key_nav 统一管理) --- ## 四、风险评估 | 风险 | 影响 | 缓解措施 | |------|------|---------| | iot_button 与 AI 模式按键冲突 | GPIO 双重处理导致崩溃 | 模式隔离:吧唧模式用 iot_button,AI 模式保持 ISR | | BLE Client + Server 同时运行 | 资源不足或状态冲突 | ble_transfer 使用独立的 GATT App ID | | FatFS vs SPIFFS 接口差异 | 图片路径/文件操作不兼容 | 统一封装图片读写接口 | | UI 头文件路径差异 | 编译错误 | 系统性检查所有 #include 路径 | | C++ 框架与纯 C 模块交互 | 链接/调用约定问题 | key_nav 等 C 模块用 `extern "C"` 包裹头文件 | --- ## 五、预估工作量 | 步骤 | 复杂度 | 说明 | |------|--------|------| | 步骤 1: 按键驱动 | 中 | 需处理模式隔离和 GPIO 冲突 | | 步骤 2: key_nav | 低 | 主要是路径适配 | | 步骤 3: ble_transfer | 高 | BLE Client 集成到已有 BLE 框架最复杂 | | 步骤 4: 新界面 | 低 | 直接复制 + 适配路径 | | 步骤 5: battery_ui | 低 | 直接复制 + 接入电量回调 | | 步骤 6: 删除确认 | 低 | UI 修改量小 | | 步骤 7: 入口适配 | 中 | 需仔细处理手势移除和模式切换 | **总结**: 移植可行性高。核心难点在 ble_transfer 的集成(步骤 3)和按键驱动的模式隔离(步骤 1),其余模块可直接复制适配。建议按步骤顺序执行,每步编译验证。