feat(control_plane): add light standby coordinator
This commit is contained in:
@@ -13,6 +13,7 @@ idf_component_register(
|
|||||||
"src/runtime/control_plane_connectivity_runtime.c"
|
"src/runtime/control_plane_connectivity_runtime.c"
|
||||||
"src/runtime/control_plane_fortune_service.c"
|
"src/runtime/control_plane_fortune_service.c"
|
||||||
"src/runtime/control_plane_system_events.c"
|
"src/runtime/control_plane_system_events.c"
|
||||||
|
"src/runtime/control_plane_light_standby.c"
|
||||||
"src/runtime/control_plane_key_input.c"
|
"src/runtime/control_plane_key_input.c"
|
||||||
"src/runtime/control_plane_reset_service.c"
|
"src/runtime/control_plane_reset_service.c"
|
||||||
"src/runtime/control_plane_remote_log_service.c"
|
"src/runtime/control_plane_remote_log_service.c"
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
#include "esp_err.h"
|
#include "esp_err.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
@@ -89,6 +90,35 @@ typedef struct {
|
|||||||
esp_err_t last_error;
|
esp_err_t last_error;
|
||||||
} control_plane_ota_snapshot_t;
|
} control_plane_ota_snapshot_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
CONTROL_PLANE_LIGHT_STANDBY_STATE_DISABLED = 0,
|
||||||
|
CONTROL_PLANE_LIGHT_STANDBY_STATE_ACTIVE,
|
||||||
|
CONTROL_PLANE_LIGHT_STANDBY_STATE_ENTERING,
|
||||||
|
CONTROL_PLANE_LIGHT_STANDBY_STATE_STANDBY,
|
||||||
|
CONTROL_PLANE_LIGHT_STANDBY_STATE_WAKING,
|
||||||
|
CONTROL_PLANE_LIGHT_STANDBY_STATE_STOPPED,
|
||||||
|
} control_plane_light_standby_state_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
CONTROL_PLANE_LIGHT_STANDBY_ACTIVITY_UNSPECIFIED = 0,
|
||||||
|
CONTROL_PLANE_LIGHT_STANDBY_ACTIVITY_KEY,
|
||||||
|
CONTROL_PLANE_LIGHT_STANDBY_ACTIVITY_LOCAL_WAKE,
|
||||||
|
CONTROL_PLANE_LIGHT_STANDBY_ACTIVITY_VOICE_INPUT,
|
||||||
|
CONTROL_PLANE_LIGHT_STANDBY_ACTIVITY_VOICE_OUTPUT,
|
||||||
|
CONTROL_PLANE_LIGHT_STANDBY_ACTIVITY_MODEL_COMMAND,
|
||||||
|
CONTROL_PLANE_LIGHT_STANDBY_ACTIVITY_EXTERNAL,
|
||||||
|
} control_plane_light_standby_activity_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
control_plane_light_standby_state_t state;
|
||||||
|
control_plane_light_standby_activity_t last_activity;
|
||||||
|
bool running;
|
||||||
|
uint32_t idle_timeout_ms;
|
||||||
|
uint64_t last_activity_ms;
|
||||||
|
uint64_t standby_since_ms;
|
||||||
|
uint32_t transition_count;
|
||||||
|
} control_plane_light_standby_snapshot_t;
|
||||||
|
|
||||||
esp_err_t control_plane_init(void);
|
esp_err_t control_plane_init(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -109,6 +139,38 @@ esp_err_t control_plane_battery_service_start(void);
|
|||||||
*/
|
*/
|
||||||
esp_err_t control_plane_prepare_runtime_storage(void);
|
esp_err_t control_plane_prepare_runtime_storage(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Start the control-plane owned light-standby coordinator.
|
||||||
|
*
|
||||||
|
* The coordinator tracks one inactivity deadline for the full runtime. Phase
|
||||||
|
* one records state transitions only; hardware power policies are attached in
|
||||||
|
* later phases. Repeated starts are idempotent.
|
||||||
|
*/
|
||||||
|
esp_err_t control_plane_light_standby_start(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Stop the light-standby coordinator with bounded teardown.
|
||||||
|
*
|
||||||
|
* Zero requests asynchronous stop. Positive values wait up to the supplied
|
||||||
|
* timeout and apply deterministic task teardown if necessary.
|
||||||
|
*/
|
||||||
|
esp_err_t control_plane_light_standby_stop(int timeout_ms);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Record user activity and return the coordinator to ACTIVE state.
|
||||||
|
*
|
||||||
|
* This call is non-blocking and safe from control-plane worker tasks. It must
|
||||||
|
* not be called directly from an ISR.
|
||||||
|
*/
|
||||||
|
esp_err_t control_plane_light_standby_record_activity(
|
||||||
|
control_plane_light_standby_activity_t activity);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Copy the current light-standby state snapshot.
|
||||||
|
*/
|
||||||
|
esp_err_t control_plane_light_standby_get_snapshot(
|
||||||
|
control_plane_light_standby_snapshot_t *out_snapshot);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Parse one direction command without side effects.
|
* @brief Parse one direction command without side effects.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -322,6 +322,8 @@ static void local_wake_handle_detection(
|
|||||||
(unsigned)event->confidence_x10000,
|
(unsigned)event->confidence_x10000,
|
||||||
(unsigned)event->detection_generation);
|
(unsigned)event->detection_generation);
|
||||||
|
|
||||||
|
(void)control_plane_light_standby_record_activity(
|
||||||
|
CONTROL_PLANE_LIGHT_STANDBY_ACTIVITY_LOCAL_WAKE);
|
||||||
control_plane_set_ptt_active_from_local_wake();
|
control_plane_set_ptt_active_from_local_wake();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -870,6 +870,21 @@ static void control_plane_multimodal_event_bridge(control_plane_multimodal_event
|
|||||||
{
|
{
|
||||||
(void)user_data;
|
(void)user_data;
|
||||||
|
|
||||||
|
switch (event) {
|
||||||
|
case CONTROL_PLANE_MULTIMODAL_EVENT_SPEECH_STARTED:
|
||||||
|
case CONTROL_PLANE_MULTIMODAL_EVENT_SPEECH_ENDED:
|
||||||
|
(void)control_plane_light_standby_record_activity(
|
||||||
|
CONTROL_PLANE_LIGHT_STANDBY_ACTIVITY_VOICE_INPUT);
|
||||||
|
break;
|
||||||
|
case CONTROL_PLANE_MULTIMODAL_EVENT_RESPONDING_STARTED:
|
||||||
|
case CONTROL_PLANE_MULTIMODAL_EVENT_RESPONDING_ENDED:
|
||||||
|
(void)control_plane_light_standby_record_activity(
|
||||||
|
CONTROL_PLANE_LIGHT_STANDBY_ACTIVITY_VOICE_OUTPUT);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
switch (event) {
|
switch (event) {
|
||||||
case CONTROL_PLANE_MULTIMODAL_EVENT_TRANSPORT_CONNECTED: {
|
case CONTROL_PLANE_MULTIMODAL_EVENT_TRANSPORT_CONNECTED: {
|
||||||
control_plane_schedule_server_success_prompt();
|
control_plane_schedule_server_success_prompt();
|
||||||
|
|||||||
@@ -0,0 +1,429 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "control_plane_internal.h"
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_timer.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
|
||||||
|
#ifndef CONFIG_TQ_LIGHT_STANDBY_ENABLE
|
||||||
|
#define CONFIG_TQ_LIGHT_STANDBY_ENABLE 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CONFIG_TQ_LIGHT_STANDBY_IDLE_TIMEOUT_MS
|
||||||
|
#define CONFIG_TQ_LIGHT_STANDBY_IDLE_TIMEOUT_MS 300000
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CONFIG_TQ_LIGHT_STANDBY_TASK_STACK_BYTES
|
||||||
|
#define CONFIG_TQ_LIGHT_STANDBY_TASK_STACK_BYTES 3072
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CONFIG_TQ_LIGHT_STANDBY_TASK_PRIORITY
|
||||||
|
#define CONFIG_TQ_LIGHT_STANDBY_TASK_PRIORITY 3
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CONFIG_TQ_LOCAL_TASK_CORE
|
||||||
|
#define CONFIG_TQ_LOCAL_TASK_CORE 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define CONTROL_PLANE_LIGHT_STANDBY_ELIGIBILITY_RECHECK_MS 1000U
|
||||||
|
#define CONTROL_PLANE_LIGHT_STANDBY_STOP_POLL_MS 20U
|
||||||
|
|
||||||
|
#if CONFIG_TQ_LOCAL_TASK_CORE < 0
|
||||||
|
#define CONTROL_PLANE_LIGHT_STANDBY_TASK_CORE tskNO_AFFINITY
|
||||||
|
#else
|
||||||
|
#define CONTROL_PLANE_LIGHT_STANDBY_TASK_CORE ((BaseType_t)CONFIG_TQ_LOCAL_TASK_CORE)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
TaskHandle_t task;
|
||||||
|
control_plane_light_standby_state_t state;
|
||||||
|
control_plane_light_standby_activity_t last_activity;
|
||||||
|
bool running;
|
||||||
|
bool stop_requested;
|
||||||
|
uint64_t last_activity_ms;
|
||||||
|
uint64_t standby_since_ms;
|
||||||
|
uint32_t transition_count;
|
||||||
|
} control_plane_light_standby_runtime_t;
|
||||||
|
|
||||||
|
static const char *TAG = "cp_light_standby";
|
||||||
|
static control_plane_light_standby_runtime_t s_standby;
|
||||||
|
static portMUX_TYPE s_standby_lock = portMUX_INITIALIZER_UNLOCKED;
|
||||||
|
|
||||||
|
static uint64_t control_plane_light_standby_now_ms(void)
|
||||||
|
{
|
||||||
|
return (uint64_t)(esp_timer_get_time() / 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *control_plane_light_standby_state_name(
|
||||||
|
control_plane_light_standby_state_t state)
|
||||||
|
{
|
||||||
|
switch (state) {
|
||||||
|
case CONTROL_PLANE_LIGHT_STANDBY_STATE_DISABLED:
|
||||||
|
return "DISABLED";
|
||||||
|
case CONTROL_PLANE_LIGHT_STANDBY_STATE_ACTIVE:
|
||||||
|
return "ACTIVE";
|
||||||
|
case CONTROL_PLANE_LIGHT_STANDBY_STATE_ENTERING:
|
||||||
|
return "ENTERING";
|
||||||
|
case CONTROL_PLANE_LIGHT_STANDBY_STATE_STANDBY:
|
||||||
|
return "STANDBY";
|
||||||
|
case CONTROL_PLANE_LIGHT_STANDBY_STATE_WAKING:
|
||||||
|
return "WAKING";
|
||||||
|
case CONTROL_PLANE_LIGHT_STANDBY_STATE_STOPPED:
|
||||||
|
return "STOPPED";
|
||||||
|
default:
|
||||||
|
return "UNKNOWN";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *control_plane_light_standby_activity_name(
|
||||||
|
control_plane_light_standby_activity_t activity)
|
||||||
|
{
|
||||||
|
switch (activity) {
|
||||||
|
case CONTROL_PLANE_LIGHT_STANDBY_ACTIVITY_KEY:
|
||||||
|
return "key";
|
||||||
|
case CONTROL_PLANE_LIGHT_STANDBY_ACTIVITY_LOCAL_WAKE:
|
||||||
|
return "local_wake";
|
||||||
|
case CONTROL_PLANE_LIGHT_STANDBY_ACTIVITY_VOICE_INPUT:
|
||||||
|
return "voice_input";
|
||||||
|
case CONTROL_PLANE_LIGHT_STANDBY_ACTIVITY_VOICE_OUTPUT:
|
||||||
|
return "voice_output";
|
||||||
|
case CONTROL_PLANE_LIGHT_STANDBY_ACTIVITY_MODEL_COMMAND:
|
||||||
|
return "model_command";
|
||||||
|
case CONTROL_PLANE_LIGHT_STANDBY_ACTIVITY_EXTERNAL:
|
||||||
|
return "external";
|
||||||
|
case CONTROL_PLANE_LIGHT_STANDBY_ACTIVITY_UNSPECIFIED:
|
||||||
|
default:
|
||||||
|
return "unspecified";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void control_plane_light_standby_log_transition(
|
||||||
|
control_plane_light_standby_state_t from,
|
||||||
|
control_plane_light_standby_state_t to,
|
||||||
|
const char *reason)
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG,
|
||||||
|
"State %s -> %s reason=%s",
|
||||||
|
control_plane_light_standby_state_name(from),
|
||||||
|
control_plane_light_standby_state_name(to),
|
||||||
|
reason != NULL ? reason : "-");
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool control_plane_light_standby_runtime_is_eligible(void)
|
||||||
|
{
|
||||||
|
if (!control_plane_voice_turn_is_idle() ||
|
||||||
|
!control_plane_ota_voice_services_allowed() ||
|
||||||
|
control_plane_is_mini_program_provisioning_running()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
control_plane_multimodal_runtime_state_t state = {0};
|
||||||
|
if (control_plane_multimodal_get_runtime_state(&state) != ESP_OK) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return !state.remote_responding &&
|
||||||
|
!state.client_speech_active &&
|
||||||
|
!state.request_to_speak_pending &&
|
||||||
|
!state.response_pending &&
|
||||||
|
!state.local_responding_started;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void control_plane_light_standby_enter(uint64_t now_ms)
|
||||||
|
{
|
||||||
|
control_plane_light_standby_state_t from = CONTROL_PLANE_LIGHT_STANDBY_STATE_ACTIVE;
|
||||||
|
bool entering = false;
|
||||||
|
|
||||||
|
portENTER_CRITICAL(&s_standby_lock);
|
||||||
|
if (s_standby.running && !s_standby.stop_requested &&
|
||||||
|
s_standby.state == CONTROL_PLANE_LIGHT_STANDBY_STATE_ACTIVE &&
|
||||||
|
(now_ms - s_standby.last_activity_ms) >=
|
||||||
|
(uint64_t)CONFIG_TQ_LIGHT_STANDBY_IDLE_TIMEOUT_MS) {
|
||||||
|
from = s_standby.state;
|
||||||
|
s_standby.state = CONTROL_PLANE_LIGHT_STANDBY_STATE_ENTERING;
|
||||||
|
s_standby.transition_count++;
|
||||||
|
entering = true;
|
||||||
|
}
|
||||||
|
portEXIT_CRITICAL(&s_standby_lock);
|
||||||
|
|
||||||
|
if (!entering) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
control_plane_light_standby_log_transition(from,
|
||||||
|
CONTROL_PLANE_LIGHT_STANDBY_STATE_ENTERING,
|
||||||
|
"idle_timeout");
|
||||||
|
|
||||||
|
bool entered = false;
|
||||||
|
portENTER_CRITICAL(&s_standby_lock);
|
||||||
|
if (s_standby.state == CONTROL_PLANE_LIGHT_STANDBY_STATE_ENTERING &&
|
||||||
|
!s_standby.stop_requested) {
|
||||||
|
s_standby.state = CONTROL_PLANE_LIGHT_STANDBY_STATE_STANDBY;
|
||||||
|
s_standby.standby_since_ms = now_ms;
|
||||||
|
s_standby.transition_count++;
|
||||||
|
entered = true;
|
||||||
|
}
|
||||||
|
portEXIT_CRITICAL(&s_standby_lock);
|
||||||
|
|
||||||
|
if (entered) {
|
||||||
|
control_plane_light_standby_log_transition(
|
||||||
|
CONTROL_PLANE_LIGHT_STANDBY_STATE_ENTERING,
|
||||||
|
CONTROL_PLANE_LIGHT_STANDBY_STATE_STANDBY,
|
||||||
|
"phase1_state_only");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void control_plane_light_standby_task(void *arg)
|
||||||
|
{
|
||||||
|
(void)arg;
|
||||||
|
ESP_LOGI(TAG,
|
||||||
|
"Coordinator started idle_timeout_ms=%u",
|
||||||
|
(unsigned)CONFIG_TQ_LIGHT_STANDBY_IDLE_TIMEOUT_MS);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
uint64_t last_activity_ms = 0U;
|
||||||
|
bool stop_requested = false;
|
||||||
|
control_plane_light_standby_state_t state =
|
||||||
|
CONTROL_PLANE_LIGHT_STANDBY_STATE_STOPPED;
|
||||||
|
|
||||||
|
portENTER_CRITICAL(&s_standby_lock);
|
||||||
|
last_activity_ms = s_standby.last_activity_ms;
|
||||||
|
stop_requested = s_standby.stop_requested;
|
||||||
|
state = s_standby.state;
|
||||||
|
portEXIT_CRITICAL(&s_standby_lock);
|
||||||
|
|
||||||
|
if (stop_requested) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (state == CONTROL_PLANE_LIGHT_STANDBY_STATE_STANDBY) {
|
||||||
|
(void)ulTaskNotifyTake(
|
||||||
|
pdTRUE,
|
||||||
|
pdMS_TO_TICKS(CONFIG_TQ_LIGHT_STANDBY_IDLE_TIMEOUT_MS));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t now_ms = control_plane_light_standby_now_ms();
|
||||||
|
uint64_t idle_ms = now_ms - last_activity_ms;
|
||||||
|
if (idle_ms >= (uint64_t)CONFIG_TQ_LIGHT_STANDBY_IDLE_TIMEOUT_MS) {
|
||||||
|
if (control_plane_light_standby_runtime_is_eligible()) {
|
||||||
|
control_plane_light_standby_enter(now_ms);
|
||||||
|
}
|
||||||
|
(void)ulTaskNotifyTake(
|
||||||
|
pdTRUE,
|
||||||
|
pdMS_TO_TICKS(CONTROL_PLANE_LIGHT_STANDBY_ELIGIBILITY_RECHECK_MS));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t remaining_ms =
|
||||||
|
(uint64_t)CONFIG_TQ_LIGHT_STANDBY_IDLE_TIMEOUT_MS - idle_ms;
|
||||||
|
TickType_t wait_ticks = pdMS_TO_TICKS((uint32_t)remaining_ms);
|
||||||
|
if (wait_ticks == 0U) {
|
||||||
|
wait_ticks = 1U;
|
||||||
|
}
|
||||||
|
(void)ulTaskNotifyTake(pdTRUE, wait_ticks);
|
||||||
|
}
|
||||||
|
|
||||||
|
portENTER_CRITICAL(&s_standby_lock);
|
||||||
|
s_standby.task = NULL;
|
||||||
|
s_standby.running = false;
|
||||||
|
s_standby.state = CONTROL_PLANE_LIGHT_STANDBY_STATE_STOPPED;
|
||||||
|
s_standby.transition_count++;
|
||||||
|
portEXIT_CRITICAL(&s_standby_lock);
|
||||||
|
ESP_LOGI(TAG, "Coordinator stopped");
|
||||||
|
vTaskDelete(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t control_plane_light_standby_start(void)
|
||||||
|
{
|
||||||
|
#if !CONFIG_TQ_LIGHT_STANDBY_ENABLE
|
||||||
|
return ESP_ERR_NOT_SUPPORTED;
|
||||||
|
#else
|
||||||
|
uint64_t now_ms = control_plane_light_standby_now_ms();
|
||||||
|
portENTER_CRITICAL(&s_standby_lock);
|
||||||
|
if (s_standby.running) {
|
||||||
|
portEXIT_CRITICAL(&s_standby_lock);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
s_standby.state = CONTROL_PLANE_LIGHT_STANDBY_STATE_ACTIVE;
|
||||||
|
s_standby.last_activity = CONTROL_PLANE_LIGHT_STANDBY_ACTIVITY_UNSPECIFIED;
|
||||||
|
s_standby.running = true;
|
||||||
|
s_standby.stop_requested = false;
|
||||||
|
s_standby.last_activity_ms = now_ms;
|
||||||
|
s_standby.standby_since_ms = 0U;
|
||||||
|
s_standby.transition_count = 0U;
|
||||||
|
portEXIT_CRITICAL(&s_standby_lock);
|
||||||
|
|
||||||
|
TaskHandle_t task = NULL;
|
||||||
|
BaseType_t created = xTaskCreatePinnedToCore(
|
||||||
|
control_plane_light_standby_task,
|
||||||
|
"cp_light_standby",
|
||||||
|
CONFIG_TQ_LIGHT_STANDBY_TASK_STACK_BYTES,
|
||||||
|
NULL,
|
||||||
|
CONFIG_TQ_LIGHT_STANDBY_TASK_PRIORITY,
|
||||||
|
&task,
|
||||||
|
CONTROL_PLANE_LIGHT_STANDBY_TASK_CORE);
|
||||||
|
if (created != pdPASS) {
|
||||||
|
portENTER_CRITICAL(&s_standby_lock);
|
||||||
|
s_standby.running = false;
|
||||||
|
s_standby.state = CONTROL_PLANE_LIGHT_STANDBY_STATE_STOPPED;
|
||||||
|
portEXIT_CRITICAL(&s_standby_lock);
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
portENTER_CRITICAL(&s_standby_lock);
|
||||||
|
s_standby.task = task;
|
||||||
|
portEXIT_CRITICAL(&s_standby_lock);
|
||||||
|
return ESP_OK;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t control_plane_light_standby_stop(int timeout_ms)
|
||||||
|
{
|
||||||
|
if (timeout_ms < 0) {
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
#if !CONFIG_TQ_LIGHT_STANDBY_ENABLE
|
||||||
|
return ESP_OK;
|
||||||
|
#else
|
||||||
|
TaskHandle_t task = NULL;
|
||||||
|
portENTER_CRITICAL(&s_standby_lock);
|
||||||
|
if (!s_standby.running) {
|
||||||
|
portEXIT_CRITICAL(&s_standby_lock);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
s_standby.stop_requested = true;
|
||||||
|
task = s_standby.task;
|
||||||
|
portEXIT_CRITICAL(&s_standby_lock);
|
||||||
|
|
||||||
|
if (task != NULL) {
|
||||||
|
xTaskNotifyGive(task);
|
||||||
|
}
|
||||||
|
if (timeout_ms == 0) {
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t deadline_ms = control_plane_light_standby_now_ms() + (uint64_t)timeout_ms;
|
||||||
|
while (control_plane_light_standby_now_ms() < deadline_ms) {
|
||||||
|
bool running = false;
|
||||||
|
portENTER_CRITICAL(&s_standby_lock);
|
||||||
|
running = s_standby.running;
|
||||||
|
portEXIT_CRITICAL(&s_standby_lock);
|
||||||
|
if (!running) {
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(CONTROL_PLANE_LIGHT_STANDBY_STOP_POLL_MS));
|
||||||
|
}
|
||||||
|
|
||||||
|
portENTER_CRITICAL(&s_standby_lock);
|
||||||
|
task = s_standby.task;
|
||||||
|
s_standby.task = NULL;
|
||||||
|
s_standby.running = false;
|
||||||
|
s_standby.state = CONTROL_PLANE_LIGHT_STANDBY_STATE_STOPPED;
|
||||||
|
s_standby.transition_count++;
|
||||||
|
portEXIT_CRITICAL(&s_standby_lock);
|
||||||
|
if (task != NULL) {
|
||||||
|
ESP_LOGW(TAG, "Coordinator stop timed out; forcing task teardown");
|
||||||
|
vTaskDelete(task);
|
||||||
|
}
|
||||||
|
return ESP_ERR_TIMEOUT;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t control_plane_light_standby_record_activity(
|
||||||
|
control_plane_light_standby_activity_t activity)
|
||||||
|
{
|
||||||
|
if (activity <= CONTROL_PLANE_LIGHT_STANDBY_ACTIVITY_UNSPECIFIED ||
|
||||||
|
activity > CONTROL_PLANE_LIGHT_STANDBY_ACTIVITY_EXTERNAL) {
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
#if !CONFIG_TQ_LIGHT_STANDBY_ENABLE
|
||||||
|
return ESP_ERR_NOT_SUPPORTED;
|
||||||
|
#else
|
||||||
|
control_plane_light_standby_state_t from = CONTROL_PLANE_LIGHT_STANDBY_STATE_ACTIVE;
|
||||||
|
TaskHandle_t task = NULL;
|
||||||
|
bool waking = false;
|
||||||
|
uint64_t now_ms = control_plane_light_standby_now_ms();
|
||||||
|
|
||||||
|
portENTER_CRITICAL(&s_standby_lock);
|
||||||
|
if (!s_standby.running || s_standby.stop_requested) {
|
||||||
|
portEXIT_CRITICAL(&s_standby_lock);
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
s_standby.last_activity = activity;
|
||||||
|
s_standby.last_activity_ms = now_ms;
|
||||||
|
task = s_standby.task;
|
||||||
|
if (s_standby.state == CONTROL_PLANE_LIGHT_STANDBY_STATE_STANDBY ||
|
||||||
|
s_standby.state == CONTROL_PLANE_LIGHT_STANDBY_STATE_ENTERING) {
|
||||||
|
from = s_standby.state;
|
||||||
|
s_standby.state = CONTROL_PLANE_LIGHT_STANDBY_STATE_WAKING;
|
||||||
|
s_standby.transition_count++;
|
||||||
|
waking = true;
|
||||||
|
}
|
||||||
|
portEXIT_CRITICAL(&s_standby_lock);
|
||||||
|
|
||||||
|
if (waking) {
|
||||||
|
const char *reason = control_plane_light_standby_activity_name(activity);
|
||||||
|
control_plane_light_standby_log_transition(
|
||||||
|
from,
|
||||||
|
CONTROL_PLANE_LIGHT_STANDBY_STATE_WAKING,
|
||||||
|
reason);
|
||||||
|
|
||||||
|
bool activated = false;
|
||||||
|
portENTER_CRITICAL(&s_standby_lock);
|
||||||
|
if (s_standby.state == CONTROL_PLANE_LIGHT_STANDBY_STATE_WAKING) {
|
||||||
|
s_standby.state = CONTROL_PLANE_LIGHT_STANDBY_STATE_ACTIVE;
|
||||||
|
s_standby.standby_since_ms = 0U;
|
||||||
|
s_standby.transition_count++;
|
||||||
|
activated = true;
|
||||||
|
}
|
||||||
|
portEXIT_CRITICAL(&s_standby_lock);
|
||||||
|
if (activated) {
|
||||||
|
control_plane_light_standby_log_transition(
|
||||||
|
CONTROL_PLANE_LIGHT_STANDBY_STATE_WAKING,
|
||||||
|
CONTROL_PLANE_LIGHT_STANDBY_STATE_ACTIVE,
|
||||||
|
reason);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (task != NULL) {
|
||||||
|
xTaskNotifyGive(task);
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t control_plane_light_standby_get_snapshot(
|
||||||
|
control_plane_light_standby_snapshot_t *out_snapshot)
|
||||||
|
{
|
||||||
|
if (out_snapshot == NULL) {
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
portENTER_CRITICAL(&s_standby_lock);
|
||||||
|
#if CONFIG_TQ_LIGHT_STANDBY_ENABLE
|
||||||
|
*out_snapshot = (control_plane_light_standby_snapshot_t) {
|
||||||
|
.state = s_standby.state,
|
||||||
|
.last_activity = s_standby.last_activity,
|
||||||
|
.running = s_standby.running,
|
||||||
|
.idle_timeout_ms = CONFIG_TQ_LIGHT_STANDBY_IDLE_TIMEOUT_MS,
|
||||||
|
.last_activity_ms = s_standby.last_activity_ms,
|
||||||
|
.standby_since_ms = s_standby.standby_since_ms,
|
||||||
|
.transition_count = s_standby.transition_count,
|
||||||
|
};
|
||||||
|
#else
|
||||||
|
*out_snapshot = (control_plane_light_standby_snapshot_t) {
|
||||||
|
.state = CONTROL_PLANE_LIGHT_STANDBY_STATE_DISABLED,
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
portEXIT_CRITICAL(&s_standby_lock);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
@@ -1212,6 +1212,9 @@ static void control_plane_model_command_task(void *arg)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
(void)control_plane_light_standby_record_activity(
|
||||||
|
CONTROL_PLANE_LIGHT_STANDBY_ACTIVITY_MODEL_COMMAND);
|
||||||
|
|
||||||
if (cmd.command_type == CONTROL_PLANE_MODEL_COMMAND_DIRECTION) {
|
if (cmd.command_type == CONTROL_PLANE_MODEL_COMMAND_DIRECTION) {
|
||||||
size_t direction_index = 0;
|
size_t direction_index = 0;
|
||||||
control_plane_direction_command_t parsed = control_plane_parse_direction_command(cmd.value,
|
control_plane_direction_command_t parsed = control_plane_parse_direction_command(cmd.value,
|
||||||
@@ -2536,6 +2539,12 @@ esp_err_t control_plane_start_runtime_workers(void)
|
|||||||
return system_event_err;
|
return system_event_err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
esp_err_t standby_err = control_plane_light_standby_start();
|
||||||
|
if (standby_err != ESP_OK && standby_err != ESP_ERR_NOT_SUPPORTED) {
|
||||||
|
control_plane_stop_runtime_workers();
|
||||||
|
return standby_err;
|
||||||
|
}
|
||||||
|
|
||||||
esp_err_t key_input_err = control_plane_start_key_input_task();
|
esp_err_t key_input_err = control_plane_start_key_input_task();
|
||||||
if (key_input_err != ESP_OK) {
|
if (key_input_err != ESP_OK) {
|
||||||
control_plane_stop_runtime_workers();
|
control_plane_stop_runtime_workers();
|
||||||
@@ -2620,6 +2629,7 @@ void control_plane_stop_event_observer_runtime(void)
|
|||||||
void control_plane_stop_runtime_workers(void)
|
void control_plane_stop_runtime_workers(void)
|
||||||
{
|
{
|
||||||
control_plane_set_runtime_stop_requested(true);
|
control_plane_set_runtime_stop_requested(true);
|
||||||
|
(void)control_plane_light_standby_stop(CONTROL_PLANE_TASK_STOP_WAIT_MS);
|
||||||
(void)control_plane_battery_service_stop(CONTROL_PLANE_TASK_STOP_WAIT_MS);
|
(void)control_plane_battery_service_stop(CONTROL_PLANE_TASK_STOP_WAIT_MS);
|
||||||
(void)control_plane_stop_ota_service(CONFIG_TQ_LOCAL_WAKE_STOP_WAIT_MS);
|
(void)control_plane_stop_ota_service(CONFIG_TQ_LOCAL_WAKE_STOP_WAIT_MS);
|
||||||
(void)control_plane_stop_mini_program_provisioning(
|
(void)control_plane_stop_mini_program_provisioning(
|
||||||
|
|||||||
@@ -130,6 +130,9 @@ static void control_plane_dispatch_key_event(const control_plane_key_event_t *ev
|
|||||||
event->press_duration_ms,
|
event->press_duration_ms,
|
||||||
event->click_count);
|
event->click_count);
|
||||||
|
|
||||||
|
(void)control_plane_light_standby_record_activity(
|
||||||
|
CONTROL_PLANE_LIGHT_STANDBY_ACTIVITY_KEY);
|
||||||
|
|
||||||
if (event->key_id == CONTROL_PLANE_KEY_ID_RESET) {
|
if (event->key_id == CONTROL_PLANE_KEY_ID_RESET) {
|
||||||
if (event->type == CONTROL_PLANE_KEY_EVENT_LONG_PRESS_START) {
|
if (event->type == CONTROL_PLANE_KEY_EVENT_LONG_PRESS_START) {
|
||||||
control_plane_reset_wifi_provisioning_and_restart();
|
control_plane_reset_wifi_provisioning_and_restart();
|
||||||
|
|||||||
@@ -323,6 +323,39 @@ menu "Example Configuration"
|
|||||||
help
|
help
|
||||||
Stack size in bytes for the control-plane system event task.
|
Stack size in bytes for the control-plane system event task.
|
||||||
|
|
||||||
|
menu "Light Standby"
|
||||||
|
|
||||||
|
config TQ_LIGHT_STANDBY_ENABLE
|
||||||
|
bool "Enable light-standby state coordinator"
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
Track a single device-wide inactivity deadline and expose
|
||||||
|
ACTIVE, ENTERING, STANDBY, and WAKING states. Hardware power
|
||||||
|
actions are attached to these transitions separately.
|
||||||
|
|
||||||
|
config TQ_LIGHT_STANDBY_IDLE_TIMEOUT_MS
|
||||||
|
int "Light-standby inactivity timeout (ms)"
|
||||||
|
default 300000
|
||||||
|
range 1000 3600000
|
||||||
|
depends on TQ_LIGHT_STANDBY_ENABLE
|
||||||
|
help
|
||||||
|
Time without key, local-wake, or voice interaction before
|
||||||
|
entering light standby. The production default is 5 minutes.
|
||||||
|
|
||||||
|
config TQ_LIGHT_STANDBY_TASK_STACK_BYTES
|
||||||
|
int "Light-standby task stack size (bytes)"
|
||||||
|
default 3072
|
||||||
|
range 2048 8192
|
||||||
|
depends on TQ_LIGHT_STANDBY_ENABLE
|
||||||
|
|
||||||
|
config TQ_LIGHT_STANDBY_TASK_PRIORITY
|
||||||
|
int "Light-standby task priority"
|
||||||
|
default 3
|
||||||
|
range 1 10
|
||||||
|
depends on TQ_LIGHT_STANDBY_ENABLE
|
||||||
|
|
||||||
|
endmenu
|
||||||
|
|
||||||
menu "Remote Logging"
|
menu "Remote Logging"
|
||||||
|
|
||||||
config TQ_REMOTE_LOG_ENABLE
|
config TQ_REMOTE_LOG_ENABLE
|
||||||
|
|||||||
@@ -50,6 +50,10 @@ CONFIG_TQ_NETWORK_AUTO_REASSOCIATION_ENABLE=y
|
|||||||
CONFIG_TQ_SYSTEM_EVENT_ENABLE=y
|
CONFIG_TQ_SYSTEM_EVENT_ENABLE=y
|
||||||
CONFIG_TQ_SYSTEM_EVENT_QUEUE_LEN=32
|
CONFIG_TQ_SYSTEM_EVENT_QUEUE_LEN=32
|
||||||
CONFIG_TQ_SYSTEM_EVENT_TASK_STACK=4096
|
CONFIG_TQ_SYSTEM_EVENT_TASK_STACK=4096
|
||||||
|
CONFIG_TQ_LIGHT_STANDBY_ENABLE=y
|
||||||
|
CONFIG_TQ_LIGHT_STANDBY_IDLE_TIMEOUT_MS=300000
|
||||||
|
CONFIG_TQ_LIGHT_STANDBY_TASK_STACK_BYTES=3072
|
||||||
|
CONFIG_TQ_LIGHT_STANDBY_TASK_PRIORITY=3
|
||||||
CONFIG_TQ_REMOTE_LOG_ENABLE=y
|
CONFIG_TQ_REMOTE_LOG_ENABLE=y
|
||||||
CONFIG_TQ_REMOTE_LOG_ENDPOINT="http://47.98.175.183:8090/log/batch"
|
CONFIG_TQ_REMOTE_LOG_ENDPOINT="http://47.98.175.183:8090/log/batch"
|
||||||
CONFIG_TQ_REMOTE_LOG_AUTH_TOKEN=""
|
CONFIG_TQ_REMOTE_LOG_AUTH_TOKEN=""
|
||||||
|
|||||||
Reference in New Issue
Block a user