============ 问题与修复 ============
### 问题 1: 配网模式按 BOOT 进入配网 → 设备 reboot
日志:
E BT_OSI: future_new unable to allocate memory for the semaphore.
assert failed: future_ready future.c:64 (future != NULL)
根因:
BLE Bluedroid 协议栈初始化时, future_new 分配 semaphore 失败 → 后续 future_ready
拿到 NULL → assert. 跟 Baji 的 vQueueDelete(NULL) 同性质 — DRAM 不足.
Kapi 用 LVGL (~50-80 KB DRAM) + RTC SDK 静态 .bss (~30-50 KB),
BLE Bluedroid stack 抢不到所需内存.
修复 (sdkconfig):
CONFIG_BT_ALLOCATION_FROM_SPIRAM_FIRST=y
让 BLE Bluedroid 全局变量动态分配优先走 PSRAM, 释放 ~30 KB DRAM
给 controller / GATT / WiFi 使用. 修复后 BLE 配网正常.
### A+B+C 三件套 (移植自 Baji commit 70f0cdd, 解决 RTC 偶发连接失败)
[A] sdkconfig: CONFIG_LWIP_MAX_SOCKETS=10 → 20
根治火山 RTC SDK 启动时 lwIP socket fd 不足触发 SocketConnection-Lite.c:191
bind local ip failed → ICE 协商失败 → wait connect bits=0x0 超时.
[B] application.h/cc + volc_rtc_protocol.h/cc: 失败 3 次后销毁 + 重建 engine
新增 VolcRtcProtocol::ForceRebuildEngine() public 方法.
application.cc 加 audio_channel_retry_count_ 重试计数,
OpenAudioChannel 连续失败 3 次时调用 ForceRebuildEngine 清理 SDK 状态.
应对 A 修复后仍可能出现的 SDK 内部状态污染 (ICE Agent 异常等).
[C] volc_rtc_protocol.cc: DIAG_RTC_BIND_ENABLE 一键开关诊断埋点
在 join_room 前/后 + ForceRebuildEngine 前/后打印 lwIP socket fd / heap /
psram / WiFi rssi / errno, 偶发失败时直接定位根因.
验证完成后改 0 关闭, 编译器消除 #if 块, 零运行时开销.
============ 文件改动 ============
sdkconfig:
+CONFIG_BT_ALLOCATION_FROM_SPIRAM_FIRST=y (BLE 配网修复)
+CONFIG_LWIP_MAX_SOCKETS=10 → 20 (方案 A)
main/application.h:
+int audio_channel_retry_count_ = 0; (RTC 重试计数)
main/application.cc:
OpenAudioChannel 失败处加重试计数 + 连续失败 3 次调 ForceRebuildEngine,
static_cast<VolcRtcProtocol*>(protocol_.get()) (ESP-IDF 默认 -fno-rtti).
main/protocols/volc_rtc_protocol.h:
+void ForceRebuildEngine() 声明.
main/protocols/volc_rtc_protocol.cc:
+DIAG_RTC_BIND_ENABLE 开关 + diag_count_used_sockets() 工具函数,
+OpenAudioChannel Pre-Join / Post-Fail DIAG 埋点,
+ForceRebuildEngine() 实现 (volc_rtc_stop + volc_rtc_destroy + 等 2 秒 + 触发重建).
============ 关联资源 ============
Baji commit 70f0cdd: feat(rtc): 偶发连接失败完整修复 (A+B+C 三件套)
Baji commit bffd316: feat(provisioning): BLE 配网完整修复
全局 CLAUDE.md "BLE 配网 DRAM 紧张完整排查流程" 章节
项目记忆 project_ble_provisioning_issues.md
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
77 lines
3.2 KiB
C++
77 lines
3.2 KiB
C++
#ifndef _VOLC_RTC_PROTOCOL_H_
|
||
#define _VOLC_RTC_PROTOCOL_H_
|
||
|
||
#include "protocol.h"
|
||
#include "volc_rtc.h"
|
||
#include "base/volc_device_manager.h"
|
||
#include <freertos/FreeRTOS.h>
|
||
#include <freertos/event_groups.h>
|
||
#include <mutex>
|
||
#include <vector>
|
||
|
||
class VolcRtcProtocol : public Protocol {
|
||
public:
|
||
VolcRtcProtocol();
|
||
~VolcRtcProtocol();
|
||
|
||
void Start() override;
|
||
void SendAudio(const std::vector<uint8_t>& data) override;// 🔊 发送音频数据到RTC
|
||
void SendPcm(const std::vector<uint8_t>& data) override;// 🔊 发送PCM音频数据到RTC
|
||
void SendG711A(const std::vector<uint8_t>& data) override;// 🔊 发送G711A音频数据到RTC
|
||
bool OpenAudioChannel() override;// 🔊 打开音频通道
|
||
void CloseAudioChannel() override;// 🔊 关闭音频通道
|
||
void LeaveRoom(bool notify_closed = true) override;// Phase 6: 退出 RTC 房间(stop,保留 handle 供唤醒复用)
|
||
|
||
// 方案 B (移植自 Baji commit 70f0cdd): 强制销毁并重建 RTC engine.
|
||
// 当 OpenAudioChannel 连续失败 N 次时调用, 清理 SDK 内部错乱状态
|
||
// (lwIP socket fd 残留 / 内部缓存污染), 触发 Phase 6 的 rtc_handle_=nullptr → Start() 重建路径
|
||
void ForceRebuildEngine();
|
||
|
||
bool IsAudioChannelOpened() const override;// 🔊 检查音频通道是否已打开
|
||
void SendAbortSpeaking(AbortReason reason) override;// 🔊 发送中止通话请求
|
||
void SendStartListening(ListeningMode mode) override;// 🔊 发送开始监听请求
|
||
void SendTextMessage(const std::string& text) override;// 🔊 发送文本消息到RTC
|
||
void SendFunctionResult(const std::string& tool_call_id, const std::string& content) override;// 🔊 发送函数调用结果到RTC
|
||
|
||
/**
|
||
* @brief 设置Agent配置参数(如音色、提示词等)
|
||
* @param params JSON格式的配置参数字符串
|
||
*/
|
||
void SetAgentConfig(const std::string& params);
|
||
|
||
private:
|
||
EventGroupHandle_t event_group_handle_;
|
||
volc_rtc_t rtc_handle_ = nullptr;
|
||
std::mutex rtc_mutex_;
|
||
std::string extra_params_; // 存储额外的Agent配置参数
|
||
|
||
bool is_connected_ = false;
|
||
bool is_audio_channel_opened_ = false;
|
||
bool iot_ready_ = false;
|
||
volc_iot_info_t iot_info_ = {};
|
||
size_t opus_bytes_accum_ = 0;
|
||
size_t pcm_bytes_accum_ = 0;
|
||
size_t g711a_bytes_accum_ = 0;
|
||
size_t down_pcm_bytes_accum_ = 0;
|
||
size_t down_opus_bytes_accum_ = 0;
|
||
int opus_frames_accum_ = 0;
|
||
int pcm_frames_accum_ = 0;
|
||
int g711a_frames_accum_ = 0;
|
||
uint64_t uplink_last_log_us_ = 0;
|
||
std::vector<uint8_t> pcm_pending_;
|
||
std::vector<uint8_t> g711a_pending_;
|
||
bool first_downlink_logged_ = false;
|
||
|
||
static void MessageCallback(void* context, volc_msg_t* message);
|
||
static void DataCallback(void* context, const void* data, size_t len, volc_data_info_t* info);
|
||
|
||
void ParseServerMessage(const char* message);
|
||
void ProcessAudioData(const void* data, int size);
|
||
void SendText(const std::string& text) override;
|
||
void LogUplinkStatsMaybe();// 打印上传统计信息
|
||
void SendCtrl(const std::string& json);// 🔊 发送控制指令到RTC
|
||
void SendFunc(const std::string& json);// 🔊 发送函数调用指令到RTC
|
||
};
|
||
|
||
#endif
|