ESP32 practical power saving: preserve gpio status, external and ULP wake up – 5

Spread the love

With the esp32, when using light sleep, deep sleep, or hibernation, you need to select the wake-up source to turn the device back on.

ESP32 practical power saving preserve gpio status on deep sleep UPL and external wake-up
ESP32 practical power-saving preserve gpio status on deep sleep ULP and external wake-up

There are many solutions; some are the standard RTC or external; others are new entries like touch or ULP.

Suppose you have already read the previous chapter. In that case, you know that there are significant differences in power consumption if you select an external or timer wake-up source, so pay attention to your choice.

We are now going to test an external wake-up source.

Function Block diagram Espressif esp32 Wi-Fi Bluetooth Microcontroller
Function Block diagram Espressif esp32 Wi-Fi Bluetooth Microcontroller

External wake up (ext0), single gpio management

RTC IO module contains logic to trigger wake-up when one of RTC GPIOs is set to a predefined logic level. RTC IO is part of the RTC peripherals power domain, so RTC peripherals will be kept powered on during deep sleep if this wake-up source is requested.

Internal pullup or pulldown resistors can also be used because the RTC IO module is enabled in this mode. They need to be configured by the application using rtc_gpio_pullup_en and rtc_gpio_pulldown_en functions, before calling esp_deep_sleep_start.

In revisions 0 and 1 of the ESP32, this wake-up source is incompatible with ULP and touch wake-up sources.

Warning: after waking up from deep sleep, the IO pin used for wake-up will be configured as RTC IO. Before using this pin as a digital GPIO, reconfigure it using rtc_gpio_deinit(gpio_num) the function.

The command to activate the wake up from a GPIO is

esp_err_t esp_deep_sleep_enable_ext0_wakeup(gpio_num_t gpio_num, int level)

This function uses the external wake-up feature of the RTC_IO peripheral. It will work only if RTC peripherals are kept on during deep sleep.

This feature can monitor any pin which is an RTC IO. Once the pin transitions into the state given by level the the argument, It will wake the chip up.

Return

  • ESP_OK on success
  • ESP_ERR_INVALID_ARG if the selected GPIO is not an RTC GPIO, or the mode is invalid
  • ESP_ERR_INVALID_STATE if wakeup triggers conflict

For more information on the device, refer to “DOIT ESP32 DEV KIT v1 high-resolution pinout and specs“.

Parameters

  • gpio_num: GPIO number used as wakeup source. Only GPIOs which are have RTC functionality can be used: 0,2,4,12-15,25-27,32-39.
  • level: input level which will trigger wakeup (0=low, 1=high)

Here is a basic example sketch:

/*
 *  ESP32
 *  DEEP Sleep and external wake up (ext0) on GPIO4
 *  by Mischianti Renzo <https://mischianti.org>
 *
 *  https://mischianti.org/esp32-practical-power-saving-preserve-gpio-status-external-and-ulp-wake-up-5/
 *
 */

RTC_DATA_ATTR int bootCount = 0;

/*
Method to print the reason by which ESP32
has been awaken from sleep
*/
void print_wakeup_reason(){
  esp_sleep_wakeup_cause_t wakeup_reason;

  wakeup_reason = esp_sleep_get_wakeup_cause();

  switch(wakeup_reason)
  {
    case ESP_SLEEP_WAKEUP_EXT0 : Serial2.println("Wakeup caused by external signal using RTC_IO"); break;
    case ESP_SLEEP_WAKEUP_EXT1 : Serial2.println("Wakeup caused by external signal using RTC_CNTL"); break;
    case ESP_SLEEP_WAKEUP_TIMER : Serial2.println("Wakeup caused by timer"); break;
    case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial2.println("Wakeup caused by touchpad"); break;
    case ESP_SLEEP_WAKEUP_ULP : Serial2.println("Wakeup caused by ULP program"); break;
    default : Serial2.printf("Wakeup was not caused by deep sleep: %d\n",wakeup_reason); break;
  }
}

void setup(){
  Serial2.begin(115200);
  delay(1000); //Take some time to open up the Serial Monitor

  //Increment boot number and print it every reboot
  ++bootCount;
  Serial2.println("Boot number: " + String(bootCount));

  //Print the wakeup reason for ESP32
  print_wakeup_reason();

  /*
  First we configure the wake up source
  We set our ESP32 to wake up for an external trigger.

  Note that using internal pullups/pulldowns also requires
  RTC peripherals to be turned on.
  */
  esp_sleep_enable_ext0_wakeup(GPIO_NUM_4,HIGH);

  //Go to sleep now
  Serial2.println("Going to sleep now");
  esp_deep_sleep_start();
  Serial2.println("This will never be printed");
}

