跳转到内容

深度睡眠与唤醒引脚

深度睡眠与唤醒引脚

本节介绍如何将 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] ←── 完成后立即进入深度睡眠 │
│ │ │
│ └── 等待下一次运动检测触发 │
│ │
└────────────────────────────────────────────────────────────┘
模式功耗CPURAMRTC 内存RTC 外设唤醒源
Active80-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 用于远程巡检时,深度睡眠可实现数月电池续航。

可用于外部唤醒的 RTC GPIO:

GPIORTC 引脚说明
GPIO 13RTC_GPIO4推荐用于 PIR 唤醒
GPIO 2RTC_GPIO12Camera 板载 LED
GPIO 4RTC_GPIO10Flash LED
GPIO 12RTC_GPIO15注意上拉电压影响启动
GPIO 14RTC_GPIO16可用
GPIO 15RTC_GPIO13可用
GPIO 25RTC_GPIO6可用
GPIO 26RTC_GPIO7可用
GPIO 27RTC_GPIO17可用
GPIO 32-39RTC_GPIO0-9可用

注意: GPIO 13 是推荐连接的 PIR 输出引脚,因为它既是 RTC GPIO 支持深度睡眠唤醒,又不会影响 ESP32 的启动模式。

#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()立即进入深度睡眠
阶段功耗持续时间占总周期比例
深度睡眠~10μA数小时/数天>99.9%
唤醒 (摄像初始化)~150mA1-2 秒<0.1%
Wi-Fi 连接~200mA3-10 秒<0.1%
拍照~180mA0.5-1 秒<0.01%
MQTT 发送~200mA1-3 秒<0.01%

电池续航估算:

电池容量每天触发 10 次每天触发 50 次
2×AA (2000mAh)~180 天~120 天
14500 Li-ion (800mAh)~70 天~45 天
18650 (3000mAh)~270 天~180 天
Terminal window
# 1. 烧录后观察串口输出
# PIR 触发前:
ESP32-CAM PIR Deep Sleep Starting...
Entering deep sleep...
# PIR 触发后:
Boot count: 1
Wakeup caused by external signal (PIR)
Photo taken: 28501 bytes
WiFi connected
MQTT connected
Photo sent via MQTT
Entering deep sleep...
# 2. 在 Node-RED 端验证接收到的图片
# 检查 /data/esp32cam/ 目录是否有新图片
# 3. 监控 MQTT 状态
mosquitto_sub -t "esp32cam/status" -v
# 输出示例:
# esp32cam/status {"size":28501,"boot":3}

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: 电池供电时如何确保可靠运行?”
  1. 使用高质量电容组稳定供电(470μF + 100μF)
  2. 唤醒后先检查电池电压,低于阈值时仅发送低电量报警不拍照
  3. 设置看门狗定时器,防止 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() 导致内存泄漏
  1. 深度睡眠将 ESP32 功耗降低至 ~10μA
  2. GPIO 13 是推荐的 PIR 唤醒引脚(RTC_GPIO4)
  3. esp_sleep_enable_ext0_wakeup() 配置外部信号唤醒
  4. RTC_DATA_ATTR 变量在深度睡眠间持久化
  5. 每次唤醒完成拍照发送后立即进入睡眠