diff --git a/components/control_plane/CMakeLists.txt b/components/control_plane/CMakeLists.txt index 34c49a1..93d10d5 100644 --- a/components/control_plane/CMakeLists.txt +++ b/components/control_plane/CMakeLists.txt @@ -13,6 +13,7 @@ idf_component_register( "src/runtime/control_plane_connectivity_runtime.c" "src/runtime/control_plane_fortune_service.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_reset_service.c" "src/runtime/control_plane_remote_log_service.c" diff --git a/components/control_plane/include/control_plane.h b/components/control_plane/include/control_plane.h index 845ce9a..d93d873 100644 --- a/components/control_plane/include/control_plane.h +++ b/components/control_plane/include/control_plane.h @@ -8,6 +8,7 @@ #include #include +#include #include "esp_err.h" #ifdef __cplusplus @@ -89,6 +90,35 @@ typedef struct { esp_err_t last_error; } 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); /** @@ -109,6 +139,38 @@ esp_err_t control_plane_battery_service_start(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. * diff --git a/components/control_plane/src/multimodal/control_plane_multimodal_local_wake.c b/components/control_plane/src/multimodal/control_plane_multimodal_local_wake.c index a47b32e..f7f080a 100644 --- a/components/control_plane/src/multimodal/control_plane_multimodal_local_wake.c +++ b/components/control_plane/src/multimodal/control_plane_multimodal_local_wake.c @@ -322,6 +322,8 @@ static void local_wake_handle_detection( (unsigned)event->confidence_x10000, (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(); } diff --git a/components/control_plane/src/multimodal/control_plane_multimodal_runtime.c b/components/control_plane/src/multimodal/control_plane_multimodal_runtime.c index 0cdf5b2..2031d08 100644 --- a/components/control_plane/src/multimodal/control_plane_multimodal_runtime.c +++ b/components/control_plane/src/multimodal/control_plane_multimodal_runtime.c @@ -870,6 +870,21 @@ static void control_plane_multimodal_event_bridge(control_plane_multimodal_event { (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) { case CONTROL_PLANE_MULTIMODAL_EVENT_TRANSPORT_CONNECTED: { control_plane_schedule_server_success_prompt(); diff --git a/components/control_plane/src/runtime/control_plane_light_standby.c b/components/control_plane/src/runtime/control_plane_light_standby.c new file mode 100644 index 0000000..b44d0e4 --- /dev/null +++ b/components/control_plane/src/runtime/control_plane_light_standby.c @@ -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 +#include +#include + +#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; +} diff --git a/components/control_plane/src/runtime/control_plane_runtime_workers.c b/components/control_plane/src/runtime/control_plane_runtime_workers.c index 1df20d3..58683a6 100644 --- a/components/control_plane/src/runtime/control_plane_runtime_workers.c +++ b/components/control_plane/src/runtime/control_plane_runtime_workers.c @@ -1212,6 +1212,9 @@ static void control_plane_model_command_task(void *arg) break; } + (void)control_plane_light_standby_record_activity( + CONTROL_PLANE_LIGHT_STANDBY_ACTIVITY_MODEL_COMMAND); + if (cmd.command_type == CONTROL_PLANE_MODEL_COMMAND_DIRECTION) { size_t direction_index = 0; 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; } + 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(); if (key_input_err != ESP_OK) { control_plane_stop_runtime_workers(); @@ -2620,6 +2629,7 @@ void control_plane_stop_event_observer_runtime(void) void control_plane_stop_runtime_workers(void) { 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_stop_ota_service(CONFIG_TQ_LOCAL_WAKE_STOP_WAIT_MS); (void)control_plane_stop_mini_program_provisioning( diff --git a/components/control_plane/src/runtime/control_plane_system_events.c b/components/control_plane/src/runtime/control_plane_system_events.c index 66cdfbe..1bfc6cd 100644 --- a/components/control_plane/src/runtime/control_plane_system_events.c +++ b/components/control_plane/src/runtime/control_plane_system_events.c @@ -130,6 +130,9 @@ static void control_plane_dispatch_key_event(const control_plane_key_event_t *ev event->press_duration_ms, 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->type == CONTROL_PLANE_KEY_EVENT_LONG_PRESS_START) { control_plane_reset_wifi_provisioning_and_restart(); diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild index 13d15d8..0452d40 100644 --- a/main/Kconfig.projbuild +++ b/main/Kconfig.projbuild @@ -323,6 +323,39 @@ menu "Example Configuration" help 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" config TQ_REMOTE_LOG_ENABLE diff --git a/sdkconfig.defaults b/sdkconfig.defaults index 0d101b0..a59a0c8 100644 --- a/sdkconfig.defaults +++ b/sdkconfig.defaults @@ -50,6 +50,10 @@ CONFIG_TQ_NETWORK_AUTO_REASSOCIATION_ENABLE=y CONFIG_TQ_SYSTEM_EVENT_ENABLE=y CONFIG_TQ_SYSTEM_EVENT_QUEUE_LEN=32 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_ENDPOINT="http://47.98.175.183:8090/log/batch" CONFIG_TQ_REMOTE_LOG_AUTH_TOKEN=""