4 Commits

Author SHA1 Message Date
bffd31645e feat(provisioning): BLE 配网完整修复 (跳过 EAF 资源 + EAF 最小化 + 音效播放)
修复 4 个配网模式核心问题, 让 Rtc_AIavatar 分支 (含火山 RTC SDK + 软件 AEC + 完整业务)
能像 adaptation_dzbjImg_shar 一样正常配网, 同时显示居中提示文字.

============ 问题与修复 ============

### 问题 1: 配网模式 BLE 广播 ADV_DATA malloc 失败 (手机搜不到设备)

  日志:
    E (3731) BLE_INIT: Malloc failed
    E (3731) BT_HCI: CC evt: op=0x2008 (HCI_BLE_WRITE_ADV_DATA), status=0x7
    I (3731) BluetoothProvisioning:  广播启动成功  (假成功, 广播数据空)

  根因:
    Rtc_AIavatar 比 adaptation_dzbjImg_shar 多 ~50-80 KB DRAM 业务 .bss
      (软件 AEC + HTTPS 完整状态机 + dialog watchdog + 完整 RTC 状态),
    + 火山 RTC SDK 静态库 .bss ~30-50 KB (g_cnxMgr 14.6KB, ack$14 12.6KB 等),
    配网模式时 BLE Bluedroid stack 抢不到广播数据 malloc 所需的 ~10KB DRAM.

  修复 (前次 commit 已做): sdkconfig 关闭 BLE 5.0 6 个特性 (项目实际只用 4.2 legacy),
    省 ~15 KB controller DRAM, 广播数据 malloc 成功.

### 问题 2: 配网模式下 LCD 绘制跟 WiFi/BLE 初始化抢 DRAM 导致 reboot

  日志:
    E (1200) wifi:Expected to init 10 rx buffer, actual is 1
    E (1220) BluetoothProvisioning: WiFi初始化失败: ESP_ERR_NO_MEM
    assert failed: vQueueDelete queue.c:2355 (pxQueue) ← BLE GATT fixed_queue_new 失败 → 反向清理 NULL 队列

  排查路径 (失败方案记录):
    - esp_lcd_panel_draw_bitmap 一次画 360x360 (253KB): SPI queue 满, 下半屏未画 + DRAM 抢 WiFi
    - 分块画 (60 行/块) + vTaskDelay 块间: SPI driver 内部 queue 持续保留 DRAM, 仍然抢
    - 强制 codec output_only=false 完整 duplex: 多 15KB DRAM, BLE BTU_StartUp malloc 失败 reboot
    - CONFIG_BT_BLE_DYNAMIC_ENV_MEMORY=y: 引入 BTU bt_workqueue 分配失败 → vQueueDelete NULL → assert

  修复 (本次 commit): EAF 最小化初始化
    movecall_moji_esp32s3.cc 配网模式调用新增的 ai_chat_screen_init_provisioning(),
    跳过 8 张 EAF 资源加载 (省 4.32 MB PSRAM + ~10KB DRAM), 跳过数字人 anim,
    只启用 gfx_emote renderer + 单个 gfx_label, flush buffer 启动时预分配 (~30KB DRAM 一次到位),
    跟 BLE 初始化不再有动态分配冲突. 跟 adaptation_dzbjImg_shar 用 LVGL 显示 GIF 同思路,
    用 EAF 替代 LVGL 避免引入 50-80KB LVGL .bss.

### 问题 3: 配网模式音效不播放

  根因:
    ResetWifiConfiguration 由 BOOT 按键 OnClick 调用, 跑在 esp_timer task 上下文,
    vTaskDelay(4000ms) 实测只等了 1.1 秒就被唤醒, 音效没播完就 esp_restart.

  修复 (前次 commit 已做): 派发到独立 task 跑 PlaySound + vTaskDelay + esp_restart,
    独立 task 中 vTaskDelay 正常工作, 等 4 秒确保 解码 + DMA + 功放尾音完整.

### 问题 4: 配网时屏幕黑屏 (UX 不友好)

  实施: ai_chat_screen_init_provisioning("请使用APP\n蓝牙配网~")
    LCD 黑底白字居中显示提示文字, 用户感知"配网中".
    label height=64 (恰好包 2 行 + 余白), GFX_ALIGN_CENTER 上下左右居中.

