¿Cómo activar el modo de suspensión profunda del ESP32 con Arduino IDE?

Libro Guía del Microcontrolador ESP32

Este artículo es una guía completa del modo Deep Sleep del ESP32 con Arduino IDE. Veremos cómo poner el ESP32 en deep sleep y exploraremos los diferentes modos de despertarlo: por timer, por touch y por señal externa.

La guía incluye ejemplos prácticos con código, explicación paso a paso y descripciones de los circuitos para que puedas aplicarlo directamente en tus proyectos.

Este artículo está dividido en cuatro partes: introducción al modo deep sleep, despertar por temporizador, despertar por pin táctil y despertar por señal externa.

Introducción al modo Deep Sleep

El ESP32 puede alternar entre diferentes modos de energía:

Modo activo (Active mode), Modo Modem Sleep, Modo Light Sleep, Modo Deep Sleep y Modo Hibernation.

Según el datasheet oficial de Espressif, la diferencia de consumo entre modos es abismal: en modo activo con Wi-Fi encendido, el ESP32 puede consumir más de 100 mA, mientras que en deep sleep el consumo baja a microamperios. Esa diferencia es la que marca si un proyecto alimentado por batería dura horas o meses.

¿Por qué usar Deep Sleep?

Tener tu ESP32 funcionando en modo activo con baterías no es viable, ya que la energía de las baterías se agotará muy rápidamente. Si pones tu ESP32 en modo deep sleep, reducirás drásticamente el consumo y tus baterías durarán mucho más.

Poner tu ESP32 en deep sleep significa cortar todas las actividades que consumen más energía, dejando solo la actividad mínima necesaria para despertar el procesador cuando ocurra algo interesante. Es un concepto clave para cualquier proyecto IoT que deba funcionar durante largos periodos sin conexión a la red eléctrica.

En deep sleep, ni la CPU ni el Wi-Fi están activos, pero el coprocesador ULP (Ultra Low Power) puede seguir encendido. Esto es lo que permite monitorizar sensores y detectar eventos externos con un consumo mínimo.

Mientras el ESP32 está en deep sleep, la memoria RTC también permanece alimentada. Esto significa que podemos escribir un programa para el coprocesador ULP y almacenarlo en la memoria RTC para acceder a dispositivos periféricos, timers internos y sensores internos.

Este modo es especialmente útil si necesitas despertar la CPU principal mediante un evento externo, un temporizador o ambos, manteniendo un consumo mínimo. Es el patrón ideal para sensores remotos alimentados por batería.

Pines RTC_GPIO

Durante el deep sleep, algunos pines del ESP32 pueden seguir siendo usados por el coprocesador ULP, concretamente los pines RTC_GPIO y los pines táctiles. Estos pines son los únicos que permanecen monitorizables mientras el chip está dormido, y son los que debes usar si necesitas despertar el ESP32 mediante una señal externa.

Fuentes de despertar (Wake Up Sources)

Después de poner el ESP32 en deep sleep, hay varias formas de despertarlo:

Puedes usar el temporizador, despertando tu ESP32 tras periodos de tiempo predefinidos. Puedes usar los pines táctiles, que detectan el contacto con un dedo. Puedes usar dos posibilidades de despertar externo: o bien un único despertar externo (ext0), o varios orígenes de despertar externos distintos (ext1). También puedes usar el coprocesador ULP para despertar, aunque esto no lo cubriremos en esta guía.

Cómo escribir un sketch para Deep Sleep

Para escribir un sketch que ponga tu ESP32 en deep sleep y luego lo despierte, debes tener en cuenta estos puntos clave:

Primero, necesitas configurar las fuentes de despertar. Esto significa decidir qué va a despertar al ESP32. Puedes usar una o combinar más de una fuente de despertar.

Puedes decidir qué periféricos apagar o mantener encendidos durante el deep sleep. Sin embargo, por defecto, el ESP32 apaga automáticamente los periféricos que no son necesarios para la fuente de despertar que definas.

Finalmente, usas la función esp_deep_sleep_start() para poner tu ESP32 en modo deep sleep.

Despertar por temporizador (Timer Wake Up)

El ESP32 puede entrar en modo deep sleep y luego despertar tras periodos de tiempo predefinidos. Esta característica es especialmente útil si estás ejecutando proyectos que requieren sellado de tiempo o tareas diarias, manteniendo un bajo consumo.

El controlador RTC del ESP32 tiene un temporizador integrado que puedes usar para despertar el ESP32 después de una cantidad de tiempo predefinida. Este es el método más sencillo y el más usado en proyectos con sensores que envían datos periódicamente.

