feat(control_plane): reduce light standby runtime power

This commit is contained in:
chenjintao
2026-08-21 14:24:01 +08:00
parent baad9dcfa0
commit e773ebc458
11 changed files with 264 additions and 9 deletions
@@ -148,6 +148,8 @@ bool control_plane_ota_voice_services_allowed(void);
bool control_plane_runtime_is_stopping(void);
esp_err_t control_plane_battery_service_stop(int timeout_ms);
void control_plane_battery_service_set_light_standby(bool standby);
void control_plane_runtime_diagnostics_set_light_standby(bool standby);
esp_err_t control_plane_start_system_event_task(void);
void control_plane_stop_system_event_task(void);
@@ -166,6 +168,7 @@ esp_err_t control_plane_enter_mini_program_reprovisioning(const char *reason);
esp_err_t control_plane_remote_log_start(void);
void control_plane_remote_log_stop(int timeout_ms);
void control_plane_remote_log_set_light_standby(bool standby);
esp_err_t control_plane_remote_log_flush_now(void);
esp_err_t control_plane_remote_log_clear(void);
esp_err_t control_plane_remote_log_format_status(char *out, size_t out_len);
@@ -28,11 +28,16 @@
#define BATTERY_LOW_PROMPT_FILE "/littlefs/audio_prompts/battery_low_20.opus"
#define BATTERY_FULL_PROMPT_FILE "/littlefs/audio_prompts/charge_complete.opus"
#ifndef CONFIG_TQ_LIGHT_STANDBY_BATTERY_INTERVAL_SEC
#define CONFIG_TQ_LIGHT_STANDBY_BATTERY_INTERVAL_SEC 300
#endif
static const char *TAG = "cp_battery";
static TaskHandle_t s_battery_task;
static bool s_stop_requested;
static bool s_starting;
static bool s_charge_gpio_isr_registered;
static bool s_light_standby;
static portMUX_TYPE s_battery_lock = portMUX_INITIALIZER_UNLOCKED;
static void IRAM_ATTR battery_charge_gpio_isr(void *arg)
@@ -122,6 +127,15 @@ static bool battery_stop_requested(void)
return requested;
}
static bool battery_light_standby(void)
{
bool standby;
portENTER_CRITICAL(&s_battery_lock);
standby = s_light_standby;
portEXIT_CRITICAL(&s_battery_lock);
return standby;
}
static void battery_worker_finish(void)
{
portENTER_CRITICAL(&s_battery_lock);
@@ -314,6 +328,8 @@ static void battery_worker(void *arg)
delay_ms = BATTERY_SUPPRESSED_RETRY_MS;
} else if (full_hint_candidate) {
delay_ms = BATTERY_FULL_CONFIRM_RETRY_MS;
} else if (battery_light_standby()) {
delay_ms = (uint32_t)CONFIG_TQ_LIGHT_STANDBY_BATTERY_INTERVAL_SEC * 1000U;
} else {
delay_ms = (uint32_t)CONFIG_TQ_BATTERY_SAMPLE_INTERVAL_SEC * 1000U;
}
@@ -340,6 +356,7 @@ esp_err_t control_plane_battery_service_start(void)
}
s_stop_requested = false;
s_starting = true;
s_light_standby = false;
portEXIT_CRITICAL(&s_battery_lock);
TaskHandle_t task = NULL;
@@ -406,6 +423,18 @@ esp_err_t control_plane_battery_service_stop(int timeout_ms)
return ESP_OK;
}
void control_plane_battery_service_set_light_standby(bool standby)
{
TaskHandle_t task;
portENTER_CRITICAL(&s_battery_lock);
s_light_standby = standby;
task = s_battery_task;
portEXIT_CRITICAL(&s_battery_lock);
if (task != NULL) {
xTaskNotifyGive(task);
}
}
#else
esp_err_t control_plane_battery_service_start(void)
@@ -418,4 +447,9 @@ esp_err_t control_plane_battery_service_stop(int timeout_ms)
return timeout_ms < 0 ? ESP_ERR_INVALID_ARG : ESP_OK;
}
void control_plane_battery_service_set_light_standby(bool standby)
{
(void)standby;
}
#endif
@@ -49,6 +49,22 @@
#define CONFIG_TQ_LIGHT_STANDBY_RELEASE_MOTOR_HOLD 0
#endif
#ifndef CONFIG_TQ_LIGHT_STANDBY_WIFI_POWER_SAVE
#define CONFIG_TQ_LIGHT_STANDBY_WIFI_POWER_SAVE 0
#endif
#ifndef CONFIG_TQ_LIGHT_STANDBY_PAUSE_REMOTE_LOG_UPLOAD
#define CONFIG_TQ_LIGHT_STANDBY_PAUSE_REMOTE_LOG_UPLOAD 0
#endif
#ifndef CONFIG_TQ_LIGHT_STANDBY_BATTERY_INTERVAL_SEC
#define CONFIG_TQ_LIGHT_STANDBY_BATTERY_INTERVAL_SEC 300
#endif
#ifndef CONFIG_TQ_LIGHT_STANDBY_MEMORY_SAMPLE_INTERVAL_MS
#define CONFIG_TQ_LIGHT_STANDBY_MEMORY_SAMPLE_INTERVAL_MS 10000
#endif
#ifndef CONFIG_TQ_SCREEN_BACKLIGHT_BRIGHTNESS_PERCENT
#define CONFIG_TQ_SCREEN_BACKLIGHT_BRIGHTNESS_PERCENT 100
#endif
@@ -179,9 +195,47 @@ static void control_plane_light_standby_unlock_transition(void)
}
}
static esp_err_t control_plane_light_standby_apply_runtime_policy(bool standby)
{
#if CONFIG_TQ_LIGHT_STANDBY_PAUSE_REMOTE_LOG_UPLOAD
control_plane_remote_log_set_light_standby(standby);
#endif
#if CONFIG_TQ_BATTERY_ENABLE
control_plane_battery_service_set_light_standby(standby);
#endif
#if CONFIG_TQ_MEMORY_MONITOR_ENABLE
control_plane_runtime_diagnostics_set_light_standby(standby);
#endif
#if CONFIG_TQ_LIGHT_STANDBY_WIFI_POWER_SAVE
if (control_plane_is_sta_connected()) {
esp_err_t wifi_err = domain_runtime_wifi_portal_set_light_standby(standby);
if (wifi_err != ESP_OK) {
ESP_LOGE(TAG,
"Set Wi-Fi light-standby policy=%d failed: %s",
standby ? 1 : 0,
esp_err_to_name(wifi_err));
if (standby) {
#if CONFIG_TQ_LIGHT_STANDBY_PAUSE_REMOTE_LOG_UPLOAD
control_plane_remote_log_set_light_standby(false);
#endif
#if CONFIG_TQ_BATTERY_ENABLE
control_plane_battery_service_set_light_standby(false);
#endif
#if CONFIG_TQ_MEMORY_MONITOR_ENABLE
control_plane_runtime_diagnostics_set_light_standby(false);
#endif
}
return wifi_err;
}
}
#endif
return ESP_OK;
}
static esp_err_t control_plane_light_standby_apply_wake_policy(void)
{
esp_err_t first_error = ESP_OK;
esp_err_t first_error = control_plane_light_standby_apply_runtime_policy(false);
#if CONFIG_TQ_LIGHT_STANDBY_DIM_BACKLIGHT
esp_err_t backlight_err = domain_runtime_set_lcd_backlight_brightness(
@@ -248,6 +302,18 @@ static esp_err_t control_plane_light_standby_apply_entry_policy(void)
}
#endif
esp_err_t runtime_err = control_plane_light_standby_apply_runtime_policy(true);
if (runtime_err != ESP_OK) {
#if CONFIG_TQ_LIGHT_STANDBY_DIM_BACKLIGHT
(void)domain_runtime_set_lcd_backlight_brightness(
CONFIG_TQ_SCREEN_BACKLIGHT_BRIGHTNESS_PERCENT);
#endif
#if CONFIG_TQ_LIGHT_STANDBY_DISABLE_SPEAKER_OUTPUT
(void)domain_runtime_multimodal_audio_set_speaker_output_enabled(true);
#endif
return runtime_err;
}
return ESP_OK;
}
@@ -78,6 +78,10 @@
#define CONFIG_TQ_NETWORK_TASK_CORE 0
#endif
#ifndef CONFIG_TQ_LIGHT_STANDBY_REMOTE_LOG_POLL_MS
#define CONFIG_TQ_LIGHT_STANDBY_REMOTE_LOG_POLL_MS 5000
#endif
#define REMOTE_LOG_TASK_STACK_SIZE (1024 * 8)
#define REMOTE_LOG_TASK_PRIORITY 3
#define REMOTE_LOG_DIR "/logfs/remote_logs"
@@ -106,6 +110,7 @@ static uint32_t s_dropped_count;
static uint32_t s_file_sequence;
static TaskHandle_t s_remote_log_task;
static volatile bool s_stop_requested;
static bool s_light_standby;
static portMUX_TYPE s_lock = portMUX_INITIALIZER_UNLOCKED;
static vprintf_like_t s_original_vprintf;
static bool s_hook_installed;
@@ -169,6 +174,31 @@ static bool remote_log_path_active(void)
return xTaskGetCurrentTaskHandle() == s_remote_log_task;
}
static bool remote_log_light_standby(void)
{
bool standby;
portENTER_CRITICAL(&s_lock);
standby = s_light_standby;
portEXIT_CRITICAL(&s_lock);
return standby;
}
static void remote_log_clear_task_handle(void)
{
portENTER_CRITICAL(&s_lock);
s_remote_log_task = NULL;
portEXIT_CRITICAL(&s_lock);
}
static void remote_log_notify_task(void)
{
portENTER_CRITICAL(&s_lock);
if (s_remote_log_task != NULL) {
xTaskNotifyGive(s_remote_log_task);
}
portEXIT_CRITICAL(&s_lock);
}
static char log_level_from_line(const char *line)
{
if (line == NULL || line[0] == '\0') {
@@ -1081,7 +1111,7 @@ static void remote_log_task(void *arg)
esp_err_t mount_err = domain_runtime_mount_logfs();
if (mount_err != ESP_OK) {
release_queue();
s_remote_log_task = NULL;
remote_log_clear_task_handle();
vTaskDelete(NULL);
return;
}
@@ -1099,12 +1129,14 @@ static void remote_log_task(void *arg)
while (!s_stop_requested) {
TickType_t now = xTaskGetTickCount();
bool light_standby = remote_log_light_standby();
if ((now - last_flush) >= pdMS_TO_TICKS(CONFIG_TQ_REMOTE_LOG_FLUSH_INTERVAL_MS)) {
drain_memory_queue_to_flash();
last_flush = now;
}
if (voice_startup_upload_gate_open(startup_tick) &&
if (!light_standby &&
voice_startup_upload_gate_open(startup_tick) &&
(backoff_ms == 0U || now >= next_upload_tick)) {
drain_memory_queue_to_flash();
(void)upload_coredump_report();
@@ -1120,12 +1152,15 @@ static void remote_log_task(void *arg)
}
}
vTaskDelay(pdMS_TO_TICKS(REMOTE_LOG_POLL_MS));
uint32_t poll_ms = light_standby ?
(uint32_t)CONFIG_TQ_LIGHT_STANDBY_REMOTE_LOG_POLL_MS :
REMOTE_LOG_POLL_MS;
(void)ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(poll_ms));
}
drain_memory_queue_to_flash();
uninstall_hook();
s_remote_log_task = NULL;
remote_log_clear_task_handle();
vTaskDelete(NULL);
}
@@ -1147,6 +1182,9 @@ esp_err_t control_plane_remote_log_start(void)
}
s_stop_requested = false;
portENTER_CRITICAL(&s_lock);
s_light_standby = false;
portEXIT_CRITICAL(&s_lock);
BaseType_t created = xTaskCreatePinnedToCore(remote_log_task,
"cp_remote_log",
REMOTE_LOG_TASK_STACK_SIZE,
@@ -1245,6 +1283,7 @@ void control_plane_remote_log_stop(int timeout_ms)
}
s_stop_requested = true;
remote_log_notify_task();
TickType_t start = xTaskGetTickCount();
TickType_t timeout_ticks = pdMS_TO_TICKS(timeout_ms > 0 ? timeout_ms : 1);
while (s_remote_log_task != NULL &&
@@ -1258,6 +1297,16 @@ void control_plane_remote_log_stop(int timeout_ms)
}
release_queue();
}
void control_plane_remote_log_set_light_standby(bool standby)
{
portENTER_CRITICAL(&s_lock);
s_light_standby = standby;
if (s_remote_log_task != NULL) {
xTaskNotifyGive(s_remote_log_task);
}
portEXIT_CRITICAL(&s_lock);
}
#else
esp_err_t control_plane_remote_log_start(void)
{
@@ -1269,6 +1318,11 @@ void control_plane_remote_log_stop(int timeout_ms)
(void)timeout_ms;
}
void control_plane_remote_log_set_light_standby(bool standby)
{
(void)standby;
}
esp_err_t control_plane_remote_log_flush_now(void)
{
return ESP_ERR_NOT_SUPPORTED;
@@ -102,6 +102,14 @@
#define CONFIG_TQ_MEMORY_MONITOR_DMA_LARGEST_WARN_BYTES 4096
#endif
#ifndef CONFIG_TQ_LIGHT_STANDBY_MEMORY_SAMPLE_INTERVAL_MS
#define CONFIG_TQ_LIGHT_STANDBY_MEMORY_SAMPLE_INTERVAL_MS 10000
#endif
#ifndef CONFIG_TQ_LIGHT_STANDBY_MEMORY_REPORT_INTERVAL_MS
#define CONFIG_TQ_LIGHT_STANDBY_MEMORY_REPORT_INTERVAL_MS 300000
#endif
#ifndef CONFIG_TQ_MULTIMODAL_DIAGNOSTICS_ENABLE
#define CONFIG_TQ_MULTIMODAL_DIAGNOSTICS_ENABLE 0
#endif
@@ -242,6 +250,7 @@ static uint32_t s_fortune_refresh_generation;
#endif
static volatile bool s_runtime_stop_requested;
static bool s_runtime_diagnostics_light_standby;
static portMUX_TYPE s_runtime_lock = portMUX_INITIALIZER_UNLOCKED;
#if CONFIG_TQ_MEMORY_MONITOR_ENABLE
@@ -303,8 +312,19 @@ static unsigned control_plane_heap_contiguous_percent(const multi_heap_info_t *i
static void control_plane_memory_monitor_sample(TickType_t now_tick, bool force)
{
TickType_t sample_ticks = pdMS_TO_TICKS(CONFIG_TQ_MEMORY_MONITOR_SAMPLE_INTERVAL_MS);
TickType_t report_ticks = pdMS_TO_TICKS(CONFIG_TQ_MEMORY_MONITOR_REPORT_INTERVAL_MS);
bool light_standby;
portENTER_CRITICAL(&s_runtime_lock);
light_standby = s_runtime_diagnostics_light_standby;
portEXIT_CRITICAL(&s_runtime_lock);
uint32_t sample_interval_ms = light_standby ?
(uint32_t)CONFIG_TQ_LIGHT_STANDBY_MEMORY_SAMPLE_INTERVAL_MS :
(uint32_t)CONFIG_TQ_MEMORY_MONITOR_SAMPLE_INTERVAL_MS;
uint32_t report_interval_ms = light_standby ?
(uint32_t)CONFIG_TQ_LIGHT_STANDBY_MEMORY_REPORT_INTERVAL_MS :
(uint32_t)CONFIG_TQ_MEMORY_MONITOR_REPORT_INTERVAL_MS;
TickType_t sample_ticks = pdMS_TO_TICKS(sample_interval_ms);
TickType_t report_ticks = pdMS_TO_TICKS(report_interval_ms);
if (sample_ticks == 0) {
sample_ticks = 1;
}
@@ -312,7 +332,10 @@ static void control_plane_memory_monitor_sample(TickType_t now_tick, bool force)
report_ticks = 1;
}
if (!force && s_memory_monitor.initialized &&
uint32_t alloc_fail_count = __atomic_load_n(&s_alloc_fail_count, __ATOMIC_ACQUIRE);
bool unreported_alloc_failure =
alloc_fail_count != s_memory_monitor.reported_alloc_fail_count;
if (!force && !unreported_alloc_failure && s_memory_monitor.initialized &&
(now_tick - s_memory_monitor.last_sample_tick) < sample_ticks) {
return;
}
@@ -325,7 +348,6 @@ static void control_plane_memory_monitor_sample(TickType_t now_tick, bool force)
heap_caps_get_info(&dma, MALLOC_CAP_DMA | MALLOC_CAP_8BIT);
heap_caps_get_info(&psram, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
uint32_t alloc_fail_count = __atomic_load_n(&s_alloc_fail_count, __ATOMIC_ACQUIRE);
bool alloc_failure_pending = alloc_fail_count != s_memory_monitor.reported_alloc_fail_count;
bool pressure = internal.total_free_bytes < CONFIG_TQ_MEMORY_MONITOR_INTERNAL_WARN_BYTES ||
dma.largest_free_block < CONFIG_TQ_MEMORY_MONITOR_DMA_LARGEST_WARN_BYTES;
@@ -439,6 +461,13 @@ static void control_plane_memory_monitor_sample(TickType_t now_tick, bool force)
}
#endif
void control_plane_runtime_diagnostics_set_light_standby(bool standby)
{
portENTER_CRITICAL(&s_runtime_lock);
s_runtime_diagnostics_light_standby = standby;
portEXIT_CRITICAL(&s_runtime_lock);
}
bool control_plane_runtime_is_stopping(void)
{
bool stop_requested = false;
+2
View File
@@ -565,6 +565,8 @@ esp_err_t domain_runtime_wifi_portal_connect_sta_with_credentials(
int timeout_ms);
esp_err_t domain_runtime_wifi_portal_reconnect_sta(int timeout_ms);
esp_err_t domain_runtime_wifi_portal_stop_sta(void);
/** Apply standby Wi-Fi power save, or restore the configured active policy. */
esp_err_t domain_runtime_wifi_portal_set_light_standby(bool standby);
bool domain_runtime_is_network_connect_error(esp_err_t err);
esp_err_t domain_runtime_wifi_portal_save_credentials(const char *ssid, const char *password);
esp_err_t domain_runtime_wifi_portal_clear_saved_credentials(void);
@@ -1319,6 +1319,11 @@ esp_err_t domain_runtime_wifi_portal_stop_sta(void)
return platform_wifi_portal_stop_sta();
}
esp_err_t domain_runtime_wifi_portal_set_light_standby(bool standby)
{
return platform_wifi_portal_set_light_standby(standby);
}
bool domain_runtime_is_network_connect_error(esp_err_t err)
{
return platform_https_is_connect_error(err);
+2
View File
@@ -345,6 +345,8 @@ void platform_wifi_portal_set_behavior_hooks(const platform_wifi_behavior_hooks_
esp_err_t platform_wifi_portal_init_defaults(const char *seed_ssid, const char *seed_password);
bool platform_wifi_portal_has_saved_credentials(void);
bool platform_wifi_portal_sta_is_connected(void);
/** Apply standby modem power save, or restore the configured active policy. */
esp_err_t platform_wifi_portal_set_light_standby(bool standby);
esp_err_t platform_wifi_portal_start_config_ap(const char *ssid_prefix);
/**
@@ -14,6 +14,11 @@
#include "wifi_portal.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "sdkconfig.h"
#ifndef CONFIG_TQ_WIFI_STA_POWER_SAVE
#define CONFIG_TQ_WIFI_STA_POWER_SAVE 0
#endif
static platform_wifi_behavior_hooks_t s_wifi_behavior_hooks;
static SemaphoreHandle_t s_wifi_behavior_hooks_mutex;
@@ -128,6 +133,13 @@ bool platform_wifi_portal_sta_is_connected(void)
return wifi_portal_sta_is_connected();
}
esp_err_t platform_wifi_portal_set_light_standby(bool standby)
{
wifi_ps_type_t ps_type = standby ? WIFI_PS_MIN_MODEM :
(CONFIG_TQ_WIFI_STA_POWER_SAVE ? WIFI_PS_MIN_MODEM : WIFI_PS_NONE);
return esp_wifi_set_ps(ps_type);
}
esp_err_t platform_wifi_portal_start_config_ap(const char *ssid_prefix)
{
return wifi_portal_start_config_ap(ssid_prefix);
+42
View File
@@ -372,6 +372,48 @@ menu "Example Configuration"
De-energize the stepper coils. A later movement automatically
restores motor drive before stepping.
config TQ_LIGHT_STANDBY_WIFI_POWER_SAVE
bool "Enable Wi-Fi modem power save in light standby"
default y
depends on TQ_LIGHT_STANDBY_ENABLE
help
Keep the STA and WebSocket connected while enabling minimum
modem sleep. Active mode restores the configured Wi-Fi policy.
config TQ_LIGHT_STANDBY_PAUSE_REMOTE_LOG_UPLOAD
bool "Pause remote log uploads in light standby"
default y
depends on TQ_LIGHT_STANDBY_ENABLE && TQ_REMOTE_LOG_ENABLE
help
Continue capturing and persisting logs, but defer HTTP
uploads until activity wakes the device.
config TQ_LIGHT_STANDBY_REMOTE_LOG_POLL_MS
int "Remote log standby poll interval (ms)"
default 5000
range 1000 60000
depends on TQ_LIGHT_STANDBY_PAUSE_REMOTE_LOG_UPLOAD
config TQ_LIGHT_STANDBY_BATTERY_INTERVAL_SEC
int "Battery standby sample interval (seconds)"
default 300
range 30 3600
depends on TQ_LIGHT_STANDBY_ENABLE && TQ_BATTERY_ENABLE
help
Charge-state GPIO edges still request an immediate sample.
config TQ_LIGHT_STANDBY_MEMORY_SAMPLE_INTERVAL_MS
int "Memory monitor standby sample interval (ms)"
default 10000
range 1000 600000
depends on TQ_LIGHT_STANDBY_ENABLE && TQ_MEMORY_MONITOR_ENABLE
config TQ_LIGHT_STANDBY_MEMORY_REPORT_INTERVAL_MS
int "Memory monitor standby report interval (ms)"
default 300000
range 10000 3600000
depends on TQ_LIGHT_STANDBY_ENABLE && TQ_MEMORY_MONITOR_ENABLE
config TQ_LIGHT_STANDBY_TASK_STACK_BYTES
int "Light-standby task stack size (bytes)"
default 4096
+6
View File
@@ -56,6 +56,12 @@ CONFIG_TQ_LIGHT_STANDBY_DIM_BACKLIGHT=y
CONFIG_TQ_LIGHT_STANDBY_BACKLIGHT_PERCENT=10
CONFIG_TQ_LIGHT_STANDBY_DISABLE_SPEAKER_OUTPUT=y
CONFIG_TQ_LIGHT_STANDBY_RELEASE_MOTOR_HOLD=y
CONFIG_TQ_LIGHT_STANDBY_WIFI_POWER_SAVE=y
CONFIG_TQ_LIGHT_STANDBY_PAUSE_REMOTE_LOG_UPLOAD=y
CONFIG_TQ_LIGHT_STANDBY_REMOTE_LOG_POLL_MS=5000
CONFIG_TQ_LIGHT_STANDBY_BATTERY_INTERVAL_SEC=300
CONFIG_TQ_LIGHT_STANDBY_MEMORY_SAMPLE_INTERVAL_MS=10000
CONFIG_TQ_LIGHT_STANDBY_MEMORY_REPORT_INTERVAL_MS=300000
CONFIG_TQ_LIGHT_STANDBY_TASK_STACK_BYTES=4096
CONFIG_TQ_LIGHT_STANDBY_TASK_PRIORITY=3
CONFIG_TQ_REMOTE_LOG_ENABLE=y