diff --git a/main/application.cc b/main/application.cc index 9980c43..017dd53 100644 --- a/main/application.cc +++ b/main/application.cc @@ -520,10 +520,22 @@ void Application::ToggleChatState() { Board::GetInstance().SetPowerSaveMode(false);// 关闭低功耗模式 if (!protocol_->OpenAudioChannel()) { auto ac = Board::GetInstance().GetAudioCodec(); - ESP_LOGW(TAG, "打开音频通道失败,将在2秒后重试"); + audio_channel_retry_count_++; + ESP_LOGW(TAG, "打开音频通道失败 (第 %d 次), 将在2秒后重试", audio_channel_retry_count_); if (ac) { ESP_LOGW(TAG, "Diag: codec out_channels=%d in_channels=%d out_sr=%d in_sr=%d", ac->output_channels(), ac->input_channels(), ac->output_sample_rate(), ac->input_sample_rate()); } + // 方案 B (移植自 Baji commit 70f0cdd): 连续失败 3 次后销毁 + 重建 RTC engine + // 清理 SDK 内部状态污染 (lwIP socket fd 残留 / 内部缓存错乱), + // 触发 Phase 6 的 rtc_handle_=nullptr → Start() 重建路径 + if (audio_channel_retry_count_ >= 3) { + ESP_LOGW(TAG, "🔄 连续失败 3 次, 触发 RTC engine 重建 (清理 SDK 状态)"); + auto* volc_rtc = static_cast(protocol_.get()); + if (volc_rtc) { + volc_rtc->ForceRebuildEngine(); + } + audio_channel_retry_count_ = 0; + } SetDeviceState(kDeviceStateIdle); Schedule([this]() { vTaskDelay(pdMS_TO_TICKS(2000)); @@ -532,6 +544,8 @@ void Application::ToggleChatState() { }); return; } + // 连接成功重置重试计数 + audio_channel_retry_count_ = 0; listening_mode_ = kListeningModeRealtime;// 设置监听模式为实时监听 SetDeviceState(kDeviceStateDialog);// 设置设备状态为对话模式 diff --git a/main/application.h b/main/application.h index ed7bfac..5c0d9dc 100644 --- a/main/application.h +++ b/main/application.h @@ -167,6 +167,7 @@ private: std::atomic https_playback_active_{false};// 🌐 HTTPS音频播放进行中标志 std::atomic https_playback_abort_{false};// 🌐 HTTPS音频播放中止标志 bool aborted_ = false; + int audio_channel_retry_count_ = 0;// RTC 偶发连接失败重试计数 (方案 B: 失败 3 次后销毁 + 重建 engine, 移植自 Baji commit 70f0cdd) bool voice_detected_ = false; bool audio_paused_ = false; // 音频暂停状态标志 float current_speaker_volume_ = 0.0f; // 当前扬声器音量,用于语音打断判断 diff --git a/main/protocols/volc_rtc_protocol.cc b/main/protocols/volc_rtc_protocol.cc index a2cb3dc..ee9fd31 100644 --- a/main/protocols/volc_rtc_protocol.cc +++ b/main/protocols/volc_rtc_protocol.cc @@ -21,6 +21,27 @@ static const char* TAG = "VolcRtcProtocol"; +// ============================================================ +// 方案 C (移植自 Baji commit 70f0cdd): RTC bind 失败诊断埋点 +// 验证完成后改 0 关闭, 编译器消除 #if 块, 不占 Flash/CPU +// ============================================================ +#ifndef DIAG_RTC_BIND_ENABLE +#define DIAG_RTC_BIND_ENABLE 1 +#endif + +#if DIAG_RTC_BIND_ENABLE +#include "esp_wifi.h" +#include "lwip/sockets.h" // LWIP_SOCKET_OFFSET +static int diag_count_used_sockets(void) { + int used = 0; + for (int fd = LWIP_SOCKET_OFFSET; fd < LWIP_SOCKET_OFFSET + CONFIG_LWIP_MAX_SOCKETS; fd++) { + struct stat st; + if (fstat(fd, &st) == 0) used++; + } + return used; +} +#endif + VolcRtcProtocol::VolcRtcProtocol() { event_group_handle_ = xEventGroupCreate(); } @@ -347,6 +368,18 @@ bool VolcRtcProtocol::OpenAudioChannel() { xEventGroupClearBits(event_group_handle_, 0x1 | 0x2); // 新增:extra_params 用于传递额外的AgentConfig配置参数 ESP_LOGI(TAG, "Join RTC: handle=%p bot=%s iot_ready=%d free_heap=%u", rtc_handle_, CONFIG_VOLC_BOT_ID, (int)iot_ready_, (unsigned)heap_caps_get_free_size(MALLOC_CAP_DEFAULT)); +#if DIAG_RTC_BIND_ENABLE + { + int sockets_used = diag_count_used_sockets(); + wifi_ap_record_t ap_info = {}; + int rssi = (esp_wifi_sta_get_ap_info(&ap_info) == ESP_OK) ? ap_info.rssi : -127; + ESP_LOGW("DIAG-RTC", "Pre-Join: sockets=%d/%d heap=%u psram=%u rssi=%d", + sockets_used, CONFIG_LWIP_MAX_SOCKETS, + (unsigned)heap_caps_get_free_size(MALLOC_CAP_DEFAULT), + (unsigned)heap_caps_get_free_size(MALLOC_CAP_SPIRAM), + rssi); + } +#endif int ret = volc_rtc_start(rtc_handle_, CONFIG_VOLC_BOT_ID, &iot_info_, extra_params_.empty() ? NULL : extra_params_.c_str()); if (ret != 0) { ESP_LOGE(TAG, "RTC启动失败:%d", ret);// RTC启动失败:%d @@ -358,6 +391,16 @@ bool VolcRtcProtocol::OpenAudioChannel() { if ((bits & 0x1) == 0) { ESP_LOGE(TAG, "RTC连接超时");// RTC连接超时 ESP_LOGW(TAG, "Diag: check Wi-Fi, SNTP time sync, IoT creds, RTC server availability");// 诊断:检查Wi-Fi、SNTP时间同步、IoT凭证、RTC服务器可用性 +#if DIAG_RTC_BIND_ENABLE + { + int sockets_used = diag_count_used_sockets(); + ESP_LOGW("DIAG-RTC", "Post-Fail: sockets=%d/%d heap=%u psram=%u errno=%d(%s)", + sockets_used, CONFIG_LWIP_MAX_SOCKETS, + (unsigned)heap_caps_get_free_size(MALLOC_CAP_DEFAULT), + (unsigned)heap_caps_get_free_size(MALLOC_CAP_SPIRAM), + errno, strerror(errno)); + } +#endif return false; } // Do not block audio readiness on remote user join; enable subscribe immediately @@ -435,6 +478,38 @@ void VolcRtcProtocol::LeaveRoom(bool notify_closed) { } } +// 方案 B (移植自 Baji commit 70f0cdd): 强制销毁并重建 RTC engine +// 用途: OpenAudioChannel 连续失败 N 次后调用, 清理 SDK 内部错乱状态 +// 实现: 销毁 rtc_handle_ + 触发 Phase 6 重建路径 +// 下次 OpenAudioChannel 看到 rtc_handle_=nullptr → Start() 异步重建 +void VolcRtcProtocol::ForceRebuildEngine() { + ESP_LOGW(TAG, "🔄 ForceRebuildEngine: 销毁 RTC engine 以清理 SDK 状态"); +#if DIAG_RTC_BIND_ENABLE + ESP_LOGW("DIAG-RTC", "Pre-Rebuild: sockets=%d/%d heap=%u", + diag_count_used_sockets(), CONFIG_LWIP_MAX_SOCKETS, + (unsigned)heap_caps_get_free_size(MALLOC_CAP_DEFAULT)); +#endif + if (rtc_handle_) { + if (is_connected_) { + volc_rtc_stop(rtc_handle_); + is_connected_ = false; + } + volc_rtc_destroy(rtc_handle_); + rtc_handle_ = nullptr; + } + is_audio_channel_opened_ = false; + downlink_is_pcm_ = false; + first_downlink_logged_ = false; + // 等 2 秒让 lwIP 释放残留 socket fd (TIME_WAIT 状态) + vTaskDelay(pdMS_TO_TICKS(2000)); +#if DIAG_RTC_BIND_ENABLE + ESP_LOGW("DIAG-RTC", "Post-Rebuild-Wait: sockets=%d/%d heap=%u", + diag_count_used_sockets(), CONFIG_LWIP_MAX_SOCKETS, + (unsigned)heap_caps_get_free_size(MALLOC_CAP_DEFAULT)); +#endif + ESP_LOGI(TAG, "🔄 engine 已销毁, 下次 OpenAudioChannel 触发 Phase 6 重建"); +} + // 🔊 检查音频通道是否已打开 bool VolcRtcProtocol::IsAudioChannelOpened() const { return is_audio_channel_opened_; diff --git a/main/protocols/volc_rtc_protocol.h b/main/protocols/volc_rtc_protocol.h index 0e49ace..e824ccd 100644 --- a/main/protocols/volc_rtc_protocol.h +++ b/main/protocols/volc_rtc_protocol.h @@ -21,6 +21,12 @@ public: 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;// 🔊 发送开始监听请求 diff --git a/sdkconfig b/sdkconfig index 7db0b92..f695839 100644 --- a/sdkconfig +++ b/sdkconfig @@ -14,6 +14,7 @@ CONFIG_SOC_GDMA_SUPPORTED=y CONFIG_SOC_AHB_GDMA_SUPPORTED=y CONFIG_SOC_GPTIMER_SUPPORTED=y CONFIG_SOC_LCDCAM_SUPPORTED=y +CONFIG_SOC_LCDCAM_CAM_SUPPORTED=y CONFIG_SOC_LCDCAM_I80_LCD_SUPPORTED=y CONFIG_SOC_LCDCAM_RGB_LCD_SUPPORTED=y CONFIG_SOC_MCPWM_SUPPORTED=y @@ -101,7 +102,7 @@ CONFIG_SOC_CPU_HAS_FPU=y CONFIG_SOC_HP_CPU_HAS_MULTIPLE_CORES=y CONFIG_SOC_CPU_BREAKPOINTS_NUM=2 CONFIG_SOC_CPU_WATCHPOINTS_NUM=2 -CONFIG_SOC_CPU_WATCHPOINT_MAX_REGION_SIZE=64 +CONFIG_SOC_CPU_WATCHPOINT_MAX_REGION_SIZE=0x40 CONFIG_SOC_SIMD_PREFERRED_DATA_ALIGNMENT=16 CONFIG_SOC_DS_SIGNATURE_MAX_BIT_LEN=4096 CONFIG_SOC_DS_KEY_PARAM_MD_IV_LENGTH=16 @@ -208,7 +209,7 @@ CONFIG_SOC_RTCIO_INPUT_OUTPUT_SUPPORTED=y CONFIG_SOC_RTCIO_HOLD_SUPPORTED=y CONFIG_SOC_RTCIO_WAKE_SUPPORTED=y CONFIG_SOC_LP_IO_CLOCK_IS_INDEPENDENT=y -CONFIG_SOC_SDM_GROUPS=y +CONFIG_SOC_SDM_GROUPS=1 CONFIG_SOC_SDM_CHANNELS_PER_GROUP=8 CONFIG_SOC_SDM_CLK_SUPPORT_APB=y CONFIG_SOC_SPI_PERIPH_NUM=3 @@ -369,6 +370,9 @@ CONFIG_SOC_BLE_DEVICE_PRIVACY_SUPPORTED=y CONFIG_SOC_BLUFI_SUPPORTED=y CONFIG_SOC_ULP_HAS_ADC=y CONFIG_SOC_PHY_COMBO_MODULE=y +CONFIG_SOC_LCDCAM_CAM_SUPPORT_RGB_YUV_CONV=y +CONFIG_SOC_LCDCAM_CAM_PERIPH_NUM=1 +CONFIG_SOC_LCDCAM_CAM_DATA_WIDTH_MAX=16 CONFIG_IDF_CMAKE=y CONFIG_IDF_TOOLCHAIN="gcc" CONFIG_IDF_TOOLCHAIN_GCC=y @@ -1021,7 +1025,7 @@ CONFIG_BT_LOG_BLUFI_TRACE_LEVEL=2 CONFIG_BT_ACL_CONNECTIONS=4 CONFIG_BT_MULTI_CONNECTION_ENBALE=y -# CONFIG_BT_ALLOCATION_FROM_SPIRAM_FIRST is not set +CONFIG_BT_ALLOCATION_FROM_SPIRAM_FIRST=y # CONFIG_BT_BLE_DYNAMIC_ENV_MEMORY is not set CONFIG_BT_SMP_ENABLE=y CONFIG_BT_SMP_MAX_BONDS=15 @@ -1033,6 +1037,7 @@ CONFIG_BT_BLE_42_FEATURES_SUPPORTED=y CONFIG_BT_BLE_42_DTM_TEST_EN=y CONFIG_BT_BLE_42_ADV_EN=y CONFIG_BT_BLE_42_SCAN_EN=y +CONFIG_BT_BLE_VENDOR_HCI_EN=y # CONFIG_BT_BLE_HIGH_DUTY_ADV_INTERVAL is not set # CONFIG_BT_ABORT_WHEN_ALLOCATION_FAILS is not set # end of Bluedroid Options @@ -1254,6 +1259,7 @@ CONFIG_ESP_TLS_USE_DS_PERIPHERAL=y # CONFIG_ESP_TLS_SERVER_MIN_AUTH_MODE_OPTIONAL is not set # CONFIG_ESP_TLS_PSK_VERIFICATION is not set # CONFIG_ESP_TLS_INSECURE is not set +CONFIG_ESP_TLS_DYN_BUF_STRATEGY_SUPPORTED=y # end of ESP-TLS # @@ -1280,6 +1286,12 @@ CONFIG_ESP_COEX_SW_COEXIST_ENABLE=y CONFIG_ESP_ERR_TO_NAME_LOOKUP=y # end of Common ESP-related +# +# ESP-Driver:Camera Controller Configurations +# +# CONFIG_CAM_CTLR_DVP_CAM_ISR_CACHE_SAFE is not set +# end of ESP-Driver:Camera Controller Configurations + # # ESP-Driver:GPIO Configurations # @@ -1597,8 +1609,11 @@ CONFIG_ESP_PHY_RF_CAL_PARTIAL=y # CONFIG_ESP_PHY_RF_CAL_NONE is not set # CONFIG_ESP_PHY_RF_CAL_FULL is not set CONFIG_ESP_PHY_CALIBRATION_MODE=0 +CONFIG_ESP_PHY_PLL_TRACK_PERIOD_MS=1000 # CONFIG_ESP_PHY_PLL_TRACK_DEBUG is not set # CONFIG_ESP_PHY_RECORD_USED_TIME is not set +CONFIG_ESP_PHY_IRAM_OPT=y +# CONFIG_ESP_PHY_DEBUG is not set # end of PHY # @@ -2081,7 +2096,7 @@ CONFIG_LWIP_DNS_SUPPORT_MDNS_QUERIES=y CONFIG_LWIP_TIMERS_ONDEMAND=y CONFIG_LWIP_ND6=y # CONFIG_LWIP_FORCE_ROUTER_FORWARDING is not set -CONFIG_LWIP_MAX_SOCKETS=10 +CONFIG_LWIP_MAX_SOCKETS=20 # CONFIG_LWIP_USE_ONLY_LWIP_SELECT is not set # CONFIG_LWIP_SO_LINGER is not set CONFIG_LWIP_SO_REUSE=y @@ -2271,6 +2286,7 @@ CONFIG_MBEDTLS_DYNAMIC_BUFFER=y # CONFIG_MBEDTLS_X509_TRUSTED_CERT_CALLBACK is not set # CONFIG_MBEDTLS_SSL_CONTEXT_SERIALIZATION is not set # CONFIG_MBEDTLS_SSL_KEEP_PEER_CERTIFICATE is not set +# CONFIG_MBEDTLS_SSL_KEYING_MATERIAL_EXPORT is not set CONFIG_MBEDTLS_PKCS7_C=y # end of mbedTLS v3.x related @@ -2981,6 +2997,7 @@ CONFIG_BT_NIMBLE_COEX_PHY_CODED_TX_RX_TLIM_DIS=y CONFIG_SW_COEXIST_ENABLE=y CONFIG_ESP32_WIFI_SW_COEXIST_ENABLE=y CONFIG_ESP_WIFI_SW_COEXIST_ENABLE=y +# CONFIG_CAM_CTLR_DVP_CAM_ISR_IRAM_SAFE is not set # CONFIG_MCPWM_ISR_IN_IRAM is not set # CONFIG_EVENT_LOOP_PROFILING is not set CONFIG_POST_EVENTS_FROM_ISR=y