44 Commits

Author SHA1 Message Date
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
3a1111e99d plan(rtc-only): Phase 9 取消 + Phase 10/11/12 规划(LVGL → esp_emote_gfx)
Phase 9 三轮增量优化(jitter buffer / codec init / Core 1 绑定)效果不
明显,用户决策完整切 EAF 验证 GIF 抢资源假设。

Phase 9 → CANCELLED:
- v1 jitter buffer device_state 判错(漏 kDeviceStateDialog)
- v1 ES7210 重试破坏 ES8311 init 导致开机播报无声
- v2 修正 device_state 后 jitter 工作但仍卡
- v3 background_task 绑 Core 1 + DIAG-5 未硬件验证
- 所有代码改动 git restore 回滚(无 commit),Phase 8 DIAG 埋点保留
- CANCELLED.md 记录教训

Phase 10 新增(数字人模式 LVGL → esp_emote_gfx 完整切换):
- 添加 espressif2022/esp_emote_gfx ~3.0.5 依赖(已 reconfigure 拉取)
- API 风险扫清:GFX_LABEL_LONG_WRAP 支持中文换行、
  gfx_font_lv_load_from_binary 兼容 LVGL bitmap font
- 双轨编译:CONFIG_BAJI_BADGE_MODE=y 保 LVGL,=n 走 EAF
- PLAN.md 含 10 个子任务从依赖到完整 UI 切换
- 预估 3-5 天

Phase 11 占位:LVGL 释放的 ~40KB DRAM + ~80KB PSRAM 投到 WiFi
缓冲扩容(STATIC_RX 10→16、DYN_RX/TX 32→48、RX_BA_WIN 6→16)+
Opus/RTC SDK jitter buffer 扩容

Phase 12 占位:原 Phase 10 集成测试 + 推送,重编号

ROADMAP 同步更新,依赖关系矫正。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 13:37:34 +08:00
3dc6cadf49 diag(rtc-only): Phase 8 - 音频卡顿根因诊断埋点 + 数据采集报告
数字人 RTC 模式音频卡顿根因定位。通过 4 类 ESP_LOGW 埋点采集运行时
数据,对照表格判定根因,输出 Phase 9 实施分支决策。

埋点实现(main/application.cc,PHASE8_DIAG_ENABLE 宏开关,关闭后零开销):
- DIAG-1 queue 深度:3 处(出队 + WebSocket 入队 + RTC 入队),50ms 节流
- DIAG-2 codec->OutputData 写入耗时:>15ms 阈值告警
- DIAG-3 WiFi RSSI:OnClockTimer 1Hz
- DIAG-4 heap 快照 + 碎片率:OnClockTimer 1Hz

实测结论(见 DIAG_REPORT.md):用户感知卡顿 = 两个独立根因
- A. 开机播报阶段 ③' codec init 时序缺陷(ES7210 I2C 失败 +
  126 次 write_slow 集中在 2-13s)
- B. RTC 对话阶段 ⑤ Opus/WebSocket 应用层帧到达抖动
  (queue 突发堆积 19 + queue=0 出现 58 次,但 codec 写入 0 次 slow)

完全排除:① CPU 争抢、② PSRAM 带宽、④ WiFi 丢包(RSSI -24~-33dBm
极强)、⑥ 内存碎片(heap 全程稳定)

Phase 9 推荐分支 B'(双线修复,原 A/C 的 EAF 方案不适用):
- 9.1 应用层 jitter buffer(fill-threshold + drain)—— 解 B
- 9.2 开机 codec init 时序修复(ES7210 reset + ready 等待)—— 解 A
- 估时 1 天

ROADMAP 同步:Phase 7 矫正为 battery_psm(实际状态)、Phase 8 新增
诊断、Phase 9 占位待 Phase 8 决策、原"集成测试"挪到 Phase 10。
新增 .planning/STATE.md 记录 roadmap evolution。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 11:40:42 +08:00
4b3206dca3 docs(rtc-only): 对比报告 v2.1-v2.4 — 卡顿真凶定位 + 触顶诊断 + P4 升级路径
v2.1 §11.10 真凶定位:
- 通过 Baji+Kapi 同 Wi-Fi 对比日志, 证明卡顿不是网络问题
- Baji WiFi 缓冲 8/10 vs Kapi 16/16, 加上 LVGL/GIF 抢 PSRAM 总线
- 同 BSSID 同信号下 Baji reor=1790, Kapi reor=0~226 (8 倍差距)
- 新增 Phase 7.8/7.9 修复方向

v2.2 §11.11 GIF 4 方案评估:
- 方案 A (GIF EMBED 转 C 数组)  否决: 占 +2MB 爆 OTA 分区, PSRAM 带宽不变
- 方案 B (通话期暂停 GIF)  强推荐: 20 行 / 0 内存代价 / reor 1800→500
- 方案 C (RGB565 帧序列)  否决: Flash 不够
- 方案 D (PNG 序列 + lv_anim) ⚠️ 复杂可考虑
- 新增 Phase 7.10/7.11

v2.3 §11.12 硬件触顶诊断:
- 用户最终需求(说话期 GIF + 触摸放大渐变 + RTC)超 ESP32-S3 极限
- 全并发 PSRAM 带宽需求 40MB/s, 接近物理极限 60MB/s 的 67%
- 给出 3 条路径: A 分模式策略 / B 极限优化 / C 换 ESP32-P4
- 新增 Phase 7.12 + Phase 8

v2.4 §11.13 ESP32-P4 完整需求容量评估:
- 几十套 GIF Flash 占用: 30 套 21MB / 100 套 70MB → P4 需 32/64MB
- PSRAM 32MB 按需 LRU 缓存, 同时驻留 10 套 GIF 仅占 5MB
- PPA 硬件 2D 加速: 触摸缩放从 30% CPU + 30MB/s PSRAM 降到零 CPU
- 外挂 ESP32-C6: WiFi 6 / BLE 5.3 (比 S3 WiFi 4 抗丢包更强)
- BOM 升级 ¥20→¥53/台 (+¥30), 开发周期 3-6 个月
- 结论: 长期产品强烈建议直接立项 P4 v2, S3 v1 作快速上市验证
- Phase 8 细化为 8.1-8.6 子任务