Habilitar el despertar por temporizador

Habilitar el ESP32 para despertar tras un tiempo predefinido es muy sencillo. En Arduino IDE, solo tienes que especificar el tiempo de sueño en microsegundos en la siguiente función:

esp_sleep_enable_timer_wakeup(tiempo_en_us)

Ejemplo: Deep Sleep con despertar por temporizador

Para programar el ESP32 usaremos Arduino IDE, así que asegúrate de tener el soporte de placas ESP32 instalado. Abre tu Arduino IDE y ve a Archivo > Ejemplos > ESP32 Deep Sleep, y abre el sketch TimerWakeUp:

/* Simple Deep Sleep with Timer Wake Up
   El ESP32 ofrece un modo deep sleep para un ahorro efectivo de energía.
   En este modo, las CPUs, la mayor parte de la RAM y todos los periféricos
   digitales que se relojan desde APB_CLK se apagan. Las únicas partes del chip
   que pueden seguir encendidas son: controlador RTC, periféricos RTC y memorias RTC.
   Este código muestra el deep sleep más básico con un temporizador para despertarlo
   y cómo almacenar datos en la memoria RTC para usarlos entre reinicios.
   Este código está bajo Licencia de Dominio Público.
   Autor: Pranav Cherukupalli <cherukupallip@gmail.com> */

#define uS_TO_S_FACTOR 1000000ULL  // Factor de conversión de microsegundos a segundos
#define TIME_TO_SLEEP  5           // Tiempo que el ESP32 estará dormido (en segundos)

RTC_DATA_ATTR int bootCount = 0;

// Método para imprimir la razón por la que el ESP32 ha despertado del sueño
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: Serial.println("Despertar causado por señal externa usando RTC_IO"); break;
    case ESP_SLEEP_WAKEUP_EXT1: Serial.println("Despertar causado por señal externa usando RTC_CNTL"); break;
    case ESP_SLEEP_WAKEUP_TIMER: Serial.println("Despertar causado por temporizador"); break;
    case ESP_SLEEP_WAKEUP_TOUCHPAD: Serial.println("Despertar causado por touchpad"); break;
    case ESP_SLEEP_WAKEUP_ULP: Serial.println("Despertar causado por programa ULP"); break;
    default: Serial.printf("El despertar no fue causado por deep sleep: %d\n", wakeup_reason); break;
  }
}

void setup(){
  Serial.begin(115200);
  delay(1000); // Da tiempo a abrir el Monitor Serie

  // Incrementa el número de arranque y lo imprime en cada reinicio
  ++bootCount;
  Serial.println("Número de arranque: " + String(bootCount));

  // Imprime la razón del despertar
  print_wakeup_reason();

  // Primero configuramos la fuente de despertar: cada 5 segundos
  esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
  Serial.println("Configurando ESP32 para dormir cada " + String(TIME_TO_SLEEP) + " segundos");

  // Ahora vamos a dormir
  Serial.println("Entrando en sueño ahora");
  delay(1000);
  Serial.flush();
  esp_deep_sleep_start();
  Serial.println("Esta línea nunca se imprimirá");
}

void loop(){
  // Nunca se llamará
}

Cómo funciona el código paso a paso

La primera línea de comentario del sketch describe qué se apaga durante el deep sleep con timer wake up:

En este modo, las CPUs, la mayor parte de la RAM y todos los periféricos digitales que se relojan desde APB_CLK se apagan. Las únicas partes del chip que pueden seguir encendidas son: controlador RTC, periféricos RTC y memorias RTC.

Definir el tiempo de sueño

Las dos primeras líneas de código definen el periodo de tiempo que el ESP32 estará durmiendo:

#define uS_TO_S_FACTOR 1000000ULL
#define TIME_TO_SLEEP  5

Este ejemplo usa un factor de conversión de microsegundos a segundos, para que puedas ajustar el tiempo de sueño en la variable TIME_TO_SLEEP directamente en segundos. En este caso, el ejemplo pondrá el ESP32 en deep sleep durante 5 segundos.

Guardar datos en la memoria RTC

Con el ESP32, puedes guardar datos en las memorias RTC. El ESP32 tiene 8 kB de SRAM en la parte RTC, llamada RTC fast memory. Los datos guardados aquí no se borran durante el deep sleep. Sin embargo, sí se borran cuando pulsas el botón de reset (el botón etiquetado EN en la placa ESP32).

Para guardar datos en la memoria RTC, solo tienes que añadir RTC_DATA_ATTR antes de la definición de una variable. El ejemplo guarda la variable bootCount en la memoria RTC. Esta variable contará cuántas veces ha despertado el ESP32 del deep sleep:

