Baji_Rtc_Toy/06-AI对话和电子吧唧双模式适配说明.md
Rdzleo 14776acb0a feat: 完成 AI/吧唧双模式完全隔离重构 + 触摸坐标日志 + SPIFFS 预烧录
## 核心变更

### 1. 双模式完全隔离 (Phase 2+4)
- 拆分 InitializeButtons() 为 InitializeBadgeModeButtons() + InitializeAiModeButtons()
- 构造函数按 device_mode 分支:吧唧模式不创建 PowerSaveTimer/BackgroundTask
- 吧唧模式不注册音量/故事按键回调,避免调用 GetAudioCodec() 崩溃
- GPIO0 由 iot_button 统一处理,dzbj_button 仅注册 KEY2(GPIO4)
- SetDeviceState() 中 background_task_ 空指针保护

### 2. 吧唧模式 BOOT 按键崩溃修复
- 新增 dzbj_boot_click_handler()(C 函数,避免 lvgl.h 与 display.h 冲突)
- 移植 dzbj 的唤醒屏幕/退出手电筒/返回Home 完整逻辑

### 3. esp_timer 阻塞 LVGL 渲染修复
- iot_button 回调在 esp_timer 任务中执行,vTaskDelay 会阻塞 lv_tick_inc
- 改为 xTaskCreate 派发到独立 FreeRTOS 任务,避免冻结 LVGL 渲染

### 4. 触摸坐标日志 + SPIFFS 预烧录
- esp_lvgl_port_touch.c 添加触摸坐标打印
- CMakeLists.txt 添加 spiffs_create_partition_image 自动打包 spiffs_image/

### 5. dzbj 模块文件新增
- device_mode: NVS 设备模式管理 (AI=0/吧唧=1)
- dzbj_button: GPIO4 KEY2 中断 + BOOT 点击处理
- dzbj_ble: BLE GATT 图传服务 (0x0B00)
- dzbj_battery: ADC 电池电压监测
- sleep_mgr: 10s 超时熄屏低功耗管理
- pages: 图片浏览/GIF播放/PWM亮度
- fatfs: SPIFFS 文件管理

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 10:23:04 +08:00

11 KiB
Raw Permalink Blame History

AI对话 + 电子吧唧 双模式适配说明

更新日期2026-02-27 硬件平台movecall-moji-esp32s3 (ESP32-S3-N16R8) ESP-IDF版本5.4.2 LVGL版本8.3.11


一、项目现状

1.1 主项目 (Baji_Rtc_Toy)

基于 AI小智 开源项目改造,当前已集成功能:

  • 火山引擎 RTC 语音对话WiFi 连接)
  • BLE 配网BluedroidService 0xABF0
  • 音频编解码ES8311 + Opus
  • 唤醒词检测esp-sr AFE
  • LVGL 8.3.11 LCD 显示Phase 1 已完成,开机显示 ScreenHome
  • ST77916 QSPI 360×360 LCD + CST816S 触摸(已初始化)

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 图片管理
  • 低功耗休眠/唤醒管理10s 超时熄屏)
  • PWM 背光控制

1.3 实施进度

阶段 状态 说明
Phase 1: 点亮屏幕 已完成 LCD + LVGL + ScreenHome 显示
Phase 2+4: 完整模式 + 切换 实施中 移植 dzbj 全模块 + 双模式切换
Phase 3: AI 聊天 UI 待定 基于 LVGL 的 emoji + 聊天气泡

二、双模式架构设计

2.1 架构概览

┌─────────────────────────────────────────────────┐
│              LVGL 8.3.11 + LCD                   │
│         (常驻,两个模式共享显示硬件)               │
├────────────────────┬────────────────────────────┤
│   AI 对话模式       │    电子吧唧模式              │
│   (mode=0, 默认)    │    (mode=1)                │
│                    │                            │
│  WiFi + RTC 协议    │    BLE GATT Server         │
│  ScreenHome (仅显示) │    ScreenHome/Img/Set      │
│  音频编解码 + 唤醒词  │    BLE 图片传输 (0x0B00)   │
│  PowerSaveTimer    │    sleep_mgr (10s熄屏)      │
│  IMU 传感器         │    battery 电池监测          │
│  电量检测 (板级)     │    SPIFFS 图片管理          │
├────────────────────┴────────────────────────────┤
│       BOOT 双击 切换写NVS + 重启)              │
└─────────────────────────────────────────────────┘