============ 文件改动 ============

  main/application.cc:
    Application 构造时显式注释: 不能在配网模式置 background_task_=nullptr
    (OnAudioOutput 无判空, 会 std::mutex::lock 异常 abort).

  main/boards/common/wifi_board.cc:
    ResetWifiConfiguration 派发独立 task 跑 PlaySound + 4s delay + esp_restart,
    EnterWifiConfigMode BLE 启动后早 return (StartBleProvisioning 内部已 Alert 音效).

  main/boards/movecall-moji-esp32s3/movecall_moji_esp32s3.cc:
    AI 对话模式分支: 配网时调 ai_chat_screen_init_provisioning() 显示文字,
    正常模式调 ai_chat_screen_init() 显示数字人.

  main/dzbj/ai_chat_ui.h:
    新增 ai_chat_screen_init_provisioning(const char* hint_text) 声明.

  main/dzbj/ai_chat_ui_eaf.c:
    新增 ai_chat_screen_init_provisioning() 实现, EAF 最小化路径:
      gfx_emote_init + gfx_disp_add + 单 label 显示文字, 跳过 EAF 资源/anim/背景图.

============ 测试结果 (设备实测) ============
  - 按 BOOT 触发配网: 听到完整配网音效 (P3_LALA_WIFICONFIG 约 1 秒)
  - 设备重启 → 配网模式启动 → LCD 显示"请使用APP\n蓝牙配网~" 居中
  - 手机能搜到 Airhub_d0:cf:13:03:bb:f2 → 能连接 → 配网完成
  - 配网完成重启 → 正常模式数字人 + RTC 对话功能正常

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 14:42:13 +08:00
be788be251 feat(ui): 8 张 EAF 数字人完整接入 (m01~m08 全情绪映射)
数字人从原本 m06+m07 两张 EAF 扩展到 m01~m08 八张, 通过 ezgif 240×320 +
抽帧 N=3 + EAF Packer 配置, 8 张 EAF 总和压缩到 4.32 MB, SPIFFS 当前
4.94 MB 分区直接装下, 无需扩分区也不丢 OTA 升级能力.

main/dzbj/ai_chat_ui_eaf.c:
  - 新版/旧版 MMAP 自动检测 (header[8] == 0x14 → entry 32B; 0x10 → 28B),
    兼容在线 EAF Packer 两种导出格式 (FSIZE/FOFFSET 偏移自动适配).
  - find_cache_index_by_name 加 fallback: 找不到精确匹配时返回 cache[0],
    PoC 阶段单张 EAF 也能验证全部情绪触发.
  - emotion_map 22 情绪 → 8 张 EAF 重排:
    * 默认/积极组 12 → m01..m05 均分 (每张 2-3 种)
    * 思考/疲倦组 5 → m06
    * 负面/严肃组 3 → m07
    * 惊讶组 2 → m08
  - 数字人对齐 GFX_ALIGN_CENTER → GFX_ALIGN_BOTTOM_MID, 240×320 在
    360×360 圆屏贴底显示, 顶部 40px 透明露出背景图, 视觉跟之前
    360×360 全屏 EAF 一致 (脚部贴底, 字幕 z-index 上层覆盖底部 56px).

spiffs_image:
  - hiyori-assets.bin: 956 KB (m06+m07) → 4.53 MB (m01~m08 + index.json)
  - 删除原 GIF (hiyori_m{03,06,07}.gif), EAF 已替代不需要烧到设备.

实测数据 (Baji 2026-05-20):
  m01=814KB m02=516KB m03=563KB m04=559KB m05=606KB m06=423KB m07=379KB m08=566KB
  8 张 EAF 总和: 4.32 MB
  SPIFFS 占用: 4.58 MB / 4.64 MB 可用 = 1.3% 余量 (临界, 未来加资源需要规划)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 18:17:09 +08:00
eceadda807 feat(ui): Phase 10 step 1+2 - 背景图 + 中文字幕 + 数字人透明
完成数字人模式 UI 的"背景图叠加 + 实时字幕"功能。所有改动基于 EAF
框架(Phase 10 commit 31982ba),保持 0 个 lv_* UI 函数链接进固件。

Step 1: JPG 背景图叠加
- ai_chat_ui_eaf.c 加 esp_jpeg 解码 Background_360x360.jpg →
  RGB565 buffer (252KB PSRAM) → gfx_img_create 作为底层
- z-index 通过创建顺序控制: 背景 → 数字人 anim → 字幕 label
- 选项 A 保留 JPG (~20KB SPIFFS) 比选项 B (252KB .bin) 省 232KB

