Baji_Rtc_Toy/06-AI对话和电子吧唧双模式适配说明.md
Rdzleo c24a9bc162 feat: 集成 dzbj LVGL 显示模块 + 配网模式内存优化
阶段1: 将 dzbj 项目的 LVGL 8.3.11 LCD 显示集成到 AI小智 主项目,
开机显示 ScreenHome 界面,同时优化配网模式下的内存使用,
确保 WiFi+BLE+LVGL 三者共存运行。

## 新增功能

### dzbj 显示模块集成
- 新增 main/dzbj/ 目录,移植 LCD 驱动(ST77916 QSPI)、触摸驱动(CST816S)、
  LVGL 初始化和 SquareLine Studio UI 界面
- I2C 总线共享:dzbj 触摸控制器复用主项目的 I2C_NUM_1 总线
- GPIO 冲突解决:LED(GPIO21)、Touch1(GPIO1)、Touch4(GPIO7) 改为 NC,
  电池 ADC 从 GPIO6 改为 GPIO3
- 添加 LVGL、esp_lcd_st77916、esp_lcd_touch_cst816s 等组件依赖
- managed_components 纳入版本管理

### 配网模式轻量化启动
- BoxAudioCodec: 新增 output_only 模式,仅创建 I2S TX 通道(省 ~13KB DMA)
  跳过 ES7210 ADC 初始化(省 ~2-4KB)
- AudioCodec: 新增 StartOutputOnly() 方法,仅启用扬声器输出
- Application: 配网模式跳过 Opus 编码器、输入重采样器、协议初始化、
  天气位置检测等网络业务
- 板级构造函数: 配网模式跳过电池检测、IMU传感器、PowerSaveTimer

### WifiBoard 配网流程修复
- NeedsProvisioning() 静态方法: 读取 NVS force_ap 和 SSID 列表,
  用于提前判断配网模式
- force_ap 竞态修复: 构造函数不再清零 force_ap,改在 StartNetwork() 清零,
  确保 NeedsProvisioning() 能正确读到 force_ap=1
- Application 缓存 provisioning_mode_ 成员变量,避免重复读 NVS

### BLE 配网重启修复
- 配网成功后用 esp_timer 延迟重启替代 xTaskCreate,
  避免内存紧张时任务创建失败导致设备不重启
- 注释掉 WiFi 连接成功后的 MAC 地址发送步骤

### sdkconfig 内存优化
- BT_ALLOCATION_FROM_SPIRAM_FIRST=y (BLE 动态分配优先 PSRAM)
- SPIRAM_MALLOC_RESERVE_INTERNAL=32768
- NVS_ALLOCATE_CACHE_IN_SPIRAM=y
- WiFi 静态缓冲区数量优化 (RX=10, TX=8)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 17:07:51 +08:00

16 KiB
Raw Blame History

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 配网BluedroidService 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 支持:

#"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 关闭
  • BLEGATT Server图片传输 + 配网服务)
  • 显示ScreenHome主界面→ ScreenImg图片浏览→ ScreenSet设置
  • 功能GIF 播放、JPEG 解码、SPIFFS 图片管理、手电筒、低功耗

三、内存预算分析(核心瓶颈)

3.1 硬件规格

  • 内部 SRAM~334KB DIRAM可用
  • PSRAM8MB OCT-SPI 80MHz
  • Flash16MB

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:2355FreeRTOS 信号量分配失败),确认内部 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_tLVGL 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

# 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点亮屏幕开始逐步推进。