2.2 模式定义

AI 对话模式 (device_mode=0, 默认)

  • 网络WiFi 连接
  • 协议:火山引擎 RTC 实时对话
  • 音频:唤醒词检测 + Opus 编解码 + I2S 输出
  • 显示ScreenHome仅显示无触摸交互
  • BLE关闭(仅配网时启动)

电子吧唧模式 (device_mode=1)

  • 网络:WiFi 关闭
  • BLEGATT Server图片传输服务 0x0B00
  • 显示ScreenHome → ScreenImg图片浏览→ ScreenSet设置
  • 功能JPEG 解码、GIF 播放、SPIFFS 图片管理、低功耗、电池监测

2.3 模式切换方案

方案重启切换NVS 标志)

BOOT 双击 → 读取 NVS device_mode → 切换 0↔1 → 写入 NVS → esp_restart()

切换时间约 3-4 秒重启时间NVS 擦写寿命 10-100万次无限次切换。

选择重启而非热切换的原因

  1. WiFi + BLE Bluedroid 同时运行内部 SRAM 不足(约需 280KB可用 ~334KB
  2. 热切换需处理大量资源释放/重建协议、音频管道、FreeRTOS 任务),复杂度极高
  3. Application 单例内部状态event_group, opus 编解码器, background_task难以干净重置
  4. 重启方式简单可靠,避免内存泄漏和碎片化风险

三、内存预算分析

3.1 硬件规格

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

3.2 各模式内存占用

场景 估算占用 剩余 可行性
AI 对话模式WiFi+RTC+音频+LVGL ~212-237KB ~97-122KB 可行(偏紧)
电子吧唧模式BLE+LVGL+SPIFFS ~190KB ~144KB 可行(充裕)
两模式同时运行 ~274-345KB 不足 不可行

3.3 关键验证数据

  • Phase 1 测试WiFi + BLE 同时运行导致 assert failed: vQueueDelete queue.c:2355FreeRTOS 信号量分配失败)
  • BLE 配网成功后 xTaskCreate 分配 2048 栈失败(已改用 esp_timer 解决)
  • 确认两模式必须互斥运行

四、启动流程

4.1 双模式启动序列

开机
 │
 ├── 板级构造函数(通用)
 │   ├── PowerSaveTimer 初始化
 │   ├── InitializeButtons()(主项目 Button 类,双击注册在此)
 │   ├── InitializeCodecI2c()
 │   ├── dzbj_display_init()  ← LCD + LVGL 始终初始化
 │   │
 │   ├── if device_mode == BADGE (吧唧模式)
 │   │   └── InitializeBadgeMode()
 │   │       ├── fatfs_init()            // SPIFFS 文件系统
 │   │       ├── init_spiffs_image_list() // 扫描图片
 │   │       ├── dzbj_button_init()      // ISR按键
 │   │       ├── battery_init()          // 电池检测
 │   │       ├── dzbj_ble_init()         // BLE 图传
 │   │       └── sleep_mgr_init()        // 低功耗管理
 │   │
 │   └── else (AI模式, 默认)
 │       ├── InitializeIot()
 │       ├── InitializeBatteryMonitor()
 │       ├── InitializeImuSensor()
 │       └── PowerSaveTimer 启用
 │
 ├── Application::Start()
 │   ├── if device_mode == BADGE
 │   │   └── SetDeviceState(Idle); return;  // 不启动WiFi/协议/音频
 │   │
 │   └── else (AI模式)
 │       ├── Opus 编解码器初始化
 │       ├── 音频管道启动
 │       ├── board.StartNetwork()  // WiFi 连接
 │       ├── RTC 协议初始化
 │       └── MainLoop + AudioLoop 启动

4.2 BOOT 按键行为

