18#include "esp_system.h"
19#include "esp_task_wdt.h"
20#include "driver/gpio.h"
23#include "freertos/FreeRTOS.h"
24#include "freertos/task.h"
27static const char*
TAG =
"SLEEP";
59 const char*
getName()
const override {
return "sleep"; }
85 bool lightSleepConfigured_ =
false;
91 size_t preSleepCount_ = 0;
92 size_t wakeupCount_ = 0;
105 LOG_I(
TAG,
"Initializing sleep controller");
111 esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
118 LOG_W(
TAG,
"DeepSleep-Diag: boot#%lu reset=%d wake=%d deepEntries=%lu wasDeep=%d",
122 if (cause == ESP_SLEEP_WAKEUP_EXT0 || cause == ESP_SLEEP_WAKEUP_EXT1) {
129 LOG_I(
TAG,
"Light sleep interval: %lu seconds", (
unsigned long)lightSleepIntervalS_);
139 if (!lightSleepConfigured_) {
141 if (lightSleepIntervalS_ > 0) {
142 esp_sleep_enable_timer_wakeup(lightSleepIntervalS_ * 1000000ULL);
146 gpio_wakeup_enable(
EXP_IRQ_PIN, GPIO_INTR_LOW_LEVEL);
147 esp_sleep_enable_gpio_wakeup();
149 lightSleepConfigured_ =
true;
150 LOG_I(
TAG,
"Light sleep configured (GPIO%d + %lus timer)",
155 invokeCallbacks(preSleepCallbacks_, preSleepCount_);
160 LOG_D(
TAG,
"Entering light sleep...");
163 esp_light_sleep_start();
170 if (esp_task_wdt_status(
nullptr) == ESP_OK) {
171 esp_task_wdt_reset();
175 esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
176 if (cause == ESP_SLEEP_WAKEUP_GPIO) {
178 }
else if (cause == ESP_SLEEP_WAKEUP_TIMER) {
186 invokeCallbacks(wakeupCallbacks_, wakeupCount_);
193 LOG_I(
TAG,
"Entering deep sleep mode...");
201 esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_ALL);
202 esp_sleep_enable_ext1_wakeup_io(1ULL <<
EXP_IRQ_PIN, ESP_EXT1_WAKEUP_ANY_LOW);
205 esp_deep_sleep_start();
216 esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
219 case ESP_SLEEP_WAKEUP_TIMER:
221 case ESP_SLEEP_WAKEUP_GPIO:
223 case ESP_SLEEP_WAKEUP_TOUCHPAD:
225 case ESP_SLEEP_WAKEUP_EXT0:
227 case ESP_SLEEP_WAKEUP_EXT1:
239 if (lightSleepIntervalS_ == seconds)
return;
241 lightSleepIntervalS_ = seconds;
242 lightSleepConfigured_ =
false;
247 LOG_I(
TAG,
"Light sleep interval set to %lus", (
unsigned long)seconds);
254 LOG_D(
TAG,
"Preparing GPIO for sleep...");
259 keypad->prepareForSleep();
267 LOG_D(
TAG,
"Stabilizing GPIO after wakeup...");
272 keypad->recoverFromSleep();
284bool Esp32SleepController::registerCallback(
SleepCallbackEntry* callbacks,
size_t* count,
286 const char* logLabel) {
288 LOG_W(
TAG,
"%s callback limit reached", logLabel);
293 size_t insertPos = *count;
294 for (
size_t i = 0; i < *count; i++) {
295 if (entry.
priority < callbacks[i].priority) {
302 for (
size_t i = *count; i > insertPos; i--) {
303 callbacks[i] = callbacks[i - 1];
306 callbacks[insertPos] = entry;
309 LOG_I(
TAG,
"Registered %s callback: %s (priority %d)",
320 return registerCallback(preSleepCallbacks_, &preSleepCount_, entry,
"pre-sleep");
329 return registerCallback(wakeupCallbacks_, &wakeupCount_, entry,
"wakeup");
337 if (!moduleName)
return;
340 for (
size_t i = 0; i < preSleepCount_; ) {
341 if (preSleepCallbacks_[i].moduleName &&
342 strcmp(preSleepCallbacks_[i].moduleName, moduleName) == 0) {
344 for (
size_t j = i; j < preSleepCount_ - 1; j++) {
345 preSleepCallbacks_[j] = preSleepCallbacks_[j + 1];
354 for (
size_t i = 0; i < wakeupCount_; ) {
355 if (wakeupCallbacks_[i].moduleName &&
356 strcmp(wakeupCallbacks_[i].moduleName, moduleName) == 0) {
357 for (
size_t j = i; j < wakeupCount_ - 1; j++) {
358 wakeupCallbacks_[j] = wakeupCallbacks_[j + 1];
366 LOG_I(
TAG,
"Unregistered callbacks for: %s", moduleName);
374void Esp32SleepController::invokeCallbacks(
SleepCallbackEntry* callbacks,
size_t count) {
375 for (
size_t i = 0; i < count; i++) {
376 if (callbacks[i].callback) {
377 LOG_D(
TAG,
"Invoking callback: %s", callbacks[i].moduleName);
378 callbacks[i].
callback(callbacks[i].context);
386void Esp32SleepController::loadFromNvs() {
387 static constexpr uint32_t MIN_INTERVAL_S = 10;
388 static constexpr uint32_t MAX_INTERVAL_S = 86400;
391 esp_err_t err = nvs_open(
NVS_NAMESPACE, NVS_READONLY, &handle);
393 uint32_t interval = 0;
395 if (interval < MIN_INTERVAL_S) {
396 interval = MIN_INTERVAL_S;
397 }
else if (interval > MAX_INTERVAL_S) {
398 interval = MAX_INTERVAL_S;
400 lightSleepIntervalS_ = interval;
409void Esp32SleepController::saveToNvs() {
411 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
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.
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.