@@ -9,6 +9,8 @@
# include <string.h>
# include "control_plane_internal.h"
# include "control_plane_provisioning_policy.h"
# include "esp_timer.h"
# include "esp_attr.h"
# include "esp_log.h"
# include "freertos/FreeRTOS.h"
@@ -64,6 +66,7 @@
# define CONTROL_PLANE_PROVISIONING_TASK_PRIORITY 5
# define CONTROL_PLANE_PROVISIONING_STOP_REQUESTED_BIT BIT0
# define CONTROL_PLANE_PROVISIONING_WORKER_DONE_BIT BIT1
# define CONTROL_PLANE_PROVISIONING_WORKER_START_BIT BIT2
# define CONTROL_PLANE_PROVISIONING_POLL_MS 100
# define CONTROL_PLANE_PROVISIONING_WAIT_LOG_MS 10000
# define CONTROL_PLANE_PROVISIONING_NOTIFY_SETTLE_MS 750
@@ -86,7 +89,66 @@ static portMUX_TYPE s_provisioning_lock = portMUX_INITIALIZER_UNLOCKED;
static QueueHandle_t s_provisioning_queue ;
static EventGroupHandle_t s_provisioning_events ;
static TaskHandle_t s_provisioning_task ;
static cp_provisioning_policy_t s_provisioning_policy ;
static uint32_t control_plane_provisioning_now_ms ( void )
{
return ( uint32_t ) ( esp_timer_get_time ( ) / 1000 ) ;
}
static bool control_plane_provisioning_is_recovery_boot ( void )
{
portENTER_CRITICAL ( & s_provisioning_lock ) ;
bool recovery_boot = s_provisioning_recovery_boot ;
portEXIT_CRITICAL ( & s_provisioning_lock ) ;
return recovery_boot ;
}
bool control_plane_provisioning_retry_due ( void )
{
uint32_t now_ms = control_plane_provisioning_now_ms ( ) ;
portENTER_CRITICAL ( & s_provisioning_lock ) ;
bool due = cp_provisioning_policy_due ( & s_provisioning_policy , now_ms ) ;
portEXIT_CRITICAL ( & s_provisioning_lock ) ;
return due ;
}
void control_plane_provisioning_configuration_changed ( void )
{
/* 产测线程只发布请求;不在串口回调中停音频、等待 BLE 或重启设备。 */
portENTER_CRITICAL ( & s_provisioning_lock ) ;
s_provisioning_policy . retry_requested = true ;
portEXIT_CRITICAL ( & s_provisioning_lock ) ;
control_plane_request_wifi_reconnect_now ( ) ;
}
bool control_plane_provisioning_allows_voice ( void )
{
portENTER_CRITICAL ( & s_provisioning_lock ) ;
bool allowed = ! s_provisioning_policy . runtime_quiesced ;
portEXIT_CRITICAL ( & s_provisioning_lock ) ;
return allowed ;
}
void control_plane_provisioning_sta_connected ( void )
{
/* 非 BLE 路径恢复 STA 时也解除语音暂停;活动配网仍由自己的 worker 负责释放。 */
bool resume_voice = false ;
portENTER_CRITICAL ( & s_provisioning_lock ) ;
if ( s_provisioning_policy . phase ! = CP_PROVISIONING_STARTING & &
s_provisioning_policy . phase ! = CP_PROVISIONING_ACTIVE & &
! s_provisioning_policy . cleanup_required ) {
resume_voice = s_provisioning_policy . runtime_quiesced ;
s_provisioning_policy . runtime_quiesced = false ;
}
portEXIT_CRITICAL ( & s_provisioning_lock ) ;
if ( resume_voice & & control_plane_ota_voice_services_allowed ( ) ) {
esp_err_t err = control_plane_local_wake_start ( ) ;
if ( err ! = ESP_OK ) {
ESP_LOGW ( TAG , " Restore local wake after STA recovery failed: %s " , esp_err_to_name ( err ) ) ;
}
}
}
bool control_plane_take_mini_program_recovery_boot_request ( void )
{
bool requested =
@@ -95,7 +157,11 @@ bool control_plane_take_mini_program_recovery_boot_request(void)
~ CONTROL_PLANE_PROVISIONING_RECOVERY_MAGIC ;
s_provisioning_recovery_marker = 0U ;
s_provisioning_recovery_marker_inverse = 0U ;
s_provisioning_recovery_boot = requested ;
portENTER_CRITICAL ( & s_provisioning_lock ) ;
s_provisioning_recovery_boot = s_provisioning_recovery_boot | | requested ;
s_provisioning_policy . recovery_attempted =
s_provisioning_policy . recovery_attempted | | requested ;
portEXIT_CRITICAL ( & s_provisioning_lock ) ;
if ( requested ) {
ESP_LOGW ( TAG , " Consumed one-shot low-memory BLE recovery boot request " ) ;
}
@@ -103,20 +169,72 @@ bool control_plane_take_mini_program_recovery_boot_request(void)
}
static void control_plane_request_provisioning_recovery_restart (
const char * stage , esp_err_t error ,
const domain_ota_memory_headroom_t * headroom )
{
s_provisioning_recovery_marker = CONTROL_PLANE_PROVISIONING_RECOVERY_MAGIC ;
s_provisioning_recovery_marker_inverse =
~ CONTROL_PLANE_PROVISIONING_RECOVERY_MAGIC ;
ESP_LOGW ( TAG ,
" Restarting into low-memory BLE recovery boot: free_internal=%u largest_internal=%u largest_dma=%u " ,
headroom ! = NULL ? ( unsigned ) headroom - > free_internal : 0U ,
headroom ! = NULL ? ( unsigned ) headroom - > largest_internal : 0U ,
headroom ! = NULL ? ( unsigned ) headroom - > largest_dma : 0U ) ;
if ( headroom ! = NULL ) {
ESP_LOGW ( TAG ,
" Restarting once for BLE memory recovery: stage=%s error=%s free_internal=%u largest_internal=%u largest_dma=%u " ,
stage , esp_err_to_name ( error ) , ( unsigned ) headroom - > free_internal ,
( unsigned ) headroom - > largest_internal , ( unsigned ) headroom - > largest_dma ) ;
} else {
/* 采样失败不伪造 0;本分支只接受已确认的 NO_MEMORY 原因。 */
ESP_LOGW ( TAG , " Restarting once for BLE memory recovery: stage=%s error=%s memory=unknown " ,
stage , esp_err_to_name ( error ) ) ;
}
vTaskDelay ( pdMS_TO_TICKS ( CONTROL_PLANE_PROVISIONING_RESTART_DELAY_MS ) ) ;
domain_runtime_restart ( ) ;
}
static const char * control_plane_provisioning_failure_name ( domain_wifi_provisioning_start_status_t status )
{
switch ( status ) {
case DOMAIN_WIFI_PROVISIONING_START_IDENTITY_REQUIRED : return " identity_required " ;
case DOMAIN_WIFI_PROVISIONING_START_INVALID_CONFIG : return " invalid_config " ;
case DOMAIN_WIFI_PROVISIONING_START_NO_MEMORY : return " no_memory " ;
case DOMAIN_WIFI_PROVISIONING_START_TRANSIENT : return " transient " ;
default : return " failed " ;
}
}
static void control_plane_provisioning_start_failed (
const char * stage , esp_err_t error , domain_wifi_provisioning_start_status_t status )
{
domain_ota_memory_headroom_t headroom = { 0 } ;
esp_err_t memory_err = domain_runtime_ota_get_memory_headroom ( & headroom ) ;
uint32_t now_ms = control_plane_provisioning_now_ms ( ) ;
portENTER_CRITICAL ( & s_provisioning_lock ) ;
if ( s_provisioning_policy . phase ! = CP_PROVISIONING_STOPPING ) {
cp_provisioning_policy_failed ( & s_provisioning_policy , status , now_ms ) ;
}
bool restart = cp_provisioning_policy_claim_recovery ( & s_provisioning_policy , status ) ;
uint32_t delay_ms = s_provisioning_policy . retry_delay_ms ;
bool attempted = s_provisioning_policy . recovery_attempted ;
bool voice_paused = s_provisioning_policy . runtime_quiesced ;
portEXIT_CRITICAL ( & s_provisioning_lock ) ;
ESP_LOGE ( TAG ,
" BLE provisioning start failed: stage=%s error=%s reason=%s retry_ms=%u recovery_attempted=%d voice_paused=%d " ,
stage , esp_err_to_name ( error ) , control_plane_provisioning_failure_name ( status ) ,
( unsigned ) delay_ms , attempted , voice_paused ) ;
if ( ! restart & & delay_ms = = 0U ) {
ESP_LOGW ( TAG , " BLE auto-retry paused; waiting for save_config or retry_provisioning " ) ;
}
if ( memory_err = = ESP_OK ) {
ESP_LOGI ( TAG , " BLE failure memory snapshot: free_internal=%u largest_internal=%u largest_dma=%u " ,
( unsigned ) headroom . free_internal , ( unsigned ) headroom . largest_internal ,
( unsigned ) headroom . largest_dma ) ;
} else {
ESP_LOGW ( TAG , " BLE failure memory snapshot unavailable: %s " , esp_err_to_name ( memory_err ) ) ;
}
if ( restart ) {
control_plane_request_provisioning_recovery_restart (
stage , error , memory_err = = ESP_OK ? & headroom : NULL ) ;
}
}
static esp_err_t control_plane_check_provisioning_memory_headroom ( void )
{
/* BLE 初始化需要连续内部/DMA 块;总空闲堆达标但最大块不足时仍必须拒绝并走恢复启动。 */
@@ -144,9 +262,6 @@ static esp_err_t control_plane_check_provisioning_memory_headroom(void)
" BLE provisioning memory headroom insufficient: need free_internal>=%u largest_dma>=%u " ,
( unsigned ) CONFIG_TQ_MINI_PROGRAM_PROVISIONING_MIN_FREE_INTERNAL_BYTES ,
( unsigned ) CONFIG_TQ_MINI_PROGRAM_PROVISIONING_MIN_LARGEST_DMA_BYTES ) ;
if ( ! s_provisioning_recovery_boot ) {
control_plane_request_provisioning_recovery_restart ( & headroom ) ;
}
return ESP_ERR_NO_MEM ;
}
@@ -359,6 +474,27 @@ static void control_plane_provisioning_worker(void *arg)
{
/* 状态通知顺序也是小程序协议的一部分:connecting→got_ip→saving→ready,失败则给明确原因。 */
( void ) arg ;
/* 创建者先发布 handle/ACTIVE,再放行,避免快速退出留下失效任务句柄。 */
EventBits_t start_bits = xEventGroupWaitBits (
s_provisioning_events , CONTROL_PLANE_PROVISIONING_WORKER_START_BIT ,
pdFALSE , pdTRUE , pdMS_TO_TICKS ( 5000 ) ) ;
if ( ( start_bits & CONTROL_PLANE_PROVISIONING_WORKER_START_BIT ) = = 0 ) {
ESP_LOGE ( TAG , " Provisioning worker start gate timed out " ) ;
portENTER_CRITICAL ( & s_provisioning_lock ) ;
s_provisioning_task = NULL ;
s_provisioning_policy . phase = CP_PROVISIONING_STOPPING ;
portEXIT_CRITICAL ( & s_provisioning_lock ) ;
esp_err_t stop_err = domain_runtime_mini_program_provisioning_stop (
CONTROL_PLANE_PROVISIONING_TRANSPORT_STOP_MS ) ;
portENTER_CRITICAL ( & s_provisioning_lock ) ;
s_provisioning_policy . cleanup_required = stop_err ! = ESP_OK ;
s_provisioning_policy . phase = CP_PROVISIONING_BLOCKED ;
portEXIT_CRITICAL ( & s_provisioning_lock ) ;
control_plane_wifi_set_keep_awake ( false ) ;
( void ) xEventGroupSetBits ( s_provisioning_events , CONTROL_PLANE_PROVISIONING_WORKER_DONE_BIT ) ;
vTaskDelete ( NULL ) ;
return ;
}
bool provisioning_succeeded = false ;
TickType_t last_wait_log_tick = xTaskGetTickCount ( ) ;
TickType_t wait_log_ticks =
@@ -433,15 +569,25 @@ static void control_plane_provisioning_worker(void *arg)
portENTER_CRITICAL ( & s_provisioning_lock ) ;
s_provisioning_task = NULL ;
s_provisioning_policy . cleanup_required = stop_err ! = ESP_OK ;
/* 取消/停止不自动重进配网;只有成功完成才重新开放普通运行。 */
s_provisioning_policy . phase = provisioning_succeeded & & stop_err = = ESP_OK
? CP_PROVISIONING_IDLE : CP_PROVISIONING_BLOCKED ;
if ( provisioning_succeeded & & stop_err = = ESP_OK ) {
s_provisioning_policy . runtime_quiesced = false ;
s_provisioning_policy . retry_count = 0 ;
}
portEXIT_CRITICAL ( & s_provisioning_lock ) ;
( void ) xEventGroupSetBits ( s_provisioning_events ,
CONTROL_PLANE_PROVISIONING_WORKER_DONE_BIT ) ;
if ( provisioning_succeeded & & stop_err = = ESP_OK & &
s_provisioning_recovery_boot ) {
control_plane_provisioning_is_recovery_boot ( ) ) {
ESP_LOGI ( TAG ,
" BLE recovery provisioning completed; restarting into normal runtime " ) ;
portENTER_CRITICAL ( & s_provisioning_lock ) ;
s_provisioning_recovery_boot = false ;
portEXIT_CRITICAL ( & s_provisioning_lock ) ;
vTaskDelay ( pdMS_TO_TICKS ( CONTROL_PLANE_PROVISIONING_RESTART_DELAY_MS ) ) ;
domain_runtime_restart ( ) ;
} else if ( provisioning_succeeded & & stop_err = = ESP_OK ) {
@@ -461,11 +607,15 @@ static void control_plane_provisioning_worker(void *arg)
vTaskDelete ( NULL ) ;
}
static esp_err_t control_plane_start_mini_program_provisioning ( void )
static esp_err_t control_plane_start_provisioning_transport (
domain_wifi_provisioning_start_status_t * status , const char * * stage )
{
# if !CONFIG_TQ_MINI_PROGRAM_PROVISIONING_ENABLE
* status = DOMAIN_WIFI_PROVISIONING_START_INVALID_CONFIG ;
return ESP_ERR_NOT_SUPPORTED ;
# else
* stage = " resources " ;
* status = DOMAIN_WIFI_PROVISIONING_START_NO_MEMORY ;
portENTER_CRITICAL ( & s_provisioning_lock ) ;
bool already_running = s_provisioning_task ! = NULL ;
portEXIT_CRITICAL ( & s_provisioning_lock ) ;
@@ -488,33 +638,74 @@ static esp_err_t control_plane_start_mini_program_provisioning(void)
( void ) xEventGroupClearBits (
s_provisioning_events ,
CONTROL_PLANE_PROVISIONING_STOP_REQUESTED_BIT |
CONTROL_PLANE_PROVISIONING_WORKER_DONE_BIT ) ;
CONTROL_PLANE_PROVISIONING_WORKER_DONE_BIT |
CONTROL_PLANE_PROVISIONING_WORKER_START_BIT ) ;
* stage = " memory_check " ;
esp_err_t err = control_plane_check_provisioning_memory_headroom ( ) ;
if ( err ! = ESP_OK ) {
* status = err = = ESP_ERR_NO_MEM ? DOMAIN_WIFI_PROVISIONING_START_NO_MEMORY
: DOMAIN_WIFI_PROVISIONING_START_FAILED ;
return err ;
}
* stage = " ble_start " ;
portENTER_CRITICAL ( & s_provisioning_lock ) ;
s_provisioning_policy . cleanup_required = true ;
s_provisioning_policy . runtime_quiesced = true ;
portEXIT_CRITICAL ( & s_provisioning_lock ) ;
err = domain_runtime_mini_program_provisioning_start (
control_plane_provisioning_event_callback ,
NULL ) ;
NULL , status ) ;
if ( err ! = ESP_OK ) {
/* 平台启动超时也可能带有尚在停止的 host,必须确认 teardown,不能直接重试。 */
esp_err_t stop_err = domain_runtime_mini_program_provisioning_stop (
CONTROL_PLANE_PROVISIONING_TRANSPORT_STOP_MS ) ;
portENTER_CRITICAL ( & s_provisioning_lock ) ;
s_provisioning_policy . cleanup_required = stop_err ! = ESP_OK ;
portEXIT_CRITICAL ( & s_provisioning_lock ) ;
if ( stop_err ! = ESP_OK ) {
* stage = " ble_start_cleanup " ;
* status = DOMAIN_WIFI_PROVISIONING_START_FAILED ;
return stop_err ;
}
return err ;
}
* stage = " worker_create " ;
TaskHandle_t worker_task = NULL ;
BaseType_t created = xTaskCreate ( control_plane_provisioning_worker ,
" mini_prog_wifi " ,
CONTROL_PLANE_PROVISIONING_TASK_STACK_BYTES ,
NULL ,
CONTROL_PLANE_PROVISIONING_TASK_PRIORITY ,
& s_provisioning_task ) ;
& worker_task ) ;
if ( created ! = pdPASS ) {
s_provisioning_task = NULL ;
* status = DOMAIN_WIFI_PROVISIONING_START_NO_MEMORY ;
ESP_LOGE ( TAG , " Create mini-program provisioning worker failed after BLE start " ) ;
( void ) domain_runtime_mini_program_provisioning_stop (
esp_err_t stop_err = domain_runtime_mini_program_provisioning_stop (
CONTROL_PLANE_PROVISIONING_TRANSPORT_STOP_MS ) ;
portENTER_CRITICAL ( & s_provisioning_lock ) ;
s_provisioning_policy . cleanup_required = stop_err ! = ESP_OK ;
portEXIT_CRITICAL ( & s_provisioning_lock ) ;
if ( stop_err ! = ESP_OK ) {
* status = DOMAIN_WIFI_PROVISIONING_START_FAILED ;
return stop_err ;
}
return ESP_ERR_NO_MEM ;
}
portENTER_CRITICAL ( & s_provisioning_lock ) ;
if ( s_provisioning_policy . phase ! = CP_PROVISIONING_STARTING ) {
/* 启动门若已超时,worker 自行收尾;不能再发布其可能失效的任务句柄。 */
portEXIT_CRITICAL ( & s_provisioning_lock ) ;
* status = DOMAIN_WIFI_PROVISIONING_START_FAILED ;
return ESP_ERR_TIMEOUT ;
}
s_provisioning_task = worker_task ;
s_provisioning_policy . phase = CP_PROVISIONING_ACTIVE ;
portEXIT_CRITICAL ( & s_provisioning_lock ) ;
/* 用常驻 event group 放行,避免异常调度时对已退出的任务句柄发送通知。 */
( void ) xEventGroupSetBits ( s_provisioning_events , CONTROL_PLANE_PROVISIONING_WORKER_START_BIT ) ;
ESP_LOGI ( TAG , " Mini-program Wi-Fi provisioning started " ) ;
return ESP_OK ;
# endif
@@ -556,42 +747,105 @@ esp_err_t control_plane_stop_mini_program_provisioning(int timeout_ms)
: ESP_ERR_TIMEOUT ;
}
esp_err_t control_plane_enter_mini_program_reprovisioning ( const char * reason )
static esp_err_t control_plane_try_provisioning ( bool quiesce_runtime )
{
/* 先静默语音/网络运行时释放内存;仍不足时写 RTC 标记并软重启进入最小资源配网路径。 */
# if !CONFIG_TQ_MINI_PROGRAM_PROVISIONING_ENABLE
( void ) reason ;
( void ) quiesce_runtime ;
return ESP_ERR_NOT_SUPPORTED ;
# else
if ( control_plane_is_mini_program_provisioning_running ( ) ) {
uint32_t now_ms = control_plane_provisioning_now_ms ( ) ;
portENTER_CRITICAL ( & s_provisioning_lock ) ;
bool allowed = cp_provisioning_policy_due ( & s_provisioning_policy , now_ms ) ;
if ( allowed ) {
s_provisioning_policy . phase = CP_PROVISIONING_STARTING ;
}
portEXIT_CRITICAL ( & s_provisioning_lock ) ;
if ( ! allowed ) {
return control_plane_is_mini_program_provisioning_running ( ) ? ESP_OK : ESP_ERR_INVALID_STATE ;
}
/* 身份缺失时 BLE 无法启动。先检查,保持已有音频/串口等资源不受影响。 */
domain_wifi_provisioning_start_status_t status =
domain_runtime_mini_program_provisioning_preflight ( ) ;
esp_err_t err = ESP_OK ;
const char * stage = " identity " ;
if ( status ! = DOMAIN_WIFI_PROVISIONING_START_READY ) {
err = ESP_ERR_NOT_FOUND ;
goto failed ;
}
control_plane_wifi_set_keep_awake ( true ) ;
if ( quiesce_runtime ) {
/* 只暂停语音,不设置全局 stop_requested: Wi-Fi 协调、LCD、电池和 CLI 必须继续运行。
* 后续启动失败时进入明确的离线等待/退避模式,语音保持暂停以免抢占 BLE 重试内存;
* 配网成功或 STA 恢复后解除暂停。 */
portENTER_CRITICAL ( & s_provisioning_lock ) ;
s_provisioning_policy . runtime_quiesced = true ;
portEXIT_CRITICAL ( & s_provisioning_lock ) ;
control_plane_set_ptt_active ( false ) ;
stage = " stop_local_wake " ;
err = control_plane_local_wake_stop ( CONTROL_PLANE_PROVISIONING_RUNTIME_QUIESCE_MS ) ;
if ( err ! = ESP_OK ) {
goto quiesce_failed ;
}
stage = " stop_multimodal " ;
err = control_plane_stop_multimodal_client ( ) ;
if ( err ! = ESP_OK ) {
goto quiesce_failed ;
}
stage = " stop_playback " ;
err = domain_runtime_multimodal_audio_stop_downstream_playback ( ) ;
if ( err ! = ESP_OK ) {
goto quiesce_failed ;
}
domain_runtime_multimodal_audio_deinit ( ) ;
}
portENTER_CRITICAL ( & s_provisioning_lock ) ;
bool cleanup_required = s_provisioning_policy . cleanup_required ;
portEXIT_CRITICAL ( & s_provisioning_lock ) ;
if ( cleanup_required ) {
/* 上轮 teardown 超时后只允许显式重试;先完成释放,不能直接复用旧 BLE 实例。 */
stage = " previous_ble_teardown " ;
err = domain_runtime_mini_program_provisioning_stop (
CONTROL_PLANE_PROVISIONING_TRANSPORT_STOP_MS ) ;
if ( err ! = ESP_OK ) {
status = DOMAIN_WIFI_PROVISIONING_START_FAILED ;
goto failed ;
}
portENTER_CRITICAL ( & s_provisioning_lock ) ;
s_provisioning_policy . cleanup_required = false ;
portEXIT_CRITICAL ( & s_provisioning_lock ) ;
}
control_plane_show_provisioning_ui ( ) ;
stage = " stop_sta " ;
err = domain_runtime_wifi_portal_stop_sta ( ) ;
if ( err ! = ESP_OK ) {
goto quiesce_failed ;
}
err = control_plane_start_provisioning_transport ( & status , & stage ) ;
if ( err = = ESP_OK ) {
return ESP_OK ;
}
ESP_LOGW ( TAG , " Entering mini-program BLE reprovisioning: reason=%s " ,
reason ! = NULL ? reason : " unspecified " ) ;
control_plane_wifi_set_keep_awake ( true ) ;
control_plane_set_runtime_stop_requested ( true ) ;
control_plane_set_ptt_active ( false ) ;
( void ) control_plane_local_wake_stop (
CONTROL_PLANE_PROVISIONING_RUNTIME_QUIESCE_MS ) ;
( void ) control_plane_stop_multimodal_client ( ) ;
( void ) domain_runtime_multimodal_audio_stop_downstream_playback ( ) ;
domain_runtime_multimodal_audio_deinit ( ) ;
control_plane_show_provisioning_ui ( ) ;
goto failed ;
esp_err_t err = domain_runtime_wifi_portal_stop_sta ( ) ;
if ( err = = ESP_OK ) {
err = control_plane_start_mini_program_provisioning ( ) ;
}
if ( err ! = ESP_OK ) {
control_plane_wifi_set_keep_awake ( false ) ;
if ( ! s_provisioning_recovery_boot ) {
control_plane_request_provisioning_recovery_restart ( NULL ) ;
}
}
quiesce_failed :
/* 停止操作失败时不能继续释放其仍在使用的资源;超时可有界重试,其余故障等待诊断。 */
status = err = = ESP_ERR_TIMEOUT ? DOMAIN_WIFI_PROVISIONING_START_TRANSIENT
: DOMAIN_WIFI_PROVISIONING_START_FAILED ;
failed :
control_plane_wifi_set_keep_awake ( false ) ;
control_plane_provisioning_start_failed ( stage , err , status ) ;
return err ;
# endif
}
esp_err_t control_plane_enter_mini_program_reprovisioning ( const char * reason )
{
ESP_LOGW ( TAG , " Entering mini-program BLE reprovisioning: reason=%s " ,
reason ! = NULL ? reason : " unspecified " ) ;
return control_plane_try_provisioning ( true ) ;
}
esp_err_t control_plane_bootstrap_connectivity ( const char * seed_ssid ,
const char * seed_password ,
int connect_timeout_ms )
@@ -608,7 +862,7 @@ esp_err_t control_plane_bootstrap_connectivity(const char *seed_ssid,
return err ;
}
if ( s_provisioning_recovery_boot ) {
if ( control_plane_provisioning_is_recovery_boot ( ) ) {
ESP_LOGW ( TAG ,
" Low-memory recovery boot skips STA reconnect and enters BLE provisioning " ) ;
} else if ( domain_runtime_wifi_portal_has_saved_credentials ( ) ) {
@@ -622,11 +876,8 @@ esp_err_t control_plane_bootstrap_connectivity(const char *seed_ssid,
esp_err_to_name ( err ) ) ;
}
control_plane_show_provisioning_ui ( ) ;
control_plane_wifi_set_keep_awake ( true ) ;
err = control_plane_start_mini_program_provisioning ( ) ;
if ( err = = ESP_OK & & s_provisioning_recovery_boot ) {
err = control_plane_try_provisioning ( false ) ;
if ( err = = ESP_OK & & control_plane_provisioning_is_recovery_boot ( ) ) {
esp_err_t lcd_err = control_plane_start_lcd_render_worker ( ) ;
if ( lcd_err ! = ESP_OK & & lcd_err ! = ESP_ERR_NOT_SUPPORTED ) {
ESP_LOGW ( TAG ,
@@ -636,11 +887,14 @@ esp_err_t control_plane_bootstrap_connectivity(const char *seed_ssid,
}
if ( err ! = ESP_OK ) {
control_plane_wifi_set_keep_awake ( false ) ;
if ( s_provisioning_recovery_boot ) {
if ( control_plane_provisioning_is_recovery_boot ( ) ) {
ESP_LOGE ( TAG ,
" Low-memory BLE recovery boot failed; resume normal boot: %s " ,
esp_err_to_name ( err ) ) ;
/* 退出精简启动模式,但已尝试恢复的锁存保留,禁止同一故障再次重启。 */
portENTER_CRITICAL ( & s_provisioning_lock ) ;
s_provisioning_recovery_boot = false ;
portEXIT_CRITICAL ( & s_provisioning_lock ) ;
}
ESP_LOGE ( TAG , " BLE provisioning unavailable; SoftAP fallback disabled: %s " ,
esp_err_to_name ( err ) ) ;