RTC_DATA_ATTR int bootCount = 0;

Razón del despertar

Después, el código define la función print_wakeup_reason(), que imprime la fuente que causó el despertar del deep sleep. Es una función muy útil para depurar y entender por qué ha despertado tu ESP32.

El setup()

En el setup() es donde debes poner tu código. Necesitas escribir todas las instrucciones antes de llamar a la función esp_deep_sleep_start().

El ejemplo empieza inicializando la comunicación serie a 115200 baudios, incrementa el contador bootCount y lo imprime, y llama a print_wakeup_reason().

Después, el código define la fuente de despertar con:

esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);

Y finalmente, después de realizar todas las tareas, el ESP32 se va a dormir llamando a:

esp_deep_sleep_start()

En cuanto llamas a esp_deep_sleep_start(), el ESP32 se dormirá y no ejecutará ningún código escrito después de esta función. Cuando despierte del deep sleep, ejecutará el código desde el principio.

El loop()

La sección loop() está vacía porque el ESP32 se dormirá antes de llegar a esta parte del código. Por eso, debes escribir todas tus tareas en el setup() antes de llamar a esp_deep_sleep_start(). Este es un patrón muy importante que rompe con la forma tradicional de programar Arduino.

Probando el ejemplo

Sube el sketch a tu ESP32. Asegúrate de tener la placa y el puerto COM correctos seleccionados. Abre el Monitor Serie a 115200 baudios.

Cada 5 segundos, el ESP32 despierta, imprime un mensaje en el Monitor Serie y vuelve a entrar en deep sleep. Cada vez que el ESP32 despierta, la variable bootCount aumenta. También imprime la razón del despertar.

Sin embargo, fíjate en que si pulsas el botón EN de la placa ESP32, se reinicia el contador de arranque a 1 de nuevo. Esto es porque el reset borra la memoria RTC.

Puedes modificar el ejemplo y, en lugar de imprimir un mensaje, puedes hacer que tu ESP32 realice cualquier otra tarea. El despertar por temporizador es útil para realizar tareas periódicas con el ESP32, como tareas diarias, sin consumir mucha energía.

Despertar por pin táctil (Touch Wake Up)

Puedes despertar el ESP32 del deep sleep usando los pines táctiles. Esta sección te muestra cómo hacerlo usando Arduino IDE.

Habilitar el despertar por touch

Habilitar el ESP32 para despertar usando un pin táctil es sencillo. En Arduino IDE, necesitas usar la siguiente función, pasando como argumento el pin táctil y el umbral de activación:

touchSleepWakeUpEnable(TOUCH_PIN, THRESHOLD);

Ejemplo: Deep Sleep con despertar por touch

Abre tu Arduino IDE y ve a Archivo > Ejemplos > ESP32 Deep Sleep, y abre el sketch TouchWakeUp:

/*
Deep Sleep with Touch Wake Up
=====================================
Este código muestra cómo usar deep sleep con
un toque como fuente de despertar y cómo almacenar datos en
la memoria RTC para usarlos entre reinicios.

El ESP32 puede tener múltiples touch pads habilitados como fuente de despertar.
ESP32-S2 y ESP32-S3 soportan solo 1 touch pad como fuente de despertar.

Este código está bajo Licencia de Dominio Público.
Autor: Pranav Cherukupalli <cherukupallip@gmail.com>
*/

#if CONFIG_IDF_TARGET_ESP32
#define THRESHOLD 40   /* Cuanto mayor el valor, mayor la sensibilidad */
#else                  // ESP32-S2 y ESP32-S3 + valor por defecto para otros chips (a ajustar)
#define THRESHOLD 5000 /* Cuanto menor el valor, mayor la sensibilidad */
#endif

RTC_DATA_ATTR int bootCount = 0;
touch_pad_t touchPin;

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: Serial.println("Despertar causado por señal externa usando RTC_IO"); break;
    case ESP_SLEEP_WAKEUP_EXT1: Serial.println("Despertar causado por señal externa usando RTC_CNTL"); break;
    case ESP_SLEEP_WAKEUP_TIMER: Serial.println("Despertar causado por temporizador"); break;
    case ESP_SLEEP_WAKEUP_TOUCHPAD: Serial.println("Despertar causado por touchpad"); break;
    case ESP_SLEEP_WAKEUP_ULP: Serial.println("Despertar causado por programa ULP"); break;
    default: Serial.printf("El despertar no fue causado por deep sleep: %d\n", wakeup_reason); break;
  }
}

