阶段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>
16 KiB
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 Serverdzbj/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 关闭
- 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):
# 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剩余 |
| dzbj 代码大改? | 不需要,UI/pages/ble 模块几乎原样复制 |
| 最大技术风险? | WiFi/BLE 反复 init/deinit 的内存泄漏和碎片化 |
总体评估:双模式互斥切换方案在技术上可行,资源预算满足单模式运行。最大不确定性在于 WiFi/BLE 的完整 deinit 是否能可靠回收内存,需要实际编码验证。建议从阶段1(点亮屏幕)开始逐步推进。