深度睡眠与唤醒引脚
深度睡眠与唤醒引脚
本节介绍如何将 ESP32-CAM 配置为深度睡眠模式,并通过 PIR 运动传感器的 GPIO 信号唤醒。学习完成后,您将能够:
- 理解 ESP32 深度睡眠模式的工作原理
- 配置外部 GPIO 唤醒源
- 实现运动触发的拍照 + 自动休眠流程
- 优化电池供电场景的功耗
┌────────────────────────────────────────────────────────────┐│ PIR 触发深度睡眠唤醒流程 │├────────────────────────────────────────────────────────────┤│ ││ [Deep Sleep] ││ │ ││ │ 功耗: ~10μA ││ │ ││ ▼ PIR 检测到运动 (GPIO 13 HIGH) ││ [Wake Up] ││ │ ││ ├──→ 1. 初始化摄像头 ││ ├──→ 2. 连接 Wi-Fi ││ ├──→ 3. 连接 MQTT Broker ││ ├──→ 4. 拍照 ││ ├──→ 5. 通过 MQTT 发送图片 ││ ├──→ 6. 释放资源 ││ │ ││ ▼ ││ [Deep Sleep] ←── 完成后立即进入深度睡眠 ││ │ ││ └── 等待下一次运动检测触发 ││ │└────────────────────────────────────────────────────────────┘ESP32 深度睡眠模式
Section titled “ESP32 深度睡眠模式”| 模式 | 功耗 | CPU | RAM | RTC 内存 | RTC 外设 | 唤醒源 |
|---|---|---|---|---|---|---|
| Active | 80-200mA | ✅ 运行 | ✅ | ✅ | ✅ | - |
| Modem Sleep | ~30mA | ✅ 运行 | ✅ | ✅ | ✅ | Timer |
| Light Sleep | ~10mA | ❌ 暂停 | ✅ | ✅ | ✅ | Timer/GPIO |
| Deep Sleep | ~10μA | ❌ 断电 | ❌ 断电 | ✅ 保留 | ✅ 可选 | Timer/GPIO/Touch |
| Hibernate | ~5μA | ❌ 断电 | ❌ 断电 | ❌ 断电 | ❌ | GPIO/Reset |
关键: Deep Sleep 模式下 CPU 和主 RAM 完全断电,仅 RTC 内存(8KB)保留数据。ESP32-CAM 用于远程巡检时,深度睡眠可实现数月电池续航。
ESP32 RTC GPIO 引脚
Section titled “ESP32 RTC GPIO 引脚”可用于外部唤醒的 RTC GPIO:
| GPIO | RTC 引脚 | 说明 |
|---|---|---|
| GPIO 13 | RTC_GPIO4 | ⭐ 推荐用于 PIR 唤醒 |
| GPIO 2 | RTC_GPIO12 | Camera 板载 LED |
| GPIO 4 | RTC_GPIO10 | Flash LED |
| GPIO 12 | RTC_GPIO15 | 注意上拉电压影响启动 |
| GPIO 14 | RTC_GPIO16 | 可用 |
| GPIO 15 | RTC_GPIO13 | 可用 |
| GPIO 25 | RTC_GPIO6 | 可用 |
| GPIO 26 | RTC_GPIO7 | 可用 |
| GPIO 27 | RTC_GPIO17 | 可用 |
| GPIO 32-39 | RTC_GPIO0-9 | 可用 |
注意: GPIO 13 是推荐连接的 PIR 输出引脚,因为它既是 RTC GPIO 支持深度睡眠唤醒,又不会影响 ESP32 的启动模式。
Implementation
Section titled “Implementation”PIR 触发的 ESP32-CAM 完整代码
Section titled “PIR 触发的 ESP32-CAM 完整代码”#include <WiFi.h>#include <PubSubClient.h>#include "esp_camera.h"#include "driver/rtc_io.h"
// ==== Wi-Fi 配置 ====const char* ssid = "YOUR_SSID";const char* password = "YOUR_PASSWORD";
// ==== MQTT 配置 ====const char* mqtt_server = "192.168.1.100";const int mqtt_port = 1883;const char* mqtt_user = "iot_user";const char* mqtt_pass = "iot_password";const char* client_id = "esp32cam-01";
// ==== 引脚定义 ====#define PIR_PIN 13 // PIR 传感器 (RTC_GPIO4)#define FLASH_PIN 4 // 闪光灯 LED
// ==== 全局对象 ====WiFiClient espClient;PubSubClient client(espClient);
// ==== 唤醒原因检测 ====RTC_DATA_ATTR int bootCount = 0; // 保留在 RTC 内存中
void printWakeupReason() { esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
switch(cause) { case ESP_SLEEP_WAKEUP_EXT0: Serial.println("Wakeup caused by external signal (PIR)"); break; case ESP_SLEEP_WAKEUP_TIMER: Serial.println("Wakeup caused by timer"); break; default: Serial.printf("Wakeup caused by %d (reset/power-on)\n", cause); }}
// ==== 拍照并发送 ====void takePhotoAndSend() { // 初始化摄像头 camera_config_t config; config.ledc_channel = LEDC_CHANNEL_0; config.ledc_timer = LEDC_TIMER_0; config.pin_d0 = 5; config.pin_d1 = 18; config.pin_d2 = 19; config.pin_d3 = 21; config.pin_d4 = 36; config.pin_d5 = 39; config.pin_d6 = 34; config.pin_d7 = 35; config.pin_xclk = 0; config.pin_pclk = 22; config.pin_vsync = 25; config.pin_href = 23; config.pin_sscb_sda = 26; config.pin_sscb_scl = 27; config.pin_pwdn = 32; config.pin_reset = -1; config.xclk_freq_hz = 20000000; config.pixel_format = PIXFORMAT_JPEG;
// 低分辨率以加快传输 config.frame_size = FRAMESIZE_VGA; // 640×480 config.jpeg_quality = 12; // JPEG 质量 config.fb_count = 1;
esp_err_t err = esp_camera_init(&config); if (err != ESP_OK) { Serial.printf("Camera init failed: 0x%x\n", err); return; }
// 拍照 camera_fb_t* fb = esp_camera_fb_get(); if (!fb) { Serial.println("Camera capture failed!"); esp_camera_deinit(); return; }
Serial.printf("Photo taken: %zu bytes\n", fb->len);
// 连接 Wi-Fi WiFi.begin(ssid, password); unsigned long timeout = millis() + 15000; // 15 秒超时 while (WiFi.status() != WL_CONNECTED && millis() < timeout) { delay(100); }
if (WiFi.status() == WL_CONNECTED) { Serial.println("WiFi connected");
// 连接 MQTT client.setServer(mqtt_server, mqtt_port); client.setBufferSize(60000);
if (client.connect(client_id, mqtt_user, mqtt_pass)) { Serial.println("MQTT connected");
// 发送图片 client.publish("esp32cam/photo", fb->buf, fb->len, false); Serial.println("Photo sent via MQTT");
// 发送元数据 String meta = "{\"size\":" + String(fb->len) + ",\"boot\":" + String(bootCount) + "}"; client.publish("esp32cam/status", meta.c_str());
delay(500); // 等待发送完成 client.disconnect(); } }
// 释放资源 esp_camera_fb_return(fb); esp_camera_deinit();
// 断开 Wi-Fi (减少功耗) WiFi.disconnect(true); WiFi.mode(WIFI_OFF);}
// ==== 完整 Setup — PIR 唤醒版本 ====void setup() { Serial.begin(115200); delay(100);
bootCount++; Serial.printf("Boot count: %d\n", bootCount);
printWakeupReason();
// 拍照并发送 takePhotoAndSend();
// ---- 准备深度睡眠 ----
// 确保闪光灯关闭 rtc_gpio_hold_dis(GPIO_NUM_4); pinMode(FLASH_PIN, OUTPUT); digitalWrite(FLASH_PIN, LOW);
// 配置外部唤醒源: GPIO 13 上升沿触发 esp_sleep_enable_ext0_wakeup(GPIO_NUM_13, 1); // 1 = HIGH 电平唤醒
Serial.println("Entering deep sleep..."); Serial.flush();
// 进入深度睡眠 esp_deep_sleep_start();}
// 注意: loop() 在深度睡眠版本中不会执行void loop() { // ESP32 在深度睡眠期间不会运行到这里}| 部分 | 说明 |
|---|---|
RTC_DATA_ATTR | 变量保留在 RTC 内存中,深度睡眠后值不丢失 |
esp_sleep_get_wakeup_cause() | 检测唤醒原因,区分 PIR 触发和上电复位 |
esp_sleep_enable_ext0_wakeup(GPIO_NUM_13, 1) | GPIO 13 电平 HIGH 时唤醒 |
esp_camera_init() / esp_camera_deinit() | 每次唤醒后初始化,睡眠前反初始化 |
WiFi.disconnect(true) | 断开 Wi-Fi 以节省功耗 |
esp_deep_sleep_start() | 立即进入深度睡眠 |
Power Consumption Analysis
Section titled “Power Consumption Analysis”| 阶段 | 功耗 | 持续时间 | 占总周期比例 |
|---|---|---|---|
| 深度睡眠 | ~10μA | 数小时/数天 | >99.9% |
| 唤醒 (摄像初始化) | ~150mA | 1-2 秒 | <0.1% |
| Wi-Fi 连接 | ~200mA | 3-10 秒 | <0.1% |
| 拍照 | ~180mA | 0.5-1 秒 | <0.01% |
| MQTT 发送 | ~200mA | 1-3 秒 | <0.01% |
电池续航估算:
| 电池容量 | 每天触发 10 次 | 每天触发 50 次 |
|---|---|---|
| 2×AA (2000mAh) | ~180 天 | ~120 天 |
| 14500 Li-ion (800mAh) | ~70 天 | ~45 天 |
| 18650 (3000mAh) | ~270 天 | ~180 天 |
# 1. 烧录后观察串口输出# PIR 触发前:ESP32-CAM PIR Deep Sleep Starting...Entering deep sleep...
# PIR 触发后:Boot count: 1Wakeup caused by external signal (PIR)Photo taken: 28501 bytesWiFi connectedMQTT connectedPhoto sent via MQTTEntering deep sleep...
# 2. 在 Node-RED 端验证接收到的图片# 检查 /data/esp32cam/ 目录是否有新图片
# 3. 监控 MQTT 状态mosquitto_sub -t "esp32cam/status" -v# 输出示例:# esp32cam/status {"size":28501,"boot":3}Common Customer Questions
Section titled “Common Customer Questions”Q1: 深度睡眠时 PIR 还能检测运动吗?
Section titled “Q1: 深度睡眠时 PIR 还能检测运动吗?”是的。ESP32 深度睡眠时 RTC 外设仍在工作,GPIO 13 作为 RTC GPIO 可以检测电平变化并触发唤醒。PIR 传感器由外部电源供电,不受 ESP32 睡眠影响。
Q2: 唤醒后如何知道是 PIR 触发还是定时唤醒?
Section titled “Q2: 唤醒后如何知道是 PIR 触发还是定时唤醒?”esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();if (cause == ESP_SLEEP_WAKEUP_EXT0) { // PIR 触发 → 拍照发送} else if (cause == ESP_SLEEP_WAKEUP_TIMER) { // 定时唤醒 → 周期性健康检查} else { // 上电复位 → 初始化配置}Q3: 电池供电时如何确保可靠运行?
Section titled “Q3: 电池供电时如何确保可靠运行?”- 使用高质量电容组稳定供电(470μF + 100μF)
- 唤醒后先检查电池电压,低于阈值时仅发送低电量报警不拍照
- 设置看门狗定时器,防止 Wi-Fi 连接卡死
✅ 推荐做法:
- 使用
RTC_DATA_ATTR在深度睡眠间保持计数器等状态 - 唤醒后调用
esp_camera_deinit()释放内存 - 发送完成后调用
WiFi.disconnect(true)彻底断电 Wi-Fi - PIR 输出连接 GPIO 13(RTC 唤醒专用引脚)
- 在 Setup 开头使用延时让 PIR 传感器稳定
❌ 避免做法:
- 在深度睡眠前未断开 Wi-Fi(增加功耗)
- 每次唤醒后未重新初始化摄像头
- 使用非 RTC GPIO 作为唤醒源(不支持深度睡眠唤醒)
- PIR 检测到运动后立即进入睡眠(照片未发送完成)
- 忽略
esp_camera_fb_return()导致内存泄漏
Summary
Section titled “Summary”- 深度睡眠将 ESP32 功耗降低至 ~10μA
- GPIO 13 是推荐的 PIR 唤醒引脚(RTC_GPIO4)
esp_sleep_enable_ext0_wakeup()配置外部信号唤醒RTC_DATA_ATTR变量在深度睡眠间持久化- 每次唤醒完成拍照发送后立即进入睡眠