void print_wakeup_touchpad() {
  touchPin = (touch_pad_t) esp_sleep_get_touchpad_wakeup_status();

#if CONFIG_IDF_TARGET_ESP32
  switch (touchPin) {
    case 0:  Serial.println("Toque detectado en GPIO 4"); break;
    case 1:  Serial.println("Toque detectado en GPIO 0"); break;
    case 2:  Serial.println("Toque detectado en GPIO 2"); break;
    case 3:  Serial.println("Toque detectado en GPIO 15"); break;
    case 4:  Serial.println("Toque detectado en GPIO 13"); break;
    case 5:  Serial.println("Toque detectado en GPIO 12"); break;
    case 6:  Serial.println("Toque detectado en GPIO 14"); break;
    case 7:  Serial.println("Toque detectado en GPIO 27"); break;
    case 8:  Serial.println("Toque detectado en GPIO 33"); break;
    case 9:  Serial.println("Toque detectado en GPIO 32"); break;
    default: Serial.println("Despertar no causado por touchpad"); break;
  }
#else
  if (touchPin < TOUCH_PAD_MAX) {
    Serial.printf("Toque detectado en GPIO %d\n", touchPin);
  } else {
    Serial.println("Despertar no causado por touchpad");
  }
#endif
}

void setup() {
  Serial.begin(115200);
  delay(1000);

  ++bootCount;
  Serial.println("Número de arranque: " + String(bootCount));

  print_wakeup_reason();
  print_wakeup_touchpad();

#if CONFIG_IDF_TARGET_ESP32
  // Configura el despertar en Touch Pad 3 + 7 (GPIO15 + GPIO 27)
  touchSleepWakeUpEnable(T3, THRESHOLD);
  touchSleepWakeUpEnable(T7, THRESHOLD);
#else
  // Configura el despertar en Touch Pad 3 (GPIO3)
  touchSleepWakeUpEnable(T3, THRESHOLD);
#endif

  Serial.println("Entrando en sueño ahora");
  esp_deep_sleep_start();
  Serial.println("Esta línea nunca se imprimirá");
}

void loop() {
  // Nunca se alcanzará
}

Configurar el umbral

Lo primero que debes hacer es establecer un valor umbral para los pines táctiles:

#define THRESHOLD 40

Los valores leídos por un pin táctil disminuyen cuando lo tocas. El valor umbral significa que el ESP32 despertará cuando el valor leído en el pin táctil esté por debajo de 40. Puedes ajustar ese valor según la sensibilidad deseada.

Importante: sin embargo, si estás usando un modelo ESP32-S2 o ESP32-S3, las cosas funcionan de forma diferente. Por eso hay una sección distinta en el código con un umbral diferente para esas placas. En este caso, cuanto menor el valor, mayor la sensibilidad.

Configurar pines táctiles como fuente de despertar

Para configurar un pin táctil como fuente de despertar, puedes usar la función touchSleepWakeUpEnable(), que acepta como argumentos el pin táctil y el valor umbral que despertará la placa.

En este ejemplo se configuran GPIO 15 (T3) y GPIO 27 (T7) como fuentes de despertar con el mismo valor umbral. Si usas un ESP32-S2 o S3, el ejemplo solo define despertar en GPIO 3 (T3). Ten en cuenta que la numeración de los pines táctiles puede ser diferente según el modelo de placa que uses.

Cablear el circuito

Para probar este ejemplo, conecta un cable al GPIO 15. Tocar el extremo libre de ese cable simulará un toque en el pin capacitivo. Si usas un ESP32-S2 o S3, consulta la ubicación de tus pines táctiles antes de conectar nada.

Probando el ejemplo

Sube el código a tu ESP32 y abre el Monitor Serie a 115200 baudios. El ESP32 entrará en modo deep sleep. Puedes despertarlo tocando el cable conectado al Touch Pin 3.

Cuando toques el pin, el ESP32 mostrará en el Monitor Serie: el número de arranque, la causa del despertar y qué GPIO táctil causó el despertar.

Despertar externo (External Wake Up)

Además del temporizador y los pines táctiles, también podemos despertar el ESP32 del deep sleep cambiando el valor de una señal en un pin, como por ejemplo al pulsar un botón. Esto se llama despertar externo (external wake up). Tienes dos posibilidades: ext0 y ext1.

Despertar externo ext0

Esta fuente de despertar te permite usar un solo pin para despertar el ESP32. El modo ext0 utiliza RTC GPIOs para despertar, por lo que los periféricos RTC se mantendrán encendidos durante el deep sleep si se solicita esta fuente.

Para usar esta fuente de despertar, se utiliza la función:

