In this article, you will see the use of functions to reduce power consumption by putting the board into sleep and how it can be awakened. A part of the article is dedicated to illustrating the application that uses the aforementioned functions with particular reference to data preservation during the sleep period.
Introduction
This article shows the use of functions for reducing the power consumption by putting the board into sleep and how it can be awakened.
I found many articles on this subject and I am grateful to their authors who allowed me to develop the program that I am about to describe.
I also believe that this is an opportunity to solicit observation and criticism as I know that I am not an expert in this field; so I apologize if the reader finds imprecisions or errors.
Background
The ESP32Power.h script contains a set of functions for enter in so called sleep
mode in order to reduce power consumption and functions to set how the board can be awakened. The sleeps modes considered here are essentially light
and deep
- they differ not only in the lesser or greater reduction in current but also in the behavior after waking up; in fact, waking up from deep sleep is an almost restart, i.e., the program is reinitialized and only data in the RTC (Real Time Clock) memory are preserved.
The following table summarizes the possible ways of reducing the power of the ESP32 board in relation to what is achieved by the script in question.
Modem Sleep Mode | Is automatic when dealing with WIFI end/or Bluetooth |
Light Sleep Mode | Implemented |
Deep Sleep Mode | Implemented |
Hibernation Mode | (not full) Implemented |
The wake up types are summarized in the below table:
Timer | Implemented |
Touch pad | Implemented |
External wakeup (ext0 ) | Implemented |
External wakeup (ext1 ) | Implemented |
ULP coprocessor wakeup | Not implemented |
GPIO wakeup | Implemented |
UART wakeup | Implemented but not yet tested |
Using the Code
The sketch
uses a scheduler (see the Code Project article Arduino scheduler) with three events:
- after 2 seconds of start, displays the help and the event is disabled
- every two seconds, the event checks the serial port in order to manage the requests
- every minute, a time is displayed
The command h
shows the help, below are some examples of commands sleeping:
d7
d
eep sleep of 7 seconds lg15
l
ight sleep that can end by GPIO pin or after 15 seconds di
wakes up from d
eep sleep by Ext0 pin
The Hardware
The board is ESP32 DEVKIT TV1 with:
- a connector Female Female connected to pin named
VIM
- a connector Female Female connected to pin named
3V3
- a connector Female Male connected to pin named
D15
- a free connector Male Male
Wake Up Types
The ESP32Power.h script contains Functions, Variables and Constants for dealing with Power Reduction.
In particular, it contains:
- functions for setting the wake up mode and entering in power reduction (these are explained in dedicated paragraphs)
- a
stub
function that is a function called where the board wake up after a deep sleep - two functions for get the clock
- a function that shows the type of wake up
All mode of wake up can be associated to a wake up by timer; in fact, the function pw_EnterSleep(dl, delayType)
being called by all types of wake up allows also to insert the timer wake up (see below).
void pw_EnterSleep(uint32_t dl,int delayType) { pw_sleepTime = dl;
if (dl > 0) esp_sleep_enable_timer_wakeup(dl*1000); if (delayType == PW_LIGHT_SLEEP) esp_light_sleep_start();
else if (delayType == PW_DEEP_SLEEP) {
pw_Clock = pw_getTime2()/1000;
esp_deep_sleep_start();
} else
delay(dl); }
In the following paragraphs, I will use some variables:
dl
the sleep time in milliseconds delayType
the sleep mode, one of the constants PW_LIGHT_SLEEP
, PW_DEEP_SLEEP
touchPin
, touchPins
the pin(s) that can work for touch Threshold
the "sensibility" of the touch pin
, pins
the pin(s) of GPIO or the pin(s) of awakening by Ext1
interrupt mode
the change of state that causes awakening
touchPins
and pins
are array terminated by -1
like:
...
int8_t GPIOpins[] = {GPIO_NUM_13,GPIO_NUM_4, -1};
int8_t touchPins[] = {T0,T3,-1}; int8_t Ext1Pins[] = {GPIO_NUM_13,GPIO_NUM_4, -1};
...
Below is a sample of function to set up the touch wake up:
void pw_wakeByTouch(uint32_t dl,int delayType, int8_t touchPins[],
int Threshold = 40) { for (int8_t i=0;touchPins[i] != -1;i++)
touchAttachInterrupt(touchPins[i], pw_callback, Threshold);
esp_sleep_enable_touchpad_wakeup();
pw_EnterSleep(dl,delayType);
esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_TOUCHPAD);
}
Note that the last statement that disables the wake up source is executed only for light sleep
.
Wake Up by Timer
There are two functions for setting a timer based interrupt:
pw_wakeByTimer(dl, delayType)
pw_EnterSleep(dl, delayType)
They are formally similar, the first is usable only for the wake up by timer and, if deep sleep is required, a (little) hibernation occurs. The second is also called by all types of wake up.
Wake Up by Touch Pad
The two functions:
pw_wakeByTouch(dl, delayType, touchPins, Threshold = 40) // pins array
pw_wakeByTouch(dl, delayType, touchPin, Threshold = 40) // single pin
enter the board in sleep; the board woke up when one of touchPin(s)
is solicited or by timer if dl
is greater than 0
.
If Threshold
is omitted, the default is 40
.
Below are the GPIO pins associated with the touches, the first row contains the RTC pins that can be used for the Ext0
and Ext1
wake ups.
GPIO | 0 | 2 | 4 | 12 | 13 | 14 | 15 | 25 | 26 | 27 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 |
Touch | T1 | T2 | T0 | T5 | T4 | T6 | T3 | | | T7 | T9 | T8 | | | | | | |
To test: Remove a connector from one of the power sources (for example, from 3V3
named pin) and insert it into pin named D4
, then insert the free connector to that connector; in this mode, we have two touch pins (T0
and T3
).
Wake Up by GPIO
All pins can be used for wake up GPIO, but only for light sleep
, the two functions:
pw_wakeByGPIO(dl, pins, mode = GPIO_INTR_HIGH_LEVEL)
pw_wakeByGPIO(dl, pin, mode = GPIO_INTR_HIGH_LEVEL)
Enter the board in sleep, the board woke up when the pin
or one of pins
assumes the state indicated by mode
or by timer if dl
is greater than 0
.
The default mode
is HIGH
(when the pin is linked to power).
To test: To wake up, connect one of the power sources (for example, the 3V3
named pin) to one of the pins named D13
or D4
.
Wake Up by Interrupt
This interrupt can be by a single pin that changes the status (Ext0
):
pw_wakeByExt0(dl, delayType, pin, mode = HIGH)
or a set of pins (Ext1
):
pw_wakeByExt1(dl, delayType, pins, mode = ESP_EXT1_WAKEUP_ANY_HIGH)
The mode
can be ESP_EXT1_WAKEUP_ANY_HIGH
(the default) that is the wake up occurs when one of pins go to HIGH
or ESP_EXT1_WAKEUP_ALL_LOW
that is all pins go to LOW
.
The script contains a function for set the Ext1
pins:
...
inline uint64_t setExt1(uint64_t a, int pin) {return bitSet(a, pin % 40);}
...
void pw_wakeByExt1(uint32_t dl,int delayType, int8_t pins[],
esp_sleep_ext1_wakeup_mode_t mode = ESP_EXT1_WAKEUP_ANY_HIGH) {
uint64_t Ext1 = 0;
for (int8_t i=0; pins[i] != -1;i++)
Ext1 = setExt1(Ext1, pins[i]); esp_sleep_enable_ext1_wakeup(Ext1,mode);
pw_EnterSleep(dl,delayType);
esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_EXT1);
}
To test Ext0: To wake up connect one of the power source (for example, the 3V3
named pin) to the pin named D13
.
To test Ext1: To wake up connect one of the power source (for example, the 3V3
named pin) to one of the pins named D13
or D4
.
Wake Up From Deep Sleep
Unlike light sleep where the contents of the memory are preserved, in deep sleep mode, only the RTC memories are preserved and the CPU and all digital peripherals are powered off. RTC controller, RTC peripherals and ULP co-processor remains powered.
As a consequence of this, upon awakening, the program performs a quasi reset, i.e., it restarts from the setup()
function, however what is present in the RTC memories is preserved and, through the function esp_sleep_get_wakeup_cause()
, it is possible to know if there has been a hardware reset (as when the board connects to the power source) or a simple wake up.
void setup() {
Serial.begin(9600);
delay(100);
if ((int) esp_sleep_get_wakeup_cause() > 0) {
Serial.println(pw_wakeup_reason());
events.begin(cron,pw_Clock);
}
}
The Stub Function
void RTC_IRAM_ATTR esp_wake_deep_sleep(void) {
esp_default_wake_deep_sleep();
pw_Clock = pw_getTime()/1000 - pw_Clock; }
Open or Unaddressed Problems
This paragraph partially reflects my lack of knowledge of the ESP32 card.
The script has a function for Uart wake up but it is not yet tried, the ULP wake up has not been implemented.
The call for wake up from deep sleep by timer has a partial hibernation obtained by turning off the RTC peripherals and the RTC Fast Memory. In this case, I was unable to access the RTC memory from the stub
function, which however had retained its contents. Turning off the RTC Low Memory has not been implemented because it causes loss of the stored data.
The time sleep isn't checked; I found and checked that it is a maximum about 71 minutes for the counter is 32 bits and the values are microseconds, therefore it seems that the counter can be 64 bits, see this article.
Finally, when the deep wake up isn't by timer, I use the RTC controls timer with two identical functions (thanks to this post):
...
#define RTC_CTNL_SLOWCLK_FREQ 160000
...
uint64_t pw_getTime2(void) {
SET_PERI_REG_MASK(RTC_CNTL_TIME_UPDATE_REG, RTC_CNTL_TIME_UPDATE_M);
while (GET_PERI_REG_MASK(RTC_CNTL_TIME_UPDATE_REG, RTC_CNTL_TIME_VALID_M) == 0) { }
uint64_t now = READ_PERI_REG(RTC_CNTL_TIME0_REG);
now |= ((uint64_t) READ_PERI_REG(RTC_CNTL_TIME1_REG)) << 32;
return now * 100 / (RTC_CTNL_SLOWCLK_FREQ / 10000); }
RTC_IRAM_ATTR uint64_t pw_getTime(void) {
SET_PERI_REG_MASK(RTC_CNTL_TIME_UPDATE_REG, RTC_CNTL_TIME_UPDATE_M);
while (GET_PERI_REG_MASK(RTC_CNTL_TIME_UPDATE_REG, RTC_CNTL_TIME_VALID_M) == 0) { }
uint64_t now = READ_PERI_REG(RTC_CNTL_TIME0_REG);
now |= ((uint64_t) READ_PERI_REG(RTC_CNTL_TIME1_REG)) << 32;
return now * 100 / (RTC_CTNL_SLOWCLK_FREQ / 10000); }
Why two functions? Because I am not able to call the function stored in the RTC memory from normal operation but only from the stub
function. Another fact, which I am perplexed about, is the frequency I used is 160kHz that give a less deviation compared to 150kHz.
Known Issues
After the try of interrupt by touch, the GPIO wake up on the same pin used for touch doesn't works (also after a deep sleep).
After a light sleep terminated by a touch, the esp_sleep_get_touchpad_wakeup_status
function crashes so I am not able to know which touch pin caused the interruption.
History
- 30th May, 2021: Initial version