void loop(){
  //This is not going to be called
}

Preserve GPIOs status

It’s also possible to preserve the status of GPIO, which is very useful to preserve open a MOSFET or set the status to the external device.

    if (ESP_SLEEP_WAKEUP_EXT0 == wakeup_reason) {
    	Serial.println("Waked up from external GPIO!");

    	gpio_hold_dis(GPIO_NUM_21);
    	gpio_hold_dis(GPIO_NUM_19);

    	gpio_deep_sleep_hold_dis();
    }else{
		delay(1000);
		Serial.println();
		Serial.println("Start sleep!");
		delay(100);

		if (ESP_OK == gpio_hold_en(GPIO_NUM_21)){
			Serial.println("HOLD 21");
		}else{
			Serial.println("NO HOLD 21");
		}
		if (ESP_OK == gpio_hold_en(GPIO_NUM_19)){
				Serial.println("HOLD 19");
			}else{
				Serial.println("NO HOLD 19");
			}

		esp_sleep_enable_ext0_wakeup(GPIO_NUM_15,LOW);

		gpio_deep_sleep_hold_en();
		//Go to sleep now
		Serial.println("Going to sleep now");
		esp_deep_sleep_start();

		delay(1);
    }

As you can see, you can force the value of GPIO with gpio_hold_en and you need to call gpio_hold_dis to allow it to be changed again.

To enable value preservation after the wake from a deep sleep, you must add gpio_deep_sleep_hold_en() a function to your code.

External wake up (ext1), management of multiple GPIOs

esp32 DOIT DEV KIT v1 Serial2 FTDI multiple external wake-up on breadboard
esp32 DOIT DEV KIT v1 Serial2 FTDI multiple external wake-ups on a breadboard

The RTC controller contains the logic for waking up using multiple RTC GPIOs. One of the two logic states can be used to trigger wake-up:

  • wake up if any of the selected pins is high (ESP_EXT1_WAKEUP_ANY_HIGH)
  • wake up if all the selected pins are low (ESP_EXT1_WAKEUP_ALL_LOW)

The RTC controller implements this wake-up source. RTC peripherals and RTC memories can be powered down in this mode. However, if RTC peripherals are powered down, internal pullup and pulldown resistors will be disabled.

esp_sleep_enable_ext1_wakeup() the function can be used to enable this wake-up source.

To set the bitmask, you can use this command:

esp_err_t esp_sleep_enable_ext1_wakeup(uint64_t mask, esp_sleep_ext1_wakeup_mode_t mode);

This feature can monitor any number of pins that are in RTC IOs. Once any selected pins go into the state given by the mode argument, the chip will be woken up.

Return

  • ESP_OK on success
  • ESP_ERR_INVALID_ARG if any of the selected GPIOs is not an RTC GPIO, or mode is invalid

Parameters

  • mask: bit mask of GPIO numbers which will cause wakeup. Only GPIOs which are have RTC functionality can be used in this bit map: 0,2,4,12-15,25-27,32-39. I create a bitmask generator so simplify the magement.
    There is a problem with GPIOs from 32 to 39, that are not identify from status and can’t be retrieved.
  • mode: select logic function used to determine wakeup condition:
    • ESP_EXT1_WAKEUP_ALL_LOW: wake up when all selected GPIOs are low
    • ESP_EXT1_WAKEUP_ANY_HIGH: wake up when any of the selected GPIOs is high
Title
GPIO Bitmask
BitMask:

You can retrieve which pin is moved with esp_sleep_get_ext1_wakeup_status() that return the bitmask that had to generate the wake-up, return 0 identify that the source isn’t ext1. Many people have a problem with pins from 32 to 39 that return 0, so pay attention.

/*
Method to print the GPIO by which ESP32
has been awaken from sleep
*/
void print_wakeup_GPIO(){
  GPIObitmask = esp_sleep_get_ext1_wakeup_status();

  Serial2.print(GPIObitmask);
  Serial2.print(" Wake UP GPIO: GPIO ");
  Serial2.println((log(GPIObitmask))/log(2), 0);
}

Here is the complete sketch:

/*
 *  ESP32
 *  DEEP Sleep and external wake up (ext1) with bitmask
 *  set on GPIO2 GPIO4 GPIO12
 *  by Mischianti Renzo <https://mischianti.org>
 *
 *  https://mischianti.org/esp32-practical-power-saving-preserve-gpio-status-external-and-ulp-wake-up-5/
 *
 */

// Refer to my GPIO bitmask generator on relativa rticle
#define BUTTON_PIN_BITMASK 0x1014 // 2^2 + 2^4 + 2^12  in hex

