19#include "esp_system.h"
20#include "esp_task_wdt.h"
21#include "driver/gpio.h"
24#include "freertos/FreeRTOS.h"
25#include "freertos/task.h"
28static const char*
TAG =
"SLEEP";
60 const char*
getName()
const override {
return "sleep"; }
87 bool lightSleepConfigured_ =
false;
88 bool lastKeypadWake_ =
false;
94 size_t preSleepCount_ = 0;
95 size_t wakeupCount_ = 0;
108 LOG_I(
TAG,
"Initializing sleep controller");
114 esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
121 LOG_W(
TAG,
"DeepSleep-Diag: boot#%lu reset=%d wake=%d deepEntries=%lu wasDeep=%d",
125 if (cause == ESP_SLEEP_WAKEUP_EXT0 || cause == ESP_SLEEP_WAKEUP_EXT1) {
132 LOG_I(
TAG,
"Light sleep interval: %lu seconds", (
unsigned long)lightSleepIntervalS_);
142 if (!lightSleepConfigured_) {
144 if (lightSleepIntervalS_ > 0) {
145 esp_sleep_enable_timer_wakeup(lightSleepIntervalS_ * 1000000ULL);
149 gpio_wakeup_enable(
EXP_IRQ_PIN, GPIO_INTR_LOW_LEVEL);
150 gpio_wakeup_enable(
CHG_IRQ_PIN, GPIO_INTR_LOW_LEVEL);
151 esp_sleep_enable_gpio_wakeup();
153 lightSleepConfigured_ =
true;
154 LOG_I(
TAG,
"Light sleep configured (GPIO%d+%d + %lus timer)",
159 invokeCallbacks(preSleepCallbacks_, preSleepCount_);
164 LOG_D(
TAG,
"Entering light sleep...");
167 esp_light_sleep_start();
173 lastKeypadWake_ = (gpio_get_level(
EXP_IRQ_PIN) == 0);
180 if (esp_task_wdt_status(
nullptr) == ESP_OK) {
181 esp_task_wdt_reset();
185 esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
186 if (cause == ESP_SLEEP_WAKEUP_GPIO) {
188 }
else if (cause == ESP_SLEEP_WAKEUP_TIMER) {
196 invokeCallbacks(wakeupCallbacks_, wakeupCount_);
203 LOG_I(
TAG,
"Entering deep sleep mode...");
211 esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_ALL);
212 esp_sleep_enable_ext1_wakeup_io(1ULL <<
EXP_IRQ_PIN, ESP_EXT1_WAKEUP_ANY_LOW);
215 esp_deep_sleep_start();
226 esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
229 case ESP_SLEEP_WAKEUP_TIMER:
231 case ESP_SLEEP_WAKEUP_GPIO:
233 case ESP_SLEEP_WAKEUP_TOUCHPAD:
235 case ESP_SLEEP_WAKEUP_EXT0:
237 case ESP_SLEEP_WAKEUP_EXT1:
249 if (lightSleepIntervalS_ == seconds)
return;
251 lightSleepIntervalS_ = seconds;
252 lightSleepConfigured_ =
false;
257 LOG_I(
TAG,
"Light sleep interval set to %lus", (
unsigned long)seconds);
264 LOG_D(
TAG,
"Preparing GPIO for sleep...");
269 keypad->prepareForSleep();
275 power->prepareForSleep();
283 LOG_D(
TAG,
"Stabilizing GPIO after wakeup...");
288 keypad->recoverFromSleep();
294 power->recoverFromSleep();
306bool Esp32SleepController::registerCallback(
SleepCallbackEntry* callbacks,
size_t* count,
308 const char* logLabel) {
310 LOG_W(
TAG,
"%s callback limit reached", logLabel);
315 size_t insertPos = *count;
316 for (
size_t i = 0; i < *count; i++) {
317 if (entry.
priority < callbacks[i].priority) {
324 for (
size_t i = *count; i > insertPos; i--) {
325 callbacks[i] = callbacks[i - 1];
328 callbacks[insertPos] = entry;
331 LOG_I(
TAG,
"Registered %s callback: %s (priority %d)",
342 return registerCallback(preSleepCallbacks_, &preSleepCount_, entry,
"pre-sleep");
351 return registerCallback(wakeupCallbacks_, &wakeupCount_, entry,
"wakeup");
359 if (!moduleName)
return;
362 for (
size_t i = 0; i < preSleepCount_; ) {
363 if (preSleepCallbacks_[i].moduleName &&
364 strcmp(preSleepCallbacks_[i].moduleName, moduleName) == 0) {
366 for (
size_t j = i; j < preSleepCount_ - 1; j++) {
367 preSleepCallbacks_[j] = preSleepCallbacks_[j + 1];
376 for (
size_t i = 0; i < wakeupCount_; ) {
377 if (wakeupCallbacks_[i].moduleName &&
378 strcmp(wakeupCallbacks_[i].moduleName, moduleName) == 0) {
379 for (
size_t j = i; j < wakeupCount_ - 1; j++) {
380 wakeupCallbacks_[j] = wakeupCallbacks_[j + 1];
388 LOG_I(
TAG,
"Unregistered callbacks for: %s", moduleName);
396void Esp32SleepController::invokeCallbacks(
SleepCallbackEntry* callbacks,
size_t count) {
397 for (
size_t i = 0; i < count; i++) {
398 if (callbacks[i].callback) {
399 LOG_D(
TAG,
"Invoking callback: %s", callbacks[i].moduleName);
400 callbacks[i].
callback(callbacks[i].context);
408void Esp32SleepController::loadFromNvs() {
409 static constexpr uint32_t MIN_INTERVAL_S = 10;
410 static constexpr uint32_t MAX_INTERVAL_S = 86400;
413 esp_err_t err = nvs_open(
NVS_NAMESPACE, NVS_READONLY, &handle);
415 uint32_t interval = 0;
417 if (interval < MIN_INTERVAL_S) {
418 interval = MIN_INTERVAL_S;
419 }
else if (interval > MAX_INTERVAL_S) {
420 interval = MAX_INTERVAL_S;
422 lightSleepIntervalS_ = interval;
431void Esp32SleepController::saveToNvs() {
433 esp_err_t err = nvs_open(
NVS_NAMESPACE, NVS_READWRITE, &handle);
CDC Log: logging over TinyUSB CDC and UART.
#define LOG_W(tag, fmt,...)
#define LOG_D(tag, fmt,...)
#define LOG_I(tag, fmt,...)
void unregisterCallbacks(const char *moduleName) override
Removes all callbacks belonging to one module.
uint32_t getLightSleepInterval() const override
void enterDeepSleep() override
Enters deep sleep mode and never returns.
void stabilizeGpioAfterWakeup() override
Restores/stabilizes GPIO/keypad state after wake.
void prepareGpioForSleep() override
Prepares GPIO/keypad state for entering sleep.
bool init() override
Initializes sleep controller configuration and wake-state tracking.
Esp32SleepController()=default
core::ServiceState getState() const override
void clearDeepSleepFlag() override
bool registerPreSleepCallback(const SleepCallbackEntry &entry) override
Registers callback invoked before sleep transition.
void setLightSleepInterval(uint32_t seconds) override
Updates light-sleep interval and persists it.
bool registerWakeupCallback(const SleepCallbackEntry &entry) override
Registers callback invoked after wakeup.
WakeupSource getWakeupSource() const override
Returns last wakeup source reported by ESP-IDF.
bool wasInDeepSleep() const override
bool wasKeypadWakeup() const override
const char * getName() const override
void enterLightSleep() override
Enters light sleep with configured wake sources.
static constexpr uint32_t DEFAULT_LIGHT_SLEEP_INTERVAL_S
Default light-sleep timer interval in seconds.
static Esp32SleepController g_sleepController
Singleton sleep-controller instance.
static constexpr size_t MAX_CALLBACKS
Maximum number of registered callbacks per callback list.
IKeypad * getKeypadInstance()
Returns the singleton keypad service instance.
static RTC_DATA_ATTR uint32_t g_diag_boot_count
static constexpr const char * NVS_KEY_INTERVAL
static constexpr const char * NVS_NAMESPACE
NVS namespace and keys for display settings.
IPowerManager * getPowerManagerInstance()
Returns the singleton power manager instance.
static RTC_DATA_ATTR bool g_was_in_deep_sleep
RTC-retained flag indicating previous deep-sleep state.
static RTC_DATA_ATTR uint32_t g_diag_deep_sleep_count
ISleepController * getSleepControllerInstance()
Returns the singleton sleep controller service instance.