事件 AI模式 吧唧模式 配网模式
单击 Idle↔Listening 切换 待定(返回 ScreenHome 显示 MAC 地址
双击 切换到吧唧模式 切换到AI模式 无响应
长按5s 无响应 无响应 进入生产测试

五、模块移植清单

5.1 从 dzbj 移植的模块

模块 源文件 目标文件 适配要点
fatfs dzbj/main/fatfs/ main/dzbj/fatfs.c/h gpio.hdzbj_gpio.h
pages dzbj/main/pages/pages.c main/dzbj/pages.c 移除 wifi.hPWM 去重
BLE图传 dzbj/main/ble/ble.c main/dzbj/dzbj_ble.c/h 新增 deinit 函数
sleep_mgr dzbj/main/sleep_mgr/ main/dzbj/sleep_mgr.c 按键回调适配
button dzbj/main/button/ main/dzbj/dzbj_button.c/h ISR+队列+去抖
battery dzbj/main/battery/ main/dzbj/battery.c/h ADC 校准 + UI 更新

5.2 新建模块

模块 文件 功能
device_mode main/dzbj/device_mode.c/h NVS 模式读写 + 重启切换

5.3 修改的现有文件

文件 修改内容
movecall_moji_esp32s3.cc 模式分支 + InitializeBadgeMode() + BOOT 双击回调
application.cc Start() 模式分支(吧唧模式早返回)
main/CMakeLists.txt 添加新源文件
main/idf_component.yml 添加 esp_jpeg 依赖
main/sleep_mgr/include/sleep_mgr.h stub 改为真实函数声明

5.4 删除的文件

文件 原因
main/pages/pages_stub.c main/dzbj/pages.c 真实实现替代

六、GPIO 引脚分配(已解决)

Phase 1 已完成的 GPIO 冲突解决:

GPIO 主项目原用途 dzbj用途 解决方案
21 BUILTIN_LED LCD D3 LED 改为 GPIO_NUM_NC
1 Touch1 (电容触摸) LCD 背光 EN Touch1 改为 GPIO_NUM_NC
7 Touch4 (电容触摸) LCD RST Touch4 改为 GPIO_NUM_NC
6 Battery ADC Touch RST Battery ADC 改为 GPIO 3
17/18 I2C_NUM_1 (音频) I2C_NUM_0 (触摸) 统一为 I2C_NUM_1 共享

七、风险评估

7.1 重启切换方案(已选定)

风险 等级 说明
内存泄漏 每次重启全新初始化,无残留
内存碎片化 重启清除所有堆分配
WiFi/BLE deinit 不可靠 无需 deinit重启自然释放
NVS 擦写寿命 极低 10-100万次日常使用完全足够
切换体验 ~3-4秒重启时间可加转场动画优化

7.2 其他风险

风险 等级 缓解方案
符号冲突pages_stub vs pages.c 删除 stub真实实现始终编译
button 模块冲突C++ Button vs C ISR 条件初始化,两模式用不同实现
SPIFFS 分区未配置 检查分区表是否有 spiffs 分区
Flash 空间 当前 app 分区 5MB固件 ~3.5MB,剩余充足

八、分区表

当前分区表:

# 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,

dzbj 图片浏览功能需要 SPIFFS 存储。model 分区3MB, spiffs 类型)可复用,或需新增 storage 分区。


九、验证计划

9.1 编译验证

idf.py build

9.2 AI 模式验证(默认 mode=0

  • 开机正常进入 WiFi 连接 + RTC 对话
  • LVGL 显示 ScreenHome
  • BOOT 单击切换对话状态
  • BOOT 双击 → 切换到吧唧模式,设备重启
  • 内存剩余 > 80KB

9.3 吧唧模式验证mode=1

  • 开机日志显示"电子吧唧模式启动"
  • 不连接 WiFi不播放开机语音
  • BLE 广播可见(手机搜索 "Airhub_XX:XX:XX"
  • 手机 APP 可传输图片到设备
  • 屏幕显示传输的图片
  • 10s 无操作后屏幕熄灭
  • 按键或触摸唤醒屏幕
  • BOOT 双击 → 切换回 AI 模式,设备重启
  • 内存剩余 > 150KB

9.4 稳定性验证

  • 来回切换 10+ 次,功能正常
  • 各模式下长时间运行(>1小时无崩溃