From 2e3f42aedc6e8fcfb5f562ffbb668cb94c0d6917 Mon Sep 17 00:00:00 2001 From: alan Date: Sat, 4 Jan 2025 14:02:22 +0800 Subject: [PATCH 01/13] add deepsleep module for battery application --- usermods/sleep_manager/README.md | 41 +++ usermods/sleep_manager/sleep_manager.h | 363 +++++++++++++++++++++++++ wled00/usermods_list.cpp | 8 + 3 files changed, 412 insertions(+) create mode 100644 usermods/sleep_manager/README.md create mode 100644 usermods/sleep_manager/sleep_manager.h diff --git a/usermods/sleep_manager/README.md b/usermods/sleep_manager/README.md new file mode 100644 index 0000000000..3018273054 --- /dev/null +++ b/usermods/sleep_manager/README.md @@ -0,0 +1,41 @@ +# Sleep Manager Module for WLED + +## Overview + +The **SleepManager** module for WLED is designed to manage power consumption by enabling the ESP32 to enter deep sleep based on idle time or battery voltage levels. This module allows for more efficient battery use by automatically putting the device to sleep and waking it up based on predefined conditions. It is especially useful for battery-powered setups where minimizing power consumption is essential. + +## Features + +- **Idle-based Sleep**: Automatically puts the device to sleep after a specified period of inactivity. +- **Battery Voltage Monitoring**: Monitors the battery voltage and enters deep sleep when the voltage falls below a set threshold. +- **Custom GPIO Configuration**: Allows configuration of GPIO pins for controlling power actions (e.g., pull-up or pull-down). +- **Touchpad Wakeup**: Supports waking up the device using a touch sensor (configurable via `WAKEUP_TOUCH_PIN`). +- **Voltage Protection**: Prevents deep sleep if the voltage is too low, protecting the device from shutting down unintentionally. +- **Preset Wakeup**: Optionally allows setting a timer to wake the device up after a set period. + +## Configuration + +To configure the **SleepManager** module, you will need to add and adjust the following settings in your WLED setup: + +### Define GPIO Pins (`CONFIGPINS`) + +You can specify a set of GPIO pins for controlling the sleep behavior. The syntax for defining this configuration is: + +```cpp +// GPIO NUM,start pull up(1)down(0),end pull up(1)down(0)dis(-1)... +#define CONFIGPINS 0,1,1, 25,0,1, 26,1,0 +``` + + +Example `plantformio.ini`: + +``` +[env:esp32dev_debug] +extends = esp32_idf_V4 +board = esp32dev +build_unflags = ${common.build_unflags} +build_flags = ${common.build_flags_esp32_V4} + -D USERMOD_SLEEP + ; GPIO NUM,start pull up(1)down(0),end pull up(1)down(0)dis(-1)... + -D CONFIGPINS="25, 0, 1, 26, 0, 1, 27, 1, 0, 0, -1, 1, 4, -1, 0" +``` \ No newline at end of file diff --git a/usermods/sleep_manager/sleep_manager.h b/usermods/sleep_manager/sleep_manager.h new file mode 100644 index 0000000000..c04e8ab569 --- /dev/null +++ b/usermods/sleep_manager/sleep_manager.h @@ -0,0 +1,363 @@ +#ifndef SLEEP_MANAGER_H +#define SLEEP_MANAGER_H + +#include "wled.h" +#include "esp_pm.h" +#include "esp_system.h" +#include +#include +#ifndef WAKEUP_TOUCH_PIN +#ifdef CONFIG_IDF_TARGET_ESP32S3 // ESP32S3 +#define WAKEUP_TOUCH_PIN 5 +#else +#define WAKEUP_TOUCH_PIN 15 +#endif +#endif // WAKEUP_TOUCH_PIN +#ifndef CONFIGPINS +#define CONFIGPINS 0, 1, 1 // GPIO NUM,start pull up(1)down(0),end pull up(1)down(0)dis(-1)... +#endif +class SleepManager : public Usermod +{ +private: + boolean enableSleep = false; + boolean sleepOnIdle = false; + int voltagePin = 34; + unsigned long voltageCheckInterval = 5; + unsigned long lastVoltageCheckTime = 0; + float voltage; + unsigned long lastLedOffTime = -1; + unsigned long idleWaitSeconds = 60; + bool isTestingBattery = false; + bool sleepNextLoop = false; + float minVoltage = 3.0; + float maxVoltage = 4.2; + float voltageDivRatio = 2.0; + float currentVoltage = 0.0; + bool presetWakeup = true; + +public: + virtual void setup() + { + phase_wakeup_reason(); + int confGpioPins[] = {CONFIGPINS}; + configureGpios(confGpioPins, sizeof(confGpioPins) / sizeof(confGpioPins[0]), true); + } + + virtual void loop() + { + if (!enableSleep) + { + return; + } + unsigned long currentMillis = millis(); + + if (currentMillis - lastVoltageCheckTime >= voltageCheckInterval * 1000) + { + lastVoltageCheckTime = currentMillis; + if (sleepNextLoop) + { + startDeepSeelp(true); + } + + if (sleepOnIdle && lastLedOffTime != -1 && currentMillis - lastLedOffTime > idleWaitSeconds * 1000) + { + DEBUG_PRINTLN("sleep on idle..."); + startDeepSeelp(false); + } + + float voltage = readVoltage() * voltageDivRatio; + DEBUG_PRINTF("Current voltage on IO%d: %.3f\n", voltagePin, voltage); + if (isTestingBattery) + { + minVoltage = voltage; + serializeConfig(); + } + + if (voltage < minVoltage) + { + if (voltage != 0) + { + DEBUG_PRINTLN("Voltage is below threshold. Entering deep sleep..."); + startDeepSeelp(false); + } + } + } + } + + virtual void onStateChange(uint8_t mode) + { + DEBUG_PRINTF("current bri value: %d,effect: %d\n", bri, effectCurrent); + if (bri == 0) + { + lastLedOffTime = millis(); + } + else + { + lastLedOffTime = -1; + } + } + + void startDeepSeelp(bool immediate) + { + if (immediate) + { + DEBUG_PRINTLN("Entering deep sleep..."); + if (presetWakeup) + { + int nextWakeupMin = findNextTimerInterval() - 1; + if (nextWakeupMin > 0) + { + esp_sleep_enable_timer_wakeup(nextWakeupMin * 60ULL * 1000000ULL); // wakeup for preset + DEBUG_PRINTF("wakeup after %d minites", nextWakeupMin); + DEBUG_PRINTLN(""); + } + } + int confGpioPins[] = {CONFIGPINS}; + configureGpios(confGpioPins, sizeof(confGpioPins) / sizeof(confGpioPins[0]), true); + WiFi.disconnect(); + WiFi.mode(WIFI_OFF); + touchSleepWakeUpEnable(WAKEUP_TOUCH_PIN, touchThreshold); + // ESP_ERROR_CHECK(esp_sleep_enable_ext0_wakeup(GPIO_NUM_26, 0)); // Conflicting wake-up triggers: touch ext0 + ESP_ERROR_CHECK(esp_sleep_enable_ext1_wakeup(1ULL << GPIO_NUM_0, ESP_EXT1_WAKEUP_ALL_LOW)); + delay(2000); // wati gpio level and wifi module restore ... +#ifndef ARDUINO_ARCH_ESP32C3 +#endif + esp_deep_sleep_start(); + } + else + { + sleepNextLoop = true; + briLast = bri; + bri = 0; + stateUpdated(CALL_MODE_DIRECT_CHANGE); + } + } + + void configureGpios(int gpioPins[], int size, bool start) + { + for (int i = 0; i < size; i += 3) + { + int gpioPin = gpioPins[i]; + int startFlag = gpioPins[i + 1]; + int endFlag = gpioPins[i + 2]; + + if (start) + { + if (startFlag == 1) + { + pull_up_down(gpioPin, true, false); + } + else if (startFlag == 0) + { + pull_up_down(gpioPin, false, true); + } + else + { + pull_up_down(gpioPin, false, false); + } + } + else + { + if (endFlag == 1) + { + pull_up_down(gpioPin, true, false); + } + else if (endFlag == 0) + { + pull_up_down(gpioPin, false, true); + } + else + { + pull_up_down(gpioPin, false, false); + } + } + } + } + + void pull_up_down(int gpioPin, bool up, bool down) + { + gpio_pullup_dis((gpio_num_t)gpioPin); + gpio_pulldown_dis((gpio_num_t)gpioPin); + if (up) + { + ESP_ERROR_CHECK(gpio_pullup_en((gpio_num_t)gpioPin)); + } + if (down) + { + ESP_ERROR_CHECK(gpio_pulldown_en((gpio_num_t)gpioPin)); + } + } + + float readVoltage() + { + int adcValue = analogRead(voltagePin); + float voltageOut = (adcValue / float(4095)) * 3.3; + return voltageOut; + } + + // add wakeup reson,battery info + void addToJsonInfo(JsonObject &root) + { + JsonObject user = root["u"]; + if (user.isNull()) + user = root.createNestedObject("u"); + + currentVoltage = readVoltage(); + + float batteryPercentage = calculateBatteryPercentage(currentVoltage * voltageDivRatio); + + JsonArray percentage = user.createNestedArray(F("Battery Percentage")); + JsonArray voltage = user.createNestedArray(F("Current Voltage")); + JsonArray boot = user.createNestedArray(F("boot type")); + percentage.add(batteryPercentage); + percentage.add(F(" %")); + voltage.add(round(currentVoltage * voltageDivRatio * 100.0) / 100.0); + voltage.add(F(" V")); + boot.add(F(phase_wakeup_reason())); + } + + // 在配置中添加电压相关配置 + void addToConfig(JsonObject &root) + { + DEBUG_PRINTLN("sleep module addToConfig"); + JsonObject top = root.createNestedObject("Sleep Module"); + + top["enable Sleep"] = enableSleep; + top["voltage Check Interval"] = voltageCheckInterval; + top["voltage Pin"] = voltagePin; + top["Min Voltage"] = minVoltage; + top["Max Voltage"] = maxVoltage; + top["Voltage Div Ratio"] = voltageDivRatio; + top["sleep On Idle"] = sleepOnIdle; + top["idle Wait Seconds"] = idleWaitSeconds; + top["battery Test Enabled"] = false; + top["preset Wakeup"] = presetWakeup; + } + + bool readFromConfig(JsonObject &root) + { + DEBUG_PRINTLN("sleep module readFromConfig"); + JsonObject top = root["Sleep Module"]; + bool configComplete = !top.isNull(); + + configComplete &= getJsonValue(top["enable Sleep"], enableSleep); + configComplete &= getJsonValue(top["voltage Check Interval"], voltageCheckInterval); + configComplete &= getJsonValue(top["voltage Pin"], voltagePin); + configComplete &= getJsonValue(top["Min Voltage"], minVoltage); + configComplete &= getJsonValue(top["Max Voltage"], maxVoltage); + configComplete &= getJsonValue(top["Voltage Div Ratio"], voltageDivRatio); + configComplete &= getJsonValue(top["sleep On Idle"], sleepOnIdle); + configComplete &= getJsonValue(top["idle Wait Seconds"], idleWaitSeconds); + configComplete &= getJsonValue(top["battery Test Enabled"], isTestingBattery); + configComplete &= getJsonValue(top["preset Wakeup"], presetWakeup); + + return configComplete; + } + + int calculateBatteryPercentage(float voltage) + { + int percent = (voltage - minVoltage) / (maxVoltage - minVoltage) * 100.0; + if (percent < 0) + { + percent = 0; + } + if (percent > 100) + { + percent = 100; + } + return percent; + } + + int calculateTimeDifference(int hour1, int minute1, int hour2, int minute2) + { + int totalMinutes1 = hour1 * 60 + minute1; + int totalMinutes2 = hour2 * 60 + minute2; + if (totalMinutes2 < totalMinutes1) + { + totalMinutes2 += 24 * 60; + } + return totalMinutes2 - totalMinutes1; + } + + int findNextTimerInterval() + { + int currentHour = hour(localTime), currentMinute = minute(localTime), currentWeekday = weekdayMondayFirst(); + int minDifference = INT_MAX; + + for (uint8_t i = 0; i < 8; i++) + { + if (!(timerMacro[i] != 0 && (timerWeekday[i] & 0x01))) + { + continue; + } + + for (int dayOffset = 0; dayOffset < 7; dayOffset++) + { + int checkWeekday = ((currentWeekday + dayOffset) % 7); // 1-7 + if (checkWeekday == 0) + { + checkWeekday = 7; + } + + if ((timerWeekday[i] >> (checkWeekday)) & 0x01) + { + if (dayOffset == 0 && + (timerHours[i] < currentHour || + (timerHours[i] == currentHour && timerMinutes[i] <= currentMinute))) + { + continue; + } + + int targetHour = timerHours[i]; + int targetMinute = timerMinutes[i]; + int timeDifference = calculateTimeDifference( + currentHour, currentMinute, + targetHour + (dayOffset * 24), targetMinute); + + if (timeDifference < minDifference) + { + minDifference = timeDifference; + } + } + } + } + return minDifference; + } + const char *phase_wakeup_reason() + { + static char reson[20]; + esp_sleep_wakeup_cause_t wakeup_reason; + + wakeup_reason = esp_sleep_get_wakeup_cause(); + + switch (wakeup_reason) + { + case ESP_SLEEP_WAKEUP_EXT0: + DEBUG_PRINTLN("Wakeup caused by external signal using RTC_IO"); + strcpy(reson, "RTC_IO"); + break; + case ESP_SLEEP_WAKEUP_EXT1: + DEBUG_PRINTLN("Wakeup caused by external signal using RTC_CNTL"); + strcpy(reson, "RTC_CNTL"); + break; + case ESP_SLEEP_WAKEUP_TIMER: + DEBUG_PRINTLN("Wakeup caused by timer"); + strcpy(reson, "timer"); + break; + case ESP_SLEEP_WAKEUP_TOUCHPAD: + DEBUG_PRINTLN("Wakeup caused by touchpad"); + strcpy(reson, "touchpad"); + break; + case ESP_SLEEP_WAKEUP_ULP: + DEBUG_PRINTLN("Wakeup caused by ULP program"); + strcpy(reson, "ULP"); + break; + default: + DEBUG_PRINTF("Wakeup was not caused by deep sleep: %d\n", wakeup_reason); + snprintf(reson, sizeof(reson), "other %d", wakeup_reason); + break; + } + return reson; + } +}; +#endif diff --git a/wled00/usermods_list.cpp b/wled00/usermods_list.cpp index 3283e013b2..820bfd5a31 100644 --- a/wled00/usermods_list.cpp +++ b/wled00/usermods_list.cpp @@ -242,6 +242,10 @@ #include "../usermods/LD2410_v2/usermod_ld2410.h" #endif +#ifdef USERMOD_SLEEP +#include "../usermods/sleep_manager/sleep_manager.h" +#endif + void registerUsermods() { /* @@ -470,4 +474,8 @@ void registerUsermods() #ifdef USERMOD_POV_DISPLAY UsermodManager::add(new PovDisplayUsermod()); #endif + + #ifdef USERMOD_SLEEP + UsermodManager::add(new SleepManager()); + #endif } From 444c1b8e582a01accdc808304b7daefdf8d26d5b Mon Sep 17 00:00:00 2001 From: alan Date: Sun, 5 Jan 2025 00:30:04 +0800 Subject: [PATCH 02/13] merge new Features to exists deep sleep usermod --- usermods/deep_sleep/readme.md | 16 + usermods/deep_sleep/usermod_deep_sleep.h | 257 +++++++++++++--- usermods/sleep_manager/README.md | 41 --- usermods/sleep_manager/sleep_manager.h | 363 ----------------------- wled00/pin_manager.h | 3 +- wled00/usermods_list.cpp | 5 - 6 files changed, 235 insertions(+), 450 deletions(-) delete mode 100644 usermods/sleep_manager/README.md delete mode 100644 usermods/sleep_manager/sleep_manager.h diff --git a/usermods/deep_sleep/readme.md b/usermods/deep_sleep/readme.md index 006aa31fd9..a34fca5338 100644 --- a/usermods/deep_sleep/readme.md +++ b/usermods/deep_sleep/readme.md @@ -3,6 +3,14 @@ This usermod unleashes the low power capabilities of th ESP: when you power off your LEDs (using the UI power button or a macro) the ESP will be put into deep sleep mode, reducing power consumption to a minimum. During deep sleep the ESP is shut down completely: no WiFi, no CPU, no outputs. The only way to wake it up is to use an external signal or a button. Once it wakes from deep sleep it reboots so ***make sure to use a boot-up preset.*** +## Features + +- **Idle-based Sleep**: Automatically puts the device to sleep after a specified period of inactivity. +- **Custom GPIO Configuration**: Allows configuration of GPIO pins for controlling power actions (e.g., pull-up or pull-down). +- **Touchpad Wakeup**: Supports waking up the device using a touch sensor. +- **RTC IO Wakeup**: Supports waking up the device using RTC IO. +- **Preset Wakeup**: Optionally allows setting a timer to wake the device up after a set period. + # A word of warning When you disable the WLED option 'Turn LEDs on after power up/reset' and 'DelaySleep' is set to zero the ESP will go into deep sleep directly after power-up and only start WLED after it has been woken up. @@ -61,6 +69,14 @@ To override the default settings, place the `#define` in wled.h or add `-D DEEPS * `DEEPSLEEP_DISABLEPULL` - if defined, internal pullup/pulldown is disabled in deep sleep (default is ebnabled) * `DEEPSLEEP_WAKEUPINTERVAL` - number of seconds after which a wake-up happens automatically, sooner if button is pressed. 0 = never. accuracy is about 2% * `DEEPSLEEP_DELAY` - delay between power-off and sleep +* `DEEPSLEEP_WAKEUP_TOUCH_PIN` - specify GPIO pin used for touch-based wakeup; 5 for ESP32S3, 15 for other models +* `DEEPSLEEP_CONFIGPINS` - configure GPIO pins: NUM=start pull-up (1)/down (0), end pull-up (1)/down (0)/disable (-1),example "4,1,0" - pull io4 up at setup,down before deepsleep +* `DEEPSLEEP_WHEN_LOW_VOLTAGE` - if true, enters deep sleep when low voltage is detected +* `DEEPSLEEP_VOTAGE_PIN` - specify GPIO pin used for voltage monitoring (e.g., pin 34) +* `DEEPSLEEP_MIN_VOTAGE` - minimum voltage threshold to trigger deep sleep (in volts) +* `DEEPSLEEP_VOTAGE_DIV_RATIO` - voltage divider ratio used to monitor voltage on the defined pin +* `DEEPSLEEP_WAKEWHENHIGH` - wake up when the pin goes high (default is when the pin goes low) + example for env build flags: `-D USERMOD_DEEP_SLEEP` diff --git a/usermods/deep_sleep/usermod_deep_sleep.h b/usermods/deep_sleep/usermod_deep_sleep.h index 7f4efd5caf..fd4854c034 100644 --- a/usermods/deep_sleep/usermod_deep_sleep.h +++ b/usermods/deep_sleep/usermod_deep_sleep.h @@ -22,7 +22,16 @@ #ifndef DEEPSLEEP_DELAY #define DEEPSLEEP_DELAY 1 #endif - +#ifndef DEEPSLEEP_WAKEUP_TOUCH_PIN +#ifdef CONFIG_IDF_TARGET_ESP32S3 // ESP32S3 +#define DEEPSLEEP_WAKEUP_TOUCH_PIN 5 +#else +#define DEEPSLEEP_WAKEUP_TOUCH_PIN 15 +#endif +#endif // DEEPSLEEP_WAKEUP_TOUCH_PIN +#ifndef DEEPSLEEP_CONFIGPINS +#define DEEPSLEEP_CONFIGPINS 0, 1, 1 // GPIO NUM,start pull up(1)down(0),end pull up(1)down(0)dis(-1)... +#endif RTC_DATA_ATTR bool powerup = true; // variable in RTC data persists on a reboot class DeepSleepUsermod : public Usermod { @@ -34,10 +43,15 @@ class DeepSleepUsermod : public Usermod { uint8_t wakeupPin = DEEPSLEEP_WAKEUPPIN; uint8_t wakeWhenHigh = DEEPSLEEP_WAKEWHENHIGH; // wake up when pin goes high if 1, triggers on low if 0 bool noPull = true; // use pullup/pulldown resistor + bool enableTouchWakeup = false; + uint8_t touchPin = DEEPSLEEP_WAKEUP_TOUCH_PIN; int wakeupAfter = DEEPSLEEP_WAKEUPINTERVAL; // in seconds, <=0: button only + bool presetWake = true; // wakeup timer for preset int sleepDelay = DEEPSLEEP_DELAY; // in seconds, 0 = immediate int delaycounter = 5; // delay deep sleep at bootup until preset settings are applied uint32_t lastLoopTime = 0; + bool sleepNextLoop = false; // tag for next starting deep sleep + // string that are used multiple time (this will save some flash memory) static const char _name[]; static const char _enabled[]; @@ -62,6 +76,187 @@ class DeepSleepUsermod : public Usermod { return false; } + const char* phase_wakeup_reason() { + static char reson[20]; + esp_sleep_wakeup_cause_t wakeup_reason; + wakeup_reason = esp_sleep_get_wakeup_cause(); + switch (wakeup_reason) { + case ESP_SLEEP_WAKEUP_EXT0: + strcpy(reson, "RTC_IO"); + break; + case ESP_SLEEP_WAKEUP_EXT1: + strcpy(reson, "RTC_CNTL"); + break; + case ESP_SLEEP_WAKEUP_TIMER: + strcpy(reson, "timer"); + break; + case ESP_SLEEP_WAKEUP_TOUCHPAD: + strcpy(reson, "touchpad"); + break; + case ESP_SLEEP_WAKEUP_ULP: + strcpy(reson, "ULP"); + break; + case ESP_SLEEP_WAKEUP_UNDEFINED: + strcpy(reson, "RESET"); + break; + default: + snprintf(reson, sizeof(reson), "%d", wakeup_reason); + break; + } + return reson; + } + + void pull_up_down(int gpioPin, bool up, bool down) { + rtc_gpio_pullup_dis((gpio_num_t)gpioPin); + rtc_gpio_pulldown_dis((gpio_num_t)gpioPin); + if (up) { + ESP_ERROR_CHECK(rtc_gpio_pullup_en((gpio_num_t)gpioPin)); + } + if (down) { + ESP_ERROR_CHECK(rtc_gpio_pulldown_en((gpio_num_t)gpioPin)); + } + } + + void configureGpios(int gpioPins[], int size, bool start) { + for (int i = 2; i < size; i += 3) { + int gpioPin = gpioPins[i - 2]; + int flag = start? gpioPins[i - 1]: gpioPins[i]; + + if (start && flag != -1 && !PinManager::allocatePin(gpioPin, false, PinOwner::UM_DEEP_SLEEP)) { + DEBUG_PRINTF("failed to allocate GPIO for usermod deep sleep: %d\n", gpioPin); + continue; + } + + if (flag == 1) + pull_up_down(gpioPin, true, false); + else if (flag == 0) + pull_up_down(gpioPin, false, true); + } + } + + void startDeepSeelp(bool immediate) { + esp_err_t halerror = ESP_OK; + int nextWakeupMin = 0; + if (immediate) { + DEBUG_PRINTLN("DeepSleep UM: Entering deep sleep..."); + if (presetWake) { + nextWakeupMin = findNextTimerInterval() - 1; // wakeup before next preset + } + if (wakeupAfter > 0) { + nextWakeupMin = nextWakeupMin < wakeupAfter / 60.0 + ? nextWakeupMin + : wakeupAfter / 60.0; + } + if (nextWakeupMin > 0) { + esp_sleep_enable_timer_wakeup(nextWakeupMin * 60ULL * + (uint64_t)1e6); // wakeup for preset + DEBUG_PRINTF("wakeup after %d minites", nextWakeupMin); + DEBUG_PRINTLN(""); + } + WiFi.disconnect(); + WiFi.mode(WIFI_OFF); + int confGpioPins[] = {DEEPSLEEP_CONFIGPINS}; + configureGpios(confGpioPins, + sizeof(confGpioPins) / sizeof(confGpioPins[0]), false); + if (enableTouchWakeup) { + touchSleepWakeUpEnable(touchPin, touchThreshold); + } + #if defined(CONFIG_IDF_TARGET_ESP32C3) // ESP32 C3 + if(noPull) + gpio_sleep_set_pull_mode((gpio_num_t)wakeupPin, GPIO_FLOATING); + else { // enable pullup/pulldown resistor + if(wakeWhenHigh) + gpio_sleep_set_pull_mode((gpio_num_t)wakeupPin, GPIO_PULLDOWN_ONLY); + else + gpio_sleep_set_pull_mode((gpio_num_t)wakeupPin, GPIO_PULLUP_ONLY); + } + if(wakeWhenHigh) + halerror = esp_deep_sleep_enable_gpio_wakeup(1<> (checkWeekday)) & 0x01) { + if (dayOffset == 0 && (timerHours[i] < currentHour || + (timerHours[i] == currentHour && + timerMinutes[i] <= currentMinute))) { + continue; + } + + int targetHour = timerHours[i]; + int targetMinute = timerMinutes[i]; + int timeDifference = calculateTimeDifference( + currentHour, currentMinute, targetHour + (dayOffset * 24), + targetMinute); + + if (timeDifference < minDifference) { + minDifference = timeDifference; + } + } + } + } + return minDifference; + } + public: inline void enable(bool enable) { enabled = enable; } // Enable/Disable the usermod @@ -71,6 +266,10 @@ class DeepSleepUsermod : public Usermod { void setup() { //TODO: if the de-init of RTC pins is required to do it could be done here //rtc_gpio_deinit(wakeupPin); + DEBUG_PRINTF("boot type: %s\n", phase_wakeup_reason()); + int confGpioPins[] = {DEEPSLEEP_CONFIGPINS}; + configureGpios(confGpioPins, sizeof(confGpioPins) / sizeof(confGpioPins[0]), + true); initDone = true; } @@ -102,51 +301,21 @@ class DeepSleepUsermod : public Usermod { return; } - DEBUG_PRINTLN(F("DeepSleep UM: entering deep sleep...")); powerup = false; // turn leds on in all subsequent bootups (overrides Turn LEDs on after power up/reset' at reboot) if(!pin_is_valid(wakeupPin)) return; - esp_err_t halerror = ESP_OK; pinMode(wakeupPin, INPUT); // make sure GPIO is input with pullup/pulldown disabled esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_ALL); //disable all wake-up sources (just in case) - if(wakeupAfter) - esp_sleep_enable_timer_wakeup((uint64_t)wakeupAfter * (uint64_t)1e6); //sleep for x seconds - - #if defined(CONFIG_IDF_TARGET_ESP32C3) // ESP32 C3 - if(noPull) - gpio_sleep_set_pull_mode((gpio_num_t)wakeupPin, GPIO_FLOATING); - else { // enable pullup/pulldown resistor - if(wakeWhenHigh) - gpio_sleep_set_pull_mode((gpio_num_t)wakeupPin, GPIO_PULLDOWN_ONLY); - else - gpio_sleep_set_pull_mode((gpio_num_t)wakeupPin, GPIO_PULLUP_ONLY); - } - if(wakeWhenHigh) - halerror = esp_deep_sleep_enable_gpio_wakeup(1<(0 = never)');")); + oappend(SET_F("addInfo('DeepSleep:presetWake',1,'(wake up before next preset timer)');")); + oappend(SET_F("addInfo('DeepSleep:voltageCheckInterval',1,'seconds');")); oappend(SET_F("addInfo('DeepSleep:delaySleep',1,'seconds (0 = sleep at powerup)');")); // first string is suffix, second string is prefix } diff --git a/usermods/sleep_manager/README.md b/usermods/sleep_manager/README.md deleted file mode 100644 index 3018273054..0000000000 --- a/usermods/sleep_manager/README.md +++ /dev/null @@ -1,41 +0,0 @@ -# Sleep Manager Module for WLED - -## Overview - -The **SleepManager** module for WLED is designed to manage power consumption by enabling the ESP32 to enter deep sleep based on idle time or battery voltage levels. This module allows for more efficient battery use by automatically putting the device to sleep and waking it up based on predefined conditions. It is especially useful for battery-powered setups where minimizing power consumption is essential. - -## Features - -- **Idle-based Sleep**: Automatically puts the device to sleep after a specified period of inactivity. -- **Battery Voltage Monitoring**: Monitors the battery voltage and enters deep sleep when the voltage falls below a set threshold. -- **Custom GPIO Configuration**: Allows configuration of GPIO pins for controlling power actions (e.g., pull-up or pull-down). -- **Touchpad Wakeup**: Supports waking up the device using a touch sensor (configurable via `WAKEUP_TOUCH_PIN`). -- **Voltage Protection**: Prevents deep sleep if the voltage is too low, protecting the device from shutting down unintentionally. -- **Preset Wakeup**: Optionally allows setting a timer to wake the device up after a set period. - -## Configuration - -To configure the **SleepManager** module, you will need to add and adjust the following settings in your WLED setup: - -### Define GPIO Pins (`CONFIGPINS`) - -You can specify a set of GPIO pins for controlling the sleep behavior. The syntax for defining this configuration is: - -```cpp -// GPIO NUM,start pull up(1)down(0),end pull up(1)down(0)dis(-1)... -#define CONFIGPINS 0,1,1, 25,0,1, 26,1,0 -``` - - -Example `plantformio.ini`: - -``` -[env:esp32dev_debug] -extends = esp32_idf_V4 -board = esp32dev -build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags_esp32_V4} - -D USERMOD_SLEEP - ; GPIO NUM,start pull up(1)down(0),end pull up(1)down(0)dis(-1)... - -D CONFIGPINS="25, 0, 1, 26, 0, 1, 27, 1, 0, 0, -1, 1, 4, -1, 0" -``` \ No newline at end of file diff --git a/usermods/sleep_manager/sleep_manager.h b/usermods/sleep_manager/sleep_manager.h deleted file mode 100644 index c04e8ab569..0000000000 --- a/usermods/sleep_manager/sleep_manager.h +++ /dev/null @@ -1,363 +0,0 @@ -#ifndef SLEEP_MANAGER_H -#define SLEEP_MANAGER_H - -#include "wled.h" -#include "esp_pm.h" -#include "esp_system.h" -#include -#include -#ifndef WAKEUP_TOUCH_PIN -#ifdef CONFIG_IDF_TARGET_ESP32S3 // ESP32S3 -#define WAKEUP_TOUCH_PIN 5 -#else -#define WAKEUP_TOUCH_PIN 15 -#endif -#endif // WAKEUP_TOUCH_PIN -#ifndef CONFIGPINS -#define CONFIGPINS 0, 1, 1 // GPIO NUM,start pull up(1)down(0),end pull up(1)down(0)dis(-1)... -#endif -class SleepManager : public Usermod -{ -private: - boolean enableSleep = false; - boolean sleepOnIdle = false; - int voltagePin = 34; - unsigned long voltageCheckInterval = 5; - unsigned long lastVoltageCheckTime = 0; - float voltage; - unsigned long lastLedOffTime = -1; - unsigned long idleWaitSeconds = 60; - bool isTestingBattery = false; - bool sleepNextLoop = false; - float minVoltage = 3.0; - float maxVoltage = 4.2; - float voltageDivRatio = 2.0; - float currentVoltage = 0.0; - bool presetWakeup = true; - -public: - virtual void setup() - { - phase_wakeup_reason(); - int confGpioPins[] = {CONFIGPINS}; - configureGpios(confGpioPins, sizeof(confGpioPins) / sizeof(confGpioPins[0]), true); - } - - virtual void loop() - { - if (!enableSleep) - { - return; - } - unsigned long currentMillis = millis(); - - if (currentMillis - lastVoltageCheckTime >= voltageCheckInterval * 1000) - { - lastVoltageCheckTime = currentMillis; - if (sleepNextLoop) - { - startDeepSeelp(true); - } - - if (sleepOnIdle && lastLedOffTime != -1 && currentMillis - lastLedOffTime > idleWaitSeconds * 1000) - { - DEBUG_PRINTLN("sleep on idle..."); - startDeepSeelp(false); - } - - float voltage = readVoltage() * voltageDivRatio; - DEBUG_PRINTF("Current voltage on IO%d: %.3f\n", voltagePin, voltage); - if (isTestingBattery) - { - minVoltage = voltage; - serializeConfig(); - } - - if (voltage < minVoltage) - { - if (voltage != 0) - { - DEBUG_PRINTLN("Voltage is below threshold. Entering deep sleep..."); - startDeepSeelp(false); - } - } - } - } - - virtual void onStateChange(uint8_t mode) - { - DEBUG_PRINTF("current bri value: %d,effect: %d\n", bri, effectCurrent); - if (bri == 0) - { - lastLedOffTime = millis(); - } - else - { - lastLedOffTime = -1; - } - } - - void startDeepSeelp(bool immediate) - { - if (immediate) - { - DEBUG_PRINTLN("Entering deep sleep..."); - if (presetWakeup) - { - int nextWakeupMin = findNextTimerInterval() - 1; - if (nextWakeupMin > 0) - { - esp_sleep_enable_timer_wakeup(nextWakeupMin * 60ULL * 1000000ULL); // wakeup for preset - DEBUG_PRINTF("wakeup after %d minites", nextWakeupMin); - DEBUG_PRINTLN(""); - } - } - int confGpioPins[] = {CONFIGPINS}; - configureGpios(confGpioPins, sizeof(confGpioPins) / sizeof(confGpioPins[0]), true); - WiFi.disconnect(); - WiFi.mode(WIFI_OFF); - touchSleepWakeUpEnable(WAKEUP_TOUCH_PIN, touchThreshold); - // ESP_ERROR_CHECK(esp_sleep_enable_ext0_wakeup(GPIO_NUM_26, 0)); // Conflicting wake-up triggers: touch ext0 - ESP_ERROR_CHECK(esp_sleep_enable_ext1_wakeup(1ULL << GPIO_NUM_0, ESP_EXT1_WAKEUP_ALL_LOW)); - delay(2000); // wati gpio level and wifi module restore ... -#ifndef ARDUINO_ARCH_ESP32C3 -#endif - esp_deep_sleep_start(); - } - else - { - sleepNextLoop = true; - briLast = bri; - bri = 0; - stateUpdated(CALL_MODE_DIRECT_CHANGE); - } - } - - void configureGpios(int gpioPins[], int size, bool start) - { - for (int i = 0; i < size; i += 3) - { - int gpioPin = gpioPins[i]; - int startFlag = gpioPins[i + 1]; - int endFlag = gpioPins[i + 2]; - - if (start) - { - if (startFlag == 1) - { - pull_up_down(gpioPin, true, false); - } - else if (startFlag == 0) - { - pull_up_down(gpioPin, false, true); - } - else - { - pull_up_down(gpioPin, false, false); - } - } - else - { - if (endFlag == 1) - { - pull_up_down(gpioPin, true, false); - } - else if (endFlag == 0) - { - pull_up_down(gpioPin, false, true); - } - else - { - pull_up_down(gpioPin, false, false); - } - } - } - } - - void pull_up_down(int gpioPin, bool up, bool down) - { - gpio_pullup_dis((gpio_num_t)gpioPin); - gpio_pulldown_dis((gpio_num_t)gpioPin); - if (up) - { - ESP_ERROR_CHECK(gpio_pullup_en((gpio_num_t)gpioPin)); - } - if (down) - { - ESP_ERROR_CHECK(gpio_pulldown_en((gpio_num_t)gpioPin)); - } - } - - float readVoltage() - { - int adcValue = analogRead(voltagePin); - float voltageOut = (adcValue / float(4095)) * 3.3; - return voltageOut; - } - - // add wakeup reson,battery info - void addToJsonInfo(JsonObject &root) - { - JsonObject user = root["u"]; - if (user.isNull()) - user = root.createNestedObject("u"); - - currentVoltage = readVoltage(); - - float batteryPercentage = calculateBatteryPercentage(currentVoltage * voltageDivRatio); - - JsonArray percentage = user.createNestedArray(F("Battery Percentage")); - JsonArray voltage = user.createNestedArray(F("Current Voltage")); - JsonArray boot = user.createNestedArray(F("boot type")); - percentage.add(batteryPercentage); - percentage.add(F(" %")); - voltage.add(round(currentVoltage * voltageDivRatio * 100.0) / 100.0); - voltage.add(F(" V")); - boot.add(F(phase_wakeup_reason())); - } - - // 在配置中添加电压相关配置 - void addToConfig(JsonObject &root) - { - DEBUG_PRINTLN("sleep module addToConfig"); - JsonObject top = root.createNestedObject("Sleep Module"); - - top["enable Sleep"] = enableSleep; - top["voltage Check Interval"] = voltageCheckInterval; - top["voltage Pin"] = voltagePin; - top["Min Voltage"] = minVoltage; - top["Max Voltage"] = maxVoltage; - top["Voltage Div Ratio"] = voltageDivRatio; - top["sleep On Idle"] = sleepOnIdle; - top["idle Wait Seconds"] = idleWaitSeconds; - top["battery Test Enabled"] = false; - top["preset Wakeup"] = presetWakeup; - } - - bool readFromConfig(JsonObject &root) - { - DEBUG_PRINTLN("sleep module readFromConfig"); - JsonObject top = root["Sleep Module"]; - bool configComplete = !top.isNull(); - - configComplete &= getJsonValue(top["enable Sleep"], enableSleep); - configComplete &= getJsonValue(top["voltage Check Interval"], voltageCheckInterval); - configComplete &= getJsonValue(top["voltage Pin"], voltagePin); - configComplete &= getJsonValue(top["Min Voltage"], minVoltage); - configComplete &= getJsonValue(top["Max Voltage"], maxVoltage); - configComplete &= getJsonValue(top["Voltage Div Ratio"], voltageDivRatio); - configComplete &= getJsonValue(top["sleep On Idle"], sleepOnIdle); - configComplete &= getJsonValue(top["idle Wait Seconds"], idleWaitSeconds); - configComplete &= getJsonValue(top["battery Test Enabled"], isTestingBattery); - configComplete &= getJsonValue(top["preset Wakeup"], presetWakeup); - - return configComplete; - } - - int calculateBatteryPercentage(float voltage) - { - int percent = (voltage - minVoltage) / (maxVoltage - minVoltage) * 100.0; - if (percent < 0) - { - percent = 0; - } - if (percent > 100) - { - percent = 100; - } - return percent; - } - - int calculateTimeDifference(int hour1, int minute1, int hour2, int minute2) - { - int totalMinutes1 = hour1 * 60 + minute1; - int totalMinutes2 = hour2 * 60 + minute2; - if (totalMinutes2 < totalMinutes1) - { - totalMinutes2 += 24 * 60; - } - return totalMinutes2 - totalMinutes1; - } - - int findNextTimerInterval() - { - int currentHour = hour(localTime), currentMinute = minute(localTime), currentWeekday = weekdayMondayFirst(); - int minDifference = INT_MAX; - - for (uint8_t i = 0; i < 8; i++) - { - if (!(timerMacro[i] != 0 && (timerWeekday[i] & 0x01))) - { - continue; - } - - for (int dayOffset = 0; dayOffset < 7; dayOffset++) - { - int checkWeekday = ((currentWeekday + dayOffset) % 7); // 1-7 - if (checkWeekday == 0) - { - checkWeekday = 7; - } - - if ((timerWeekday[i] >> (checkWeekday)) & 0x01) - { - if (dayOffset == 0 && - (timerHours[i] < currentHour || - (timerHours[i] == currentHour && timerMinutes[i] <= currentMinute))) - { - continue; - } - - int targetHour = timerHours[i]; - int targetMinute = timerMinutes[i]; - int timeDifference = calculateTimeDifference( - currentHour, currentMinute, - targetHour + (dayOffset * 24), targetMinute); - - if (timeDifference < minDifference) - { - minDifference = timeDifference; - } - } - } - } - return minDifference; - } - const char *phase_wakeup_reason() - { - static char reson[20]; - esp_sleep_wakeup_cause_t wakeup_reason; - - wakeup_reason = esp_sleep_get_wakeup_cause(); - - switch (wakeup_reason) - { - case ESP_SLEEP_WAKEUP_EXT0: - DEBUG_PRINTLN("Wakeup caused by external signal using RTC_IO"); - strcpy(reson, "RTC_IO"); - break; - case ESP_SLEEP_WAKEUP_EXT1: - DEBUG_PRINTLN("Wakeup caused by external signal using RTC_CNTL"); - strcpy(reson, "RTC_CNTL"); - break; - case ESP_SLEEP_WAKEUP_TIMER: - DEBUG_PRINTLN("Wakeup caused by timer"); - strcpy(reson, "timer"); - break; - case ESP_SLEEP_WAKEUP_TOUCHPAD: - DEBUG_PRINTLN("Wakeup caused by touchpad"); - strcpy(reson, "touchpad"); - break; - case ESP_SLEEP_WAKEUP_ULP: - DEBUG_PRINTLN("Wakeup caused by ULP program"); - strcpy(reson, "ULP"); - break; - default: - DEBUG_PRINTF("Wakeup was not caused by deep sleep: %d\n", wakeup_reason); - snprintf(reson, sizeof(reson), "other %d", wakeup_reason); - break; - } - return reson; - } -}; -#endif diff --git a/wled00/pin_manager.h b/wled00/pin_manager.h index 73a4a36564..c129de4e76 100644 --- a/wled00/pin_manager.h +++ b/wled00/pin_manager.h @@ -66,7 +66,8 @@ enum struct PinOwner : uint8_t { UM_LDR_DUSK_DAWN = USERMOD_ID_LDR_DUSK_DAWN, // 0x2B // Usermod "usermod_LDR_Dusk_Dawn_v2.h" UM_MAX17048 = USERMOD_ID_MAX17048, // 0x2F // Usermod "usermod_max17048.h" UM_BME68X = USERMOD_ID_BME68X, // 0x31 // Usermod "usermod_bme68x.h -- Uses "standard" HW_I2C pins - UM_PIXELS_DICE_TRAY = USERMOD_ID_PIXELS_DICE_TRAY // 0x35 // Usermod "pixels_dice_tray.h" -- Needs compile time specified 6 pins for display including SPI. + UM_PIXELS_DICE_TRAY = USERMOD_ID_PIXELS_DICE_TRAY, // 0x36 // Usermod "pixels_dice_tray.h" -- Needs compile time specified 6 pins for display including SPI. + UM_DEEP_SLEEP = USERMOD_ID_DEEP_SLEEP // 0x37 // Usermod "usermod_deep_sleep.h" -- Needs pins to control pmos, nmos, other devices }; static_assert(0u == static_cast(PinOwner::None), "PinOwner::None must be zero, so default array initialization works as expected"); diff --git a/wled00/usermods_list.cpp b/wled00/usermods_list.cpp index ce64cd5eab..6c03617aed 100644 --- a/wled00/usermods_list.cpp +++ b/wled00/usermods_list.cpp @@ -242,11 +242,6 @@ #include "../usermods/LD2410_v2/usermod_ld2410.h" #endif -#ifdef USERMOD_SLEEP -#include "../usermods/sleep_manager/sleep_manager.h" -#endif - - #ifdef USERMOD_DEEP_SLEEP #include "../usermods/deep_sleep/usermod_deep_sleep.h" #endif From 994c7e18088656ee6f33120bd057b7b4c1aa3f6f Mon Sep 17 00:00:00 2001 From: alan Date: Sun, 12 Jan 2025 11:52:38 +0800 Subject: [PATCH 03/13] remove gpio configuretion; add touch pin option --- usermods/deep_sleep/readme.md | 19 +-- usermods/deep_sleep/usermod_deep_sleep.h | 168 ++++++++++------------- wled00/usermods_list.cpp | 1 + 3 files changed, 72 insertions(+), 116 deletions(-) diff --git a/usermods/deep_sleep/readme.md b/usermods/deep_sleep/readme.md index a34fca5338..264f5f584f 100644 --- a/usermods/deep_sleep/readme.md +++ b/usermods/deep_sleep/readme.md @@ -1,15 +1,7 @@ # Deep Sleep usermod This usermod unleashes the low power capabilities of th ESP: when you power off your LEDs (using the UI power button or a macro) the ESP will be put into deep sleep mode, reducing power consumption to a minimum. -During deep sleep the ESP is shut down completely: no WiFi, no CPU, no outputs. The only way to wake it up is to use an external signal or a button. Once it wakes from deep sleep it reboots so ***make sure to use a boot-up preset.*** - -## Features - -- **Idle-based Sleep**: Automatically puts the device to sleep after a specified period of inactivity. -- **Custom GPIO Configuration**: Allows configuration of GPIO pins for controlling power actions (e.g., pull-up or pull-down). -- **Touchpad Wakeup**: Supports waking up the device using a touch sensor. -- **RTC IO Wakeup**: Supports waking up the device using RTC IO. -- **Preset Wakeup**: Optionally allows setting a timer to wake the device up after a set period. +During deep sleep the ESP is shut down completely: no WiFi, no CPU, no outputs. The only way to wake it up is by using an external signal, a button, or an automation timer set to the nearest preset time. Once it wakes from deep sleep it reboots so ***make sure to use a boot-up preset.*** # A word of warning @@ -69,14 +61,7 @@ To override the default settings, place the `#define` in wled.h or add `-D DEEPS * `DEEPSLEEP_DISABLEPULL` - if defined, internal pullup/pulldown is disabled in deep sleep (default is ebnabled) * `DEEPSLEEP_WAKEUPINTERVAL` - number of seconds after which a wake-up happens automatically, sooner if button is pressed. 0 = never. accuracy is about 2% * `DEEPSLEEP_DELAY` - delay between power-off and sleep -* `DEEPSLEEP_WAKEUP_TOUCH_PIN` - specify GPIO pin used for touch-based wakeup; 5 for ESP32S3, 15 for other models -* `DEEPSLEEP_CONFIGPINS` - configure GPIO pins: NUM=start pull-up (1)/down (0), end pull-up (1)/down (0)/disable (-1),example "4,1,0" - pull io4 up at setup,down before deepsleep -* `DEEPSLEEP_WHEN_LOW_VOLTAGE` - if true, enters deep sleep when low voltage is detected -* `DEEPSLEEP_VOTAGE_PIN` - specify GPIO pin used for voltage monitoring (e.g., pin 34) -* `DEEPSLEEP_MIN_VOTAGE` - minimum voltage threshold to trigger deep sleep (in volts) -* `DEEPSLEEP_VOTAGE_DIV_RATIO` - voltage divider ratio used to monitor voltage on the defined pin -* `DEEPSLEEP_WAKEWHENHIGH` - wake up when the pin goes high (default is when the pin goes low) - +* `DEEPSLEEP_WAKEUP_TOUCH_PIN` - specify GPIO pin used for touch-based wakeup example for env build flags: `-D USERMOD_DEEP_SLEEP` diff --git a/usermods/deep_sleep/usermod_deep_sleep.h b/usermods/deep_sleep/usermod_deep_sleep.h index fd4854c034..8fabbfdc58 100644 --- a/usermods/deep_sleep/usermod_deep_sleep.h +++ b/usermods/deep_sleep/usermod_deep_sleep.h @@ -2,6 +2,7 @@ #include "wled.h" #include "driver/rtc_io.h" +#include "soc/touch_sensor_periph.h" #ifdef ESP8266 #error The "Deep Sleep" usermod does not support ESP8266 @@ -22,6 +23,7 @@ #ifndef DEEPSLEEP_DELAY #define DEEPSLEEP_DELAY 1 #endif + #ifndef DEEPSLEEP_WAKEUP_TOUCH_PIN #ifdef CONFIG_IDF_TARGET_ESP32S3 // ESP32S3 #define DEEPSLEEP_WAKEUP_TOUCH_PIN 5 @@ -29,9 +31,6 @@ #define DEEPSLEEP_WAKEUP_TOUCH_PIN 15 #endif #endif // DEEPSLEEP_WAKEUP_TOUCH_PIN -#ifndef DEEPSLEEP_CONFIGPINS -#define DEEPSLEEP_CONFIGPINS 0, 1, 1 // GPIO NUM,start pull up(1)down(0),end pull up(1)down(0)dis(-1)... -#endif RTC_DATA_ATTR bool powerup = true; // variable in RTC data persists on a reboot class DeepSleepUsermod : public Usermod { @@ -106,102 +105,21 @@ class DeepSleepUsermod : public Usermod { return reson; } - void pull_up_down(int gpioPin, bool up, bool down) { - rtc_gpio_pullup_dis((gpio_num_t)gpioPin); - rtc_gpio_pulldown_dis((gpio_num_t)gpioPin); - if (up) { - ESP_ERROR_CHECK(rtc_gpio_pullup_en((gpio_num_t)gpioPin)); - } - if (down) { - ESP_ERROR_CHECK(rtc_gpio_pulldown_en((gpio_num_t)gpioPin)); - } - } - - void configureGpios(int gpioPins[], int size, bool start) { - for (int i = 2; i < size; i += 3) { - int gpioPin = gpioPins[i - 2]; - int flag = start? gpioPins[i - 1]: gpioPins[i]; - - if (start && flag != -1 && !PinManager::allocatePin(gpioPin, false, PinOwner::UM_DEEP_SLEEP)) { - DEBUG_PRINTF("failed to allocate GPIO for usermod deep sleep: %d\n", gpioPin); - continue; - } - - if (flag == 1) - pull_up_down(gpioPin, true, false); - else if (flag == 0) - pull_up_down(gpioPin, false, true); - } - } - void startDeepSeelp(bool immediate) { - esp_err_t halerror = ESP_OK; - int nextWakeupMin = 0; if (immediate) { - DEBUG_PRINTLN("DeepSleep UM: Entering deep sleep..."); - if (presetWake) { - nextWakeupMin = findNextTimerInterval() - 1; // wakeup before next preset - } - if (wakeupAfter > 0) { - nextWakeupMin = nextWakeupMin < wakeupAfter / 60.0 - ? nextWakeupMin - : wakeupAfter / 60.0; - } - if (nextWakeupMin > 0) { - esp_sleep_enable_timer_wakeup(nextWakeupMin * 60ULL * - (uint64_t)1e6); // wakeup for preset - DEBUG_PRINTF("wakeup after %d minites", nextWakeupMin); - DEBUG_PRINTLN(""); - } + esp_err_t halerror = ESP_OK; WiFi.disconnect(); - WiFi.mode(WIFI_OFF); - int confGpioPins[] = {DEEPSLEEP_CONFIGPINS}; - configureGpios(confGpioPins, - sizeof(confGpioPins) / sizeof(confGpioPins[0]), false); + WiFi.mode(WIFI_OFF); // Completely shut down the Wi-Fi module if (enableTouchWakeup) { touchSleepWakeUpEnable(touchPin, touchThreshold); } - #if defined(CONFIG_IDF_TARGET_ESP32C3) // ESP32 C3 - if(noPull) - gpio_sleep_set_pull_mode((gpio_num_t)wakeupPin, GPIO_FLOATING); - else { // enable pullup/pulldown resistor - if(wakeWhenHigh) - gpio_sleep_set_pull_mode((gpio_num_t)wakeupPin, GPIO_PULLDOWN_ONLY); - else - gpio_sleep_set_pull_mode((gpio_num_t)wakeupPin, GPIO_PULLUP_ONLY); - } - if(wakeWhenHigh) - halerror = esp_deep_sleep_enable_gpio_wakeup(1< 0) { + esp_sleep_enable_timer_wakeup(nextWakeupMin * 60ULL * + (uint64_t)1e6); + DEBUG_PRINTF("wakeup after %d minites", nextWakeupMin); + DEBUG_PRINTLN(""); + } + + #if defined(CONFIG_IDF_TARGET_ESP32C3) // ESP32 C3 + if(noPull) + gpio_sleep_set_pull_mode((gpio_num_t)wakeupPin, GPIO_FLOATING); + else { // enable pullup/pulldown resistor + if(wakeWhenHigh) + gpio_sleep_set_pull_mode((gpio_num_t)wakeupPin, GPIO_PULLDOWN_ONLY); + else + gpio_sleep_set_pull_mode((gpio_num_t)wakeupPin, GPIO_PULLUP_ONLY); + } + if(wakeWhenHigh) + halerror = esp_deep_sleep_enable_gpio_wakeup(1< Date: Mon, 13 Jan 2025 11:29:18 +0800 Subject: [PATCH 04/13] config for esp32 c3 --- usermods/deep_sleep/usermod_deep_sleep.h | 49 ++++++++++++++++-------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/usermods/deep_sleep/usermod_deep_sleep.h b/usermods/deep_sleep/usermod_deep_sleep.h index 8fabbfdc58..63051ae8c7 100644 --- a/usermods/deep_sleep/usermod_deep_sleep.h +++ b/usermods/deep_sleep/usermod_deep_sleep.h @@ -2,7 +2,9 @@ #include "wled.h" #include "driver/rtc_io.h" -#include "soc/touch_sensor_periph.h" +#ifndef CONFIG_IDF_TARGET_ESP32C3 + #include "soc/touch_sensor_periph.h" +#endif #ifdef ESP8266 #error The "Deep Sleep" usermod does not support ESP8266 @@ -184,7 +186,9 @@ class DeepSleepUsermod : public Usermod { void setup() { //TODO: if the de-init of RTC pins is required to do it could be done here //rtc_gpio_deinit(wakeupPin); - DEBUG_PRINTF("boot type: %s\n", phase_wakeup_reason()); + #ifdef WLED_DEBUG + DEBUG_PRINTF("boot type: %s\n", phase_wakeup_reason()); + #endif initDone = true; } @@ -280,6 +284,13 @@ class DeepSleepUsermod : public Usermod { //void connected() {} //unused, this is called every time the WiFi is (re)connected + void addToJsonInfo(JsonObject& root) { + JsonObject user = root["u"]; + if (user.isNull()) user = root.createNestedObject("u"); + JsonArray boot = user.createNestedArray(F("boot type")); + boot.add(F(phase_wakeup_reason())); + } + void addToConfig(JsonObject& root) override { JsonObject top = root.createNestedObject(FPSTR(_name)); @@ -288,8 +299,10 @@ void addToConfig(JsonObject& root) override top["gpio"] = wakeupPin; top["wakeWhen"] = wakeWhenHigh; top["pull"] = noPull; - top["enableTouchWakeup"] = enableTouchWakeup; - top["touchPin"] = touchPin; + #ifndef CONFIG_IDF_TARGET_ESP32C3 + top["enableTouchWakeup"] = enableTouchWakeup; + top["touchPin"] = touchPin; + #endif top["presetWake"] = presetWake; top["wakeAfter"] = wakeupAfter; top["delaySleep"] = sleepDelay; @@ -310,8 +323,10 @@ void addToConfig(JsonObject& root) override } configComplete &= getJsonValue(top["wakeWhen"], wakeWhenHigh, DEEPSLEEP_WAKEWHENHIGH); // default to wake on low configComplete &= getJsonValue(top["pull"], noPull, DEEPSLEEP_DISABLEPULL); // default to no pullup/pulldown - configComplete &= getJsonValue(top["enableTouchWakeup"], enableTouchWakeup); - configComplete &= getJsonValue(top["touchPin"], touchPin, DEEPSLEEP_WAKEUP_TOUCH_PIN); + #ifndef CONFIG_IDF_TARGET_ESP32C3 + configComplete &= getJsonValue(top["enableTouchWakeup"], enableTouchWakeup); + configComplete &= getJsonValue(top["touchPin"], touchPin, DEEPSLEEP_WAKEUP_TOUCH_PIN); + #endif configComplete &= getJsonValue(top["presetWake"], presetWake); configComplete &= getJsonValue(top["wakeAfter"], wakeupAfter, DEEPSLEEP_WAKEUPINTERVAL); configComplete &= getJsonValue(top["delaySleep"], sleepDelay, DEEPSLEEP_DELAY); @@ -337,16 +352,18 @@ void addToConfig(JsonObject& root) override oappend(SET_F(");")); } } - // dropdown for touch wakeupPin - touch_sensor_channel_io_map[SOC_TOUCH_SENSOR_NUM]; - oappend(SET_F("dd=addDropdown('DeepSleep','touchPin');")); - for (int pin = 0; pin < SOC_TOUCH_SENSOR_NUM; pin++) { - oappend(SET_F("addOption(dd,'")); - oappend(String(touch_sensor_channel_io_map[pin]).c_str()); - oappend(SET_F("',")); - oappend(String(touch_sensor_channel_io_map[pin]).c_str()); - oappend(SET_F(");")); - } + #ifndef CONFIG_IDF_TARGET_ESP32C3 + // dropdown for touch wakeupPin + touch_sensor_channel_io_map[SOC_TOUCH_SENSOR_NUM]; + oappend(SET_F("dd=addDropdown('DeepSleep','touchPin');")); + for (int pin = 0; pin < SOC_TOUCH_SENSOR_NUM; pin++) { + oappend(SET_F("addOption(dd,'")); + oappend(String(touch_sensor_channel_io_map[pin]).c_str()); + oappend(SET_F("',")); + oappend(String(touch_sensor_channel_io_map[pin]).c_str()); + oappend(SET_F(");")); + } + #endif oappend(SET_F("dd=addDropdown('DeepSleep','wakeWhen');")); oappend(SET_F("addOption(dd,'Low',0);")); From 464abadf1fbf0ba2cfbbb79ec66fb394c0137890 Mon Sep 17 00:00:00 2001 From: alan Date: Mon, 13 Jan 2025 15:47:41 +0800 Subject: [PATCH 05/13] remove wakeup reason from ui --- usermods/deep_sleep/usermod_deep_sleep.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/usermods/deep_sleep/usermod_deep_sleep.h b/usermods/deep_sleep/usermod_deep_sleep.h index 63051ae8c7..a158bf1e1b 100644 --- a/usermods/deep_sleep/usermod_deep_sleep.h +++ b/usermods/deep_sleep/usermod_deep_sleep.h @@ -284,13 +284,6 @@ class DeepSleepUsermod : public Usermod { //void connected() {} //unused, this is called every time the WiFi is (re)connected - void addToJsonInfo(JsonObject& root) { - JsonObject user = root["u"]; - if (user.isNull()) user = root.createNestedObject("u"); - JsonArray boot = user.createNestedArray(F("boot type")); - boot.add(F(phase_wakeup_reason())); - } - void addToConfig(JsonObject& root) override { JsonObject top = root.createNestedObject(FPSTR(_name)); From cc29953113c597f74434a62eeef8f3bbd6efdc8d Mon Sep 17 00:00:00 2001 From: alan Date: Wed, 15 Jan 2025 15:17:59 +0800 Subject: [PATCH 06/13] add #ifdef in debug mode --- usermods/deep_sleep/usermod_deep_sleep.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/usermods/deep_sleep/usermod_deep_sleep.h b/usermods/deep_sleep/usermod_deep_sleep.h index a158bf1e1b..552a377f91 100644 --- a/usermods/deep_sleep/usermod_deep_sleep.h +++ b/usermods/deep_sleep/usermod_deep_sleep.h @@ -77,6 +77,7 @@ class DeepSleepUsermod : public Usermod { return false; } +#ifdef WLED_DEBUG const char* phase_wakeup_reason() { static char reson[20]; esp_sleep_wakeup_cause_t wakeup_reason; @@ -106,6 +107,7 @@ class DeepSleepUsermod : public Usermod { } return reson; } +#endif void startDeepSeelp(bool immediate) { if (immediate) { From 1767bc0897cb72688352e530153a335438ef2dd2 Mon Sep 17 00:00:00 2001 From: alan Date: Wed, 22 Jan 2025 20:30:55 +0800 Subject: [PATCH 07/13] For ESP32-C3 and ESP32-S2 development boards --- usermods/deep_sleep/usermod_deep_sleep.h | 39 ++++++------------------ 1 file changed, 10 insertions(+), 29 deletions(-) diff --git a/usermods/deep_sleep/usermod_deep_sleep.h b/usermods/deep_sleep/usermod_deep_sleep.h index 552a377f91..b5ee47b24f 100644 --- a/usermods/deep_sleep/usermod_deep_sleep.h +++ b/usermods/deep_sleep/usermod_deep_sleep.h @@ -27,11 +27,7 @@ #endif #ifndef DEEPSLEEP_WAKEUP_TOUCH_PIN -#ifdef CONFIG_IDF_TARGET_ESP32S3 // ESP32S3 -#define DEEPSLEEP_WAKEUP_TOUCH_PIN 5 -#else -#define DEEPSLEEP_WAKEUP_TOUCH_PIN 15 -#endif +#define DEEPSLEEP_WAKEUP_TOUCH_PIN -1 #endif // DEEPSLEEP_WAKEUP_TOUCH_PIN RTC_DATA_ATTR bool powerup = true; // variable in RTC data persists on a reboot @@ -109,28 +105,6 @@ class DeepSleepUsermod : public Usermod { } #endif - void startDeepSeelp(bool immediate) { - if (immediate) { - esp_err_t halerror = ESP_OK; - WiFi.disconnect(); - WiFi.mode(WIFI_OFF); // Completely shut down the Wi-Fi module - if (enableTouchWakeup) { - touchSleepWakeUpEnable(touchPin, touchThreshold); - } - delay(2000); // wati gpio level and wifi module restore ... - if (halerror == ESP_OK) - esp_deep_sleep_start(); // go into deep sleep - else - DEBUG_PRINTLN(F("sleep failed")); - } else { - // Not to be used for now - sleepNextLoop = true; - briLast = bri; - bri = 0; - stateUpdated(CALL_MODE_DIRECT_CHANGE); - } - } - int calculateTimeDifference(int hour1, int minute1, int hour2, int minute2) { int totalMinutes1 = hour1 * 60 + minute1; @@ -278,9 +252,15 @@ class DeepSleepUsermod : public Usermod { halerror = esp_sleep_enable_ext1_wakeup(1ULL << wakeupPin, ESP_EXT1_WAKEUP_ALL_LOW); #endif - + WiFi.disconnect(); + WiFi.mode(WIFI_OFF); // Completely shut down the Wi-Fi module + #ifndef CONFIG_IDF_TARGET_ESP32C3 + if (enableTouchWakeup && touchPin) { + touchSleepWakeUpEnable(touchPin, touchThreshold); + } + #endif delay(1); // wait for pin to be ready - if(halerror == ESP_OK) startDeepSeelp(true); // go into deep sleep + if(halerror == ESP_OK) esp_deep_sleep_start(); // go into deep sleep else DEBUG_PRINTLN(F("sleep failed")); } @@ -351,6 +331,7 @@ void addToConfig(JsonObject& root) override // dropdown for touch wakeupPin touch_sensor_channel_io_map[SOC_TOUCH_SENSOR_NUM]; oappend(SET_F("dd=addDropdown('DeepSleep','touchPin');")); + oappend(SET_F("addOption(dd,'Unused',-1);")); for (int pin = 0; pin < SOC_TOUCH_SENSOR_NUM; pin++) { oappend(SET_F("addOption(dd,'")); oappend(String(touch_sensor_channel_io_map[pin]).c_str()); From 439b6d6d39d1d66ab109e10d6e0b453561ec2bca Mon Sep 17 00:00:00 2001 From: alan Date: Thu, 17 Apr 2025 14:30:52 +0800 Subject: [PATCH 08/13] Correct sleep logic for durations shorter than 1 minute --- usermods/deep_sleep/usermod_deep_sleep.h | 31 ++++++++++++------------ wled00/pin_manager.h | 2 +- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/usermods/deep_sleep/usermod_deep_sleep.h b/usermods/deep_sleep/usermod_deep_sleep.h index b5ee47b24f..1b6c69fc6e 100644 --- a/usermods/deep_sleep/usermod_deep_sleep.h +++ b/usermods/deep_sleep/usermod_deep_sleep.h @@ -116,6 +116,10 @@ class DeepSleepUsermod : public Usermod { } int findNextTimerInterval() { + if (localTime < 100000) { + DEBUG_PRINTLN("Invalid local time, skipping timer check."); + return -1; + } int currentHour = hour(localTime), currentMinute = minute(localTime), currentWeekday = weekdayMondayFirst(); int minDifference = INT_MAX; @@ -203,20 +207,18 @@ class DeepSleepUsermod : public Usermod { pinMode(wakeupPin, INPUT); // make sure GPIO is input with pullup/pulldown disabled esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_ALL); //disable all wake-up sources (just in case) - int nextWakeupMin = 0; + uint32_t wakeupAfterSec = 0; if (presetWake) { - nextWakeupMin = findNextTimerInterval() - 1; // wakeup before next preset + wakeupAfterSec = (findNextTimerInterval() - 1) * 60; // wakeup before next preset } if (wakeupAfter) { - nextWakeupMin = nextWakeupMin < wakeupAfter / 60.0 - ? nextWakeupMin - : wakeupAfter / 60.0; + wakeupAfterSec = wakeupAfterSec < wakeupAfter + ? wakeupAfterSec + : wakeupAfter; } - if (nextWakeupMin > 0) { - esp_sleep_enable_timer_wakeup(nextWakeupMin * 60ULL * - (uint64_t)1e6); - DEBUG_PRINTF("wakeup after %d minites", nextWakeupMin); - DEBUG_PRINTLN(""); + if (wakeupAfterSec > 0) { + esp_sleep_enable_timer_wakeup(wakeupAfterSec * (uint64_t)1e6); + DEBUG_PRINTF("wakeup after %d seconds\n", wakeupAfterSec); } #if defined(CONFIG_IDF_TARGET_ESP32C3) // ESP32 C3 @@ -251,14 +253,11 @@ class DeepSleepUsermod : public Usermod { else halerror = esp_sleep_enable_ext1_wakeup(1ULL << wakeupPin, ESP_EXT1_WAKEUP_ALL_LOW); + if (enableTouchWakeup && touchPin) { + touchSleepWakeUpEnable(touchPin, touchThreshold); + } #endif - WiFi.disconnect(); WiFi.mode(WIFI_OFF); // Completely shut down the Wi-Fi module - #ifndef CONFIG_IDF_TARGET_ESP32C3 - if (enableTouchWakeup && touchPin) { - touchSleepWakeUpEnable(touchPin, touchThreshold); - } - #endif delay(1); // wait for pin to be ready if(halerror == ESP_OK) esp_deep_sleep_start(); // go into deep sleep else DEBUG_PRINTLN(F("sleep failed")); diff --git a/wled00/pin_manager.h b/wled00/pin_manager.h index c129de4e76..1efe207e9d 100644 --- a/wled00/pin_manager.h +++ b/wled00/pin_manager.h @@ -66,7 +66,7 @@ enum struct PinOwner : uint8_t { UM_LDR_DUSK_DAWN = USERMOD_ID_LDR_DUSK_DAWN, // 0x2B // Usermod "usermod_LDR_Dusk_Dawn_v2.h" UM_MAX17048 = USERMOD_ID_MAX17048, // 0x2F // Usermod "usermod_max17048.h" UM_BME68X = USERMOD_ID_BME68X, // 0x31 // Usermod "usermod_bme68x.h -- Uses "standard" HW_I2C pins - UM_PIXELS_DICE_TRAY = USERMOD_ID_PIXELS_DICE_TRAY, // 0x36 // Usermod "pixels_dice_tray.h" -- Needs compile time specified 6 pins for display including SPI. + UM_PIXELS_DICE_TRAY = USERMOD_ID_PIXELS_DICE_TRAY, // 0x35 // Usermod "pixels_dice_tray.h" -- Needs compile time specified 6 pins for display including SPI. UM_DEEP_SLEEP = USERMOD_ID_DEEP_SLEEP // 0x37 // Usermod "usermod_deep_sleep.h" -- Needs pins to control pmos, nmos, other devices }; static_assert(0u == static_cast(PinOwner::None), "PinOwner::None must be zero, so default array initialization works as expected"); From 140b5fa3959741d392f7589950fd3634177e7b97 Mon Sep 17 00:00:00 2001 From: alan Date: Tue, 25 Nov 2025 16:48:01 +0800 Subject: [PATCH 09/13] code format --- usermods/deep_sleep/deep_sleep.cpp | 97 +++++++++--------------------- 1 file changed, 28 insertions(+), 69 deletions(-) diff --git a/usermods/deep_sleep/deep_sleep.cpp b/usermods/deep_sleep/deep_sleep.cpp index d8c11c86bf..e152d654d8 100644 --- a/usermods/deep_sleep/deep_sleep.cpp +++ b/usermods/deep_sleep/deep_sleep.cpp @@ -3,7 +3,6 @@ #ifndef CONFIG_IDF_TARGET_ESP32C3 #include "soc/touch_sensor_periph.h" #endif - #ifdef ESP8266 #error The "Deep Sleep" usermod does not support ESP8266 #endif @@ -71,38 +70,6 @@ class DeepSleepUsermod : public Usermod { return false; } -#ifdef WLED_DEBUG - const char* phase_wakeup_reason() { - static char reson[20]; - esp_sleep_wakeup_cause_t wakeup_reason; - wakeup_reason = esp_sleep_get_wakeup_cause(); - switch (wakeup_reason) { - case ESP_SLEEP_WAKEUP_EXT0: - strcpy(reson, "RTC_IO"); - break; - case ESP_SLEEP_WAKEUP_EXT1: - strcpy(reson, "RTC_CNTL"); - break; - case ESP_SLEEP_WAKEUP_TIMER: - strcpy(reson, "timer"); - break; - case ESP_SLEEP_WAKEUP_TOUCHPAD: - strcpy(reson, "touchpad"); - break; - case ESP_SLEEP_WAKEUP_ULP: - strcpy(reson, "ULP"); - break; - case ESP_SLEEP_WAKEUP_UNDEFINED: - strcpy(reson, "RESET"); - break; - default: - snprintf(reson, sizeof(reson), "%d", wakeup_reason); - break; - } - return reson; - } -#endif - int calculateTimeDifference(int hour1, int minute1, int hour2, int minute2) { int totalMinutes1 = hour1 * 60 + minute1; @@ -114,12 +81,13 @@ class DeepSleepUsermod : public Usermod { } int findNextTimerInterval() { - if (localTime < 100000) { - DEBUG_PRINTLN("Invalid local time, skipping timer check."); + if (localTime < 1672502400) { // time invalid before NTP sync 2023-1-1 + DEBUG_PRINTLN("Skipping timer check: local time not yet synchronized."); return -1; } - int currentHour = hour(localTime), currentMinute = minute(localTime), - currentWeekday = weekdayMondayFirst(); + int currentHour = hour(localTime); + int currentMinute = minute(localTime); + int currentWeekday = weekdayMondayFirst(); int minDifference = INT_MAX; for (uint8_t i = 0; i < 8; i++) { @@ -134,18 +102,13 @@ class DeepSleepUsermod : public Usermod { } if ((timerWeekday[i] >> (checkWeekday)) & 0x01) { - if (dayOffset == 0 && (timerHours[i] < currentHour || - (timerHours[i] == currentHour && - timerMinutes[i] <= currentMinute))) { + if (dayOffset == 0 && (timerHours[i] < currentHour || (timerHours[i] == currentHour && timerMinutes[i] <= currentMinute))) { continue; } int targetHour = timerHours[i]; int targetMinute = timerMinutes[i]; - int timeDifference = calculateTimeDifference( - currentHour, currentMinute, targetHour + (dayOffset * 24), - targetMinute); - + int timeDifference = calculateTimeDifference(currentHour, currentMinute, targetHour + (dayOffset * 24), targetMinute); if (timeDifference < minDifference) { minDifference = timeDifference; } @@ -165,7 +128,7 @@ class DeepSleepUsermod : public Usermod { //TODO: if the de-init of RTC pins is required to do it could be done here //rtc_gpio_deinit(wakeupPin); #ifdef WLED_DEBUG - DEBUG_PRINTF("boot type: %s\n", phase_wakeup_reason()); + DEBUG_PRINTF("sleep wakeup cause: %d\n", esp_sleep_get_wakeup_cause()); #endif initDone = true; } @@ -210,9 +173,7 @@ class DeepSleepUsermod : public Usermod { wakeupAfterSec = (findNextTimerInterval() - 1) * 60; // wakeup before next preset } if (wakeupAfter) { - wakeupAfterSec = wakeupAfterSec < wakeupAfter - ? wakeupAfterSec - : wakeupAfter; + wakeupAfterSec = wakeupAfterSec < wakeupAfter ? wakeupAfterSec : wakeupAfter; } if (wakeupAfterSec > 0) { esp_sleep_enable_timer_wakeup(wakeupAfterSec * (uint64_t)1e6); @@ -246,11 +207,9 @@ class DeepSleepUsermod : public Usermod { rtc_gpio_pullup_en((gpio_num_t)wakeupPin); } if (wakeWhenHigh) - halerror = esp_sleep_enable_ext1_wakeup(1ULL << wakeupPin, - ESP_EXT1_WAKEUP_ANY_HIGH); // only RTC pins can be used + halerror = esp_sleep_enable_ext1_wakeup(1ULL << wakeupPin, ESP_EXT1_WAKEUP_ANY_HIGH); // only RTC pins can be used else - halerror = esp_sleep_enable_ext1_wakeup(1ULL << wakeupPin, - ESP_EXT1_WAKEUP_ALL_LOW); + halerror = esp_sleep_enable_ext1_wakeup(1ULL << wakeupPin, ESP_EXT1_WAKEUP_ALL_LOW); if (enableTouchWakeup && touchPin) { touchSleepWakeUpEnable(touchPin, touchThreshold); } @@ -295,10 +254,10 @@ void addToConfig(JsonObject& root) override } configComplete &= getJsonValue(top["wakeWhen"], wakeWhenHigh, DEEPSLEEP_WAKEWHENHIGH); // default to wake on low configComplete &= getJsonValue(top["pull"], noPull, DEEPSLEEP_DISABLEPULL); // default to no pullup/pulldown - #ifndef CONFIG_IDF_TARGET_ESP32C3 - configComplete &= getJsonValue(top["enableTouchWakeup"], enableTouchWakeup); - configComplete &= getJsonValue(top["touchPin"], touchPin, DEEPSLEEP_WAKEUP_TOUCH_PIN); - #endif +#ifndef CONFIG_IDF_TARGET_ESP32C3 + configComplete &= getJsonValue(top["enableTouchWakeup"], enableTouchWakeup); + configComplete &= getJsonValue(top["touchPin"], touchPin, DEEPSLEEP_WAKEUP_TOUCH_PIN); +#endif configComplete &= getJsonValue(top["presetWake"], presetWake); configComplete &= getJsonValue(top["wakeAfter"], wakeupAfter, DEEPSLEEP_WAKEUPINTERVAL); configComplete &= getJsonValue(top["delaySleep"], sleepDelay, DEEPSLEEP_DELAY); @@ -324,19 +283,19 @@ void addToConfig(JsonObject& root) override oappend(SET_F(");")); } } - #ifndef CONFIG_IDF_TARGET_ESP32C3 - // dropdown for touch wakeupPin - touch_sensor_channel_io_map[SOC_TOUCH_SENSOR_NUM]; - oappend(SET_F("dd=addDropdown('DeepSleep','touchPin');")); - oappend(SET_F("addOption(dd,'Unused',-1);")); - for (int pin = 0; pin < SOC_TOUCH_SENSOR_NUM; pin++) { - oappend(SET_F("addOption(dd,'")); - oappend(String(touch_sensor_channel_io_map[pin]).c_str()); - oappend(SET_F("',")); - oappend(String(touch_sensor_channel_io_map[pin]).c_str()); - oappend(SET_F(");")); - } - #endif +#ifndef CONFIG_IDF_TARGET_ESP32C3 + // dropdown for touch wakeupPin + touch_sensor_channel_io_map[SOC_TOUCH_SENSOR_NUM]; + oappend(SET_F("dd=addDropdown('DeepSleep','touchPin');")); + oappend(SET_F("addOption(dd,'Unused',-1);")); + for (int pin = 0; pin < SOC_TOUCH_SENSOR_NUM; pin++) { + oappend(SET_F("addOption(dd,'")); + oappend(String(touch_sensor_channel_io_map[pin]).c_str()); + oappend(SET_F("',")); + oappend(String(touch_sensor_channel_io_map[pin]).c_str()); + oappend(SET_F(");")); + } +#endif oappend(SET_F("dd=addDropdown('DeepSleep','wakeWhen');")); oappend(SET_F("addOption(dd,'Low',0);")); From 7725313a06079c24c54d956b39a93d7299571808 Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Sat, 29 Nov 2025 20:49:06 +0100 Subject: [PATCH 10/13] bugfixes and macro call improvements - fix powerup behaviour: now works as intended i.e. if no sleep delay set, it enters deepsleep on powerup but only then - also wake if no macro is defined - apply macro after wake up even if the window is missed - respect date-range setting of macro - removed non-existing pin from touch list - removed non existing voltageCheck setting - fixed incorrect threshold setting - code cleanup --- usermods/deep_sleep/deep_sleep.cpp | 106 ++++++++++++++++------------- wled00/fcn_declare.h | 1 + 2 files changed, 60 insertions(+), 47 deletions(-) diff --git a/usermods/deep_sleep/deep_sleep.cpp b/usermods/deep_sleep/deep_sleep.cpp index e152d654d8..aa15bf3b9c 100644 --- a/usermods/deep_sleep/deep_sleep.cpp +++ b/usermods/deep_sleep/deep_sleep.cpp @@ -25,13 +25,13 @@ #ifndef DEEPSLEEP_WAKEUP_TOUCH_PIN #define DEEPSLEEP_WAKEUP_TOUCH_PIN -1 -#endif // DEEPSLEEP_WAKEUP_TOUCH_PIN -RTC_DATA_ATTR bool powerup = true; // variable in RTC data persists on a reboot +#endif +RTC_DATA_ATTR bool powerup = true; // this is first boot after power cycle. note: variable in RTC data persists on a reboot +RTC_DATA_ATTR uint8_t wakeupPreset = 0; // preset to apply after deep sleep wakeup (0 = none), set to timer macro preset class DeepSleepUsermod : public Usermod { private: - bool enabled = true; bool initDone = false; uint8_t wakeupPin = DEEPSLEEP_WAKEUPPIN; @@ -42,7 +42,7 @@ class DeepSleepUsermod : public Usermod { int wakeupAfter = DEEPSLEEP_WAKEUPINTERVAL; // in seconds, <=0: button only bool presetWake = true; // wakeup timer for preset int sleepDelay = DEEPSLEEP_DELAY; // in seconds, 0 = immediate - int delaycounter = 5; // delay deep sleep at bootup until preset settings are applied + int delaycounter = 10; // delay deep sleep at bootup until preset settings are applied, force wake up if offmode persists after bootup uint32_t lastLoopTime = 0; bool sleepNextLoop = false; // tag for next starting deep sleep @@ -70,8 +70,8 @@ class DeepSleepUsermod : public Usermod { return false; } - int calculateTimeDifference(int hour1, int minute1, int hour2, - int minute2) { + // functions to calculate time difference between now and next scheduled timer event + int calculateTimeDifference(int hour1, int minute1, int hour2, int minute2) { int totalMinutes1 = hour1 * 60 + minute1; int totalMinutes2 = hour2 * 60 + minute2; if (totalMinutes2 < totalMinutes1) { @@ -87,30 +87,31 @@ class DeepSleepUsermod : public Usermod { } int currentHour = hour(localTime); int currentMinute = minute(localTime); - int currentWeekday = weekdayMondayFirst(); + int currentWeekday = weekdayMondayFirst(); // 1=Monday ... 7=Sunday int minDifference = INT_MAX; for (uint8_t i = 0; i < 8; i++) { - if (!(timerMacro[i] != 0 && (timerWeekday[i] & 0x01))) { - continue; - } - - for (int dayOffset = 0; dayOffset < 7; dayOffset++) { - int checkWeekday = ((currentWeekday + dayOffset) % 7); // 1-7 - if (checkWeekday == 0) { - checkWeekday = 7; - } - - if ((timerWeekday[i] >> (checkWeekday)) & 0x01) { - if (dayOffset == 0 && (timerHours[i] < currentHour || (timerHours[i] == currentHour && timerMinutes[i] <= currentMinute))) { - continue; + // check if timer is enabled and date is in range, also wakes up if no macro is used + if ((timerWeekday[i] & 0x01) && isTodayInDateRange(((timerMonth[i] >> 4) & 0x0F), timerDay[i], timerMonth[i] & 0x0F, timerDayEnd[i])) { + + // if timer is enabled (bit0 of timerWeekday) and date is in range, check all weekdays it is set for + for (int dayOffset = 0; dayOffset < 7; dayOffset++) { + int checkWeekday = ((currentWeekday + dayOffset) % 7); // 1-7, check all weekdays starting from today + if (checkWeekday == 0) { + checkWeekday = 7; // sunday is 7 not 0 } int targetHour = timerHours[i]; int targetMinute = timerMinutes[i]; - int timeDifference = calculateTimeDifference(currentHour, currentMinute, targetHour + (dayOffset * 24), targetMinute); - if (timeDifference < minDifference) { - minDifference = timeDifference; + if ((timerWeekday[i] >> (checkWeekday)) & 0x01) { + if (dayOffset == 0 && (targetHour < currentHour || (targetHour == currentHour && targetMinute <= currentMinute))) + continue; // skip if time has already passed today + + int timeDifference = calculateTimeDifference(currentHour, currentMinute, targetHour + (dayOffset * 24), targetMinute); + if (timeDifference < minDifference) { + minDifference = timeDifference; + wakeupPreset = timerMacro[i]; + } } } } @@ -119,7 +120,6 @@ class DeepSleepUsermod : public Usermod { } public: - inline void enable(bool enable) { enabled = enable; } // Enable/Disable the usermod inline bool isEnabled() { return enabled; } //Get usermod enabled/disabled state @@ -130,26 +130,35 @@ class DeepSleepUsermod : public Usermod { #ifdef WLED_DEBUG DEBUG_PRINTF("sleep wakeup cause: %d\n", esp_sleep_get_wakeup_cause()); #endif + if (esp_sleep_get_wakeup_cause() != ESP_SLEEP_WAKEUP_TIMER) { + wakeupPreset = 0; // not a timed wakeup, don't apply preset + } initDone = true; } void loop() { if (!enabled || !offMode) { // disabled or LEDs are on lastLoopTime = 0; // reset timer + if (delaycounter) delaycounter--; // decrease delay counter if LEDs are on (they are always turned on after a wake-up, see below) + else if (wakeupPreset) applyPreset(wakeupPreset); // apply preset if set, this ensures macro is applied even if we missed the wake-up time return; } if (sleepDelay > 0) { - if(lastLoopTime == 0) lastLoopTime = millis(); // initialize + powerup = false; // disable powerup sleep if delay is set + if (lastLoopTime == 0) lastLoopTime = millis(); // initialize if (millis() - lastLoopTime < sleepDelay * 1000) { - return; // wait until delay is over + return; // wait until delay is over } + } else if (powerup && delaycounter) { + delaycounter--; // on first boot without sleepDelay set, do not force-turn on + delay(1000); // just in case: give user a short ~10s window to turn LEDs on in UI + return; } - - if(powerup == false && delaycounter) { // delay sleep in case a preset is being loaded and turnOnAtBoot is disabled (handleIO() does enable offMode temporarily in this case) + if (powerup == false && delaycounter) { // delay sleep in case a preset is being loaded and turnOnAtBoot is disabled (handleIO() does enable offMode temporarily in this case) delaycounter--; - if(delaycounter == 2 && offMode) { // force turn on, no matter the settings (device is bricked if user set sleepDelay=0, no bootup preset and turnOnAtBoot=false) - if (briS == 0) bri = 10; // turn on at low brightness + if (delaycounter == 1 && offMode) { // force turn on, no matter the settings (device is bricked if user set sleepDelay=0, no bootup preset and turnOnAtBoot=false) + if (briS == 0) bri = 10; // turn on and set low brightness to avoid automatic turn off else bri = briS; strip.setBrightness(bri); // needed to make handleIO() not turn off LEDs (really? does not help in bootup preset) offMode = false; @@ -160,10 +169,9 @@ class DeepSleepUsermod : public Usermod { } return; } - DEBUG_PRINTLN(F("DeepSleep UM: entering deep sleep...")); powerup = false; // turn leds on in all subsequent bootups (overrides Turn LEDs on after power up/reset' at reboot) - if(!pin_is_valid(wakeupPin)) return; + if (!pin_is_valid(wakeupPin)) return; esp_err_t halerror = ESP_OK; pinMode(wakeupPin, INPUT); // make sure GPIO is input with pullup/pulldown disabled esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_ALL); //disable all wake-up sources (just in case) @@ -181,27 +189,27 @@ class DeepSleepUsermod : public Usermod { } #if defined(CONFIG_IDF_TARGET_ESP32C3) // ESP32 C3 - if(noPull) + if (noPull) gpio_sleep_set_pull_mode((gpio_num_t)wakeupPin, GPIO_FLOATING); else { // enable pullup/pulldown resistor - if(wakeWhenHigh) + if (wakeWhenHigh) gpio_sleep_set_pull_mode((gpio_num_t)wakeupPin, GPIO_PULLDOWN_ONLY); else gpio_sleep_set_pull_mode((gpio_num_t)wakeupPin, GPIO_PULLUP_ONLY); } - if(wakeWhenHigh) + if (wakeWhenHigh) halerror = esp_deep_sleep_enable_gpio_wakeup(1<= 0) { + oappend(SET_F("addOption(dd,'")); + oappend(String(touch_sensor_channel_io_map[touchchannel]).c_str()); + oappend(SET_F("',")); + oappend(String(touch_sensor_channel_io_map[touchchannel]).c_str()); + oappend(SET_F(");")); + } } #endif @@ -304,7 +317,6 @@ void addToConfig(JsonObject& root) override oappend(SET_F("addInfo('DeepSleep:pull',1,'','-up/down disable: ');")); // first string is suffix, second string is prefix oappend(SET_F("addInfo('DeepSleep:wakeAfter',1,'seconds (0 = never)');")); oappend(SET_F("addInfo('DeepSleep:presetWake',1,'(wake up before next preset timer)');")); - oappend(SET_F("addInfo('DeepSleep:voltageCheckInterval',1,'seconds');")); oappend(SET_F("addInfo('DeepSleep:delaySleep',1,'seconds (0 = sleep at powerup)');")); // first string is suffix, second string is prefix } diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index 2346ee450b..33b1780f50 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -200,6 +200,7 @@ void getTimeString(char* out); bool checkCountdown(); void setCountdown(); byte weekdayMondayFirst(); +bool isTodayInDateRange(byte monthStart, byte dayStart, byte monthEnd, byte dayEnd); void checkTimers(); void calculateSunriseAndSunset(); void setTimeFromAPI(uint32_t timein); From f0f70a28a4514410cc6c0721cf837db0d6d8e680 Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Sun, 30 Nov 2025 15:54:10 +0100 Subject: [PATCH 11/13] more bugfixes in timing --- usermods/deep_sleep/deep_sleep.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/usermods/deep_sleep/deep_sleep.cpp b/usermods/deep_sleep/deep_sleep.cpp index aa15bf3b9c..d526235797 100644 --- a/usermods/deep_sleep/deep_sleep.cpp +++ b/usermods/deep_sleep/deep_sleep.cpp @@ -24,7 +24,7 @@ #endif #ifndef DEEPSLEEP_WAKEUP_TOUCH_PIN -#define DEEPSLEEP_WAKEUP_TOUCH_PIN -1 +#define DEEPSLEEP_WAKEUP_TOUCH_PIN 1 #endif RTC_DATA_ATTR bool powerup = true; // this is first boot after power cycle. note: variable in RTC data persists on a reboot RTC_DATA_ATTR uint8_t wakeupPreset = 0; // preset to apply after deep sleep wakeup (0 = none), set to timer macro preset @@ -44,7 +44,6 @@ class DeepSleepUsermod : public Usermod { int sleepDelay = DEEPSLEEP_DELAY; // in seconds, 0 = immediate int delaycounter = 10; // delay deep sleep at bootup until preset settings are applied, force wake up if offmode persists after bootup uint32_t lastLoopTime = 0; - bool sleepNextLoop = false; // tag for next starting deep sleep // string that are used multiple time (this will save some flash memory) static const char _name[]; @@ -178,10 +177,14 @@ class DeepSleepUsermod : public Usermod { uint32_t wakeupAfterSec = 0; if (presetWake) { - wakeupAfterSec = (findNextTimerInterval() - 1) * 60; // wakeup before next preset + int nextInterval = findNextTimerInterval(); + if (nextInterval > 1 && nextInterval < INT_MAX) + wakeupAfterSec = (nextInterval - 1) * 60; // wakeup before next preset } - if (wakeupAfter) { - wakeupAfterSec = wakeupAfterSec < wakeupAfter ? wakeupAfterSec : wakeupAfter; + if (wakeupAfter > 0) { // user-defined interval + if (wakeupAfterSec == 0 || (uint32_t)wakeupAfter < wakeupAfterSec) { + wakeupAfterSec = wakeupAfter; + } } if (wakeupAfterSec > 0) { esp_sleep_enable_timer_wakeup(wakeupAfterSec * (uint64_t)1e6); @@ -218,7 +221,7 @@ class DeepSleepUsermod : public Usermod { halerror = esp_sleep_enable_ext1_wakeup(1ULL << wakeupPin, ESP_EXT1_WAKEUP_ANY_HIGH); // only RTC pins can be used else halerror = esp_sleep_enable_ext1_wakeup(1ULL << wakeupPin, ESP_EXT1_WAKEUP_ALL_LOW); - if (enableTouchWakeup && touchPin) { + if (enableTouchWakeup) { #ifdef SOC_TOUCH_VERSION_2 // S2 and S3 use much higher thresholds touchSleepWakeUpEnable(touchPin, touchThreshold << 4); // ESP32 S2 & S3: lower threshold = more sensitive #else From 35e659c8568f2ea91aa61e03d5f04baa92daf620 Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Sat, 10 Jan 2026 11:19:05 +0100 Subject: [PATCH 12/13] fix pullup/pulldown for deep-sleep GPIOs pin init is now working correctly, tested on all platforms --- usermods/deep_sleep/deep_sleep.cpp | 90 ++++++++++++++++-------------- usermods/deep_sleep/readme.md | 2 +- 2 files changed, 49 insertions(+), 43 deletions(-) diff --git a/usermods/deep_sleep/deep_sleep.cpp b/usermods/deep_sleep/deep_sleep.cpp index d526235797..dbdbffb8d7 100644 --- a/usermods/deep_sleep/deep_sleep.cpp +++ b/usermods/deep_sleep/deep_sleep.cpp @@ -32,7 +32,7 @@ RTC_DATA_ATTR uint8_t wakeupPreset = 0; // preset to apply after deep sleep wake class DeepSleepUsermod : public Usermod { private: - bool enabled = true; + bool enabled = false; // do not enable by default bool initDone = false; uint8_t wakeupPin = DEEPSLEEP_WAKEUPPIN; uint8_t wakeWhenHigh = DEEPSLEEP_WAKEWHENHIGH; // wake up when pin goes high if 1, triggers on low if 0 @@ -50,7 +50,7 @@ class DeepSleepUsermod : public Usermod { static const char _enabled[]; bool pin_is_valid(uint8_t wakePin) { - #ifdef CONFIG_IDF_TARGET_ESP32 //ESP32: GPIOs 0,2,4, 12-15, 25-39 can be used for wake-up + #ifdef CONFIG_IDF_TARGET_ESP32 //ESP32: GPIOs 0,2,4, 12-15, 25-39 can be used for wake-up. note: input-only GPIOs 34-39 do not have internal pull resistors if (wakePin == 0 || wakePin == 2 || wakePin == 4 || (wakePin >= 12 && wakePin <= 15) || (wakePin >= 25 && wakePin <= 27) || (wakePin >= 32 && wakePin <= 39)) { return true; } @@ -136,7 +136,8 @@ class DeepSleepUsermod : public Usermod { } void loop() { - if (!enabled || !offMode) { // disabled or LEDs are on + if (!enabled) return; + if (!offMode) { // LEDs are on lastLoopTime = 0; // reset timer if (delaycounter) delaycounter--; // decrease delay counter if LEDs are on (they are always turned on after a wake-up, see below) else if (wakeupPreset) applyPreset(wakeupPreset); // apply preset if set, this ensures macro is applied even if we missed the wake-up time @@ -192,52 +193,58 @@ class DeepSleepUsermod : public Usermod { } #if defined(CONFIG_IDF_TARGET_ESP32C3) // ESP32 C3 - if (noPull) - gpio_sleep_set_pull_mode((gpio_num_t)wakeupPin, GPIO_FLOATING); - else { // enable pullup/pulldown resistor + gpio_hold_dis((gpio_num_t)wakeupPin); // disable hold and configure pin if (wakeWhenHigh) - gpio_sleep_set_pull_mode((gpio_num_t)wakeupPin, GPIO_PULLDOWN_ONLY); + halerror = esp_deep_sleep_enable_gpio_wakeup(1< Date: Sat, 10 Jan 2026 11:25:08 +0100 Subject: [PATCH 13/13] remove unused statement --- usermods/deep_sleep/deep_sleep.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/usermods/deep_sleep/deep_sleep.cpp b/usermods/deep_sleep/deep_sleep.cpp index dbdbffb8d7..08ef61a0c1 100644 --- a/usermods/deep_sleep/deep_sleep.cpp +++ b/usermods/deep_sleep/deep_sleep.cpp @@ -307,7 +307,6 @@ class DeepSleepUsermod : public Usermod { } #ifndef CONFIG_IDF_TARGET_ESP32C3 // dropdown for touch wakeupPin - touch_sensor_channel_io_map[SOC_TOUCH_SENSOR_NUM]; oappend(SET_F("dd=addDropdown('DeepSleep','touchPin');")); for (int touchchannel = 0; touchchannel < SOC_TOUCH_SENSOR_NUM; touchchannel++) { if (touch_sensor_channel_io_map[touchchannel] >= 0) {