文档累计 1335 行, 完整覆盖"S3 优化探索 → 触顶诊断 → P4 升级"决策链。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 16:51:34 +08:00
244f28a0ab docs(rtc-only): 新增官方 Korvo-2 方案全面对比分析报告
基于 5 方对照(ES7210 datasheet + 电子吧唧原理图 + Korvo-2 V3.1.2 原理图 +
官方 pipeline.c + 当前 box_audio_codec.cc)输出 826 行分析报告:

- §一~五: 音频管线/RTC 编解码/任务线程/本地音频兼容性/抗抖动机制对比
- §六/补充·一~五: 麦克风收音根因分析 + 硬件 100% 等效证据 + GPIO 全对照 +
  ES7210 + AEC 完整启用方案(方案 D 2mic+1ref / 方案 E 1mic+1ref)
- §七~十: 实施路径/风险/执行建议
- §十一 决策记录: 基于 internal SRAM 仅剩 44KB 实测 + 服务端已有
  VAD/打断/AI 降噪 → 暂不实施方案 E,先走低风险高收益路径(服务端调优 +
  mic gain 27→33dB + Phase 7.5 网络优化)
- §11.9 交叉引用 Kapi_Rtc_toy 项目,指出 Kapi 资源更宽松更适合 AFE 实验
- §十二 版本历史 v1 → v2.0

关键结论:
- 你的板子 = 官方 Korvo-2 精简版(AEC 电路元件值 100% 相同, 音频 GPIO 100% 一致)
- 麦克风收音不准 95% 是网络/物理/服务端原因, 仅 5% 是缺设备端 AEC
- ESP-ADF 重构不推荐(工作量 3-4 周, 等同重写整个项目)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 14:37:05 +08:00
4b7b1949d4 perf(rtc-only): Phase 6 收尾 - 卡顿优化 + PowerSaveTimer 守卫 + 开机加速
代码改动:
- AudioLoop 加 vTaskDelay(1),让出 Core 1 idle task 防 WiFi/RTC 饥饿
- BackgroundTask 优先级 2 → 5,提升 Opus 解码实时性
- LVGL 刷新 5ms → 16ms (60Hz),CPU 占用降 60%
- GIF 定时器 20ms → 33ms (3 处),PSRAM 流量减半
- AI 字幕推送 100ms 节流,避免 LVGL 锁争抢
- EnterIdleHibernate 清空 audio_decode_queue_,防 standby_sound 残留误触发首帧
- PowerSaveTimer OnEnterSleepMode 加 device_state 守卫,拦截 dialog/connecting
  期间关功放(修复欢迎语期间被静音 bug)
- 取消开机 ADC 阻塞采样,开机播报响应从 6 秒缩到 < 3 秒

新增规划:
- Phase 7 占位文档:电量保护 + PowerSaveTimer 重构 + 唤醒杂音根治 + RTC 抖动缓解

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 11:38:48 +08:00
b8a5fe958f feat(rtc-only): Phase 6 - RTC 空闲软休眠(B+C 双源 + 真退房 + 字幕提示 + 内存兜底)
按 GSD 框架 .planning/milestones/digital_human_rtc/phases/phase_06_idle_hibernate/
规划完成 Phase 6 软退出 RTC 机制。替代旧的"40s 硬重启退出"方案。

## 核心变更

### 1. 倒计时刷新(B+C 双源方案)

| 方案 | 监听源 | 实施位置 | 状态 |
|------|--------|---------|------|
| A 扬声器流 | I2S/PCM 输出 | application.cc audio output 3 处 | **宏关闭**(PHASE6_ENABLE_AUDIO_FALLBACK) |
| **B 字幕监听** | RTC subtitle 消息 | application.cc:1300 subtitle 分支 | **启用** |
| **C 智能体状态** | RTC conv_status 消息 | application.cc:1260 conv_status 分支 | **启用** |

复用现有 DIALOG_IDLE_COUNTDOWN_SECONDS=40 不新增常量。

### 2. 真退出 RTC 房间(释放 License)

- 新增 Protocol 基类虚函数 LeaveRoom(默认回退到 CloseAudioChannel)
- VolcRtcProtocol::LeaveRoom 覆写:volc_rtc_stop + volc_rtc_destroy
  - 火山官方文档明确:真退房必须 leaveRoom + destroyRTCEngine
  - CloseAudioChannel 只 stop 不够(真人仍在房间继续计费)
- 服务端 AI 任务在 180s 内自动清理(火山平台机制)

### 3. EnterIdleHibernate / WakeFromHibernate

EnterIdleHibernate 流程(严格顺序):
1. protocol_->LeaveRoom()                  # 真退房
2. codec->EnableInput/Output(false)        # 重置 codec 状态机
3. recorder_pipeline_close()
4. hibernating_.store(true)                # 关键:先设标志阻止 PowerSaveTimer
5. esp_pm_configure(light_sleep=false)     # 双保险禁用 Light Sleep
6. SetDeviceState(kDeviceStateIdle)
7. idle_cycles_++ + NVS 持久化
8. 字幕"已自动退出RTC对话,按BOOT键重新连接RTC"(5 次重试间隔 200ms)

WakeFromHibernate 流程:
1. 检查 idle_cycles_ >= 50 → 硬重启清理碎片(兜底)
2. 清空字幕
3. ToggleChatState → OpenAudioChannel → 自动重建 rtc_handle_
4. RTC 重新加入房间(实测 2-3s 完成)

### 4. CanEnterSleepMode 加 hibernating 检查