RTC_DATA_ATTR int bootCount = 0;
int GPIObitmask;

/*
Method to print the reason by which ESP32
has been awaken from sleep
*/
void print_wakeup_reason(){
  esp_sleep_wakeup_cause_t wakeup_reason;

  wakeup_reason = esp_sleep_get_wakeup_cause();

  switch(wakeup_reason)
  {
    case ESP_SLEEP_WAKEUP_EXT0 : Serial2.println("Wakeup caused by external signal using RTC_IO"); break;
    case ESP_SLEEP_WAKEUP_EXT1 : Serial2.println("Wakeup caused by external signal using RTC_CNTL"); break;
    case ESP_SLEEP_WAKEUP_TIMER : Serial2.println("Wakeup caused by timer"); break;
    case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial2.println("Wakeup caused by touchpad"); break;
    case ESP_SLEEP_WAKEUP_ULP : Serial2.println("Wakeup caused by ULP program"); break;
    default : Serial2.printf("Wakeup was not caused by deep sleep: %d\n",wakeup_reason); break;
  }
}

/*
Method to print the GPIO by which ESP32
has been awaken from sleep
*/
void print_wakeup_GPIO(){
  GPIObitmask = esp_sleep_get_ext1_wakeup_status();

  Serial2.print(GPIObitmask);
  Serial2.print(" Wake UP GPIO: GPIO ");
  Serial2.println((log(GPIObitmask))/log(2), 0);
}

void setup(){
  Serial2.begin(115200);
  delay(1000); //Take some time to open up the Serial Monitor

  //Increment boot number and print it every reboot
  ++bootCount;
  Serial2.println("Boot number: " + String(bootCount));

  //Print the wakeup reason for ESP32
  print_wakeup_reason();

  //Print the wakeup GPIO for ESP32
  print_wakeup_GPIO();

  //If you were to use ext1, you would use it like
  esp_sleep_enable_ext1_wakeup(BUTTON_PIN_BITMASK,ESP_EXT1_WAKEUP_ANY_HIGH);

  //Go to sleep now
  Serial2.println("Going to sleep now");
  esp_deep_sleep_start();
  Serial2.println("This will never be printed");
}

void loop(){
  //This is not going to be called
}

ULP Wake UP

ULP coprocessor is part of the RTC peripherals power domain, and it runs the program stored in RTC slow memory. ULP coprocessor can run while the chip is in sleep mode and may be used to poll sensors, monitor ADC or touch sensor values, and wake up the chip when a specific event is detected. RTC slow memory will be powered on during sleep if this wake-up mode is requested. RTC peripherals will be automatically powered on before the ULP coprocessor starts running the program; once the program stops running, RTC peripherals are automatically powered down again.

esp_sleep_enable_ulp_wakeup() the function is used to enable this wake-up source.

You can upload the ULP program with these commands:

void init_ulp_program() {
    const ulp_insn_t program[] = {
            // initiate wakeup of the SoC
            I_WAKE(),
			// stop the ULP program
            I_HALT()
    };

    size_t load_addr = 0;
    size_t size = sizeof(program)/sizeof(ulp_insn_t);
    ulp_process_macros_and_load(load_addr, program, &size);

    ulp_run(0);
}

The command to set wake up period of ULP is:

esp_err_t ulp_set_wakeup_period(size_t period_index, uint32_t period_us);

Params:
period_index index of the alarm period setting (0 – 4)
period_us a wake-up period in µs

Return
ESP_OK on success
ESP_ERR_INVALID_ARG if period_index is out of range

The ULP command to wake the esp32 are:

        // initiate wakeup of the SoC
        I_WAKE(),
	// stop the ULP program
        I_HALT()

Here is the complete sketch:

/*
 *  ESP32
 *  DEEP Sleep and ULP wake up
 *  by Mischianti Renzo <https://mischianti.org>
 *
 *  https://mischianti.org/esp32-practical-power-saving-preserve-gpio-status-external-and-ulp-wake-up-5/
 *
 */

#include "esp32/ulp.h"
#include "soc/rtc_cntl_reg.h"
#include "driver/rtc_io.h"

void init_ulp_program();

RTC_DATA_ATTR int bootCount = 0;

/*
Method to print the reason by which ESP32
has been awaken from sleep
*/
void print_wakeup_reason(){
  esp_sleep_wakeup_cause_t wakeup_reason;

  wakeup_reason = esp_sleep_get_wakeup_cause();

  switch(wakeup_reason)
  {
    case ESP_SLEEP_WAKEUP_EXT0 : Serial2.println("Wakeup caused by external signal using RTC_IO"); break;
    case ESP_SLEEP_WAKEUP_EXT1 : Serial2.println("Wakeup caused by external signal using RTC_CNTL"); break;
    case ESP_SLEEP_WAKEUP_TIMER : Serial2.println("Wakeup caused by timer"); break;
    case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial2.println("Wakeup caused by touchpad"); break;
    case ESP_SLEEP_WAKEUP_ULP : Serial2.println("Wakeup caused by ULP program"); break;
    default : Serial2.printf("Wakeup was not caused by deep sleep: %d\n",wakeup_reason); break;
  }
}

