audio_power_save

Example of the component espressif/adf_examples v0.2.1
# 音频低功耗与唤醒

- [English Version](./README.md)
- 例程难度:⭐⭐

## 例程简介

本例程演示设备在 Wi-Fi 联网与 MQTT keepalive 保活下的自动 light sleep 低功耗流程,以及 UART、MQTT、GPIO、定时器等多种唤醒源的处理;进入 idle 前与唤醒后可播放提示音。

- 连接 Wi-Fi 后,MQTT 在 Auto light sleep 模式下维持保活
- 支持 UART、MQTT、GPIO、定时器等多种唤醒方式
- 例程执行两轮「休眠—唤醒」验证,每轮唤醒后恢复运行并继续下一轮

### 典型场景

本例程适用于空闲时需保持联网、并支持本地或远程唤醒的语音/音频类设备。

### 提示音资源

提示音文件存放在 `tone/` 目录,编译时通过 `littlefs_create_partition_image()` 打包进名为 `storage` 的 LittleFS 分区,挂载路径为 `/littlefs`。如需更换提示音,替换 `tone/` 目录中的文件后重新编译烧录即可。

## 环境配置

### 硬件要求

- 开启提示音播放时需 Audio DAC 和扬声器;关闭 `Enable prompt tone playback` 时可仅验证低功耗与唤醒
- 可连接到 MQTT broker 的 Wi-Fi 网络
- 可通过串口输入触发 UART 唤醒;如启用 GPIO 唤醒,需连接可控 GPIO 输入

### 默认 IDF 分支

本例程支持 IDF release/v5.4 (>= v5.4.3) 与 release/v5.5 (>= v5.5.2) 分支。

### 软件要求

- 默认 MQTT broker:`mqtt://broker.emqx.io`
- 默认 MQTT 唤醒 topic:`/gmf/audio_power_save/wakeup`

## 编译和下载

### 编译准备