防止 hibernate 期间 PowerSaveTimer 触发 esp_pm_configure(light_sleep=true)
导致 I2C 总线进入低功耗 → 唤醒后 ES7210/ES8311 通信失败 abort。

### 5. Dialog Watchdog 触发动作改造

旧:esp_restart() 整机重启(黑屏 15-25s + WiFi 重连)
新:Schedule(EnterIdleHibernate) 软退房(不熄屏 + 字幕提示)

### 6. BOOT 唤醒走 WakeFromHibernate 路径

iot_button 回调中检测 IsHibernating(),派发到独立 task 执行
WakeFromHibernate(避免阻塞 esp_timer 任务,CLAUDE.md 经验)。

### 7. OpenAudioChannel 适配重建

LeaveRoom 销毁 rtc_handle_ 后,OpenAudioChannel 头部检测 NULL
触发 Start() 异步重建,轮询 5s 等待就绪。NVS 缓存 device_secret
所以重建通常 100ms 完成。

## 实测验证(用户协作)

| 阶段 | 时间 |
|------|------|
| 40s 触发软休眠 |  |
| LeaveRoom 真退房 |  "✓ 已真退出 RTC 房间(leaveRoom + destroyRTCEngine)" |
| 屏幕保持 + 字幕显示 |  "已自动退出RTC对话,按BOOT键重新连接RTC" |
| BOOT 按键唤醒 |  |
| RTC 实例重建 |  100ms |
| RTC 重新加入房间 |  2-3s |
| 连续 2 次软休眠+唤醒 |  无 abort/I2C 失败 |
| 时间对比 | 旧硬重启 15-25s → 软休眠 3-5s(省 80%) |

## 6 个关键踩坑修复(详见 HIBERNATE_REPORT.md)

1. codec 状态机未重置 → 唤醒后 I2C abort
2. PowerSaveTimer Light Sleep 干扰 I2C 总线
3. hibernating_ 设置时序错误
4. dynamic_cast 在 -fno-rtti 下编译失败 → 改基类虚函数
5. LeaveRoom 后 OpenAudioChannel 直接失败 → 加重建逻辑
6. 字幕 LVGL 锁竞争 → 推迟到最后 + 5 次重试

## 文档产出(同时提交)

- .planning/.../phase_06_idle_hibernate/PLAN.md(含实施变更记录 V1-V6)
- .planning/.../phase_06_idle_hibernate/HIBERNATE_REPORT.md(验证报告)
- .planning/.../ROADMAP.md(Phase 1-5  + Phase 6 进行中状态更新)
- docs/Rtc_AIavatar/数字人表情渲染方案_云端预渲染+BLE+OTA.md
  新增第 19 章 RTC 空闲倒计时方案选型与软退出(9 小节)
- docs/Rtc_AIavatar/RTC软退出方案_移植参考.md
  完整移植参考(10 章 + 3 附录,可移植到其他火山 RTC 项目)
- docs/Rtc_AIavatar/音频卡顿_全局资源分析.md
  全局资源分析 + 13 项优化建议(不改代码)
2026-05-13 17:28:36 +08:00
f2be9922b6 feat(rtc-only): Phase 5 - RTC 字幕显示恢复(透明背景 + 2 行黑字 + 锁优化)
按 GSD 框架 .planning/milestones/digital_human_rtc/phases/phase_05_subtitle_restore/
规划完成 Phase 5 字幕显示恢复。

## 核心变更(main/dzbj/ai_chat_ui.c)

### 1. chat_container 重构

- 新增 static chat_container 变量(lv_obj 父容器)
- 尺寸 320×56(= 2 行字 + padding 4px*2)
- 位置 LV_ALIGN_BOTTOM_MID 距底 10px
- 完全透明背景(LV_OPA_TRANSP),无灰底
- 初始 HIDDEN,有内容时显示

### 2. chat_label 改造

- 黑色文本(用户反馈白字在浅色背景上不清晰)
- 尺寸 312×48 限制最多 2 行
- LV_LABEL_LONG_WRAP → LV_LABEL_LONG_DOT,超出 2 行自动 ... 截断
- font_puhui_20_4 中文字体不变

### 3. ai_chat_set_chat_message() 实现

原为空函数(PoC 期间 return),本 Phase 完整实现:
- 锁外去重:static last_content[256],相同内容直接返回
- lvgl_port_lock 200ms → 500ms(GIF 解码繁忙时给予更长等待)
- 内容空时隐藏容器,非空显示
- 成功更新后缓存 last_content

### 4. z-index 修复

bg_gif_demo_start() 后立即 lv_obj_move_foreground(chat_container)
否则 bg_img/gif_obj 后于 chat_container 创建会遮挡字幕

## 实测验证(用户协作)

60s 对话期间:
-  AI 字幕完整推送 3 次(含 54 字符长字幕)
-  LVGL 锁超时 14 次 → 0 次(锁外去重生效)
-  表情切换 + 字幕同步工作
-  长字幕自动 2 行截断
-  无 abort/重启

## 调用链(已对接 application.cc 现有逻辑,无需改协议层)

RTC 字幕 → display->SetChatMessage(role, msg)
  → AiChatDisplay::SetChatMessage
  → ai_chat_set_chat_message() ← 本 Phase 实现

## GSD 文档

- PLAN.md
- SUBTITLE_REPORT.md(含锁优化对比 + 布局规划 + 用户决策记录)
2026-05-13 13:35:41 +08:00
497c1b4654 feat(rtc-only): Phase 4 - 情绪标签 → 数字人 hiyori GIF 映射 + 切换接口
按 GSD 框架 .planning/milestones/digital_human_rtc/phases/phase_04_emotion_mapping/
规划完成 Phase 4 情绪映射 + 运行时切换。

## 核心变更

### bg_gif_demo 新增运行时切换接口

```c
esp_err_t bg_gif_demo_switch_gif(const char *new_gif_path);
```

