# AI对话 + 电子吧唧 双模式适配可行性分析 > 分析日期:2026-02-27 > 硬件平台:movecall-moji-esp32s3 (ESP32-S3-N16R8) > ESP-IDF版本:5.4.2 > LVGL版本:8.3.11 (dzbj项目) --- ## 一、项目现状 ### 1.1 主项目 (Baji_Rtc_Toy) 基于 AI小智 开源项目改造,当前功能: - 火山引擎 RTC 语音对话(WiFi 连接) - BLE 配网(Bluedroid,Service 0xABF0) - 音频编解码(ES8311 + Opus) - 唤醒词检测(esp-sr AFE) - **无 LCD 显示**(`lcd_display.cc` 已注释,managed_components 中无 LVGL) 关键文件: - `main/application.cc` — 应用主类,状态管理 - `main/boards/movecall-moji-esp32s3/` — 板级实现 - `main/boards/common/wifi_board.cc` — WiFi/BLE 网络管理 - `main/protocols/volc_rtc_protocol.cc` — 火山 RTC 协议 - `main/bluetooth_provisioning.cc` — BLE 配网 - `main/display/` — Display 抽象层(已预留 LCD 接口,未编译) ### 1.2 dzbj 子项目 (电子吧唧) 独立的 ESP32-S3 LVGL 项目,位于 `/dzbj/` 目录,当前功能: - 360×360 ST77916 QSPI LCD + CST816S 触摸 - LVGL 8.3.11 三屏界面(ScreenHome/ScreenImg/ScreenSet) - BLE GATT 图片传输服务(Service 0x0B00) - GIF 播放、JPEG 解码、SPIFFS 图片管理 - 低功耗休眠/唤醒管理 - PWM 背光控制、手电筒功能 关键文件: - `dzbj/main/lcd/lcd.c` — ST77916 QSPI + CST816S 驱动 - `dzbj/main/ui/` — SquareLine Studio 生成的 UI 代码 - `dzbj/main/pages/pages.c` — 图片处理 + PWM 亮度 - `dzbj/main/ble/ble.c` — BLE 图片传输 GATT Server - `dzbj/main/sleep_mgr/sleep_mgr.c` — 低功耗管理 ### 1.3 AI小智原生 LVGL 版本 **AI小智原生项目不使用 LVGL 9.2.2**。实际情况: | 项目 | LVGL 版本 | 状态 | |------|-----------|------| | 主项目 (Baji_Rtc_Toy) | **无 LVGL** | `lcd_display.cc` 已注释,managed_components 中无 lvgl | | dzbj 子项目 | **8.3.11** | 完整集成 LVGL + esp_lvgl_port 2.5.0 | | AI小智 Display 框架 | **预留接口** | `Display` 基类已编译,方法为空操作(no-op) | `main/CMakeLists.txt` 第11-12行明确注释了 LCD 支持: ```cmake #"display/lcd_display.cc" # 移除LCD显示器支持 #"display/oled_display.cc" # 移除OLED显示器支持 ``` AI小智框架的 `LcdDisplay` 类已预留 emoji 表情(21种)、聊天气泡(微信风格)、主题切换(深色/浅色) 功能,但当前未编译链接。 --- ## 二、双模式架构设计 ### 2.1 架构概览 ``` ┌─────────────────────────────────────────────────┐ │ LVGL 8.3.11 │ │ (常驻,两个模式共享) │ ├────────────────────┬────────────────────────────┤ │ AI 聊天模式 │ 电子吧唧模式 │ │ │ │ │ WiFi + RTC 协议 │ BLE GATT Server │ │ emoji 表情显示 │ ScreenHome/Img/Set │ │ 聊天气泡文本 │ 图片浏览 + GIF │ │ 唤醒词检测 │ BLE 图片传输 │ │ 音频编解码 │ 手电筒/低功耗 │ ├────────────────────┴────────────────────────────┤ │ 长按 BOOT 5秒 切换 │ │ AI→吧唧: 关WiFi+RTC → 启BLE → 切换UI │ │ 吧唧→AI: 关BLE → 启WiFi+RTC → 切换UI │ └─────────────────────────────────────────────────┘ ``` ### 2.2 模式定义 **AI 聊天模式**: - 网络:WiFi 连接 - 协议:火山引擎 RTC 实时对话 - 音频:唤醒词检测 + Opus 编解码 + I2S 输出 - 显示:emoji 表情 + 聊天气泡文本 + 状态栏 - BLE:**关闭** **电子吧唧模式**: - 网络:**WiFi 关闭** - BLE:GATT Server(图片传输 + 配网服务) - 显示:ScreenHome(主界面)→ ScreenImg(图片浏览)→ ScreenSet(设置) - 功能:GIF 播放、JPEG 解码、SPIFFS 图片管理、手电筒、低功耗 --- ## 三、内存预算分析(核心瓶颈) ### 3.1 硬件规格 - **内部 SRAM**:~334KB DIRAM(可用) - **PSRAM**:8MB OCT-SPI 80MHz - **Flash**:16MB ### 3.2 常驻组件(两模式共享) | 组件 | DIRAM 占用 | 说明 | |------|-----------|------| | LVGL 库 (.bss/.data) | ~34KB | 图形引擎核心 | | FreeRTOS | ~22KB | 内核 + idle/timer 任务 | | HAL + SPI Flash | ~30KB | 硬件抽象 + Flash 驱动 | | Heap 管理器 | ~8KB | 内存分配器 | | esp_system / esp_hw | ~18KB | 系统支持 | | lwip 协议栈 | ~4KB | TCP/IP(即使 WiFi 关闭也常驻) | | 主应用 (main) | ~5KB | Application 框架 | | LVGL 任务栈 | 8KB | LVGL 刷新任务 | | **常驻小计** | **~129KB** | | ### 3.3 AI 聊天模式额外占用 | 组件 | DIRAM 占用 | 说明 | |------|-----------|------| | WiFi 驱动 (net80211+pp) | ~13KB 静态 + ~40-50KB 动态 | TX/RX 缓冲区 | | RTC 协议 | ~5-10KB | 火山引擎连接 | | 音频处理 | ~10-15KB | Opus编解码 + 管道 | | 唤醒词 (esp-sr) | ~15-20KB | AFE + 模型加载 | | **AI模式小计** | **~83-108KB** | | | **AI模式总计** | **~212-237KB** | 常驻 + AI | | **AI模式剩余** | **~97-122KB** | 用于堆分配 | ### 3.4 电子吧唧模式额外占用 | 组件 | DIRAM 占用 | 说明 | |------|-----------|------| | BLE Bluedroid 静态 | ~13KB | libbt.a 静态数据 | | BLE 控制器 | ~15KB | 动态分配(PSRAM优先后减少) | | BLE 任务栈 | ~15KB | BTC(3KB) + BTU(4KB) + 控制器 | | dzbj 业务任务 | ~18KB | GIF(4KB) + 按键(3KB) + 电池(4KB) + 睡眠(3KB) + BLE处理(8KB) | | **吧唧模式小计** | **~61KB** | | | **吧唧模式总计** | **~190KB** | 常驻 + 吧唧 | | **吧唧模式剩余** | **~144KB** | 充裕 | ### 3.5 关键结论 | 场景 | 内存占用 | 剩余 | 可行性 | |------|----------|------|--------| | AI 聊天模式(单独) | ~212-237KB | ~97-122KB | **可行**(偏紧) | | 电子吧唧模式(单独) | ~190KB | ~144KB | **可行**(充裕) | | 两模式同时运行 | ~274-345KB | 不足 | **不可行** | | 模式切换(互斥) | 单次一个模式 | 够用 | **可行,需验证** | **验证数据**:之前测试中 WiFi + BLE 同时运行导致 `assert failed: vQueueDelete queue.c:2355`(FreeRTOS 信号量分配失败),确认内部 SRAM 不足以支撑两者同时运行。 --- ## 四、模式切换技术方案 ### 4.1 AI → 电子吧唧 切换流程 ``` 用户长按 BOOT 5秒 │ ├─ 1. 关闭 AI 资源 │ ├─ protocol_->CloseAudioChannel() // 关闭 RTC 音频通道 │ ├─ volc_rtc_stop() + volc_rtc_destroy() // 销毁 RTC 实例 │ ├─ StopAudioProcessor() // 停止音频处理器 │ ├─ 停止唤醒词检测 │ └─ esp_wifi_stop() + esp_wifi_deinit() // 完全释放 WiFi │ → 释放 ~83-108KB 内部 SRAM │ ├─ 2. 启动吧唧资源 │ ├─ esp_bt_controller_init() // 初始化 BLE 控制器 │ ├─ esp_bluedroid_init() + enable() // 启动 Bluedroid │ ├─ 注册 GATT Server(图片传输服务) │ ├─ 启动 BLE 广播 │ └─ 启动 dzbj 业务任务(按键/电池/睡眠管理) │ └─ 3. 切换界面 └─ lv_scr_load_anim(ui_ScreenHome, ...) ``` ### 4.2 电子吧唧 → AI 切换流程 ``` 用户长按 BOOT 5秒 │ ├─ 1. 关闭吧唧资源 │ ├─ esp_ble_gap_stop_advertising() // 停止广播 │ ├─ esp_ble_gatts_app_unregister() // 注销 GATT │ ├─ esp_bluedroid_disable() + deinit() // 关闭 Bluedroid │ ├─ esp_bt_controller_disable() + deinit() // 关闭控制器 │ └─ 停止 dzbj 业务任务 │ → 释放 ~43-61KB 内部 SRAM │ ├─ 2. 启动 AI 资源 │ ├─ esp_wifi_init() + esp_wifi_start() // 初始化 WiFi │ ├─ WifiStation::Start() // 连接 WiFi │ ├─ WaitForConnected() // 等待连接 │ ├─ 创建 VolcRtcProtocol 实例 │ ├─ protocol_->Start() // 启动 RTC 连接 │ └─ 启动音频处理器 + 唤醒词检测 │ └─ 3. 切换界面 └─ lv_scr_load_anim(ai_chat_screen, ...) ``` ### 4.3 BOOT 按键检测逻辑 ``` 当前逻辑(movecall_moji_esp32s3.cc): - 单击: Idle↔Listening 切换 - 长按 5s(仅配网模式): 生产测试 需要改为: - 单击: 保持原逻辑(AI模式下 Idle↔Listening,吧唧模式下返回 ScreenHome) - 长按 5s: 模式切换(AI ↔ 吧唧) ``` --- ## 五、实施方案 ### 5.1 改动清单 #### 第一步:硬件层(改动小) | 文件 | 改动内容 | 改动程度 | |------|----------|----------| | `movecall-moji-esp32s3/config.h` | 添加 LCD/Touch GPIO 定义 | 小 | | `movecall_moji_esp32s3.cc` | 初始化 LCD 驱动(参考 dzbj lcd.c) | 中 | **GPIO 冲突注意**: - movecall-moji `BUILTIN_LED_GPIO = GPIO_NUM_21` 与 dzbj LCD D3 (GPIO 21) 冲突 - 需要重新映射 LED 引脚或调整 LCD 引脚 - dzbj 触摸用独立 I2C 引脚(GPIO 5/6),与音频 ES8311 (GPIO 17/18) 不冲突 #### 第二步:LVGL 集成(改动中等) | 文件 | 改动内容 | 改动程度 | |------|----------|----------| | `main/idf_component.yml` | 添加 lvgl 8.3.11 + esp_lvgl_port 2.5.0 + esp_lcd_st77916 | 小 | | `main/CMakeLists.txt` | 取消注释 lcd_display.cc,添加 dzbj 模块源文件 | 中 | | `main/ui/` | 从 dzbj 复制 SquareLine 生成的 UI 代码 | 复制 | | `main/pages/` | 从 dzbj 复制页面管理模块 | 复制+小改 | | `main/sleep_mgr/` | 从 dzbj 复制低功耗管理模块 | 复制+小改 | #### 第三步:AI 聊天 UI(新开发) | 内容 | 说明 | |------|------| | 创建 AI 聊天屏幕 | 基于 LVGL 8.3.11,包含 emoji 显示区 + 聊天气泡容器 + 状态栏 | | emoji 表情渲染 | 参考 `LcdDisplay::SetEmotion()` 实现,21种表情映射 | | 聊天气泡 | 参考 `LcdDisplay::SetChatMessage()` 实现微信风格气泡 | | 对接 Application | SetEmotion/SetChatMessage 调用新 UI | **注意**:`main/display/lcd_display.h` 中使用了 `lv_draw_buf_t`(LVGL 9.x 类型),需要适配为 8.3.11 的 `lv_disp_draw_buf_t`。 #### 第四步:模式切换管理(核心改动) | 文件 | 改动内容 | 改动程度 | |------|----------|----------| | 新增 `mode_manager.h/cc` | 双模式状态机 + WiFi/BLE init/deinit | 新建 | | `movecall_moji_esp32s3.cc` | BOOT 长按 5s 检测 | 中 | | `application.cc` | 添加模式切换回调 | 中 | ### 5.2 不需要大改动的模块 | 模块 | 改动程度 | 说明 | |------|----------|------| | dzbj UI 代码 (ui_ScreenHome/Img/Set) | **几乎不改** | 直接复制使用 | | dzbj pages.c (GIF/JPEG/PWM) | **小改** | 适配新的 GPIO 定义 | | dzbj sleep_mgr | **小改** | 与 AI小智的 PowerSaveTimer 整合 | | dzbj ble.c (图片传输) | **不改** | 电子吧唧模式下直接使用 | | bluetooth_provisioning.cc | **不改** | 配网逻辑保持不变 | | VolcRtcProtocol | **不改** | AI模式下原样使用 | --- ## 六、风险评估 ### 6.1 高风险 | 风险 | 影响 | 缓解方案 | |------|------|----------| | **WiFi deinit 内存泄漏** | 每次切换泄漏几KB,多次切换后崩溃 | 实测 `esp_wifi_deinit()` 后用 `heap_caps_get_free_size()` 验证回收量 | | **BLE deinit 内存泄漏** | Bluedroid 完全释放困难 | 考虑使用 NimBLE 替代 Bluedroid(更轻量,deinit 更可靠) | | **内部 SRAM 碎片化** | 反复 init/deinit 导致碎片,大块分配失败 | 用 `heap_caps_get_largest_free_block()` 监控最大连续块 | ### 6.2 中等风险 | 风险 | 影响 | 缓解方案 | |------|------|----------| | **GPIO 引脚冲突** | LCD 引脚与现有外设冲突 (GPIO 21) | 仔细对照两个项目的 GPIO 分配表,重新映射 | | **LVGL API 版本差异** | `lcd_display.h` 用了 `lv_draw_buf_t` (9.x) | 适配为 8.3.11 的 `lv_disp_draw_buf_t` | | **Flash 空间** | 新增 LVGL(323KB) + emoji字体 + UI资源 | 当前 app 分区 5MB,固件 ~3.5MB,剩余 ~1.5MB 充足 | ### 6.3 低风险 | 风险 | 影响 | 缓解方案 | |------|------|----------| | I2C 总线共享 | 音频 ES8311 (GPIO 17/18) vs 触摸 CST816S | dzbj 触摸用独立 I2C 引脚(GPIO 5/6),不冲突 | | PSRAM 带宽 | LVGL DMA + 音频 + WiFi 并行 | AI模式无 GIF 播放,PSRAM 带宽充足 | | LVGL 界面切换 | 两套 UI 共存 | LVGL 对象可存放 PSRAM,界面切换无需重启 LVGL | --- ## 七、NimBLE vs Bluedroid 选型建议 当前 dzbj 和配网都使用 Bluedroid,但在双模式切换场景下 NimBLE 更优: | 对比项 | Bluedroid | NimBLE | |--------|-----------|--------| | Flash 占用 | ~277KB (libbt+libbtdm) | ~120KB | | 内部 SRAM | ~35-45KB 动态 | ~15-20KB 动态 | | deinit 可靠性 | 一般(可能有泄漏) | 较好 | | Classic BT | 支持 | 不支持(仅 BLE) | | GATT Server | 支持 | 支持 | | 迁移工作量 | — | 中等(API 不同,逻辑相同) | **建议**:如果不需要经典蓝牙,优先考虑迁移到 NimBLE。节省 ~150KB Flash + ~20KB 内部 SRAM,并且 deinit 更可靠。 --- ## 八、分区表设计 当前分区表(已移除 storage): ```csv # Name, Type, SubType, Offset, Size, Flags nvs, data, nvs, 0x9000, 0x4000, otadata, data, ota, 0xd000, 0x2000, phy_init, data, phy, 0xf000, 0x1000, model, data, spiffs, 0x10000, 0x300000, ota_0, app, ota_0, 0x310000, 5M, ota_1, app, ota_1, 0x820000, 5M, ``` **建议**:如果需要 SPIFFS 存储图片(dzbj 的图片浏览功能),需要重新添加 storage 分区,或复用 model 分区的一部分空间。 --- ## 九、推荐实施路线 ``` 阶段 1: 点亮屏幕(基础验证) 预计改动量: 小 ├─ 确认 LCD GPIO 映射(解决 GPIO 21 冲突) ├─ 在 main/idf_component.yml 添加 LVGL 8.3.11 依赖 ├─ 在 movecall-moji 板上初始化 LCD + LVGL ├─ 显示 dzbj ScreenHome 界面 └─ 验证 LVGL + WiFi 内存占用(确认不冲突) 阶段 2: 电子吧唧模式完整复制 预计改动量: 中 ├─ 复制 dzbj 的 UI/pages/ble/sleep_mgr 模块到主项目 ├─ 关闭 WiFi 后启动 BLE + ScreenHome ├─ 验证 BLE 图片传输功能 └─ 验证低功耗管理 阶段 3: AI 聊天 UI 开发 预计改动量: 中 ├─ 基于 LVGL 8.3.11 创建聊天屏幕(emoji + 气泡) ├─ 对接 Application 的 SetEmotion/SetChatMessage ├─ 关闭 BLE 后启动 WiFi + RTC └─ 验证语音对话 + 屏幕显示联动 阶段 4: 模式切换集成 预计改动量: 中 ├─ 实现 BOOT 长按 5s 检测 ├─ 实现 WiFi ↔ BLE 完整 init/deinit 切换 ├─ 界面切换 + 资源释放验证 └─ 长时间稳定性测试(反复切换 100+ 次) ``` --- ## 十、结论 | 问题 | 结论 | |------|------| | AI小智用 LVGL 9.2.2? | **不是**,当前项目无 LVGL,框架预留了 LCD 接口 | | 显示 emoji 需要 LVGL? | **是**,emoji 字体渲染和聊天气泡都依赖 LVGL | | 保持 LVGL 8.3.11? | **可行**,dzbj 代码直接复用 | | 双模式切换可行? | **可行**,但需验证 WiFi/BLE deinit 的内存回收 | | 内存够用? | **单模式够用**(AI剩余~100KB,吧唧剩余~144KB),同时运行不够 | | dzbj 代码大改? | **不需要**,UI/pages/ble 模块几乎原样复制 | | 最大技术风险? | **WiFi/BLE 反复 init/deinit 的内存泄漏和碎片化** | **总体评估**:双模式互斥切换方案在技术上可行,资源预算满足单模式运行。最大不确定性在于 WiFi/BLE 的完整 deinit 是否能可靠回收内存,需要实际编码验证。建议从阶段1(点亮屏幕)开始逐步推进。