编译本例程前需先确保已配置 ESP-IDF 环境;若已配置可跳过本段,直接进入工程目录。若未配置,请在 ESP-IDF 根目录运行以下脚本完成环境设置,完整步骤请参阅 [《ESP-IDF 编程指南》](https://docs.espressif.com/projects/esp-idf/zh_CN/latest/esp32s3/index.html)。

```
./install.sh
. ./export.sh
```

下面是简略步骤:

- 进入本例程工程目录:

```
cd adf_examples/system/audio_power_save
```

本示例使用 [ESP Board Manager](https://github.com/espressif/esp-board-manager) 管理板级资源。安装辅助工具 [`esp-bmgr-assist`](https://pypi.org/project/esp-bmgr-assist/) 后,可通过下文命令选择开发板。

- 在已激活的 ESP-IDF Python 环境下安装(同一环境只需安装一次):

```bash
pip install esp-bmgr-assist
pip install --upgrade esp-bmgr-assist
```

- 查看支持的板子:

```bash
idf.py bmgr -l
```

  输出示例:

```text
ℹ️  Main Boards:
  [1] dual_eyes_board_v1_0
  [2] esp32_c3_lyra
  [3] esp32_c5_spot
  [4] esp32_p4_function_ev
  [5] esp32_s3_korvo2_v3
  [6] esp32_s3_korvo2l
  [7] esp_box_3
  [8] esp_box_lite
  [9] esp_hi
```

- 选择开发板:

```bash
idf.py bmgr -b <board_index|board_name>
```

  本例程使用 `esp32_s3_korvo2_v3`:

```bash
idf.py bmgr -b 5
# 或
idf.py bmgr -b esp32_s3_korvo2_v3
```

  首次执行 `idf.py bmgr` 时,组件会根据本工程 `main/idf_component.yml` 中声明的 `espressif/esp_board_manager` 依赖自动下载。

> [!NOTE]
> 如果切换为其他 `esp_board_manager` 支持的开发板,请按相同步骤执行并替换板型名称/索引。
> 自定义开发板请参考 [自定义开发板指南](https://github.com/espressif/esp-board-manager/blob/main/esp_board_manager/docs/how_to_customize_board_cn.md)。
> `esp_board_manager` 更多信息请参考 [ESP_BOARD_MANAGER 入门指南](https://github.com/espressif/esp-board-manager/blob/main/esp_board_manager/README_CN.md)。

### 项目配置

```bash
idf.py menuconfig
```

在 menuconfig 中进行以下配置:

- `Wi-Fi Configuration` → `WiFi SSID`
- `Wi-Fi Configuration` → `WiFi Password`
- `MQTT Configuration` → `MQTT broker URI`
- `MQTT Configuration` → `MQTT wakeup topic`
- `MQTT Configuration` → `MQTT status topic`
- `MQTT Configuration` → `MQTT keepalive interval in seconds`
- `Power Management Configuration` → `Maximum CPU frequency in MHz`
- `Power Management Configuration` → `Minimum CPU frequency in MHz`
- `Enable prompt tone playback`
- `Wakeup Source Configuration` → `Timer wakeup delay in milliseconds`
- `Wakeup Source Configuration` → `Maximum wakeup wait time in milliseconds`
- `Wakeup Source Configuration` → `Enable GPIO wakeup source`
- `Wakeup Source Configuration` → `GPIO wakeup number`(需先启用 GPIO 唤醒)
- `Wakeup Source Configuration` → `GPIO wakeup active level`(Low level / High level)
- `Wakeup Source Configuration` → `Enable UART wakeup source`

> 配置完成后按 `s` 保存,然后按 `Esc` 退出。

### 编译与烧录

- 编译示例程序:

```
idf.py build
```

- 烧录程序并运行 monitor 工具来查看串口输出 (替换 PORT 为端口名称):

```
idf.py -p PORT flash monitor
```

- 退出调试界面使用 `Ctrl-]`

## 如何使用例程

### 功能和用法

烧录运行后,例程按以下顺序执行:

1. 初始化提示音播放资源并配置低功耗运行参数。
2. 连接 Wi-Fi 并启动 MQTT keepalive,等待 MQTT client 连接成功。
3. 配置 UART / MQTT / GPIO / 定时器等唤醒源。
4. 进入两轮「休眠—唤醒」验证循环,每轮包括:
   - 播放 `enter_sleep.mp3`,随后释放提示音播放资源;
   - 进入空闲低功耗(自动 light sleep + Wi-Fi modem sleep),等待唤醒;
   - 唤醒后恢复提示音播放资源并播放 `exit_sleep.mp3`,再运行约 3 秒后继续下一轮。
5. 两轮验证完成后释放全部资源并结束。

每次进入空闲低功耗后,UART、MQTT、GPIO 或定时器均可触发唤醒。开启提示音播放时(`CONFIG_EXAMPLE_ENABLE_PROMPT_PLAYBACK=y`),例程会在进入 idle 前播放 `enter_sleep.mp3`,唤醒后播放 `exit_sleep.mp3`。

### 日志输出

成功运行时的关键 log 如下。自动化测试(`pytest_audio_power_save.py`)第二轮通过向默认 MQTT topic 发布消息触发唤醒;下文 log 片段为实机采集,第二轮为 GPIO(默认 GPIO0 / BOOT 键)唤醒,二者均符合例程行为:

```text
I (1700) AUDIO_POWER_SAVE: [ 1 ] Initialize audio power save
I (1740) AUDIO_POWER_SAVE: [ 2 ] Connect Wi-Fi and start MQTT keepalive
W (1799) wifi:Haven't to connect to a suitable AP now!
I (1801) NETWORK_MGR: Connect Wi-Fi, ssid:ESP-Audio, listen_interval:10
W (1805) wifi:Haven't to connect to a suitable AP now!
I (1805) NETWORK_MGR: STA_CONFIG: listen_interval=10
W (1809) wifi:Password length matches WPA2 standards, authmode threshold changes from OPEN to WPA2
I (4440) NETWORK_MGR: Wi-Fi power save mode: WIFI_PS_NONE, reason: Wi-Fi connected
I (5182) NETWORK_MGR: MQTT connected, broker=mqtt://broker.emqx.io, keepalive=30 s
I (5186) NETWORK_MGR: Wi-Fi power save mode: WIFI_PS_NONE, reason: prepare wakeup sources
I (5187) AUDIO_POWER_SAVE: [ 3 ] Configure wakeup sources
I (5192) WAKEUP_MGR: Waiting for GPIO0 to become inactive (level=1)...
I (5198) WAKEUP_MGR: GPIO wakeup enabled, gpio=0, active level=0
I (5215) WAKEUP_MGR: UART wakeup enabled, uart=0, threshold=3
I (5216) WAKEUP_MGR: Timer wakeup configured, timeout=30000 ms
I (5216) AUDIO_POWER_SAVE: [ 4 ] Enter idle and wait for wakeup
W (5229) ESP_GMF_ASMP_DEC: Not enough memory for out, need:1152, old: 1024, new: 1152
E (6929) i2s_common: i2s_channel_disable(1262): the channel has not been enabled yet
W (6930) PERIPH_I2S: Caution: Releasing TX (0x3c1c09e0).
W (6931) PERIPH_I2S: Caution: RX (0x3c1c0b9c) forced to stop.
I (6937) AUDIO_POWER_SAVE: Enter idle and wait for wakeup
I (6942) NETWORK_MGR: Wi-Fi power save mode: WIFI_PS_MAX_MODEM, reason: enter idle low power
I (6950) WAKEUP_MGR: Player idle; waiting for wakeup (UART/MQTT/GPIO/timer) in automatic light sleep
I (10831) WAKEUP_MGR: Wakeup event: uart, event=8
I (10833) NETWORK_MGR: Wi-Fi power save mode: WIFI_PS_NONE, reason: wakeup handled
I (10833) WAKEUP_MGR: Wakeup handled by UART
I (10836) AUDIO_POWER_SAVE: Exit idle after UART wakeup
W (10888) ESP_GMF_ASMP_DEC: Not enough memory for out, need:1152, old: 1024, new: 1152
E (17371) i2s_common: i2s_channel_disable(1262): the channel has not been enabled yet
W (17372) PERIPH_I2S: Caution: Releasing TX (0x3c1c322c).
W (17373) PERIPH_I2S: Caution: RX (0x3c1c33e8) forced to stop.
I (17379) AUDIO_POWER_SAVE: Enter idle and wait for wakeup
I (17384) NETWORK_MGR: Wi-Fi power save mode: WIFI_PS_MAX_MODEM, reason: enter idle low power
I (17392) WAKEUP_MGR: Player idle; waiting for wakeup (UART/MQTT/GPIO/timer) in automatic light sleep
I (20830) NETWORK_MGR: Wi-Fi power save mode: WIFI_PS_NONE, reason: wakeup handled
I (20830) WAKEUP_MGR: Wakeup handled by GPIO
I (20830) AUDIO_POWER_SAVE: Exit idle after GPIO wakeup
W (20883) ESP_GMF_ASMP_DEC: Not enough memory for out, need:1152, old: 1024, new: 1152
I (25654) AUDIO_POWER_SAVE: Wakeup validation done
I (25654) AUDIO_POWER_SAVE: [ 5 ] Destroy all the resources
E (26039) i2s_common: i2s_channel_disable(1262): the channel has not been enabled yet
W (26039) PERIPH_I2S: Caution: Releasing TX (0x3c1c322c).
W (26041) PERIPH_I2S: Caution: RX (0x3c1c33e8) forced to stop.
I (26047) AUDIO_POWER_SAVE: Func:app_main, Line:76, MEM Total:8629212 Bytes, Inter:281251 Bytes, Dram:281251 Bytes

I (26056) AUDIO_POWER_SAVE: Example finished
```

## 故障排除

### MQTT broker 连接超时

如果日志出现如下提示,说明当前网络无法连接已配置的 MQTT broker。由于本例程现在会等待 MQTT 连接成功后才进入空闲低功耗,只有配置可访问的 broker 后才会继续执行唤醒流程:

```text
E (17164) esp-tls: [sock=54] select() timeout
E (17164) transport_base: Failed to open a new connection: 32774
E (17164) mqtt_client: Error transport connect
W (17167) NETWORK_MGR: MQTT error
I (17170) NETWORK_MGR: MQTT disconnected
```

如需验证 MQTT 唤醒,请将 `CONFIG_EXAMPLE_MQTT_BROKER_URI` 配置为当前网络可访问的 broker。

## 相关参考

- [低功耗保活常见问题总结](https://github.com/espressif/esp-adf/blob/release/v2.x/examples/system/power_save/README_CN.md#%E6%95%85%E9%9A%9C%E6%8E%92%E9%99%A4)
- [Wi-Fi 低功耗模式](https://docs.espressif.com/projects/esp-idf/zh_CN/latest/esp32s3/api-guides/low-power-mode/low-power-mode-wifi.html)

## 技术支持

请按照下面的链接获取技术支持:

- 技术支持参见 [esp32.com](https://esp32.com/viewforum.php?f=20) 论坛
- 问题反馈与功能需求,请创建 [GitHub issue](https://github.com/espressif/esp-adf/issues)

我们会尽快回复。

To create a project from this example, run:

idf.py create-project-from-example "espressif/adf_examples=0.2.1:audio_power_save"

or download archive (~37.35 KB)