esp_sleep_enable_ext0_wakeup(GPIO_NUM_X, level)

Esta función acepta como primer argumento el pin que quieres usar, en formato GPIO_NUM_X, donde X representa el número de GPIO de ese pin. El segundo argumento, level, puede ser 1 o 0. Esto representa el estado del GPIO que disparará el despertar.

Nota importante: con esta fuente de despertar, solo puedes usar pines que sean RTC GPIOs. Si intentas usar un pin que no lo sea, la función fallará.

Despertar externo ext1

Esta fuente de despertar te permite usar múltiples RTC GPIOs. Está implementada por el controlador RTC, por lo que los periféricos y las memorias RTC pueden apagarse en este modo, consumiendo aún menos energía que ext0.

Para usar esta fuente de despertar, se utiliza la función:

esp_sleep_enable_ext1_wakeup_io(bitmask, mode);

Esta función acepta dos argumentos: un bitmask de los números GPIO que causarán el despertar, y el modo, que es la lógica con la que se despertará el ESP32. Los modos disponibles son:

ESP_EXT1_WAKEUP_ALL_LOW: despierta cuando todos los GPIOs van a LOW. ESP_EXT1_WAKEUP_ANY_HIGH: despierta si cualquiera de los GPIOs va a HIGH.

Si estás usando un ESP32-S2, ESP32-S3, ESP32-C6 o ESP32-H2, los modos disponibles son: ESP_EXT1_WAKEUP_ANY_LOW (despierta cuando cualquiera de los GPIOs seleccionados está en LOW) y ESP_EXT1_WAKEUP_ANY_HIGH (despierta cuando cualquiera de los GPIOs seleccionados está en HIGH).

Nota: al igual que con ext0, solo puedes usar pines que sean RTC GPIOs.

Ejemplo: Deep Sleep con despertar externo

Abre el ejemplo de la librería. Ve a Archivo > Ejemplos > ESP32 Deep Sleep > ExternalWakeUp:

/*
  Deep Sleep with External Wake Up
  =====================================
  Este código muestra cómo usar deep sleep con
  un disparador externo como fuente de despertar y cómo
  almacenar datos en la memoria RTC para usarlos entre reinicios.

  Este código está bajo Licencia de Dominio Público.

  Conexiones de hardware
  ======================
  Botón pulsador al GPIO 33 con una resistencia
  pull-down de 10 kΩ.

  NOTA:
  ======
  Solo se puede usar RTC IO como fuente de despertar externo.
  Son los pines: 0, 2, 4, 12-15, 25-27, 32-39.

  Autor: Pranav Cherukupalli <cherukupallip@gmail.com>
*/
#include "driver/rtc_io.h"

#define BUTTON_PIN_BITMASK(GPIO) (1ULL << GPIO)  // 2 ^ GPIO_NUMBER en hex
#define USE_EXT0_WAKEUP          1               // 1 = EXT0 wakeup, 0 = EXT1 wakeup
#define WAKEUP_GPIO              GPIO_NUM_33     // Solo se permiten RTC IO
RTC_DATA_ATTR int bootCount = 0;

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: Serial.println("Despertar causado por señal externa usando RTC_IO"); break;
    case ESP_SLEEP_WAKEUP_EXT1: Serial.println("Despertar causado por señal externa usando RTC_CNTL"); break;
    case ESP_SLEEP_WAKEUP_TIMER: Serial.println("Despertar causado por temporizador"); break;
    case ESP_SLEEP_WAKEUP_TOUCHPAD: Serial.println("Despertar causado por touchpad"); break;
    case ESP_SLEEP_WAKEUP_ULP: Serial.println("Despertar causado por programa ULP"); break;
    default: Serial.printf("El despertar no fue causado por deep sleep: %d\n", wakeup_reason); break;
  }
}

void setup() {
  Serial.begin(115200);
  delay(1000);

  ++bootCount;
  Serial.println("Número de arranque: " + String(bootCount));

  print_wakeup_reason();

#if USE_EXT0_WAKEUP
  esp_sleep_enable_ext0_wakeup(WAKEUP_GPIO, 1);  // 1 = High, 0 = Low
  // Configura pullup/pulldowns vía RTCIO para fijar los pines de despertar
  // al nivel inactivo durante el deepsleep.
  rtc_gpio_pullup_dis(WAKEUP_GPIO);
  rtc_gpio_pulldown_en(WAKEUP_GPIO);
#else  // EXT1 WAKEUP
  esp_sleep_enable_ext1_wakeup_io(BUTTON_PIN_BITMASK(WAKEUP_GPIO), ESP_EXT1_WAKEUP_ANY_HIGH);
  rtc_gpio_pulldown_en(WAKEUP_GPIO);  // GPIO33 se ata a GND para despertar en HIGH
  rtc_gpio_pullup_dis(WAKEUP_GPIO);   // Deshabilita PULL_UP para permitir despertar en HIGH
#endif

  Serial.println("Entrando en sueño ahora");
  esp_deep_sleep_start();
  Serial.println("Esta línea nunca se imprimirá");
}