实现要点:
- 先 heap_caps_free(g_gif_data) 释放旧 PSRAM,再加载新 GIF,单峰仅一份
- 内部 static last_gif_path 去重(相同路径直接返回 ESP_OK)
- 切换后立即 lv_timer_set_period(timer, 20) 防止 lv_gif_set_src 重建为默认 10ms
- LVGL 锁保护 200ms 超时

### 22 情绪 → 3 hiyori GIF 映射表

定义在 main/dzbj/ai_chat_ui.c USE_BG_GIF_POC 包裹内:

| GIF | 情绪标签 (个数) |
|-----|----------------|
| m06 (默认/积极) | neutral, happy, laughing, funny, loving, relaxed, delicious, kissy, confident, silly, blink, curious (12) |
| m07 (思考/疲倦) | sleepy, thinking, confused, embarrassed, dizzy (5) |
| m03 (负面/严肃) | sad, crying, angry, surprised, shocked (5) |

22 个标准情绪 100% 覆盖,未映射的情绪默认 fallback 到 m06。

### ai_chat_set_emotion 改造

PoC 模式下优先切换数字人大图(不再切隐藏的 emoji 200x89):

```c
#ifdef USE_BG_GIF_POC
if (bg_gif_demo_is_running()) {
    const char *path = find_hiyori_gif(emotion);
    bg_gif_demo_switch_gif(path);
    return;
}
#endif
// 非 PoC 模式 fallback emoji 路径保留
```

## 调用链(已与现有 RTC 字幕解析对接,无需改 application.cc)

RTC 字幕 → application.cc:1419 display->SetEmotion(emo)
  → AiChatDisplay::SetEmotion (display/ai_chat_display.cc)
  → ai_chat_set_emotion (dzbj/ai_chat_ui.c)
  → bg_gif_demo_switch_gif (PoC 模式)
  → 数字人 hiyori GIF 切换

## 烧录验证(用户实测)

监控 60s 期间捕获情绪切换:
- neutral / happy → m06(已在播,去重)
- thinking → m07 切换成功 (590ms 延迟)
- confused → m07 去重跳过
- AI 回复结束 → 自动回到 neutral
- 无 abort / 重启

m06 ↔ m07 切换屏幕可视确认,m03 走相同代码路径无需重复测试。

## GSD 文档(同时提交)

- .planning/milestones/digital_human_rtc/phases/phase_04_emotion_mapping/PLAN.md
- .planning/milestones/digital_human_rtc/phases/phase_04_emotion_mapping/EMOTION_REPORT.md
2026-05-13 11:59:38 +08:00
7d1c7dc1f0 feat(rtc-only): Phase 3 - 数字人 GIF 资源准备(hiyori m03/m06/m07,209x360)
按 GSD 框架 .planning/milestones/digital_human_rtc/phases/phase_03_gif_resources/
规划完成 Phase 3 数字人表情 GIF 资源处理。

## 处理方式(与 PoC 阶段 hiyori_m05.gif 一致)

```bash
gifsicle --resize _x360 -O3 input.gif -o output.gif
```

- 高度 = LCD 360px,宽度按原比例自动算 → 209px
- 不裁剪(保持源 GIF 完整人物)
- 不加 --lossy / --colors(保留 256 色,画质优先)
- 只用 -O3 优化文件大小

## 处理结果

| GIF | 用途 | 源 | 处理后 | 节省 |
|-----|------|-----|--------|------|
| m03 | 负面/严肃 | 407×700 3.3MB | 209×360 1.15MB | 66% |
| m06 | 默认/积极 | 407×700 1.3MB | 209×360 0.44MB | 66% |
| m07 | 思考/疲倦 | 407×700 1.2MB | 209×360 0.40MB | 66% |
| 合计 | — | 5.7MB | 1.94MB | 66% |

## 决策过程(避免后续重复犯错)

Phase 3 初稿曾尝试裁剪到 240×320 + PIL 全帧 bbox 居中裁剪,
用户烧录后反馈"视觉感官不好"——角色被横向压扁(240×320 纵横比 0.75
vs 源 407×700 纵横比 0.583)。回归 PoC 等比例缩放方式后效果与 PoC 一致。

PoC 处理标准已写入用户级 feedback memory(feedback_hiyori_gif_processing.md),
后续 hiyori GIF 处理一律用本方式,除非用户主动要求修改。

## 显示效果(用户已目视确认)

LCD 360×360 居中显示 209×360 GIF:
- 垂直方向: 360 = 360,完全充满
- 横向: 209 < 360,左右各 75.5px 留边显示背景图
- 角色比例: 完整保留源 GIF 的 407:700 = 0.582 纵横比,人物细高自然

## 删除项

- spiffs_image/hiyori_m05.gif (2.3MB) 已删除 - 被 m06/m07/m03 替代
  文件历史保留在 git,可通过 git show eb96130:spiffs_image/hiyori_m05.gif 恢复

## 默认表情切换

main/dzbj/ai_chat_ui.c:234:
- PoC: bg_gif_demo_start(..., "/spiflash/hiyori_m05.gif")
- Phase 3: bg_gif_demo_start(..., "/spiflash/hiyori_m06.gif")

## 烧录运行时验证

- 烧录后 0 次重启(连续监控 18 秒)
- BG_GIF: GIF 已加载到 PSRAM: /spiflash/hiyori_m06.gif (441.8 KB)
- AudioCodec: Audio codec started(首次冷启动直接成功)
- 用户目视确认显示效果良好

## GSD 文档(同时提交)

- .planning/milestones/digital_human_rtc/phases/phase_03_gif_resources/PLAN.md
- .planning/milestones/digital_human_rtc/phases/phase_03_gif_resources/GIF_REPORT.md

## SPIFFS 容量

新 SPIFFS 4.94MB 当前实际占用 ~2MB(40%),余量 ~2.94MB 充足。
2026-05-13 11:42:30 +08:00
ce7a3aad63 feat(rtc-only): Phase 2 - 16MB Flash 分区调整(OTA 5.5MB + SPIFFS 4.9MB)
按 GSD 框架 .planning/milestones/digital_human_rtc/phases/phase_02_partition_resize/
规划完成 Phase 2 分区表调整,为 Phase 3 数字人 GIF 资源扩容铺路。