数字人透明: esp_emote_gfx local patch (gfx_anim.c::gfx_anim_render_24bit_pixels)
- 根因: 在线 EAF Packer 默认导出 24-bit 模式,工具不暴露 bit_depth
  选项,alpha 滑块拉到 0 无法保存,导致 GIF 透明像素被烘焙成屏幕背景
  色 (黑色 RGB888 #000000)
- 解决: 在 24-bit 渲染函数加 chroma key,跳过近黑像素让背景图露出
- 阈值演化 v1 (0x0000) → v3 (16) → v4 (24),最终 RGB888 ≤ (24,24,24)
- 保留 R/G/B AND 关系(三分量都小才透明),保护数字人本体暗色不破洞
- 双字节序判定,兼容 disp_config_t.flags.swap = true

Step 2: 中文字幕 (gfx_label + LVGL bitmap font 方案 A)
- 字体方案对比 3 方案后选方案 A(C 数组 XIP from Flash):
  • A: 1.4MB Flash + 0 RAM (推荐)
  • B: xiaozhi-fonts .bin 1.18MB SPIFFS + 1.18MB PSRAM
  • C: 自转 .bin ~2.8MB 总占用
- extern const lv_font_t font_puhui_20_4 → gfx_label_set_font 直接喂
- linker 副作用: 仅引入 7 个 LVGL 函数 ~2.2KB(lv_font_get_bitmap_fmt_txt
  / lv_mem_* 幽灵符号),无 lv_obj/lv_disp/lv_indev 等 UI 框架函数
- 字幕参数: 300×56 (2 行限制) + 行间距 4 + 贴底 y_ofs=-4
- GFX_LABEL_LONG_WRAP 字符级断行(中文友好),CENTER 居中
- 流式 TTS 节流 50ms(比 LVGL 100ms 短,EAF 渲染更快)

工具脚本 (tools/patch_eaf_transparency.py)
- 探索性脚本:解析 hiyori-assets.bin 尝试修补 EAF palette alpha
- 实际未生效(工具导出 24-bit 无 palette),保留作为 EAF bin layout
  解析参考

固件大小: 2.75MB → 4.30MB(+1.55MB = 字体 1.4MB + 字幕代码 + 背景图代码)
分区余量: 50% → 25% (1.42MB 空闲,安全)

完整踩坑经验已沉淀到 ~/.claude/CLAUDE.md §13 + 项目 memory。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 17:38:31 +08:00
31982ba7b9 feat(ui): Phase 10 - 数字人模式 LVGL → esp_emote_gfx 完整切换
 验证完成:
- 音频卡顿明显改善(用户实测)
- 数字人 hiyori 动画正常显示
- nm 验证:固件中 0 个 lv_*/lvgl_* 函数符号
- kapi.bin: 4.7MB → 2.75MB(-42%)

关键改动:
- main/dzbj/ai_chat_ui_eaf.c (404 行新增):
  完全替代 LVGL 版 ai_chat_ui.c,提供同名 C API(ai_chat_screen_init
  / set_status / set_emotion / set_chat_message / resume_animation)。
  AiChatDisplay C++ 桥接层无需改动。
  内部用 gfx_emote_init + gfx_disp_add + gfx_anim + mmap_assets。
- main/CMakeLists.txt:双轨编译
  CONFIG_BAJI_BADGE_MODE=y → ai_chat_ui.c (LVGL) + bg_gif_demo.c
  CONFIG_BAJI_BADGE_MODE=n → ai_chat_ui_eaf.c (esp_emote_gfx)
- main/dzbj/dzbj_init.c:EAF 模式跳过 lvgl_lcd_init() 调用
- main/dzbj/lcd.c/h:暴露 lcd_io_handle 给 EAF 注册 IO 完成回调

踩坑修复(commit message 留档供后续参考):
1. esp_mmap_assets v2.0.0 在 use_fs=true 模式下 mmap_assets_get_mem()
   返回的是文件内偏移量而非 RAM 指针(fseek bug + offset 没加
   data_section_start),导致 LoadProhibited panic。
   解决:完全绕过 mmap_assets,自己 fopen + 解析 MMAP bin 头
   (layout: 头 16B + 每 entry 28B + data 段每文件 2B magic + 数据)。
2. esp_emote_gfx 期望 esp_lcd_touch v2.x 新 API,项目用 v1.1.2 旧 API。
   在 managed_components 内 gfx_touch.c 加 shim 桥接(local patch,
   reconfigure 后需 reapply)。
3. EAF format magic 是 0x89 'EAF'(gfx_eaf_dec.h),不是 0x5A5A
   (那是 esp_mmap_assets 内部文件分隔符)。
4. SPIFFS 需要在 ai_chat_screen_init 入口自动挂载(不能依赖
   bg_gif_demo 的惰性挂载,那个已被 CONFIG 排除)。

依赖增量:
- espressif2022/esp_emote_gfx: ~3.0.5
- espressif/esp_mmap_assets: * (仅用于声明依赖,运行时被绕过)

数字人模式核心 UI 范围:
- 显示数字人动画  (hiyori_m06/m07, 居中循环)
- 情绪 → GIF 映射  (23 情绪 → 2 EAF,sad/angry 暂用 m07,m03 待补)
- 字幕/状态文字: stub (字体接驳留待后续,需打包 .bin 字体到资源)
- 触摸: 不支持(PoC 阶段不需要)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 15:53:21 +08:00