Files
lucky-cat/docs/light-standby-power-optimization.zh-CN.md
T

656 lines
24 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# TQ CatN 轻待机低功耗优化说明
## 1. 文档范围
本文整理以下五个低功耗相关提交的设计和最终实现:
| 提交 | 内容 |
| --- | --- |
| `988dc92` | 增加 LCD 背光 PWM 亮度控制 |
| `6a11aca` | 增加轻待机状态协调器和活动事件接入 |
| `baad9dc` | 增加背光、扬声器功放和电机的待机策略 |
| `e773ebc` | 增加 Wi-Fi、电池检测和内存监控的待机策略(远程日志部分现已移除) |
| `cb203a7` | 进入轻待机前播放提示音,并等待真实播放完成 |
`bba99a0 fix(control-plane): trigger PTT on short press` 是独立的按键行为修复,不属于低功耗改动,因此不在本文范围内。
## 2. 需求与问题原因
设备需要在 5 分钟无交互后降低功耗,同时满足以下约束:
1. 基础表情必须持续显示。
2. 唤醒词检测必须持续工作。
3. 按键和 SW2 抽签操作必须能够快速唤醒。
4. 进入待机前必须完整播放 `standby_5min.opus`。
这些约束决定了设备不能直接进入 ESP Light Sleep 或 Deep Sleep:
- 基础表情当前由 LCD 渲染任务循环播放,需要 CPU、LCD 控制器和 SPI 链路继续工作。
- 唤醒词依赖麦克风采集、I2S RX、AFE 和本地识别任务持续运行。
- 设备还需要及时响应按键、抽签和业务事件。
因此,本次实现的是“轻待机”,即系统仍保持运行,只关闭或降低非必要硬件和周期任务的功耗。优化前存在以下主要问题:
- LCD 背光只有开关控制,无法在保留画面的同时降低背光功耗。
- 扬声器功放在无音频播放时仍可能保持供电。
- 步进电机完成动作后可能继续保持线圈电流。
- Wi-Fi 始终使用活跃策略。
- 电池检测和内存监控仍按活跃周期运行。
- 缺少统一的待机状态、空闲计时、唤醒入口和失败回滚机制。
- 如果进入待机时立即关闭功放,待机提示音将无法完整播放。
## 3. 总体解决方式
系统增加统一的轻待机状态协调器,状态变化如下:
```text
ACTIVE
| 5 分钟无交互,且当前业务允许待机
v
ENTERING
| 播放 standby_5min.opus,并等待真实播放完成
| 再依次应用硬件和运行时低功耗策略
v
STANDBY
| 唤醒词、按键、抽签、模型命令或语音活动
v
WAKING
| 恢复背光、功放、Wi-Fi 和周期任务策略
v
ACTIVE
```
进入待机前还会检查远端回复、用户讲话、待发送请求、配网等运行状态。播放期间如果出现新活动,或提示音播放失败、停止、卡死、被其他播放抢占,本次进入待机将被取消。应用低功耗策略中途失败时,已修改的策略会尽量恢复,避免设备停留在半待机状态。
## 4. 默认配置
当前默认配置如下,均可通过 `menuconfig` 或 `sdkconfig.defaults` 调整:
```ini
CONFIG_TQ_SCREEN_BACKLIGHT_BRIGHTNESS_PERCENT=100
CONFIG_TQ_LIGHT_STANDBY_ENABLE=y
CONFIG_TQ_LIGHT_STANDBY_IDLE_TIMEOUT_MS=300000
CONFIG_TQ_LIGHT_STANDBY_DIM_BACKLIGHT=y
CONFIG_TQ_LIGHT_STANDBY_BACKLIGHT_PERCENT=10
CONFIG_TQ_LIGHT_STANDBY_PROMPT_ENABLE=y
CONFIG_TQ_LIGHT_STANDBY_PROMPT_FILE="audio_prompts/standby_5min.opus"
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_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
```
其中:
- 活跃状态背光默认 100%。
- 轻待机背光默认 10%,仍保持基础表情可见。
- 空闲超时默认 300000 ms,即 5 分钟。
- 协调器任务栈为 4096 字节。原 3072 字节配置在日志压力场景下出现过 `cp_light_standby` 栈溢出,增大后解决该问题。
## 5. 各项优化及具体代码
### 5.1 LCD 背光 PWM
#### 问题原因
背光 GPIO 原先只能输出固定电平。基础表情必须保留,因此不能直接关闭 LCD;若背光一直保持全亮,LCD 背光会成为待机阶段的主要持续耗电项之一。
#### 解决方式
使用 ESP-IDF LEDC 输出 20 kHz、10 位 PWM,并兼容背光高电平有效和低电平有效。提供运行时亮度接口,活跃状态使用正常亮度,待机状态切换为配置的 10%。
#### 具体代码
```c
#define LCD_DIRECT_BACKLIGHT_PWM_FREQUENCY_HZ 20000U
#define LCD_DIRECT_BACKLIGHT_PWM_DUTY_RESOLUTION LEDC_TIMER_10_BIT
#define LCD_DIRECT_BACKLIGHT_PWM_DUTY_MAX ((1U << 10U) - 1U)
#define LCD_DIRECT_BACKLIGHT_PWM_TIMER LEDC_TIMER_0
#define LCD_DIRECT_BACKLIGHT_PWM_CHANNEL LEDC_CHANNEL_0
static uint32_t lcd_direct_backlight_duty(uint8_t brightness_percent)
{
uint32_t duty = ((uint32_t)brightness_percent *
LCD_DIRECT_BACKLIGHT_PWM_DUTY_MAX + 50U) / 100U;
#if !CONFIG_TQ_SCREEN_BACKLIGHT_ACTIVE_HIGH
duty = LCD_DIRECT_BACKLIGHT_PWM_DUTY_MAX - duty;
#endif
return duty;
}
static esp_err_t lcd_direct_backlight_init(void)
{
const ledc_timer_config_t timer_config = {
.speed_mode = LEDC_LOW_SPEED_MODE,
.duty_resolution = LCD_DIRECT_BACKLIGHT_PWM_DUTY_RESOLUTION,
.timer_num = LCD_DIRECT_BACKLIGHT_PWM_TIMER,
.freq_hz = LCD_DIRECT_BACKLIGHT_PWM_FREQUENCY_HZ,
.clk_cfg = LEDC_AUTO_CLK,
};
ESP_RETURN_ON_ERROR(ledc_timer_config(&timer_config), TAG,
"backlight PWM timer config failed");
const ledc_channel_config_t channel_config = {
.gpio_num = CONFIG_TQ_SCREEN_BACKLIGHT_GPIO,
.speed_mode = LEDC_LOW_SPEED_MODE,
.channel = LCD_DIRECT_BACKLIGHT_PWM_CHANNEL,
.intr_type = LEDC_INTR_DISABLE,
.timer_sel = LCD_DIRECT_BACKLIGHT_PWM_TIMER,
.duty = lcd_direct_backlight_duty(
CONFIG_TQ_SCREEN_BACKLIGHT_BRIGHTNESS_PERCENT),
.hpoint = 0,
};
return ledc_channel_config(&channel_config);
}
esp_err_t lcd_direct_set_backlight_brightness(uint8_t brightness_percent)
{
ESP_RETURN_ON_FALSE(brightness_percent <= 100U,
ESP_ERR_INVALID_ARG, TAG,
"backlight brightness must be between 0 and 100 percent");
esp_err_t err = ledc_set_duty(
LEDC_LOW_SPEED_MODE,
LCD_DIRECT_BACKLIGHT_PWM_CHANNEL,
lcd_direct_backlight_duty(brightness_percent));
if (err == ESP_OK) {
err = ledc_update_duty(
LEDC_LOW_SPEED_MODE,
LCD_DIRECT_BACKLIGHT_PWM_CHANNEL);
}
return err;
}
```
#### 功耗与影响
- 降低 LED 背光平均电流,是本方案中最直接的节电项。
- LCD 控制器、SPI 传输和基础表情渲染仍在运行,因此并未消除显示链路功耗。
- 亮度百分比与实际电流通常不是严格线性关系,需要真机测量后确定最佳值。
### 5.2 扬声器功放与音频输出链路
#### 问题原因
待机期间不需要声音输出,但仍要保留麦克风和唤醒词。直接关闭整套音频设备会破坏快速语音唤醒;只停止播放又不能保证外部功放已断电。
#### 解决方式
待机时先将 Codec 下行输出静音,再关闭 PA GPIO。唤醒时重新打开 PA。麦克风采集、I2S RX、AFE 和唤醒词保持运行。
#### 具体代码
```c
esp_err_t multimodal_audio_set_speaker_output_enabled(bool enabled)
{
multimodal_audio_ctx_t *ctx = &s_audio;
if (!ctx->initialized || ctx->codec_dev == NULL ||
ctx->gpio_if == NULL || ctx->gpio_if->set == NULL) {
return ESP_ERR_INVALID_STATE;
}
if (!enabled) {
esp_err_t mute_err = set_downstream_output_mute_if_needed(
ctx, true, "Light standby");
if (mute_err != ESP_OK) {
return mute_err;
}
}
int ret = ctx->gpio_if->set(CONFIG_MULTIMODAL_AUDIO_PA_GPIO, enabled);
if (ret != ESP_CODEC_DEV_OK) {
return (esp_err_t)ret;
}
return ESP_OK;
}
```
#### 功耗与影响
- 降低外部扬声器功放的静态功耗。
- 不关闭音频输入侧,因此语音唤醒能力不受该策略影响。
- I2S TX、解码和下行缓冲相关任务未完全停用,仍有进一步优化空间。
- 唤醒后 PA 恢复存在很短的硬件稳定时间,首段声音需要在真机上确认是否完整。
### 5.3 待机提示音完整播放
#### 问题原因
如果空闲时间到达后立即关闭功放,`standby_5min.opus` 无法播放。仅按音频时长固定延时也不可靠,因为缓冲、解码、播放被打断或管线卡死都会使预计时长与真实播放状态不一致。
#### 解决方式
提示音发送完成后,以本次播放的 generation 为标识等待真实终止事件:
- `PLAYBACK_COMPLETED`:允许继续进入待机。
- `PLAYBACK_STOPPED`:取消进入待机。
- `PLAYBACK_STALLED` 或等待超时:取消进入待机。
- 播放期间出现新用户活动:提示音结束后重新检查活动时间,取消进入待机。
超时时间只作为故障保护上限,不是用来代替播放完成事件的固定等待。
#### 具体代码
```c
esp_err_t voice_prompt_play_file(const char *file_name)
{
/* 省略文件读取和 Opus 包发送过程。 */
esp_err_t finish_err = multimodal_audio_finish_downstream_response();
if (err == ESP_OK) {
err = finish_err;
}
if (err == ESP_OK) {
uint64_t timeout_ms =
(uint64_t)packet_count * VOICE_PROMPT_OPUS_FRAME_MS +
VOICE_PROMPT_COMPLETION_TIMEOUT_MARGIN_MS;
if (timeout_ms > INT32_MAX) {
timeout_ms = INT32_MAX;
}
err = multimodal_audio_wait_downstream_playback_complete(
playback_generation,
(int)timeout_ms);
}
return err;
}
static esp_err_t downstream_terminal_type_to_error(
multimodal_audio_downstream_playback_event_type_t type)
{
switch (type) {
case MULTIMODAL_AUDIO_DOWNSTREAM_PLAYBACK_COMPLETED:
return ESP_OK;
case MULTIMODAL_AUDIO_DOWNSTREAM_PLAYBACK_STALLED:
return ESP_ERR_TIMEOUT;
case MULTIMODAL_AUDIO_DOWNSTREAM_PLAYBACK_STOPPED:
default:
return ESP_ERR_INVALID_STATE;
}
}
```
待机协调器按“先播报、后关功放”的顺序执行:
```c
esp_err_t policy_err = control_plane_light_standby_play_entry_prompt();
bool entry_current = policy_err == ESP_OK &&
control_plane_light_standby_entry_is_current(entry_activity_ms);
bool runtime_eligible = entry_current &&
control_plane_light_standby_runtime_is_eligible();
if (runtime_eligible) {
policy_applied = true;
policy_err = control_plane_light_standby_apply_entry_policy();
}
```
#### 功耗与影响
- 提示音播放期间仍保持正常音频输出功耗。
- 实际进入轻待机的时间为“5 分钟空闲时间 + 提示音实际播放时间”。
- 播放异常时宁可取消本次待机,也不会在提示音未完成时关闭功放。
### 5.4 电机驱动
#### 问题原因
步进电机停止移动后,如果线圈仍保持励磁,会持续产生保持电流。轻待机期间没有必要维持该静态扭矩。
#### 解决方式
进入待机时获取运动互斥锁并释放线圈;后续运动命令会按原有流程重新驱动电机。
#### 具体代码
```c
esp_err_t platform_stepper_driver_release_hold(int timeout_ms)
{
if (timeout_ms < 0) {
return ESP_ERR_INVALID_ARG;
}
if (!ensure_stepper_motion_mutex()) {
return ESP_ERR_INVALID_STATE;
}
TickType_t lock_ticks = pdMS_TO_TICKS(timeout_ms);
if (timeout_ms > 0 && lock_ticks == 0) {
lock_ticks = 1;
}
if (xSemaphoreTake(s_stepper_motion_mutex, lock_ticks) != pdTRUE) {
return ESP_ERR_TIMEOUT;
}
portENTER_CRITICAL(&s_stepper_mux);
stepper_release_locked();
portEXIT_CRITICAL(&s_stepper_mux);
xSemaphoreGive(s_stepper_motion_mutex);
return ESP_OK;
}
```
#### 功耗与影响
- 消除电机线圈的待机保持电流。
- 电机不再提供静态保持扭矩,外力可能改变机构位置。
- 唤醒本身不重新励磁;下一次实际运动时才恢复驱动,避免无意义耗电。
### 5.5 Wi-Fi 省电
#### 问题原因
设备需要保留网络连接,但活跃 Wi-Fi 策略会使射频和基带更频繁工作。
#### 解决方式
进入待机后切换为 `WIFI_PS_MIN_MODEM`,保留 STA 和 WebSocket 连接。唤醒后恢复项目配置的正常策略;当前默认正常策略为 `WIFI_PS_NONE`。
#### 具体代码
```c
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);
}
```
#### 功耗与影响
- 保持联网能力的同时降低 Wi-Fi 平均功耗。
- 可能增加网络首包延迟,实际幅度受 AP 的 DTIM、信号质量和网络负载影响。
- 不等同于关闭 Wi-Fi,网络任务和 WebSocket 仍会消耗资源。
### 5.7 电池检测
#### 问题原因
电池 ADC 和状态处理在待机期间没有必要保持活跃状态下的采样频率。
#### 解决方式
普通采样周期在轻待机时切换为 300 秒。充电状态 GPIO 中断仍可立即唤醒电池检测任务并进行防抖后的新采样,满电确认和测量受限时的重试周期也保持原有优先级。
#### 具体代码
```c
uint32_t delay_ms;
if (status_err == ESP_OK && status.measurement_suppressed) {
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;
}
uint32_t notifications = ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(delay_ms));
if (notifications > 0U && !battery_stop_requested()) {
vTaskDelay(pdMS_TO_TICKS(BATTERY_CHARGE_DEBOUNCE_MS));
ulTaskNotifyTake(pdTRUE, 0U);
}
```
#### 功耗与影响
- 减少 ADC 采样、状态计算和相关日志频率。
- 普通电量百分比更新变慢,最长可能接近配置的 300 秒。
- 插拔充电等 GPIO 边沿仍会触发及时检测,不受普通采样周期影响。
### 5.8 内存监控
#### 问题原因
内存监控在活跃状态下每 1 秒采样、每 30 秒报告。待机期间这种诊断频率会造成不必要的 CPU 唤醒和日志输出。
#### 解决方式
待机时改为每 10 秒采样、每 300 秒周期报告。内存分配失败仍会绕过普通采样周期并立即处理。
#### 具体代码
```c
uint32_t sample_interval_ms = light_standby ?
CONFIG_TQ_LIGHT_STANDBY_MEMORY_SAMPLE_INTERVAL_MS :
CONFIG_TQ_MEMORY_MONITOR_SAMPLE_INTERVAL_MS;
uint32_t report_interval_ms = light_standby ?
CONFIG_TQ_LIGHT_STANDBY_MEMORY_REPORT_INTERVAL_MS :
CONFIG_TQ_MEMORY_MONITOR_REPORT_INTERVAL_MS;
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;
}
```
#### 功耗与影响
- 减少堆信息采集和周期日志带来的 CPU 开销。
- 常规内存水位变化的可见时间变长。
- 分配失败仍立即上报,关键故障检测没有被普通周期限制。
### 5.9 BLE 广播
#### 当前处理
本次没有增加轻待机下的 BLE 广播切换逻辑。项目的 BLE 广播只在小程序配网期间使用,而配网期间轻待机资格检查会直接返回不允许进入待机:
```c
static bool control_plane_light_standby_runtime_is_eligible(void)
{
if (control_plane_is_reset_in_progress() ||
control_plane_is_mini_program_provisioning_running()) {
return false;
}
/* 继续检查语音会话和远端回复状态。 */
return true;
}
```
#### 功耗与影响
- 正常业务状态没有持续 BLE 广播,因此没有可应用的待机节电策略。
- 配网状态优先保证 BLE 配网可用性,不进入轻待机。
- 如果后续增加常驻 BLE 广播,需要重新设计待机广播间隔和连接参数。
## 6. 统一进入、回滚和唤醒策略
### 6.1 进入策略
进入顺序为:释放电机保持电流、关闭扬声器输出、降低背光、应用运行时策略。任一步骤失败都会阻止状态进入 `STANDBY`,必要时恢复此前已修改的项目。
```c
static esp_err_t control_plane_light_standby_apply_entry_policy(void)
{
#if CONFIG_TQ_LIGHT_STANDBY_RELEASE_MOTOR_HOLD
ESP_RETURN_ON_ERROR(
domain_direction_release_motor_hold(MOTOR_RELEASE_WAIT_MS),
TAG, "release motor hold failed");
#endif
#if CONFIG_TQ_LIGHT_STANDBY_DISABLE_SPEAKER_OUTPUT
ESP_RETURN_ON_ERROR(
domain_runtime_multimodal_audio_set_speaker_output_enabled(false),
TAG, "disable speaker output failed");
#endif
#if CONFIG_TQ_LIGHT_STANDBY_DIM_BACKLIGHT
esp_err_t backlight_err = domain_runtime_set_lcd_backlight_brightness(
CONFIG_TQ_LIGHT_STANDBY_BACKLIGHT_PERCENT);
if (backlight_err != ESP_OK) {
domain_runtime_multimodal_audio_set_speaker_output_enabled(true);
return backlight_err;
}
#endif
esp_err_t runtime_err =
control_plane_light_standby_apply_runtime_policy(true);
if (runtime_err != ESP_OK) {
domain_runtime_set_lcd_backlight_brightness(
CONFIG_TQ_SCREEN_BACKLIGHT_BRIGHTNESS_PERCENT);
domain_runtime_multimodal_audio_set_speaker_output_enabled(true);
}
return runtime_err;
}
```
以上代码为便于阅读的等价摘录,实际代码还包含每个配置项的条件编译和详细错误日志。
### 6.2 运行时策略
```c
static esp_err_t control_plane_light_standby_apply_runtime_policy(bool standby)
{
control_plane_battery_service_set_light_standby(standby);
control_plane_runtime_diagnostics_set_light_standby(standby);
if (control_plane_is_sta_connected()) {
esp_err_t wifi_err =
domain_runtime_wifi_portal_set_light_standby(standby);
if (wifi_err != ESP_OK && standby) {
control_plane_battery_service_set_light_standby(false);
control_plane_runtime_diagnostics_set_light_standby(false);
return wifi_err;
}
}
return ESP_OK;
}
```
### 6.3 唤醒来源
当前活动类型包括:
```c
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;
```
- 唤醒词对应 `LOCAL_WAKE`。
- 普通按键和 SW2 抽签按键对应 `KEY`。
- 麦克风语音活动对应 `VOICE_INPUT`。
- 远端回复对应 `VOICE_OUTPUT`。
- 本地识别的抽签、方向等命令对应 `MODEL_COMMAND`。
记录活动时会立即刷新空闲计时。如果已经处于 `STANDBY`,状态切换为 `WAKING`,恢复运行时策略、正常背光和扬声器功放后返回 `ACTIVE`。
```c
esp_err_t control_plane_light_standby_record_activity(
control_plane_light_standby_activity_t activity)
{
uint64_t now_ms = control_plane_light_standby_now_ms();
portENTER_CRITICAL(&s_standby_lock);
s_standby.last_activity = activity;
s_standby.last_activity_ms = now_ms;
bool wake_required =
s_standby.state == CONTROL_PLANE_LIGHT_STANDBY_STATE_STANDBY;
portEXIT_CRITICAL(&s_standby_lock);
if (!wake_required) {
return ESP_OK;
}
s_standby.state = CONTROL_PLANE_LIGHT_STANDBY_STATE_WAKING;
esp_err_t wake_err = control_plane_light_standby_apply_wake_policy();
s_standby.state = CONTROL_PLANE_LIGHT_STANDBY_STATE_ACTIVE;
return wake_err;
}
```
以上为突出主流程的等价摘录;实际实现使用临界区和转换互斥锁保护状态,避免进入待机和唤醒同时修改硬件策略。
## 7. 解决成果
本轮改动已经实现:
1. 5 分钟无交互后自动进入轻待机。
2. 基础表情保持显示,待机背光默认降至 10%。
3. 进入待机前完整播放 `standby_5min.opus`,等待真实播放完成事件后才关闭功放。
4. 待机时 Codec 输出静音并关闭扬声器 PA,同时保留麦克风、AFE 和唤醒词。
5. 释放步进电机线圈保持电流。
6. Wi-Fi 切换为最小 Modem 省电,同时保留连接。
8. 电池常规采样周期放宽到 300 秒,充电状态边沿仍及时处理。
9. 内存监控采样放宽到 10 秒、周期报告放宽到 300 秒,分配失败仍及时处理。
10. 支持唤醒词、按键、SW2 抽签、模型命令和语音活动快速唤醒。
11. 唤醒后恢复正常背光、功放、Wi-Fi、电池和内存监控策略。
12. 增加进入失败回滚和状态竞态保护。
13. 将轻待机任务栈增至 4096 字节,解决已观察到的任务栈溢出。
## 8. 验证情况
已完成的软件侧验证:
- ESP-IDF 5.5.4 完整构建通过。
- 当前构建生成的应用固件约 2.08 MiB,应用分区剩余约 41%。
- `audio_prompts/standby_5min.opus` 已纳入应用镜像中的音频素材包。
- 源码差异格式检查通过。
- 已确认提示音等待的是播放管线终止事件,而不是固定时长硬等待。
当前尚未完成以下真机量化测试:
- 活跃状态和轻待机状态的整机电流对比。
- 各单项策略分别节省的电流。
- 不同 AP/DTIM 条件下的网络首包延迟。
- 唤醒词、按键和抽签从触发到完全恢复的耗时。
- PA 恢复后第一段声音是否存在削头或延迟。
因此,目前只能确认节电机制已生效,不能在没有仪器数据的情况下声称降低了具体多少 mA 或百分比。
## 9. 后续影响与剩余优化空间
### 9.1 明确的行为影响
| 项目 | 后续影响 |
| --- | --- |
| LCD | 画面继续刷新,背光变暗,但 LCD 控制器、SPI 和渲染功耗仍存在 |
| 扬声器 | 待机无声音输出;唤醒后 PA 需要短暂恢复时间 |
| 麦克风 | 保持运行,以满足唤醒词快速唤醒要求 |
| 电机 | 待机不保持静态扭矩,机构可能被外力移动 |
| Wi-Fi | 保持连接,但首包延迟可能增加 |
| 电池检测 | 普通电量更新变慢,充电状态边沿仍及时检测 |
| 内存监控 | 普通水位报告变慢,分配失败仍及时处理 |
| 提示音 | 真正进入待机的时间会比 5 分钟多出提示音播放时间 |
| BLE | 配网期间禁止进入轻待机,因此本次不修改 BLE 广播 |
### 9.2 仍可继续优化的项目
在“不关闭基础表情”和“不关闭唤醒词”的约束下,仍有以下优化空间:
1. 避免 LCD 重复传输完全相同的画面。
2. 轻待机时降低基础表情动画帧率,或停在静态基础帧。
3. 在确保提示音和唤醒后首包完整的前提下,进一步暂停 I2S TX、解码器和下行音频任务。
4. 继续检查任务监控、网络保活、传感器和其他周期诊断任务的唤醒频率。
5. 通过功耗仪逐项关闭策略,建立各模块的实际 mA 收益表,再决定优化优先级。
受持续显示和持续唤醒词约束,ESP Light Sleep/Deep Sleep 目前不适合直接使用。若未来允许基础表情静止、LCD 使用自刷新能力,或增加独立低功耗唤醒芯片,才有机会进入更深的睡眠状态并获得数量级更高的节电收益。
## 10. 结论
本次改动已经完成当前产品约束下的主要轻待机优化:保留基础表情、唤醒词和按键响应,同时降低背光、功放、电机、Wi-Fi、电池检测和内存监控的持续功耗。实现具备统一状态管理、真实音频完成等待、进入失败回滚和快速唤醒恢复。
下一步重点不是继续盲目关闭模块,而是进行真机电流和唤醒延迟测试。测试数据可以确认主要耗电来源,并判断 LCD 刷新和音频下行链路是否值得进入下一轮优化。