# BOOT按键聆听状态切换修改总结 ## 修改目标 将BOOT按键在说话状态下的行为从"切换到待命状态并播放'卡卡正在待命'"改为"切换到聆听状态并播放'卡卡在呢'"。 ## 修改文件清单 ### 1. application.h **文件路径**: `c:\Users\Admin\Desktop\20250806_V2\main\application.h` **修改内容**: - 新增函数声明: `void AbortSpeakingAndReturnToListening();` - 添加🔵标记注释,表示专门处理到聆听状态的切换 **修改位置**: 第84行 ```cpp void AbortSpeakingAndReturnToIdle(); // 🔴 专门处理从说话状态到空闲状态的切换 void AbortSpeakingAndReturnToListening(); // 🔵 专门处理从说话状态到聆听状态的切换 ``` ### 2. application.cc **文件路径**: `c:\Users\Admin\Desktop\20250806_V2\main\application.cc` **修改内容**: - 新增完整的`AbortSpeakingAndReturnToListening()`函数实现 - 包含状态检查、安全验证、中止消息发送、连接关闭、状态切换和语音播放 - 使用🔵标记的详细日志记录 **修改位置**: 第1437-1505行(新增68行代码) **核心功能**: 1. 状态验证(确保当前为说话状态) 2. 安全操作检查(防止频繁操作) 3. 发送中止消息给服务器 4. 延迟100ms后主动关闭音频通道 5. 延迟200ms后切换到聆听状态 6. 播放"卡卡在呢"语音(P3_KAKAZAINNE) ### 3. application.h (第111行) - 添加状态标志 **文件路径**: `c:\Users\Admin\Desktop\20250806_V2\main\application.h` **修改内容**: - 添加`std::atomic is_switching_to_listening_{false};`原子标志 - 用于跟踪是否正在主动切换到聆听状态 **修改位置**: Application类私有成员变量 ### 4. application.cc (状态保持优化) **文件路径**: `c:\Users\Admin\Desktop\20250806_V2\main\application.cc` **修改内容**: - 移除聆听状态下自动回退到idle状态的逻辑 - 确保设备切换到聆听状态后能够稳定保持该状态 - 这是解决用户问题的核心修改 **修改位置**: `SetDeviceState()`函数中聆听状态处理逻辑 **技术细节**: - 移除音频通道不可用时自动回退机制 - 保持聆听状态的稳定性 - 避免状态意外切换导致的用户体验问题 ### 5. application.cc (第1437-1502行) - 标志管理 **文件路径**: `c:\Users\Admin\Desktop\20250806_V2\main\application.cc` **修改内容**: - 在`AbortSpeakingAndReturnToListening()`函数开始时设置`is_switching_to_listening_`标志 - 在状态切换完成后清除标志 - 标记主动切换到聆听状态的过程 **修改位置**: `AbortSpeakingAndReturnToListening()`函数内部 ### 6. application.cc (第561-568行) - 回调保护 **文件路径**: `c:\Users\Admin\Desktop\20250806_V2\main\application.cc` **修改内容**: - 在`OnAudioChannelClosed`回调函数中检查`is_switching_to_listening_`标志 - 如果正在主动切换到聆听状态则跳过设置为idle状态 - 防止音频通道关闭回调干扰主动的状态切换 **修改位置**: `OnAudioChannelClosed`回调函数 ### 4. movecall_moji_esp32s3.cc **文件路径**: `c:\Users\Admin\Desktop\20250806_V2\main\boards\movecall-moji-esp32s3\movecall_moji_esp32s3.cc` **修改内容**: - 修改BOOT按键在说话状态下的处理逻辑 - 将函数调用从`AbortSpeakingAndReturnToIdle()`改为`AbortSpeakingAndReturnToListening()` - 更新日志消息和注释 **修改位置**: 第389-392行 ```cpp // 修改前 ESP_LOGI(TAG, "🔴 BOOT按键:设备处于说话状态,启动专门的中止和切换流程"); app.AbortSpeakingAndReturnToIdle(); // 修改后 ESP_LOGI(TAG, "🔵 BOOT按键:设备处于说话状态,启动专门的中止和切换到聆听状态流程"); app.AbortSpeakingAndReturnToListening(); ``` ## 技术实现特点 ### 1. 函数职责分离 - 保留原有`AbortSpeakingAndReturnToIdle()`函数不变 - 新增专用`AbortSpeakingAndReturnToListening()`函数 - 遵循单一职责原则,避免修改核心函数 ### 2. 安全机制 - 状态验证:确保只在说话状态下执行 - 操作频率限制:通过`IsSafeToOperate()`防止频繁操作 - 异常处理:网络异常时的降级处理 ### 3. 时序控制 - 100ms延迟:确保服务器处理中止消息 - 200ms延迟:确保连接完全关闭后再切换状态 - 异步执行:使用`Schedule()`避免阻塞主线程 ### 4. 日志系统 - 🔴标记:待命状态相关操作 - 🔵标记:聆听状态相关操作 - 详细的操作步骤记录,便于调试和监控 ## 执行流程 ``` 用户按下BOOT按键(设备处于说话状态) ↓ movecall_moji_esp32s3.cc: 检测到说话状态 ↓ 调用 app.AbortSpeakingAndReturnToListening() ↓ application.cc: 执行状态和安全检查 ↓ 发送中止消息给服务器 ↓ 延迟100ms后关闭音频通道 ↓ 延迟200ms后切换到聆听状态 ↓ 播放"卡卡在呢"语音提示 ↓ 设备进入聆听模式,等待用户语音输入 ``` ## 语音资源使用 - **原来**: `Lang::Sounds::P3_DAIMING` ("卡卡正在待命") - **现在**: `Lang::Sounds::P3_KAKAZAINNE` ("卡卡在呢") - **资源位置**: `main/assets/lang_config.h` 中定义 - **音频文件**: `audios_p3/kakazainne.p3` ## 兼容性保证 ### 1. 向后兼容 - 原有`AbortSpeakingAndReturnToIdle()`函数完全保留 - 其他调用该函数的地方不受影响 - 非说话状态下的BOOT按键行为完全不变 ### 2. 状态覆盖 - 待命状态 → 聆听状态(不变) - 聆听状态 → 待命状态(不变) - 说话状态 → 聆听状态(新行为) - 其他状态 → 设备唤醒(不变) ## 测试要点 ### 1. 功能测试 - 说话状态下BOOT按键响应 - 状态切换的正确性 - 语音提示播放 - 聆听功能正常工作 ### 2. 性能测试 - TTS停止响应时间 - 状态切换完成时间 - 内存和CPU使用情况 ### 3. 异常测试 - 网络断开情况 - 快速连续按键 - 音频队列异常 ## 优势总结 1. **用户体验优化**: 从说话状态直接进入聆听状态,交互更流畅 2. **代码结构清晰**: 专用函数处理特定场景,职责明确 3. **维护性良好**: 不影响现有功能,扩展性强 4. **安全性保证**: 完整的状态检查和异常处理机制 5. **日志完善**: 详细的操作记录,便于问题定位 ## 风险评估 - **低风险**: 新增函数不影响现有逻辑 - **可回滚**: 如需恢复原行为,只需修改一行函数调用 - **测试充分**: 提供完整的测试指南和场景覆盖 本次修改通过最小化的代码变更,实现了用户需求,同时保持了系统的稳定性和可维护性。