void loop() {
  // Nunca se llamará
}

Cómo funciona el código paso a paso

Este ejemplo despierta el ESP32 cuando se activa el GPIO 33 a HIGH. El ejemplo muestra cómo usar ambos métodos: ext0 y ext1. Si subes el código tal cual, usarás ext0. La función para usar ext1 está comentada.

Guardar datos en la memoria RTC

Igual que antes, se usa RTC_DATA_ATTR para guardar el contador de arranques en la memoria RTC, que sobrevive al deep sleep pero no al reset.

Razón del despertar

La función print_wakeup_reason() imprime la fuente que causó el despertar. Es esencial para depurar y entender el comportamiento del chip.

El setup() y la habilitación del despertar externo

Después de inicializar el puerto serie y el contador de arranques, se habilita la fuente de despertar:

Para ext0, se despierta cuando el GPIO 33 se activa a HIGH:

esp_sleep_enable_ext0_wakeup(WAKEUP_GPIO, 1);  // 1 = High, 0 = Low

En lugar de GPIO 33, puedes usar cualquier otro pin RTC GPIO. Solo tienes que definirlo en la variable WAKEUP_GPIO al principio del código.

Resistencias pull-up y pull-down internas

También se llaman estas dos líneas:

rtc_gpio_pullup_dis(WAKEUP_GPIO);
rtc_gpio_pulldown_en(WAKEUP_GPIO);

La función rtc_gpio_pullup_dis() deshabilita cualquier resistencia pull-up interna en el GPIO de despertar. Esto asegura que el pin no se mantenga en HIGH de forma no intencionada. Esto es importante porque una resistencia pull-up mantendría el pin en HIGH, causando despertares no deseados.

La función rtc_gpio_pulldown_en() habilita la resistencia pull-down interna en el GPIO de despertar. Esto asegura que el pin se mantenga en LOW hasta que reciba una señal de despertar válida (HIGH). Al configurar el pin con una resistencia pull-down, garantizamos que permanezca en un estado LOW estable durante el deep sleep. Esa estabilidad garantiza que el ESP32 solo despierte cuando el pin GPIO especificado reciba una señal HIGH externa, coincidiendo con la condición de despertar establecida por esp_sleep_enable_ext0_wakeup(WAKEUP_GPIO, 1).

Cablear el circuito

Para probar este ejemplo, conecta un pulsador a tu ESP32 siguiendo este esquema: el botón se conecta al GPIO 33 usando una resistencia pull-down de 10 kΩ hacia GND. El otro lado del botón va a 3,3 V.

Nota: solo se pueden usar RTC GPIOs como fuente de despertar. En lugar de GPIO 33, también puedes usar cualquier pin RTC GPIO para conectar tu botón.

Probando el ejemplo

Sube el código de ejemplo a tu ESP32. Asegúrate de tener la placa y el puerto COM correctos seleccionados. Abre el Monitor Serie a 115200 baudios. Pulsa el botón para despertar el ESP32.

Prueba esto varias veces y observa cómo el contador de arranques aumenta con cada pulsación del botón. Usar este método es útil para despertar tu ESP32 con un pulsador, por ejemplo, para ejecutar una tarea concreta. Sin embargo, con este método solo puedes usar un GPIO como fuente de despertar.

¿Qué pasa si quieres tener diferentes botones, todos ellos despiertan el ESP32, pero hacen tareas diferentes? Para eso necesitas usar el método ext1.

Despertar externo con múltiples GPIOs (ext1)

La fuente de despertar ext1 te permite despertar el ESP32 usando diferentes GPIOs y realizar tareas diferentes según el GPIO que causó el despertar. En lugar de usar la función esp_sleep_enable_ext0_wakeup(), usas la función:

esp_sleep_enable_ext1_wakeup_io(bitmask, ESP_EXT1_WAKEUP_ANY_HIGH);

En este ejemplo, para ejecutar esa parte del código, asegúrate de poner la variable USE_EXT0_WAKEUP a 0 al principio del código:

#define USE_EXT0_WAKEUP  0  // 1 = EXT0 wakeup, 0 = EXT1 wakeup