## 分区表变更

| 分区 | 旧 | 新 | 变化 |
|------|-----|-----|------|
| model | 0x10000 / 64KB | — 已移除 | -64KB |
| ota_0 | 0x20000 / 6.5MB | 0x10000 / 5.5MB | -1MB |
| ota_1 | 0x6A0000 / 6.5MB | 0x590000 / 5.5MB | -1MB |
| storage | 0xD20000 / 2.875MB | 0xB10000 / 4.9375MB | +2.0625MB |

合计:16MB(不变),SPIFFS 净扩容 2.0625MB

## 决策依据

- 当前 =n 固件 4.63MB,5.5MB ota 留 19% 成长 buffer
- SPIFFS 4.9MB 为 3 个 hiyori GIF + 背景图扩容(Phase 3 预算 ~3.9MB)
- 移除未使用的 64KB model 分区
- NVS 分区位置/大小不变,WiFi 凭据等数据完整保留

## 验收结果(详见 PARTITION_REPORT.md)

- =n 编译: kapi.bin 4.63MB ≤ 5.5MB ota_0
- =y 双模式编译:链接通过但 6.05MB > 5.5MB ota(**预期降级**,
  数字人 RTC 单一形态只用 =n 固件,Phase 3 资源精简后或可恢复)
- 烧录运行时验证:
   Running partition: ota_0
   NVS 数据完整保留(WiFi 凭据/设备配置)
   SPIFFS 自动挂载,hiyori_m05.gif 加载成功
   AI 对话模式启动,Codec 工作,WiFi 连接,RTC WebSocket 协议初始化
   free_heap=5.2MB(无 regression)

## GSD 文档(同时提交)

- .planning/milestones/digital_human_rtc/phases/phase_02_partition_resize/PLAN.md
- .planning/milestones/digital_human_rtc/phases/phase_02_partition_resize/PARTITION_REPORT.md
2026-05-13 11:00:35 +08:00
672506e7c7 feat(rtc-only): Phase 1 - 通过 CONFIG_BAJI_BADGE_MODE 屏蔽电子吧唧模式
按 GSD 框架 .planning/milestones/digital_human_rtc/ 规划完成 Phase 1。
源代码全部保留,通过 Kconfig 开关 + CMakeLists 条件编译 + #ifdef 调用点保护
实现"吧唧模式不进固件但代码可恢复"。

## 核心变更

### Kconfig 开关(默认关闭)
- 新增 CONFIG_BAJI_BADGE_MODE(main/Kconfig.projbuild)
- sdkconfig.defaults 默认 =n
- =y 时恢复双模式(电子吧唧 + AI 对话)
- =n 时仅 AI 数字人 RTC 模式

### CMakeLists 剥离(剥离式不重写)
- 9 个 dzbj/ 吧唧专属 + 9 个 ui/screens/ 吧唧 UI 进入 if(CONFIG_BAJI_BADGE_MODE) 条件块
- 公共保留: dzbj/lcd.c, ai_chat_ui.c, sprite_demo.c, dual_gif_demo.c,
  bg_gif_demo.c, pages_pwm.c, dzbj_init.c, fatfs.c
- 修正 PLAN 漏判:dzbj_init/fatfs 公共化(AI 模式调用 dzbj_hw_display_init/DecodeImg)

### 调用点 #ifdef 保护
- application.cc: L20 include, L63-66 background_task, L536 device_mode 分支
- movecall_moji_esp32s3.cc: dzbj headers, init_spiffs_image_list extern,
  dzbj_boot_click_handler extern, device_mode_is_badge 分支, InitializeBadgeMode,
  InitializeBadgeModeButtons, mode_switch_combo 注册, device_mode_in_switch_suppress
- 保留公共 extern: ai_chat_screen_init, ai_chat_resume_animation, pwm_init

### 整体文件级 #ifdef 包裹
- dzbj/dzbj_button.c/h
- dzbj/sleep_mgr.c
- sleep_mgr/include/sleep_mgr.h

### 6 个文件显式 #include "sdkconfig.h"
- ESP-IDF 不会 force-include,必须手动 include 才能拿到 CONFIG_* 宏

## G7 验收双向编译

- =n 模式 build:  EXIT=0(数字人 RTC 单一形态)
- =y 模式 build:  EXIT=0(双模式恢复可用)

## 固件大小变化

| 段 | =n | =y | 节省 |
|----|-----|------|------|
| .text | 2.03 MB | 2.06 MB | 27 KB |
| .rodata | 2.48 MB | 3.87 MB | 1.39 MB |
| Total | 4.63 MB | 6.05 MB | 1.45 MB |

## GSD 文档(同时提交)

- .planning/milestones/digital_human_rtc/MILESTONE.md
- .planning/milestones/digital_human_rtc/ROADMAP.md
- .planning/milestones/digital_human_rtc/INTEL.md
- .planning/milestones/digital_human_rtc/phases/phase_01_kconfig_isolation/PLAN.md
- .planning/milestones/digital_human_rtc/phases/phase_01_kconfig_isolation/SIZE_REPORT.md
- .planning/milestones/digital_human_rtc/phases/phase_01_kconfig_isolation/BADGE_MODE_ISOLATION_MAP.md
- 编译大小原始数据: size_*.txt

## 已知事项

