✨ feat(sparkbot): 优化显示与设备控制 - #362
Conversation
按 reminderTriggerId 执行 IM 动作,避免最近提醒窗口误操作其他提醒链。 持久化 operationId、动作终态和 nextTriggerAt,使设备任务重启后复用已提交结果。 主机测试、Gateway 门禁和 SparkBot 固件构建已通过;acknowledge 多仓储写入仍沿用现有非事务边界。 Closes 1024XEngineer#179
Linx 启动与提醒到点时,TLS、MCP 和音频初始化可能让内部 RAM 不再有连续任务栈空间。将唤醒控制、串口测试及提醒动作任务栈迁移到已启用的 PSRAM,并保持 WithCaps 创建与删除配对。 Refs 1024XEngineer#179
WakeTask 会在加载本地模型时调用关闭 cache 的分区映射,不能使用 PSRAM 栈。保持其内部栈属性,并在 VoiceSession 启动 TLS、MCP 与音频初始化前完成分配。 Refs 1024XEngineer#179
IM 与语音同时启用后,I2S 播放任务创建时内部堆最大连续块可能不足 16 KiB,导致 VoiceSession 启动失败。 将播放任务栈迁移到 PSRAM,并配对使用 WithCaps 创建和删除接口;ESP-IDF 6.0 构建已通过,真机复测继续进行。 Refs 1024XEngineer#179
设备可能在串口任务确认 BEGIN 前完成语音状态切换并发布 CAPTURE_READY。测试脚本从同一发送起点等待两个标记,避免因日志顺序竞争漏发 PCM。 Refs 1024XEngineer#179
Linx 与 IM 的网络初始化会碎片化内部堆,导致 I2S 双工通道偶发无法分配 DMA 描述符。先预留唤醒任务内部栈并预开固定 PCM 端口,再启动 Wi-Fi、TLS 和 Gateway;失败路径按双工设备顺序关闭预开端口。 Refs 1024XEngineer#179
记录通知提交、动作窗口、SSE 命令与结果统计,日志不包含 URL、凭据或 Action token。 Refs: 1024XEngineer#179
撤回 2929553,保留提醒播报后的语音 follow-up 交互。重连问题继续按 Linx 传输链路诊断,不通过关闭自动采集规避。 Refs: 1024XEngineer#179
主干新增提醒事件快照后与 1024XEngineer#179 的 v007 动作状态迁移冲突,导致 PR 处于 DIRTY 状态且 CI 无法启动。 保留事件快照、最终提醒文案和精确 action 幂等字段,将动作迁移顺延为 v008,并维持 Linx 释放共享串口后启动 IM provisioning 的资源顺序。 clang-format 18、完整 host 编译及 92 项 host 测试已通过。 Refs 1024XEngineer#179
ESP-IDF 6.0 将聚合字段遗漏警告提升为错误。为精确提醒动作的首次执行和幂等重放路径显式初始化空事件列表,不改变动作结果协议。 Refs 1024XEngineer#179
提醒任务 ID 是本地 SQLite 行号,数据库重建后可能从 1 重新分配。业务事件键加入稳定的 device_id,保持同一任务重试幂等并避免 Gateway 全局键冲突。 Refs: 1024XEngineer#179
Codecov Report❌ Patch coverage is @@ Coverage Diff @@
## main #362 +/- ##
==========================================
- Coverage 88.70% 88.69% -0.02%
==========================================
Files 215 217 +2
Lines 27129 27188 +59
Branches 8509 8553 +44
==========================================
+ Hits 24066 24115 +49
- Misses 1434 1438 +4
- Partials 1629 1635 +6
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
已检查固定范围 3b7b8e751a6871b283616f6dd4fe24cb8eb70bed...290748156d2f8a2934ac28d22edc9315e479fb31,覆盖显示渲染、Linx 情感证据、MCP 音量工具和 Runtime 集成。情感事件链与音量工具 schema 逻辑整体完整;但新增回调存在一个运行时线程归属问题,见内联意见。git diff --check 通过;主机全量构建被仓库现有的 [[maybe_unused]] 数据成员警告在 -Werror 下阻断,未能完成 CTest。
| schedule_operation_service_, schedule_reminder_service_.get()); | ||
| if (!init_status_.ok()) return fail_startup(init_status_); | ||
| init_status_ = mcp::RegisterAudioMcpTools(mcp_server_, [this](int volume) { | ||
| SetVolume(volume); |
There was a problem hiding this comment.
[P1] 将音量修改转回 Runtime 事件循环
该闭包由 StartMcpWorker 的 MCP worker 线程执行,但它直接调用 SetVolume。该函数会同时写入 volume_、调用板级 SetOutputVolume、提交显示事件并读写音量 overlay 定时器状态;这些状态在普通按键路径中由交互事件循环线程维护。MCP 请求与按键/显示事件并发到达时会产生数据竞争,并可能从非显示任务触碰板级输出和 timer 状态,导致音量 overlay 或运行时状态损坏。请在此处只入队一个控制事件,让 SetVolume 在 Runtime 事件循环中执行(或为所有相关状态提供一致的同步机制)。
设备端不再用请求头 API 校验响应 Content-Type,改由 HTTP 状态和 SSE 解码器共同判断动作流。避免合法 SSE 被误关闭,确保提醒确认结果可以继续回传。 Refs: 1024XEngineer#179
回执未被网关受理时立即释放 SSE,让运行时按 operationId 重放未确认命令,避免 Gateway processing 状态与设备等待互相阻塞直至动作过期。 Refs: 1024XEngineer#179
SSE 提前收到 FIN 时不要误判为正常空流,保留提醒动作窗口并触发重连,确保稍后到达的确认命令仍能被设备消费。 Refs: 1024XEngineer#179
阶段更新:BMI270 IMU 接入这次把 SparkBot 的 IMU 型号先按官方实现定为 BMI270,依据是 SparkBot 资料和对应示例都使用 BMI270;代码不再把 QMA6100P/QMA7981 的旧 capability 注释当成确定硬件结论。 已完成:
验证结果:
本阶段没有访问、读取或刷写实板。你后续测试时重点看三类明文日志: 摇晃后的“晕眼睛”或提示音暂不固化,等你确认实板型号、地址和阈值后再做产品行为决策。 |
资源与干净基线复核补充复核已完成:
|
为每个提醒动作窗口创建独立的动作通道,并将并发 worker 限制为 4 个。空闲 SSE 不再阻塞后续提醒,断线时保留当前窗口并重连。 Refs: 1024XEngineer#179
SSE 连接在尚未解析动作命令时收到 EOF,统一按网络断线重连,避免 ESP-IDF 对 chunked FIN 的误判丢弃未确认提醒。 Refs: 1024XEngineer#179
# Conflicts: # components/voicelife_runtime/src/runtime.cc # components/voicelife_runtime/src/serial_voice_test.cc # tests/python/test_firmware.py
阶段更新:预合并 PR #359 到 PR #362已在 PR #362 分支
本地验证:
远端检查当前状态:
|
…and-controls-20260825 # Conflicts: # components/voicelife_audio_esp/include/voicelife/audio_esp/esp_opus_codec_strategy.h # components/voicelife_audio_esp/include/voicelife/audio_esp/sparkbot_audio_budget.h # components/voicelife_runtime/src/runtime.cc # components/voicelife_schedule/include/voicelife/schedule/schedule_reminder_service.h # components/voicelife_schedule/src/service/schedule_reminder_service.cc # components/voicelife_voice/include/voicelife/voice/voice_ports.h # tests/host/schedule_reminder_action_execution_test.cc # tests/host/usb_serial_frame_router_test.cc # tests/python/test_im_wifi_credential_isolation.py
PR #362 实验矩阵与验收计划结论与范围本评论只验收 PR #362 相对最新 当前基线:
自动化基线(已完成)
实板验收矩阵(待通知后执行)实板测试时保存完整明文串口日志;不使用本机扬声器播放。串口端口以现场枚举为准,优先使用 SparkBot 原生 USB-Serial/JTAG 的应用日志口,不把 CH340 bootloader 口当作应用日志口。每项均需要在日志和屏幕/硬件现象两侧同时验收。
明确不纳入本次验收
实板验收记录格式每个 HIL 编号在后续测试中补充:时间、固件 commit、板号/串口、原始输入、完整串口日志路径、屏幕/硬件观察、是否通过、失败时最小复现步骤。只有日志、屏幕现象和验收标准同时满足,才将该项从“待执行”改为“通过”。 |
|
本 PR 现关闭,后续实现已迁移到 #377。 替代 PR #377 基于最新 main,关联 Issue #376,收敛以下问题:
|
|
实现已由 PR #377 接替,关闭本历史 PR。 |
结论
这个 PR 把 SparkBot 的显示、设备控制和板载 IMU 接入收回到用户能读懂、也能稳定使用的形状。显示改动保持原有语音链路不变;IMU 先完成可靠的 BMI270 采样与摇晃事件上报,摇晃后的产品反馈留给实板确认后再决定。
改动
矽(U+77D2)字形是否可解析。LV_LABEL_LONG_SCROLL_CIRCULAR改为单向横向滚动。只有正文变化时才重设 LVGL label,并按实际溢出宽度把滚动时长限制在 3000 到 12000 ms,避免重复、加速和一闪而过。llm消息中的emotion/action通过 VoiceSession evidence 传到 SparkBot 表情映射,不改变语音会话状态。self.audio_speaker.set_volume,参数为 0..100 的整数,回调复用现有板级输出音量接口,成功返回true。viewport_y=6下移到viewport_y=10。espressif2022/bmi270组件(1.1.0~2)。按 SparkBot 硬件资料使用 I2C0 GPIO4/5、默认地址0x68(同时探测0x69),加速度计配置为 2G、200 Hz。BoardInputAction::kShakeDetected,Runtime 当前记录SPARKBOT_IMU_SHAKE detected=1,不伪造语音事件、不改变语音状态机。IMU 实板验证边界
代码侧已覆盖“传感器可用”和“传感器不存在”两条路径:
SPARKBOT_IMU_READY sensor=BMI270 addr=0x68 sample_hz=200 threshold_mps2=4.5SPARKBOT_IMU_UNAVAILABLE ...SPARKBOT_IMU_SHAKE detected=1本次没有访问、读取或刷写实板。实板验证时先确认
READY或UNAVAILABLE,再轻微摇晃观察SHAKE是否只出现一次且不会连续刷屏。摇晃后的屏幕动画和提示音暂不固化,等硬件事实和阈值由实板测试确认。聆听时长基线
本 PR 没有改动语音门限。当前实现是:唤醒后未检测到用户说话时最多等待 6000 ms;检测到语音后取消这个门限;VAD 静音 900 ms 触发端点;端点后最终 STT 最多等待 5000 ms。
验证
sparkbot_imu_test。esp32s3-esp-sparkbot构建通过,voicelife.bin为0x2944d0字节,应用分区剩余约 8%。git diff --check通过。0ae06dd(✨ feat(imu): 接入 SparkBot BMI270 摇晃检测)。Fixes #361