La función esp_sleep_enable_ext1_wakeup_io()

El primer argumento es un bitmask de los GPIOs que usarás como fuente de despertar, y el segundo argumento define la lógica de despertar.

Bitmask de GPIOs

Para definir el bitmask de GPIOs, podemos usar el macro BUTTON_PIN_BITMASK() definido al principio del código:

#define BUTTON_PIN_BITMASK(GPIO) (1ULL << GPIO)  // 2 ^ GPIO_NUMBER en hex

BUTTON_PIN_BITMASK(GPIO) es un macro que crea un bitmask para un GPIO específico. No es una función, pero se comporta de forma similar aceptando un argumento y devolviendo un valor. Así, si quieres devolver el bitmask de un GPIO específico, solo tienes que llamarlo así:

BUTTON_PIN_BITMASK(WAKEUP_GPIO)

Bitmask para múltiples GPIOs

Para crear un bitmask para múltiples GPIOs, puedes usar el operador OR bit a bit (|) para combinar los bitmasks individuales de cada pin. Por ejemplo, si quieres crear un bitmask para los pines GPIO 2 y GPIO 15:

uint64_t bitmask = BUTTON_PIN_BITMASK(GPIO_NUM_2) | BUTTON_PIN_BITMASK(GPIO_NUM_15);

Modo de despertar

El segundo argumento de la función es el modo de despertar. Como hemos visto, las opciones son ESP_EXT1_WAKEUP_ALL_LOW (despierta cuando todos los GPIOs van a LOW) y ESP_EXT1_WAKEUP_ANY_HIGH (despierta cuando cualquiera de los GPIOs va a HIGH). En nuestro ejemplo concreto, usamos ESP_EXT1_WAKEUP_ANY_HIGH, así que el ESP32 despertará cuando cualquiera de los GPIOs del bitmask esté en HIGH.

Al igual que con ext0, deshabilitamos las resistencias pull-up internas y habilitamos las pull-down para garantizar la estabilidad de la lectura del GPIO de despertar.

Ejemplo completo con múltiples GPIOs

Vamos a despertar el ESP32 usando diferentes botones e identificar cuál de ellos causó el despertar. En este ejemplo usaremos GPIO 2 y GPIO 15 como fuentes de despertar.

Esquema

Conecta dos botones a tu ESP32. En este ejemplo usamos GPIO 2 y GPIO 15, pero puedes conectar tus botones a cualquier RTC GPIO. Cada botón debe tener su resistencia pull-down de 10 kΩ hacia GND.

Código completo para múltiples GPIOs

Necesitas hacer algunas modificaciones al código de ejemplo anterior:

Crear un bitmask para usar GPIO 15 y GPIO 2. Habilitar ext1 como fuente de despertar. Crear una función que identifique el GPIO que causó el despertar.

El siguiente sketch tiene todos esos cambios implementados:

/*
  Rui Santos & Sara Santos - Random Nerd Tutorials
  https://RandomNerdTutorials.com/learn-esp32-with-arduino-ide/
*/

#include "driver/rtc_io.h"

#define BUTTON_PIN_BITMASK(GPIO) (1ULL << GPIO)  // 2 ^ GPIO_NUMBER en hex
#define WAKEUP_GPIO_2              GPIO_NUM_2     // Solo se permiten RTC IO
#define WAKEUP_GPIO_15              GPIO_NUM_15     // Solo se permiten RTC IO

// Define el bitmask para múltiples GPIOs
uint64_t bitmask = BUTTON_PIN_BITMASK(WAKEUP_GPIO_2) | BUTTON_PIN_BITMASK(WAKEUP_GPIO_15);

RTC_DATA_ATTR int bootCount = 0;

/*
Método para imprimir el GPIO que disparó el despertar
*/
void print_GPIO_wake_up(){
  int GPIO_reason = esp_sleep_get_ext1_wakeup_status();
  Serial.print("GPIO que disparó el despertar: GPIO ");
  Serial.println((log(GPIO_reason))/log(2), 0);
}

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:     
      Serial.println("Despertar causado por señal externa usando RTC_IO");
      break;
    case ESP_SLEEP_WAKEUP_EXT1:
      Serial.println("Despertar causado por señal externa usando RTC_CNTL");
      print_GPIO_wake_up();
      break;
    case ESP_SLEEP_WAKEUP_TIMER:
      Serial.println("Despertar causado por temporizador");
      break;
    case ESP_SLEEP_WAKEUP_TOUCHPAD:
      Serial.println("Despertar causado por touchpad");
      break;
    case ESP_SLEEP_WAKEUP_ULP:
      Serial.println("Despertar causado por programa ULP");
      break;
    default:
      Serial.printf("El despertar no fue causado por deep sleep: %d\n", wakeup_reason);
      break;
  }
}

