一个专为 ESP32-H2 设计的 ESPHome 自定义 Deep Sleep 组件,支持定时唤醒、GPIO 唤醒,以及智能电平反转唤醒机制。 A production‑grade Deep Sleep controller for ESPHome, purpose‑built for the ESP32‑H2 platform. It provides precise wakeup control, GPIO EXT1 wakeup, smart inverted‑level logic, and seamless integration with other ESPHome components — ideal for ultra‑low‑power sensor nodes and battery‑powered devices.
- ✅ 定时唤醒 - 支持配置睡眠时长,自动定时唤醒 Timer‑based wakeup with configurable sleep duration
- ✅ GPIO EXT1 唤醒 - 支持单个 GPIO 引脚作为唤醒源 GPIO EXT1 wakeup with full Pin Schema support
- ✅ 智能电平反转 - 自动读取 GPIO 电平,设置相反的唤醒条件,避免立即再次唤醒 Smart inverted‑level wakeup to avoid immediate re‑trigger
- ✅ 运行时长控制 - 设置唤醒后运行时间,自动进入睡眠 Run‑duration control for automatic sleep entry
- ✅ 唤醒原因检测 - 获取唤醒原因(定时器、GPIO、重置等)方便执行不同的初始化逻辑 Wakeup reason detection with human‑readable descriptions
- ✅ FreeRTOS Task - 使用独立任务处理睡眠流程,等待其他任务完成 FreeRTOS task‑based sleep handling for safe transitions
- ✅ 引脚共享 - 支持
allow_other_uses,允许多个组件使用同一 GPIO Pin sharing support (allow_other_uses) - ✅ 手动控制 - 提供 Actions 用于手动触发睡眠、阻止睡眠等 Manual sleep control actions (enter, prevent, allow)
Designed specifically for ESP32‑H2 (ESP‑IDF)
ESPHome 内置的 deep_sleep 组件主要针对 ESP32 和 ESP8266,对于 ESP32-H2 这样的新芯片,需要更精细的控制。
fatal error: esphome/components/deep_sleep/deep_sleep_component.h: No such file or directory
解决方案: 放弃继承 deep_sleep::DeepSleepComponent,创建独立组件。
E (10767) sleep: Incorrect wakeup source (3) to disable.
解决方案:
- 移除
esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_EXT1) - 改用
esp_sleep_disable_ext1_wakeup_io(pin_mask)禁用特定引脚 - 使用
esp_sleep_enable_ext1_wakeup_io()替代esp_sleep_enable_ext1_wakeup()
[20:57:46][I][h2_deep_sleep:083]: === RUN DURATION EXPIRED (10000 ms) ===
[20:57:46][I][h2_deep_sleep:084]: Entering deep sleep automatically...
... (重复多次)
解决方案: 添加 entering_deep_sleep_ 标志位,防止 loop() 函数重复调用 begin_sleep()。
# 需要支持这种配置
wakeup_pin:
number: GPIO10
allow_other_uses: true解决方案:
- Python:
pins.internal_gpio_input_pin_number→pins.internal_gpio_input_pin_schema - Python: 使用
await cg.gpio_pin_expression(config[CONF_WAKEUP_PIN]) - C++:
uint8_t wakeup_pin_→InternalGPIOPin *wakeup_pin_
w_comps/
└── h2_deep_sleep/
├── __init__.py # Python 配置验证和代码生成
├── h2_deep_sleep.h # C++ 头文件
└── h2_deep_sleep.cpp # C++ 实现
Local Installation
- 在你的 ESPHome 配置目录下创建
w_comps文件夹 - 将
h2_deep_sleep文件夹复制到w_comps中 - 在 YAML 中添加:
external_components:
- source:
type: local
path: w_comps
components: [h2_deep_sleep]h2_deep_sleep:
id: deep_sleep_ctrl # 组件 ID(必需)
run_duration: 30s # 唤醒后运行时长(可选)
sleep_duration: 5min # 睡眠时长(可选)
wakeup_pin: # 唤醒引脚(可选)
number: GPIO10
allow_other_uses: true
wakeup_pin_mode: INVERT_WAKEUP # 唤醒模式(可选)| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
id |
ID | - | 组件标识符(必需) |
run_duration |
Time | - | 唤醒后运行时长,超时自动睡眠 |
sleep_duration |
Time | - | Deep Sleep 睡眠时长 |
wakeup_pin |
Pin Schema | - | EXT1 唤醒引脚配置 |
wakeup_pin_mode |
Enum | INVERT_WAKEUP |
唤醒引脚模式 |
| 模式 | 说明 |
|---|---|
IGNORE |
忽略唤醒引脚,不配置 EXT1 唤醒 |
KEEP_AWAKE |
如果引脚为高电平,阻止进入睡眠 |
INVERT_WAKEUP |
智能反转 - 读取当前电平,设置相反的唤醒条件(推荐) |
esp32:
board: esp32-h2-devkitm-1
variant: esp32h2
framework:
type: esp-idf
logger:
level: DEBUG
h2_deep_sleep:
id: deep_sleep_ctrl
run_duration: 20s # 唤醒后运行 20 秒
sleep_duration: 5min # 睡眠 5 分钟h2_deep_sleep:
id: deep_sleep_ctrl
run_duration: 30s
sleep_duration: 10min
wakeup_pin:
number: GPIO10
allow_other_uses: true
wakeup_pin_mode: INVERT_WAKEUP
# 可以同时使用 GPIO10 作为按钮
binary_sensor:
- platform: gpio
name: "Wake Button"
pin:
number: GPIO10
mode:
input: true
allow_other_uses: true
on_press:
- logger.log: "Button pressed!"h2_deep_sleep:
id: deep_sleep_ctrl
run_duration: 20s
sleep_duration: 5min
wakeup_pin:
number: GPIO10
allow_other_uses: true
esphome:
name: h2-smart-device
on_boot:
priority: -100
then:
- lambda: |-
uint8_t cause = id(deep_sleep_ctrl).get_wakeup_cause();
ESP_LOGI("main", "Wakeup cause: %s",
id(deep_sleep_ctrl).get_wakeup_cause_string());
switch(cause) {
case 0: // ESP_SLEEP_WAKEUP_UNDEFINED - 首次启动
ESP_LOGI("main", "First boot - full initialization");
// 执行完整初始化
break;
case 4: // ESP_SLEEP_WAKEUP_TIMER - 定时唤醒
ESP_LOGI("main", "Timer wakeup - quick sensor read");
// 快速读取传感器
break;
case 3: // ESP_SLEEP_WAKEUP_EXT1 - GPIO 唤醒
ESP_LOGI("main", "GPIO wakeup - handle event");
uint8_t level = id(deep_sleep_ctrl).get_wakeup_pin_level();
ESP_LOGI("main", "Wakeup pin was at level: %d", level);
// 处理按钮事件
break;
}h2_deep_sleep:
id: deep_sleep_ctrl
sleep_duration: 1min
button:
- platform: template
name: "Sleep Now"
on_press:
# 立即进入睡眠,覆盖默认睡眠时长
- h2_deep_sleep.enter:
id: deep_sleep_ctrl
sleep_duration: 30s
- platform: template
name: "Prevent Sleep"
on_press:
- h2_deep_sleep.prevent:
id: deep_sleep_ctrl
- logger.log: "Sleep prevented"
- platform: template
name: "Allow Sleep"
on_press:
- h2_deep_sleep.allow:
id: deep_sleep_ctrl
- logger.log: "Sleep allowed"h2_deep_sleep:
id: deep_sleep_ctrl
run_duration: 15s
sleep_duration: 10min
wakeup_pin:
number: GPIO10
allow_other_uses: true
wakeup_pin_mode: INVERT_WAKEUP
sensor:
- platform: adc
pin: GPIO2
name: "Battery Voltage"
id: battery_voltage
update_interval: never
script:
- id: read_and_send
then:
- logger.log: "Reading sensors..."
- component.update: battery_voltage
- delay: 1s
# 发送数据到服务器...
- logger.log: "Data sent, waiting for sleep..."
esphome:
on_boot:
- script.execute: read_and_send# ❌ 这个方案失败了
from esphome.components import deep_sleep
EnhancedDeepSleep = enhanced_deep_sleep_ns.class_(
"EnhancedDeepSleep",
deep_sleep.DeepSleepComponent
)问题: 找不到 deep_sleep_component.h 头文件
# ✅ 成功的方案
H2DeepSleep = h2_deep_sleep_ns.class_("H2DeepSleep", cg.Component)// 读取当前 GPIO 电平
int current_level = gpio_get_level((gpio_num_t)pin);
// 根据当前电平设置相反的唤醒条件
if (current_level == 0) {
wakeup_mode = ESP_EXT1_WAKEUP_ANY_HIGH; // LOW→HIGH 唤醒
} else {
wakeup_mode = ESP_EXT1_WAKEUP_ALL_LOW; // HIGH→LOW 唤醒
}void H2DeepSleep::loop() {
// 检查标志位
if (this->entering_deep_sleep_) {
return; // 已在进入睡眠,直接返回
}
// ... 检查运行时长 ...
if (elapsed >= target) {
this->begin_sleep(); // 设置 entering_deep_sleep_ = true
}
}void H2DeepSleep::begin_sleep(uint32_t sleep_duration_ms) {
// 配置唤醒源
this->prepare_deep_sleep(duration);
// 创建独立任务
xTaskCreate(
this->deep_sleep_task,
"deep_sleep_task",
4096, // 栈大小
this, // 传递 this 指针
5, // 优先级
&this->deep_sleep_task_handle_
);
}
void H2DeepSleep::deep_sleep_task(void *param) {
// 等待其他任务完成
vTaskDelay(pdMS_TO_TICKS(200));
// 进入睡眠
esp_deep_sleep_start();
}# Python 端
CONFIG_SCHEMA = cv.Schema({
cv.Optional(CONF_WAKEUP_PIN): pins.internal_gpio_input_pin_schema,
})
async def to_code(config):
if CONF_WAKEUP_PIN in config:
pin = await cg.gpio_pin_expression(config[CONF_WAKEUP_PIN])
cg.add(var.set_wakeup_pin(pin))// C++ 端
class H2DeepSleep : public Component {
void set_wakeup_pin(InternalGPIOPin *pin) {
this->wakeup_pin_ = pin;
}
protected:
InternalGPIOPin *wakeup_pin_{nullptr};
};
// 使用时
uint8_t pin_num = this->wakeup_pin_->get_pin();
bool level = this->wakeup_pin_->digital_read();A: 这是因为 GPIO 电平没有变化。使用 wakeup_pin_mode: INVERT_WAKEUP 可以自动检测并设置相反的唤醒条件。
A: 在 pin 配置中添加 allow_other_uses: true:
wakeup_pin:
number: GPIO10
allow_other_uses: trueA: 这是因为 loop() 函数执行时间过长。已通过添加 entering_deep_sleep_ 标志位解决。
A: 使用 on_boot 配合唤醒原因检测:
esphome:
on_boot:
- lambda: |-
if (id(deep_sleep_ctrl).get_wakeup_cause() == 4) {
// 定时唤醒的处理逻辑
}A: 可以!不设置 run_duration 时,需要手动调用 Action 进入睡眠:
h2_deep_sleep:
id: deep_sleep_ctrl
sleep_duration: 1min
# 不设置 run_duration
button:
- platform: template
name: "Sleep"
on_press:
- h2_deep_sleep.enter: deep_sleep_ctrl进入 Deep Sleep 模式
- h2_deep_sleep.enter:
id: deep_sleep_ctrl
sleep_duration: 30s # 可选,覆盖默认值阻止进入 Deep Sleep
- h2_deep_sleep.prevent:
id: deep_sleep_ctrl允许进入 Deep Sleep(取消阻止)
- h2_deep_sleep.allow:
id: deep_sleep_ctrl// 获取唤醒原因代码 (0-6)
uint8_t cause = id(deep_sleep_ctrl).get_wakeup_cause();
// 获取唤醒原因字符串描述
const char* cause_str = id(deep_sleep_ctrl).get_wakeup_cause_string();
// 获取唤醒引脚电平 (0 或 1)
uint8_t level = id(deep_sleep_ctrl).get_wakeup_pin_level();
// 手动进入睡眠
id(deep_sleep_ctrl).begin_sleep();
id(deep_sleep_ctrl).begin_sleep(60000); // 60秒
// 控制睡眠
id(deep_sleep_ctrl).prevent_deep_sleep();
id(deep_sleep_ctrl).allow_deep_sleep();| 代码 | 常量 | 说明 |
|---|---|---|
| 0 | ESP_SLEEP_WAKEUP_UNDEFINED |
重置/首次启动 |
| 2 | ESP_SLEEP_WAKEUP_EXT0 |
外部唤醒 (RTC_IO) |
| 3 | ESP_SLEEP_WAKEUP_EXT1 |
外部唤醒 (RTC_CNTL/GPIO) |
| 4 | ESP_SLEEP_WAKEUP_TIMER |
定时器唤醒 |
| 5 | ESP_SLEEP_WAKEUP_TOUCHPAD |
触摸唤醒 |
| 6 | ESP_SLEEP_WAKEUP_ULP |
ULP 协处理器唤醒 |
logger:
level: DEBUG
logs:
h2_deep_sleep: VERBOSEesphome:
on_boot:
- priority: 800 # 早期执行
then:
- logger.log: "=== BOOT START ==="
- priority: -100 # 晚期执行
then:
- lambda: |-
ESP_LOGI("boot", "Wakeup: %s",
id(deep_sleep_ctrl).get_wakeup_cause_string());# 脚本:模拟不同场景
script:
- id: test_timer_wakeup
then:
- logger.log: "Testing timer wakeup..."
- h2_deep_sleep.enter:
id: deep_sleep_ctrl
sleep_duration: 10s
- id: test_gpio_wakeup
then:
- logger.log: "Testing GPIO wakeup (toggle GPIO10)..."
- h2_deep_sleep.enter:
id: deep_sleep_ctrl
sleep_duration: 1min感谢 ESPHome 社区和 Anthropic Claude 在开发过程中提供的帮助。
MIT License - 自由使用和修改
欢迎提交 Issue 和 Pull Request!
开发者: [wacsy] 最后更新: 2026-01-19 ESPHome 版本: 2025.12.7+ 支持芯片: ESP32-H2