ESP32-C3 深睡眠下 RTC 内存保持 Wi-Fi 快速重连的工程技巧
👁 2 阅读 · 2026-08-27 · 嵌入式
在电池供电的物联网设备中,深睡眠是延长续航的关键,但唤醒后 Wi-Fi 重连的耗时往往导致功耗飙升。ESP32-C3 提供了 RTC 内存,可在深睡眠期间保留 Wi-Fi 连接所需的上下文数据,实现毫秒级快速重连。本文将深入剖析其原理,并给出完整的工程配置步骤、代码示例及注意事项,帮助开发者显著降低平均功耗。
# ESP32-C3 深睡眠下 RTC 内存保持 Wi-Fi 快速重连的工程技巧
## 一、为什么需要快速重连?
在低功耗物联网应用中,ESP32-C3 常采用深睡眠(Deep Sleep)模式来降低功耗,典型电流可低至 5μA 左右。然而,唤醒后若重新初始化 Wi-Fi 并扫描、连接 AP,通常需要 1~3 秒,期间电流高达 100~200mA,导致平均功耗急剧上升。例如,若每 10 秒唤醒一次,重连耗时 2 秒,则平均功耗约为 (2s*150mA + 8s*0.005mA)/10s ≈ 30mA,远高于纯睡眠时的 0.005mA。因此,缩短重连时间至关重要。
## 二、原理:RTC 内存与 Wi-Fi 上下文
ESP32-C3 内部包含一块 RTC 快速内存(RTC Fast Memory),在深睡眠期间由 RTC 域供电,数据不会丢失。Wi-Fi 协议栈在连接 AP 后,会生成一份“上下文”数据,包括 BSSID、信道、认证信息、密钥等。默认情况下,这些数据存储在普通 RAM 中,深睡眠时丢失。但 ESP-IDF 提供了 `esp_wifi_set_ps(WIFI_PS_MIN_MODEM)` 和 `esp_wifi_set_storage(WIFI_STORAGE_RAM)` 等接口,允许将 Wi-Fi 配置和状态保存到 RTC 内存。
具体机制:
- `esp_wifi_set_storage(WIFI_STORAGE_RAM)` 使 Wi-Fi 配置不写入 flash,而是保存在 RAM 中,但深睡眠时 RAM 掉电,因此需要配合 `esp_wifi_set_keep_alive` 或使用 `esp_wifi_bt_rtc_resume` 等 API。
- 更直接的方式是使用 `esp_wifi_set_storage(WIFI_STORAGE_RAM)` 并调用 `esp_wifi_get_mac` 等,但关键在于启用 `CONFIG_ESP32_WIFI_RTOS` 和 `CONFIG_ESP32_WIFI_SAVE_STATE` 选项,让 Wi-Fi 驱动在深睡眠前自动保存上下文到 RTC 内存。
实际上,ESP-IDF 提供了 `esp_wifi_set_storage(WIFI_STORAGE_RAM)` 和 `esp_wifi_set_ps(WIFI_PS_MIN_MODEM)`,但快速重连的核心是 `esp_wifi_stop()` 和 `esp_wifi_start()` 之间的状态保留。通过 `esp_wifi_set_storage(WIFI_STORAGE_RAM)` 并启用 `CONFIG_ESP32_WIFI_SAVE_STATE`,驱动会将连接信息存入 RTC 内存,唤醒后 `esp_wifi_start()` 可直接恢复,无需重新扫描。
## 三、配置步骤
### 1. 启用必要配置
在 `menuconfig` 中设置:
- `Component config → Wi-Fi → WiFi storage` 选择 `RAM`(或使用代码设置)。
- `Component config → Wi-Fi → Enable WiFi state save` 勾选(即 `CONFIG_ESP32_WIFI_SAVE_STATE=y`)。
- `Component config → Power Management` 启用 `CONFIG_PM_ENABLE`,并选择 `CONFIG_PM_SLP_DISABLE` 或 `CONFIG_PM_SLP_IRAM_OPT` 等,确保深睡眠可用。
### 2. 代码初始化
在 `app_main` 中,先初始化 NVS(用于存储校准数据),然后设置 Wi-Fi 存储为 RAM,并注册深睡眠唤醒回调。
```c
#include "esp_wifi.h"
#include "esp_sleep.h"
#include "nvs_flash.h"
void app_main() {
// 初始化 NVS
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES) {
nvs_flash_erase();
nvs_flash_init();
}
// 初始化 Wi-Fi
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
esp_wifi_init(&cfg);
esp_wifi_set_storage(WIFI_STORAGE_RAM); // 关键:配置存 RAM
esp_wifi_set_mode(WIFI_MODE_STA);
// 配置连接参数
wifi_config_t wifi_config = {
.sta = {
.ssid = "YOUR_SSID",
.password = "YOUR_PASS",
.threshold.authmode = WIFI_AUTH_WPA2_PSK,
},
};
esp_wifi_set_config(WIFI_IF_STA, &wifi_config);
esp_wifi_start();
// 连接并等待成功
esp_wifi_connect();
// 等待连接事件...
// 进入深睡眠前,确保 Wi-Fi 已连接,然后设置唤醒源
esp_sleep_enable_timer_wakeup(10 * 1000000); // 10秒唤醒
esp_deep_sleep_start();
}
```
### 3. 唤醒后快速重连
在唤醒后,`esp_wifi_start()` 会自动恢复之前保存的状态,但需要确保调用顺序正确。通常,在 `esp_sleep_wakeup_cause()` 判断为定时器唤醒后,直接调用 `esp_wifi_start()` 即可,无需重新配置和连接。
```c
void on_wakeup() {
if (esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_TIMER) {
// 直接启动 Wi-Fi,驱动会从 RTC 内存恢复上下文
esp_wifi_start();
// 此时 Wi-Fi 已自动连接,无需调用 esp_wifi_connect()
}
}
```
但注意,`esp_wifi_start()` 是异步的,需要等待 `WIFI_EVENT_STA_START` 和 `IP_EVENT_STA_GOT_IP` 事件。为了简化,可以使用事件组或信号量。
## 四、完整示例代码
下面是一个完整的示例,演示了深睡眠唤醒后快速重连,并测量重连时间。
```c
#include
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_sleep.h"
#include "nvs_flash.h"
#include "esp_log.h"
static const char *TAG = "fast_reconnect";
static EventGroupHandle_t wifi_event_group;
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT BIT1
static void wifi_event_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data) {
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
esp_wifi_connect();
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
xEventGroupSetBits(wifi_event_group, WIFI_FAIL_BIT);
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
xEventGroupSetBits(wifi_event_group, WIFI_CONNECTED_BIT);
}
}
void wifi_init() {
wifi_event_group = xEventGroupCreate();
esp_event_loop_create_default();
esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL);
esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &wifi_event_handler, NULL);
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
esp_wifi_init(&cfg);
esp_wifi_set_storage(WIFI_STORAGE_RAM);
esp_wifi_set_mode(WIFI_MODE_STA);
wifi_config_t wifi_config = {
.sta = {
.ssid = "YOUR_SSID",
.password = "YOUR_PASS",
.threshold.authmode = WIFI_AUTH_WPA2_PSK,
},
};
esp_wifi_set_config(WIFI_IF_STA, &wifi_config);
esp_wifi_start();
}
void app_main() {
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES) {
nvs_flash_erase();
nvs_flash_init();
}
// 判断唤醒原因
if (esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_TIMER) {
ESP_LOGI(TAG, "Wakeup from deep sleep, fast reconnecting...");
// 直接启动 Wi-Fi,无需重新配置
esp_wifi_start();
// 等待连接事件(通常 < 50ms)
EventBits_t bits = xEventGroupWaitBits(wifi_event_group,
WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
pdFALSE, pdFALSE, 2000 / portTICK_PERIOD_MS);
if (bits & WIFI_CONNECTED_BIT) {
ESP_LOGI(TAG, "Reconnected successfully!");
} else {
ESP_LOGE(TAG, "Reconnect failed, fallback to full init");
// 重新初始化
wifi_init();
}
} else {
// 首次启动
wifi_init();
// 等待连接
xEventGroupWaitBits(wifi_event_group, WIFI_CONNECTED_BIT, pdFALSE, pdTRUE, 10000);
}
// 模拟工作,然后进入深睡眠
vTaskDelay(1000 / portTICK_PERIOD_MS);
ESP_LOGI(TAG, "Entering deep sleep...");
esp_wifi_stop(); // 停止 Wi-Fi,但状态会保存到 RTC
esp_sleep_enable_timer_wakeup(10 * 1000000);
esp_deep_sleep_start();
}
```
## 五、注意事项
- **必须启用 `CONFIG_ESP32_WIFI_SAVE_STATE`**:否则 Wi-Fi 状态不会保存到 RTC 内存,唤醒后仍需重新扫描连接。
- **存储方式选择 RAM**:`esp_wifi_set_storage(WIFI_STORAGE_RAM)` 确保配置不写入 flash,减少 flash 磨损,但需配合状态保存。
- **深睡眠前调用 `esp_wifi_stop()`**:这会将当前连接上下文保存到 RTC 内存。若直接进入深睡眠而不停止 Wi-Fi,可能丢失状态。
- **唤醒后不要立即调用 `esp_wifi_connect()`**:因为驱动会自动恢复连接,若手动调用可能导致重复连接或冲突。
- **事件处理**:快速重连时,`WIFI_EVENT_STA_START` 事件会触发,但无需在事件中调用 `esp_wifi_connect()`,否则会中断自动恢复。建议在事件处理中判断是否首次启动。
- **RTC 内存大小**:ESP32-C3 的 RTC 快速内存为 8KB,Wi-Fi 上下文大约占用 4KB,足够使用。但若同时使用其他 RTC 数据,需注意容量。
- **功耗测量**:实际重连时间取决于 AP 响应速度,通常 10~50ms。建议用 GPIO 翻转测量时间,优化唤醒流程。
- **兼容性**:此技巧适用于 ESP-IDF v4.4 及以上版本,旧版本可能不支持 `CONFIG_ESP32_WIFI_SAVE_STATE`。
## 六、总结
通过利用 ESP32-C3 的 RTC 内存保存 Wi-Fi 上下文,可以将深睡眠唤醒后的重连时间从秒级降至毫秒级,显著降低平均功耗。本文提供了完整的配置和代码示例,开发者可根据实际需求调整唤醒周期和重连策略。记住,关键点在于启用状态保存、正确停止 Wi-Fi 并避免重复连接。掌握这一技巧,你的电池供电设备将拥有更长的续航。