void setup() {
  Serial.begin(115200);
  delay(1000);

  ++bootCount;
  Serial.println("Número de arranque: " + String(bootCount));

  print_wakeup_reason();

  // Usa ext1 como fuente de despertar
  esp_sleep_enable_ext1_wakeup_io(bitmask, ESP_EXT1_WAKEUP_ANY_HIGH);
  // Habilita resistencias pull-down y deshabilita pull-up
  rtc_gpio_pulldown_en(WAKEUP_GPIO_2);
  rtc_gpio_pullup_dis(WAKEUP_GPIO_2);
  rtc_gpio_pulldown_en(WAKEUP_GPIO_15);
  rtc_gpio_pullup_dis(WAKEUP_GPIO_15);

  Serial.println("Entrando en sueño ahora");
  esp_deep_sleep_start();
  Serial.println("Esta línea nunca se imprimirá");
}

void loop() {
  // Nunca se llamará
}

Cómo funciona el código paso a paso

Bitmask de GPIOs

Se define el bitmask al principio del código combinando los pines GPIO 2 y GPIO 15 mediante el operador OR.

Identificar el GPIO que causó el despertar

Se crea una función llamada print_GPIO_wake_up() que imprime el GPIO que causó el despertar:

void print_GPIO_wake_up(){
  int GPIO_reason = esp_sleep_get_ext1_wakeup_status();
  Serial.print("GPIO que disparó el despertar: GPIO ");
  Serial.println((log(GPIO_reason))/log(2), 0);
}

La función esp_sleep_get_ext1_wakeup_status() devuelve un bitmask con el GPIO que causó el despertar. El número de GPIO se obtiene aplicando logaritmo en base 2 al valor del bitmask: log(GPIO_reason)/log(2).

Imprimir la razón del despertar

Se modifica la función print_wakeup_reason() para imprimir el GPIO que causó el despertar cuando la fuente es ext1. Esto se hace añadiendo la llamada a print_GPIO_wake_up() dentro del case ESP_SLEEP_WAKEUP_EXT1.

Habilitar ext1 y configurar pull-downs

Se habilita ext1 pasando el bitmask de GPIOs y el modo de despertar. Después, se deshabilitan las resistencias pull-up internas y se habilitan las pull-down en los GPIOs usados como fuente de despertar. Esto garantiza que los pines estén estables en LOW hasta que se pulse el botón.

Entrar en deep sleep

Finalmente, se llama a esp_deep_sleep_start() para poner el ESP32 en modo deep sleep.

Probando el sketch

Después de subir el código a la placa, el ESP32 estará en modo deep sleep. Puedes despertarlo pulsando los botones. Abre el Monitor Serie a 115200 baudios. Pulsa los botones para despertar el ESP32. En el Monitor Serie deberías obtener la razón del despertar y el GPIO que causó el despertar.

Conclusión: lo que has aprendido

En este artículo te hemos mostrado cómo usar deep sleep con el ESP32 y diferentes formas de despertarlo. Puedes despertar el ESP32 usando un temporizador, los pines táctiles o un cambio en el estado de un GPIO.

Resumamos lo que has aprendido sobre cada fuente de despertar:

Despertar por temporizador

Para habilitar el despertar por temporizador, usas la función esp_sleep_enable_timer_wakeup(tiempo_en_us). Después, usas la función esp_deep_sleep_start() para iniciar el deep sleep.

Despertar por pin táctil

Para habilitar los pines táctiles como fuente de despertar, usas la función touchSleepWakeUpEnable(). Finalmente, usas la función esp_deep_sleep_start() para poner el ESP32 en modo deep sleep.

Despertar externo

Solo puedes usar RTC GPIOs como despertar externo. Puedes usar dos métodos diferentes: ext0 y ext1. ext0 permite despertar el ESP32 usando un único pin GPIO, mientras que ext1 permite despertar el ESP32 usando varios pines GPIO simultáneamente, e incluso identificar cuál de ellos causó el despertar.

El deep sleep es una de las características más poderosas del ESP32 para proyectos IoT alimentados por batería. Dominar sus fuentes de despertar te permite diseñar dispositivos que duren meses o incluso años con una sola batería, enviando datos periódicamente o reaccionando a eventos externos de forma eficiente.

Vistas: 0

Descubre más desde CIBERED

Suscríbete y recibe las últimas entradas en tu correo electrónico.

Scroll al inicio