fix(connectivity): harden BLE reprovisioning under memory pressure
This commit is contained in:
@@ -236,6 +236,15 @@ esp_err_t control_plane_calibrate(void);
|
||||
esp_err_t control_plane_bootstrap_connectivity(const char *seed_ssid,
|
||||
const char *seed_password,
|
||||
int connect_timeout_ms);
|
||||
|
||||
/**
|
||||
* @brief Consume a one-shot request to use the low-memory BLE recovery boot.
|
||||
*
|
||||
* The request survives a software restart in RTC no-init memory. A true result
|
||||
* is latched for the current boot so connectivity skips STA reconnect and
|
||||
* starts BLE provisioning before full runtime workers.
|
||||
*/
|
||||
bool control_plane_take_mini_program_recovery_boot_request(void);
|
||||
bool control_plane_is_sta_connected(void);
|
||||
esp_err_t control_plane_stop_mini_program_provisioning(int timeout_ms);
|
||||
bool control_plane_is_mini_program_provisioning_running(void);
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "control_plane_internal.h"
|
||||
#include "esp_attr.h"
|
||||
#include "esp_log.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/event_groups.h"
|
||||
@@ -40,26 +41,103 @@
|
||||
#define CONFIG_TQ_MINI_PROGRAM_PROVISIONING_CONNECT_TIMEOUT_MS 30000
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_TQ_MINI_PROGRAM_PROVISIONING_MIN_FREE_INTERNAL_BYTES
|
||||
#define CONFIG_TQ_MINI_PROGRAM_PROVISIONING_MIN_FREE_INTERNAL_BYTES 49152
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_TQ_MINI_PROGRAM_PROVISIONING_MIN_LARGEST_DMA_BYTES
|
||||
#define CONFIG_TQ_MINI_PROGRAM_PROVISIONING_MIN_LARGEST_DMA_BYTES 12288
|
||||
#endif
|
||||
|
||||
#define CONTROL_PLANE_WIFI_SUCCESS_PROMPT_FILE "/littlefs/audio_prompts/network_connect_success.opus"
|
||||
|
||||
#define CONTROL_PLANE_STA_CONNECT_MAX_ATTEMPTS 3
|
||||
#define CONTROL_PLANE_STA_RETRY_BASE_DELAY_MS 300
|
||||
#define CONTROL_PLANE_PROVISIONING_QUEUE_LENGTH 1
|
||||
#define CONTROL_PLANE_PROVISIONING_TASK_STACK_WORDS 4096
|
||||
#define CONTROL_PLANE_PROVISIONING_TASK_STACK_BYTES 4096
|
||||
#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_POLL_MS 100
|
||||
#define CONTROL_PLANE_PROVISIONING_WAIT_LOG_MS 10000
|
||||
#define CONTROL_PLANE_PROVISIONING_NOTIFY_SETTLE_MS 750
|
||||
#define CONTROL_PLANE_PROVISIONING_TRANSPORT_STOP_MS 2000
|
||||
#define CONTROL_PLANE_PROVISIONING_RUNTIME_QUIESCE_MS 2000
|
||||
#define CONTROL_PLANE_PROVISIONING_RESTART_DELAY_MS 100
|
||||
#define CONTROL_PLANE_PROVISIONING_RECOVERY_MAGIC 0x5451424cU
|
||||
|
||||
static const char *TAG = "control_plane_rt";
|
||||
static bool s_wifi_success_prompt_played;
|
||||
static bool s_provisioning_recovery_boot;
|
||||
RTC_NOINIT_ATTR static uint32_t s_provisioning_recovery_marker;
|
||||
RTC_NOINIT_ATTR static uint32_t s_provisioning_recovery_marker_inverse;
|
||||
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;
|
||||
|
||||
bool control_plane_take_mini_program_recovery_boot_request(void)
|
||||
{
|
||||
bool requested =
|
||||
s_provisioning_recovery_marker == CONTROL_PLANE_PROVISIONING_RECOVERY_MAGIC &&
|
||||
s_provisioning_recovery_marker_inverse ==
|
||||
~CONTROL_PLANE_PROVISIONING_RECOVERY_MAGIC;
|
||||
s_provisioning_recovery_marker = 0U;
|
||||
s_provisioning_recovery_marker_inverse = 0U;
|
||||
s_provisioning_recovery_boot = requested;
|
||||
if (requested) {
|
||||
ESP_LOGW(TAG, "Consumed one-shot low-memory BLE recovery boot request");
|
||||
}
|
||||
return requested;
|
||||
}
|
||||
|
||||
static void control_plane_request_provisioning_recovery_restart(
|
||||
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);
|
||||
vTaskDelay(pdMS_TO_TICKS(CONTROL_PLANE_PROVISIONING_RESTART_DELAY_MS));
|
||||
domain_runtime_restart();
|
||||
}
|
||||
|
||||
static esp_err_t control_plane_check_provisioning_memory_headroom(void)
|
||||
{
|
||||
domain_ota_memory_headroom_t headroom = {0};
|
||||
esp_err_t err = domain_runtime_ota_get_memory_headroom(&headroom);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Read BLE provisioning memory headroom failed: %s",
|
||||
esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG,
|
||||
"BLE provisioning memory headroom: free_internal=%u largest_internal=%u largest_dma=%u",
|
||||
(unsigned)headroom.free_internal,
|
||||
(unsigned)headroom.largest_internal,
|
||||
(unsigned)headroom.largest_dma);
|
||||
if (headroom.free_internal >=
|
||||
CONFIG_TQ_MINI_PROGRAM_PROVISIONING_MIN_FREE_INTERNAL_BYTES &&
|
||||
headroom.largest_dma >=
|
||||
CONFIG_TQ_MINI_PROGRAM_PROVISIONING_MIN_LARGEST_DMA_BYTES) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
ESP_LOGE(TAG,
|
||||
"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;
|
||||
}
|
||||
|
||||
static int elapsed_ms_since(TickType_t start_tick)
|
||||
{
|
||||
TickType_t elapsed_ticks = xTaskGetTickCount() - start_tick;
|
||||
@@ -225,6 +303,19 @@ static void control_plane_finish_provisioning_ui(void)
|
||||
}
|
||||
}
|
||||
|
||||
static void control_plane_show_provisioning_ui(void)
|
||||
{
|
||||
const domain_expression_request_t request = {
|
||||
.id = DOMAIN_EXPRESSION_WIFI_CONNECTING,
|
||||
.trigger = DOMAIN_EXPRESSION_TRIGGER_WIFI_BOOTSTRAP,
|
||||
};
|
||||
esp_err_t err = domain_runtime_request_expression(&request);
|
||||
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
|
||||
ESP_LOGW(TAG, "Wi-Fi provisioning display unavailable: %s",
|
||||
esp_err_to_name(err));
|
||||
}
|
||||
}
|
||||
|
||||
static void control_plane_provisioning_event_callback(
|
||||
const domain_wifi_provisioning_event_t *event,
|
||||
void *user_data)
|
||||
@@ -254,12 +345,24 @@ static void control_plane_provisioning_worker(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
bool provisioning_succeeded = false;
|
||||
TickType_t last_wait_log_tick = xTaskGetTickCount();
|
||||
TickType_t wait_log_ticks =
|
||||
pdMS_TO_TICKS(CONTROL_PLANE_PROVISIONING_WAIT_LOG_MS);
|
||||
if (wait_log_ticks == 0U) {
|
||||
wait_log_ticks = 1U;
|
||||
}
|
||||
|
||||
while (!control_plane_provisioning_stop_requested()) {
|
||||
domain_wifi_provisioning_event_t event = {0};
|
||||
if (xQueueReceive(s_provisioning_queue,
|
||||
&event,
|
||||
pdMS_TO_TICKS(CONTROL_PLANE_PROVISIONING_POLL_MS)) != pdTRUE) {
|
||||
TickType_t now_tick = xTaskGetTickCount();
|
||||
if ((now_tick - last_wait_log_tick) >= wait_log_ticks) {
|
||||
ESP_LOGI(TAG,
|
||||
"BLE provisioning active; waiting for mini-program credentials");
|
||||
last_wait_log_tick = now_tick;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -319,7 +422,14 @@ static void control_plane_provisioning_worker(void *arg)
|
||||
(void)xEventGroupSetBits(s_provisioning_events,
|
||||
CONTROL_PLANE_PROVISIONING_WORKER_DONE_BIT);
|
||||
|
||||
if (provisioning_succeeded && stop_err == ESP_OK) {
|
||||
if (provisioning_succeeded && stop_err == ESP_OK &&
|
||||
s_provisioning_recovery_boot) {
|
||||
ESP_LOGI(TAG,
|
||||
"BLE recovery provisioning completed; restarting into normal runtime");
|
||||
s_provisioning_recovery_boot = false;
|
||||
vTaskDelay(pdMS_TO_TICKS(CONTROL_PLANE_PROVISIONING_RESTART_DELAY_MS));
|
||||
domain_runtime_restart();
|
||||
} else if (provisioning_succeeded && stop_err == ESP_OK) {
|
||||
control_plane_finish_provisioning_ui();
|
||||
ESP_LOGI(TAG,
|
||||
"BLE provisioning completed; starting deferred runtime workers");
|
||||
@@ -365,31 +475,31 @@ static esp_err_t control_plane_start_mini_program_provisioning(void)
|
||||
CONTROL_PLANE_PROVISIONING_STOP_REQUESTED_BIT |
|
||||
CONTROL_PLANE_PROVISIONING_WORKER_DONE_BIT);
|
||||
|
||||
esp_err_t err = control_plane_check_provisioning_memory_headroom();
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
err = domain_runtime_mini_program_provisioning_start(
|
||||
control_plane_provisioning_event_callback,
|
||||
NULL);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
BaseType_t created = xTaskCreate(control_plane_provisioning_worker,
|
||||
"mini_prog_wifi",
|
||||
CONTROL_PLANE_PROVISIONING_TASK_STACK_WORDS,
|
||||
CONTROL_PLANE_PROVISIONING_TASK_STACK_BYTES,
|
||||
NULL,
|
||||
CONTROL_PLANE_PROVISIONING_TASK_PRIORITY,
|
||||
&s_provisioning_task);
|
||||
if (created != pdPASS) {
|
||||
s_provisioning_task = NULL;
|
||||
ESP_LOGE(TAG, "Create mini-program provisioning worker failed after BLE start");
|
||||
(void)domain_runtime_mini_program_provisioning_stop(
|
||||
CONTROL_PLANE_PROVISIONING_TRANSPORT_STOP_MS);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
esp_err_t err = domain_runtime_mini_program_provisioning_start(
|
||||
control_plane_provisioning_event_callback,
|
||||
NULL);
|
||||
if (err != ESP_OK) {
|
||||
(void)xEventGroupSetBits(s_provisioning_events,
|
||||
CONTROL_PLANE_PROVISIONING_STOP_REQUESTED_BIT);
|
||||
(void)xEventGroupWaitBits(
|
||||
s_provisioning_events,
|
||||
CONTROL_PLANE_PROVISIONING_WORKER_DONE_BIT,
|
||||
pdFALSE,
|
||||
pdTRUE,
|
||||
pdMS_TO_TICKS(CONTROL_PLANE_PROVISIONING_TRANSPORT_STOP_MS));
|
||||
return err;
|
||||
}
|
||||
ESP_LOGI(TAG, "Mini-program Wi-Fi provisioning started");
|
||||
return ESP_OK;
|
||||
#endif
|
||||
@@ -443,12 +553,24 @@ 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");
|
||||
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();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
return err;
|
||||
#endif
|
||||
@@ -469,7 +591,10 @@ esp_err_t control_plane_bootstrap_connectivity(const char *seed_ssid,
|
||||
return err;
|
||||
}
|
||||
|
||||
if (domain_runtime_wifi_portal_has_saved_credentials()) {
|
||||
if (s_provisioning_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()) {
|
||||
int timeout_ms = control_plane_normalize_connect_timeout_ms(connect_timeout_ms, 0);
|
||||
err = control_plane_connect_sta_with_feedback(timeout_ms, true);
|
||||
if (err == ESP_OK) {
|
||||
@@ -480,20 +605,26 @@ esp_err_t control_plane_bootstrap_connectivity(const char *seed_ssid,
|
||||
esp_err_to_name(err));
|
||||
}
|
||||
|
||||
const domain_expression_request_t connecting_request = {
|
||||
.id = DOMAIN_EXPRESSION_WIFI_CONNECTING,
|
||||
.trigger = DOMAIN_EXPRESSION_TRIGGER_WIFI_BOOTSTRAP,
|
||||
};
|
||||
esp_err_t display_err = domain_runtime_request_expression(&connecting_request);
|
||||
if (display_err != ESP_OK && display_err != ESP_ERR_INVALID_STATE) {
|
||||
ESP_LOGW(TAG, "Wi-Fi connecting QR display unavailable: %s",
|
||||
esp_err_to_name(display_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) {
|
||||
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,
|
||||
"Start LCD after BLE recovery provisioning failed: %s",
|
||||
esp_err_to_name(lcd_err));
|
||||
}
|
||||
}
|
||||
if (err != ESP_OK) {
|
||||
control_plane_wifi_set_keep_awake(false);
|
||||
if (s_provisioning_recovery_boot) {
|
||||
ESP_LOGE(TAG,
|
||||
"Low-memory BLE recovery boot failed; resume normal boot: %s",
|
||||
esp_err_to_name(err));
|
||||
s_provisioning_recovery_boot = false;
|
||||
}
|
||||
ESP_LOGE(TAG, "BLE provisioning unavailable; SoftAP fallback disabled: %s",
|
||||
esp_err_to_name(err));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user