void setup() {
    Serial2.begin(115200);
    while( !Serial ){}

    Serial2.println("Init");

    //Increment boot number and print it every reboot
    ++bootCount;
    Serial2.println("Boot number: " + String(bootCount));

    //Print the wakeup reason for ESP32
    print_wakeup_reason();

    esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
    if (cause != ESP_SLEEP_WAKEUP_ULP) {
    	Serial2.println("Initializing ULP");
        init_ulp_program();
        /* Set ULP wake up period to 5s */
        ulp_set_wakeup_period(0, 5 * 1000 * 1000);
    }

    Serial2.println("Entering deep sleep\n");
    ESP_ERROR_CHECK( esp_sleep_enable_ulp_wakeup() );

    esp_deep_sleep_start();
}

void loop(){
}

void init_ulp_program() {
    const ulp_insn_t program[] = {
            // initiate wakeup of the SoC
            I_WAKE(),
			// stop the ULP program
            I_HALT()
    };

    size_t load_addr = 0;
    size_t size = sizeof(program)/sizeof(ulp_insn_t);
    ulp_process_macros_and_load(load_addr, program, &size);

    ulp_run(0);
}

The Serial2 output is

Init
Boot number: 1
Wakeup was not caused by deep sleep: 0
Entering deep sleep

Init
Boot number: 2
Wakeup caused by ULP program
ULP wakeup
Entering deep sleep

Init
Boot number: 3
Wakeup caused by ULP program
ULP wakeup
Entering deep sleep

Thanks

  1. ESP32: pinout, specs and Arduino IDE configuration
  2. ESP32: integrated SPIFFS Filesystem
  3. ESP32: manage multiple Serial and logging
  4. ESP32 practical power saving
    1. ESP32 practical power saving: manage WiFi and CPU
    2. ESP32 practical power saving: modem and light sleep
    3. ESP32 practical power saving: deep sleep and hibernation
    4. ESP32 practical power saving: preserve data, timer and touch wake up
    5. ESP32 practical power saving: external and ULP wake up
    6. ESP32 practical power saving: UART and GPIO wake up
  5. ESP32: integrated LittleFS FileSystem
  6. ESP32: integrated FFat (Fat/exFAT) FileSystem
  7. ESP32-wroom-32
    1. ESP32-wroom-32: flash, pinout, specs and IDE configuration
  8. ESP32-CAM
    1. ESP32-CAM: pinout, specs and Arduino IDE configuration
    2. ESP32-CAM: upgrade CamerWebServer with flash features
  9. ESP32: use ethernet w5500 with plain (HTTP) and SSL (HTTPS)
  10. ESP32: use ethernet enc28j60 with plain (HTTP) and SSL (HTTPS)
  11. How to use SD card with esp32
  12. esp32 and esp8266: FAT filesystem on external SPI flash memory
  1. Firmware and OTA update management
    1. Firmware management
      1. ESP32: flash compiled firmware (.bin)
      2. ESP32: flash compiled firmware and filesystem (.bin) with GUI tools
    2. OTA update with Arduino IDE
      1. ESP32 OTA update with Arduino IDE: filesystem, firmware, and password
    3. OTA update with Web Browser
      1. ESP32 OTA update with Web Browser: firmware, filesystem, and authentication
      2. ESP32 OTA update with Web Browser: upload in HTTPS (SSL/TLS) with self-signed certificate
      3. ESP32 OTA update with Web Browser: custom web interface
    4. Self OTA uptate from HTTP server
      1. ESP32 self OTA update firmware from the server
      2. ESP32 self OTA update firmware from the server with version check
      3. ESP32 self-OTA update in HTTPS (SSL/TLS) with trusted self-signed certificate
    5. Non-standard Firmware update
      1. ESP32 firmware and filesystem update from SD card
      2. ESP32 firmware and filesystem update with FTP client
  1. Integrating LAN8720 with ESP32 for Ethernet Connectivity with plain (HTTP) and SSL (HTTPS)
  2. Connecting the EByte E70 to ESP32 c3/s3 devices and a simple sketch example
  3. ESP32-C3: pinout, specs and Arduino IDE configuration

Spread the love

Leave a Reply

Your email address will not be published. Required fields are marked *