- =n 固件 4.63 MB 仍 > 4 MB 目标,Phase 2 调整分区 + Phase 3 物理移除图片资源解决
- main/dzbj/ 下所有源文件完整保留,无任何物理删除
2026-05-13 10:22:48 +08:00
eb96130fc9 feat(Rtc_AIavatar): 数字人透明 GIF 显示方案 PoC 完成(背景图+透明GIF叠加)
源代码变更:
- main/dzbj/bg_gif_demo.c/h: 方案 C 最终实现 - JPG 背景图(lv_img) + 透明 GIF(lv_gif) 叠加
- main/dzbj/dual_gif_demo.c/h: 方案 B 中间产物 - 双 GIF 循环切换
- main/dzbj/sprite_demo.c/h: 方案 A 已弃用 - DMA 直写 GRAM 与 LVGL 争抢 LCD IO 失败
- main/dzbj/ai_chat_ui.c: 集成 USE_BG_GIF_POC 开关,加载背景图+透明 GIF
- main/dzbj/lcd.c: panel_handle 移除 static,便于其他模块访问
- main/CMakeLists.txt: 新增 3 个 dzbj 模块编译

资源新增:
- spiffs_image/Background_360x360.jpg: 设备背景图(20KB)
- spiffs_image/hiyori_m05.gif: Cubism Editor 直接导出的透明 GIF(2.3MB)
- docs/Rtc_AIavatar/: Live2D 模型(Hiyori/Haru) + 32 段 Haru GIF + 方案文档第18章 PoC 实战记录
- tools/sprite_poc/: Python GIF→RGB565 转换脚本

踩坑要点(详见 docs/Rtc_AIavatar 第18章):
- PIL Image.quantize() 会破坏 RGBA 透明度,必须改用 gifsicle
- PIL 保存动画 GIF 仅第1帧有透明,后续帧不透明 - LVGL gifdec 按帧读取
- Cubism Editor 直接导出 GIF 才能逐帧保留透明信息(FREE 版限制部分模型)
- gifsicle --lossy 会严重锯齿化,去掉只保留 --colors 256 + -O3 即可
- 裁剪居中需用全帧 bbox 不能只看第1帧(Live2D 角色每帧位置有偏移)
- LVGL 默认不支持 PNG,背景图用 JPG + esp_jpeg 解码到 RGB565 buffer
- 透明 GIF 显示黑色背景: gifdec.c canvas 初始化 alpha 须改为 0x00
2026-05-12 17:14:49 +08:00
6208930001 chore: 更新插件指南/sdkconfig/IDF串口配置 + 从仓库移除 .DS_Store
变更清单:
1、Claude Code 插件指南:新增 GSD 执行框架章节 + 第三方 Skills(think/health)说明
   + 新电脑环境恢复指南完善(增加 GSD 安装步骤、第三方 Skills 数量更新为 7 个)
2、sdkconfig:ESP-IDF SoC 配置项更新(GDMA/LCDCAM/CPU watchpoint 格式调整)
3、.vscode/settings.json:IDF 串口号从 usbmodem834101 更新为 usbmodem834401
4、从 Git 仓库移除 3 个 .DS_Store(.gitignore 已有规则但早期被跟踪)
   - .DS_Store
   - main/.DS_Store
   - main/ui/.DS_Store

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-11 14:10:36 +08:00
7868efed9b docs: 插件指南新增 GSD 执行框架章节 + GSD 项目参考文档
1、Claude Code 插件指南新增第九章:GSD 与工具链协作说明
   - 任务规模自动匹配(小事直接做/中事 gsd-quick/大事 gsd-plan)
   - GSD 与 hw-driver-workflow 的分层协作关系
   - GSD 常用命令速查表
   - 防上下文腐烂覆盖范围说明
2、插件指南恢复指南新增步骤 3.2(GSD 安装命令)
3、新增 docs/Get Shit Done项目.html(GSD 项目介绍参考文档)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 13:15:01 +08:00
b35d86f84c docs: 更新 Claude Code 插件指南 + 新增开发参考文档
Claude Code插件高效运用指南.md:
1、补充遗漏的内置 Skills(schedule/update-config/keybindings-help)
2、新增第三方 Skills 记录(find-skills/tmux/summarize/tavily-research/embedded-systems)
3、新增第八章:新电脑环境恢复指南(含 ESP-IDF v5.4.2 安装步骤)
4、新增步骤 3.1:第三方 Skills 安装命令

新增文档:
5、Claude Code 十个最值得装的 Skills copy.md — 微信文章内容存档
6、资深嵌入式工程师开发思维.md — 嵌入式开发核心思维与注意事项

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-02 10:09:08 +08:00
0bdf7be875 feat: 应援灯防撕裂优化 - DMA直接填充GRAM + LVGL flush拦截 + PWM黑屏遮蔽
应援灯颜色切换从 LVGL 渲染改为直接 DMA 填充 GRAM,彻底消除 LVGL 刷新竞争:

lcd.c/lcd.h:
- 新增 lcd_fill_color_with_buf() 直接 DMA 分条填充全屏纯色
- 新增 TEON(0x35) 启用 TE 内部同步信号
- 新增 lcd_read_scanline()/lcd_wait_vsync_timeout() VSYNC 读取接口
  (实测 QSPI 模式下 TESLRD 始终返回 0xFFFF,软件 VSYNC 不可用)

ui_ScreenSet.c:
- LVGL flush 回调拦截:进入应援灯时替换为空操作,退出时恢复
  解决 LVGL 周期刷新覆盖 DMA 颜色导致红色方块残留的问题
- DMA 缓冲区生命周期管理:进入时分配,退出时释放
- 颜色切换 PWM=0 黑屏遮蔽:DMA 期间完全熄灭背光,撕裂不可见
- 滑块交互优化:拖动期间锁定其他按钮 + PWM 50ms 节流
- 手动滑动检测替代 LVGL 手势(layer_top 上手势不可靠)

.gitignore: 排除 docs/*.pdf 文档文件

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-30 15:18:41 +08:00
99d7e910f1 补充提交:无关紧要的文件修改,不用理会 2026-03-27 10:59:33 +08:00
0735d45e52 feat: 从按键版迁移APP传图、设备间图片分享/接收、组合键模式切换功能
## 功能迁移清单(从 Dzbj_ESP32_S3_Key → Baji_Rtc_Toy)

### 1. 设备间BLE图片传输(GATT Client + 协议)
- 新增 ble_transfer.c/h:发送方 GATT Client 扫描→连接→MTU协商→分包写入
- 接收方复用现有 GATT Server(IMAGE_WRITE 0x0B01),协议完全兼容
- 发送完成/失败自动跳转 Img 界面并关闭蓝牙

### 2. APP传图显示 Update 界面
- 新增 ui_ScreenUpdate.c/h:更新进度界面(Gengxin背景 + Update_GIF动画)
- dzbj_ble.c WRITE_EVT 中通过 ble_transfer_is_receiving() 区分 APP传图 vs 设备间传输
- APP传图 → ScreenUpdate,设备间传输 → ScreenReceiving

### 3. KEY2 按键功能入口(iot_button 单击/双击/长按)
- KEY2 单击:开蓝牙 → Peiwang 配对界面(APP传图)
- KEY2 双击:接收模式 → ScreenImageReception(等待配对)
- KEY2 长按:发送模式 → ScreenImageShar(等待配对)
- 按键参数与按键版对齐:long_press_time=1200ms, short_press_time=300ms

### 4. BOOT+KEY2 组合键模式切换(替代 BOOT 长按3秒)
- BOOT 2秒长按 + KEY2 同时按下 → 触发模式切换
- 消除单键长按的误触发问题
- AI模式和吧唧模式均注册组合键

### 5. 按键上下文状态机
- btn_context_t 枚举:HOME/IMG/SET/PEIWANG/IMAGE_SHAR/IMAGE_RECEPTION/SHARING/RECEIVING/UPDATE
- 所有界面切换点(手势/按键/BLE自动跳转)同步设置 context
- BOOT 单击按 context 分发:Home无操作、Img/Set返回Home、配对退出蓝牙、传输等待取消

### 6. 新增 UI 界面(6个Screen + 7张图片)
- ScreenPeiwang:蓝牙配对等待
- ScreenUpdate:APP传图更新中
- ScreenImageShar:发送方等待配对
- ScreenImageReception:接收方等待配对
- ScreenSharing:发送方传输中
- ScreenReceiving:接收方接收中

### 7. 其他适配
- BLE 广播改为按需启动(dzbj_ble_start/stop/is_active)
- sleep_mgr 移除 KEY2 唤醒(仅 BOOT 唤醒屏幕)
- device_mode 新增模式切换按键抑制(防止重启后立即触发)
- battery_ui 电池指示器组件
- sdkconfig 启用 BLE GATTC 支持

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-27 10:55:17 +08:00
32c3dc69bc fix: 滑动切换图片时自动跳过解码失败的无效图片
1、update_ui_ImgBle 返回类型 void → bool,解码成功返回 true,失败返回 false;
2、左滑/右滑手势回调添加 for 循环,解码失败自动跳到下一张(最多尝试 10 张防死循环);
3、更新最新设备运行日志;

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 18:22:53 +08:00
4e2f6906f9 feat: 启用 BLE 5.0 2M PHY 图传加速 + 移除未使用的 BluFi 组件 + BLE 断连内存泄漏修复
1、dzbj_ble.c 新增 BLE 5.0 2M PHY 请求(连接时自动协商,不支持则回退 1M);
2、dzbj_ble.c 新增 PHY 更新事件日志(tx_phy/rx_phy: 1=1M, 2=2M, 3=Coded);
3、dzbj_ble.c 断连时清理未完成的图片传输状态,释放 img_data/filepath/file_img 防止内存泄漏;
4、移除未使用的 BluFi 组件依赖(Kconfig select、编译检查、sdkconfig),解除与 BLE 5.0 的兼容冲突;
5、sdkconfig.defaults 及生产环境配置统一启用 BLE 5.0 + 保留 BLE 4.2 legacy advertising 兼容;
6、新增 BLE 图片传输问题分析与优化建议文档(含 UniApp vs Flutter 对比分析,供 APP 开发者参考);

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 17:12:35 +08:00
af58123d66 1、更新Claude插件高效运用的使用方法;
2、更新电子吧唧AI触屏版 → 按键版迁移总结
2026-03-23 10:30:44 +08:00
919bf8f28f feat: GIF动画表情系统 + 情绪映射增强 + HTTPS音频中止修复
一、新增功能:
1、新增8种GIF动画表情(200x89) + 3种叠加图标(45x45),实现22种情绪标签到GIF的映射表;
2、新增30+组英文近义词情绪fallback映射(如worried→sad),防止AI使用非标准标签时GIF无法切换;
3、新增HTTPS中止后诊断日志,自动追踪前20帧音频处理流程便于定位无声问题;

二、Bug修复:
4、修复HTTPS播放中止后RTC音频解码参数未恢复(16000/60→8000/20),通过background_task_串行化恢复;
5、修复AbortHttpsPlayback解码器竞态崩溃,将重置/恢复/DMA flush全部串行化执行;
6、修复LVGL gifdec不支持无全局颜色表GIF的问题,支持仅使用局部颜色表的压缩GIF;
7、修复GIF透明区域显示黑色方块,canvas初始alpha改为0x00;
8、修复lv_gif定时器gif对象为NULL时的空指针崩溃;

三、优化:
9、情绪标签从等待is_final改为第一条字幕即时触发GIF切换,新增去重和回复结束自动恢复neutral;
10、对话状态表情映射优化:THINKING→thinking、ANSWERING→happy、INTERRUPTED→surprised;
11、CPU核心绑定:LVGL任务Core0,音频循环Core1,避免GIF解码与音频争抢;
12、中文情绪词映射扩展,新增担心/心疼/着急等映射;

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 15:28:14 +08:00
da098bf482 1、新增了在日志打印中接收字幕详细信息的代码 2026-03-17 09:45:29 +08:00
e2ec9f36aa 1、启用LVGL GIF解码器(CONFIG_LV_USE_GIF=y),支持吧唧模式GIF图片BLE传输和播放;
2、LVGL内存管理切换为系统malloc(CONFIG_LV_MEM_CUSTOM=y),使gifdec大块分配走PSRAM避免内部堆不足;
3、pages.h stub头文件补充pages_cleanup_gif()声明,修复ui_ScreenImg.c编译隐式声明错误;

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 17:36:18 +08:00
23613e1cd3 1、新增obtain_music Function Calling音乐播放功能,4处协议分支均支持URL直播和音乐API两种HTTPS方式;
2、Kconfig新增MUSIC_API_URL音乐播放API地址配置;
3、重构SendStoryRequest和SendMusicRequest为HttpsApiPlayback通用方法,消除故事/音乐播放~440行重复代码;
4、修正4处obtain_story注释和日志:WebSocket描述改为HTTPS API(与实际实现一致);
2026-03-05 15:44:47 +08:00
ccea0c681c feat: HTTPS故事播放 + RTC/HTTPS双向音频切换状态机 + 协议层优化
1、新增HTTPS故事播放功能:SendStoryRequest通过蓝牙MAC请求故事API,支持intro+body两段式无缝播放,替换原WebSocket故事请求方式;
2、新增HttpsPlaybackFromUrl通用HTTPS音频下载播放方法,支持JSON格式Opus帧流式解码播放;
3、新增RTC↔HTTPS双向音频切换三标志位状态机(opus_playback_active_/https_playback_active_/https_playback_abort_),HTTPS播放期间静默丢弃RTC PCM包,OnAudioOutput捕获is_opus_frame防止残留Opus帧杂音;
4、新增AbortHttpsPlayback中止方法,使用独立高优先级任务(priority=10)执行DMA flush;AbortSpeaking也新增DMA缓冲区flush确保扬声器立即静音;
5、协议层新增OnBotMessage回调,非字幕Bot下行消息立即中止HTTPS播放;volc_rtc_protocol移除is_binary依赖改为直接前缀检测,新增info前缀识别,subv字幕排除on_bot_message_由subtitle handler单独处理;
6、subtitle字幕USER/AI区分从CONFIG_VOLC_DEVICE_NAME比较改为bot_前缀判断,用户说话时立即中止HTTPS播放;
7、Kconfig新增STORY_API_URL故事播放API地址配置;
8、设备注册RTC服务时,设备名称从Wi-Fi MAC地址改为使用蓝牙MAC地址

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 13:45:52 +08:00
1e7ba0763a 1、更新了postman参数、约束和语音请求讲故事的Function Call参数;
2、字幕增加了情绪标签,并且emoji表情跟随情绪和设备状态而更新;
2026-03-02 17:27:03 +08:00
04d26d7cee 1、本次新增了Function Calling的讲故事功能,可以语音获取小故事 2026-03-02 15:34:41 +08:00
7082d2a818 补充提交postman请求参数 2026-03-02 15:12:28 +08:00
98e40ac163 feat: 火山RTC字幕文本实时显示 + 字幕日志精简
1. DataCallback 新增 subv(字幕)二进制消息前缀识别,字幕数据正确解析并转发到应用层
2. OnIncomingJson 处理 type:"subtitle",通过 userId 区分用户语音识别(STT)和 AI 回复文本
3. 字幕日志简化:subv 消息仅打印"接收下行二进制消息(字幕)",不展开 JSON 内容
4. 新增postman请求的参数

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 15:11:34 +08:00
0a6b35d979 1、新增提交postman请求的Open_API参数(稳定无报错版本) 2026-03-02 10:12:59 +08:00
58c33e3cb4 feat: AI对话模式emoji表情系统 + 中文字体 + 分区优化
1. 新增AI对话LVGL界面(ai_chat_ui),支持emoji图片 + 状态文本 + 聊天消息
2. 新增7个emoji表情资源(64×64 PNG C数组):neutral/happy/sad/angry/crying/funny/laughing
3. 新增阿里巴巴普惠体20px 4bpp中文字体(GB2312字符集)
4. 利用火山RTC会话状态(VOLC_MSG_CONV_STATUS)驱动emoji切换:
   - LISTENING→happy, THINKING→neutral, ANSWERING→laughing
   - INTERRUPTED→funny, ANSWER_FINISH→happy
5. 设备状态emoji映射:Listening→happy, Speaking→laughing, Dialog→happy
6. 配网模式显示happy emoji
7. 分区优化:model 3MB→64KB,OTA 5MB→6.5MB
8. 编译优化:-Og→-Os,移除SimSun CJK字体

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 18:14:19 +08:00
38b71ce221 chore: 添加 esp_jpeg 托管组件到仓库
将 ESP Component Registry 自动下载的 espressif__esp_jpeg 组件纳入版本管理,
避免其他开发者拉取后需要重新下载。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 10:32:38 +08:00
c4de88d7ee chore: 移除 pages_stub.c(已被 dzbj/pages.c 替代)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 10:23:28 +08:00
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
bcfd35b9b8 chore: 从 Git 索引中移除 build/ 和 sdkconfig.old
build/ 目录和 sdkconfig.old 之前已被提交到仓库,
.gitignore 只能忽略未跟踪文件。
使用 git rm --cached 从索引中移除,本地文件不受影响。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 17:10:45 +08:00
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
d5fcb917b1 补充提交dzbj文件夹 2026-02-27 10:44:58 +08:00
b544de56bb 1、补充提交LVGL的UI文件夹和spiffs_image资源文件夹 2026-02-27 10:42:11 +08:00
a003370db7 1、删除了build文件,用于忽略仓库代码提交 2026-02-27 10:34:45 +08:00
77f22c3f7d 提交.gitignore文件 2026-02-27 10:33:32 +08:00
7498e7b4a9 初始化补充提交 2026-02-24 15:57:32 +08:00
03496efecb 1、初始化代码,待适配中.... 2026-02-24 15:28:34 +08:00