Deep Sleep en ESP32 y sus fuentes de activación

Cuando se trabaja en proyectos de IoT (Internet de las Cosas), la gestión del consumo de energía se vuelve crítica, especialmente si se alimentan de baterías. Cada miliamperio cuenta, y el ESP32, aunque es un potente microcontrolador, puede ser relativamente "hambriento" de energía. Dependiendo del estado en el que se encuentre, el ESP32 puede consumir aproximadamente 75 mA en operaciones normales y hasta 240 mA cuando está transmitiendo datos a través de WiFi. Por ello, es crucial implementar modos de bajo consumo, como el Deep Sleep.

El modo de Deep Sleep permite que el ESP32 reduzca drásticamente su consumo al apagar la CPU, la mayor parte de la RAM y todos los periféricos digitales. Solo permanecen activos ciertas partes del chip, lo que permite que el dispositivo mantenga un consumo de alrededor de 0.15 mA (si el coprocessador ULP está encendido) hasta solo 10 µA. Esto lo convierte en una opción excelente para aplicaciones que deben funcionar con baterías durante largos períodos.

En este artículo, exploraremos los diferentes modos de sueño del ESP32, sus fuentes de activación y cómo implementarlos en sus proyectos. Aprenderemos sobre el modo de Hibernación, Light Sleep, y cómo manejar los diferentes tipos de activación del sueño profundo.

Índice de contenido
  1. Fuentes de activación del sueño profundo en ESP32
  2. Activación del ESP32: Temporizador
    1. Código de ejemplo
    2. Explicación del código
  3. Activación del ESP32: Pantalla táctil
    1. Conexiones
    2. Código de ejemplo
    3. Explicación del código
  4. Activación del ESP32: Despertar Externo
  5. Fuente de activación externa ext0
    1. Conexiones
    2. Código de ejemplo para ext0
    3. Explicación del código para ext0
  6. Fuente de activación externa ext1
    1. Máscara de bits
    2. Conexiones para ext1
    3. Código de ejemplo para ext1
    4. Explicación del código para ext1

Fuentes de activación del sueño profundo en ESP32

El ESP32 puede despertarse de su modo de sueño profundo utilizando varias fuentes, que incluyen:

  • Temporizador
  • Pantalla táctil
  • Despertar externo (ext0 y ext1)

Es importante señalar que se pueden combinar múltiples fuentes de activación, lo que significa que el chip se despertará al activarse cualquiera de las fuentes configuradas. Sin embargo, hay que tener cuidado, ya que si se configura el modo de sueño profundo sin ninguna fuente de activación, el chip permanecerá en ese estado indefinidamente hasta que se aplique un reinicio externo.

Activación del ESP32: Temporizador

El controlador RTC del ESP32 incluye un temporizador que puede utilizarse para activar el chip después de un período de tiempo predefinido. Esta característica es especialmente útil en proyectos que requieren tareas programadas o marcas de tiempo, todo mientras se mantiene un bajo consumo de energía.

Para configurar el temporizador como fuente de activación, se usa la función esp_sleep_enable_timer_wakeup(time_in_us), que acepta un tiempo en microsegundos. Por ejemplo, si se desea que el ESP32 despierte cada 5 segundos, se puede configurar de la siguiente manera:

Código de ejemplo

Vamos a ver cómo funciona, utilizando un ejemplo de la biblioteca. Abre tu IDE de Arduino, navega a Archivo > Ejemplos > ESP32 > Deep Sleep y abre el sketch TimerWakeUp.

#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;

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 por señal externa usando RTC_IO"); break;
        case ESP_SLEEP_WAKEUP_EXT1 : Serial.println("Despertar por señal externa usando RTC_CNTL"); break;
        case ESP_SLEEP_WAKEUP_TIMER : Serial.println("Despertar por temporizador"); break;
        case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.println("Despertar por pantalla táctil"); break;
        case ESP_SLEEP_WAKEUP_ULP : Serial.println("Despertar por programa ULP"); break;
        default : Serial.printf("El despertar no fue causado por el sueño profundo: %dn", wakeup_reason); break;
    }
}

void setup(){
    Serial.begin(115200);
    delay(1000); // Esperar un poco para abrir el Monitor Serial
    ++bootCount;
    Serial.println("Número de inicio: " + String(bootCount));
    print_wakeup_reason();
    esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
    Serial.println("El ESP32 se dormirá en ahora");
    esp_deep_sleep_start();
}

void loop(){
    // Esto no se va a llamar
}

Una vez que se sube el sketch, abre tu monitor serial y establece la velocidad de baudios en 115200 bps. El ESP32 despertará cada 5 segundos, imprime el motivo del despertar y el contador de arranque en el monitor serial antes de volver a entrar en sueño profundo.

Explicación del código

Las primeras dos líneas de código definen el tiempo durante el cual el ESP32 estará dormido. Este ejemplo utiliza el factor de conversión de microsegundos a segundos, permitiendo establecer el tiempo de sueño en la variable TIME_TO_SLEEP.

La variable bootCount se guarda en la memoria RTC, que no se borra durante el sueño profundo, pero sí cuando se reinicia el ESP32. Esta variable cuenta cuántas veces el ESP32 se ha despertado del sueño profundo.

Activación del ESP32: Pantalla táctil

El ESP32 puede despertarse del sueño profundo utilizando pines táctiles. Para habilitar esta funcionalidad, simplemente se usa la función esp_sleep_enable_touchpad_wakeup().

Conexiones

Conecte un cable al GPIO#15 (Touch#3), que actuará como fuente de activación táctil. Puede adjuntar cualquier objeto conductor, como un cable, papel de aluminio, tela conductora, pintura conductora, etc., al pin sensible al tacto y convertirlo en un panel táctil.

Código de ejemplo

Abre nuevamente tu IDE de Arduino y dirígete a Archivo > Ejemplos > ESP32 > Deep Sleep para abrir el sketch TouchWakeUp.

#define Threshold 40 // Cuanto mayor sea el valor, mayor será la sensibilidad

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 por señal externa usando RTC_IO"); break;
        case ESP_SLEEP_WAKEUP_EXT1 : Serial.println("Despertar por señal externa usando RTC_CNTL"); break;
        case ESP_SLEEP_WAKEUP_TIMER : Serial.println("Despertar por temporizador"); break;
        case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.println("Despertar por pantalla táctil"); break;
        case ESP_SLEEP_WAKEUP_ULP : Serial.println("Despertar por programa ULP"); break;
        default : Serial.printf("El despertar no fue causado por el sueño profundo: %dn", wakeup_reason); break;
    }
}

void print_wakeup_touchpad(){
    touchPin = esp_sleep_get_touchpad_wakeup_status();
    switch (touchPin)
    {
        case 0 : Serial.println("Tacto detectado en GPIO 4"); break;
        case 1 : Serial.println("Tacto detectado en GPIO 0"); break;
        case 2 : Serial.println("Tacto detectado en GPIO 2"); break;
        case 3 : Serial.println("Tacto detectado en GPIO 15"); break;
        case 4 : Serial.println("Tacto detectado en GPIO 13"); break;
        case 5 : Serial.println("Tacto detectado en GPIO 12"); break;
        case 6 : Serial.println("Tacto detectado en GPIO 14"); break;
        case 7 : Serial.println("Tacto detectado en GPIO 27"); break;
        case 8 : Serial.println("Tacto detectado en GPIO 33"); break;
        case 9 : Serial.println("Tacto detectado en GPIO 32"); break;
        default : Serial.println("Despertar no por pantalla táctil"); break;
    }
}

void setup(){
    Serial.begin(115200);
    delay(1000); // Esperar un poco para abrir el Monitor Serial
    ++bootCount;
    Serial.println("Número de inicio: " + String(bootCount));
    print_wakeup_reason();
    print_wakeup_touchpad();
    touchAttachInterrupt(T3, callback, Threshold);
    esp_sleep_enable_touchpad_wakeup();
    Serial.println("El ESP32 se dormirá en ahora");
    esp_deep_sleep_start();
}

void loop(){
    // Esto no se va a llamar
}

Una vez que se sube el sketch, abre tu monitor serial y establece la velocidad de baudios en 115200 bps. Ahora, al tocar el pin, el ESP32 mostrará el contador de arranque, el motivo del despertar y qué GPIO fue tocado en el monitor serial.

Explicación del código

La primera línea de código establece el umbral de sensibilidad del pin táctil en 40. Cuanto mayor sea el umbral, mayor será la sensibilidad. Además, puedes guardar datos en la memoria RTC del ESP32, que no se borra durante el sueño profundo.

Activación del ESP32: Despertar Externo

Se pueden utilizar dos tipos de activadores externos para despertar al ESP32 del sueño profundo:

  • ext0: Utiliza este modo cuando deseas despertar al chip solo con un pin GPIO específico.
  • ext1: Utiliza este modo cuando deseas despertar al chip utilizando múltiples pines GPIO.

Si deseas usar un pin de interrupción para activar el ESP32, debes utilizar pines RTC_GPIO. Estos GPIO están conectados al subsistema de bajo consumo RTC, lo que permite que estén activos incluso en modo de sueño profundo.

Fuente de activación externa ext0

El ESP32 puede configurarse para despertar del sueño profundo cuando uno de los pines RTC_GPIO cambia su nivel lógico. La función esp_sleep_enable_ext0_wakeup(GPIO_PIN, LOGIC_LEVEL) se usa para habilitar esta fuente de activación. Esta función toma dos parámetros: el número del pin GPIO y el nivel lógico (BAJO o ALTO) que se desea usar para activar el despertar.

Como ext0 utiliza RTC IO para despertar al ESP32, los periféricos RTC se mantienen activos durante el sueño profundo. También puedes aprovechar los resistores internos pull-up o pull-down, que deben configurarse utilizando las funciones rtc_gpio_pullup_en() y rtc_gpio_pulldown_en() antes de llamar a esp_deep_sleep_start().

Conexiones

Conecta un botón pulsador al GPIO#33 utilizando una resistencia de 10K pull-down.

Código de ejemplo para ext0

Veamos cómo funciona, usando un ejemplo de la biblioteca. Abre tu IDE de Arduino, navega a Archivo > Ejemplos > ESP32 > Deep Sleep y abre el sketch ExternalWakeUp.

#define BUTTON_PIN_BITMASK 0x200000000 // 2^33 en hexadecimal

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 por señal externa usando RTC_IO"); break;
        case ESP_SLEEP_WAKEUP_EXT1 : Serial.println("Despertar por señal externa usando RTC_CNTL"); break;
        case ESP_SLEEP_WAKEUP_TIMER : Serial.println("Despertar por temporizador"); break;
        case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.println("Despertar por pantalla táctil"); break;
        case ESP_SLEEP_WAKEUP_ULP : Serial.println("Despertar por programa ULP"); break;
        default : Serial.printf("El despertar no fue causado por el sueño profundo: %dn", wakeup_reason); break;
    }
}

void setup(){
    Serial.begin(115200);
    delay(1000); // Esperar un poco para abrir el Monitor Serial
    ++bootCount;
    Serial.println("Número de inicio: " + String(bootCount));
    print_wakeup_reason();
    esp_sleep_enable_ext0_wakeup(GPIO_NUM_33, 1); // 1 = Alto, 0 = Bajo
    Serial.println("El ESP32 se dormirá en ahora");
    esp_deep_sleep_start();
}

void loop(){
    // Esto no se va a llamar
}

Una vez que se sube el sketch, abre tu monitor serial y establece la velocidad de baudios en 115200 bps. Cuando presiones el botón, el ESP32 mostrará el contador de arranque y el motivo del despertar en el monitor serial.

Explicación del código para ext0

La primera línea de código establece la máscara de bits, aunque no es necesaria para el despertar externo, por lo que puedes ignorarla por ahora.

Fuente de activación externa ext1

El ESP32 puede configurarse para despertar del sueño profundo utilizando múltiples pines. Recuerda que estos pines deben ser parte de los pines GPIO RTC. A diferencia de ext0, el ext1 utiliza un controlador RTC, por lo que no requiere que los periféricos RTC estén activos durante el sueño. Sin embargo, los resistores internos pull-up y pull-down no estarán disponibles en este modo.

Para usar los resistores internos, se deben solicitar que los periféricos RTC permanezcan encendidos durante el sueño y configurar los resistores utilizando las funciones rtc_gpio_pullup_en() y rtc_gpio_pulldown_en() antes de entrar en sueño.

La función esp_sleep_enable_ext1_wakeup(BUTTON_PIN_MASK, LOGIC_LEVEL) se utiliza para habilitar esta fuente de activación, tomando dos parámetros: un máscara de bits que indica los pines que se usarán y un nivel lógico que puede ser uno de los siguientes:

  • Despertar si uno de los pines seleccionados está ALTO (ESP_EXT1_WAKEUP_ANY_HIGH)
  • Despertar si todos los pines seleccionados están BAJOS (ESP_EXT1_WAKEUP_ALL_LOW)

Máscara de bits

Para entender la máscara de bits, puedes escribirla en formato binario. La numeración de los bits se basa en la numeración normal de GPIO. El bit menos significativo (LSB) representa GPIO#0 y el bit más significativo (MSB) representa GPIO#39.

  • 0 representa los pines enmascarados
  • 1 representa los pines que se habilitarán como fuente de activación

Por ejemplo, si deseas usar GPIO#32 y GPIO#33 como fuentes de activación externas, la máscara de bits se vería así:

Conexiones para ext1

Conecta dos botones pulsadores a GPIO#32 y GPIO#33 utilizando resistores de 10K pull-down.

Código de ejemplo para ext1

<pUsamos el mismo ejemplo de ExternalWakeUp de la biblioteca, y realizamos tres cambios en el sketch:

  1. Cambia la constante BUTTON_PIN_BITMASK
  2. Comenta el código para ext0
  3. Descomenta el código para ext1

Los cambios en el sketch se destacan en verde.

#define BUTTON_PIN_BITMASK 0x300000000

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 por señal externa usando RTC_IO"); break;
        case ESP_SLEEP_WAKEUP_EXT1 : Serial.println("Despertar por señal externa usando RTC_CNTL"); break;
        case ESP_SLEEP_WAKEUP_TIMER : Serial.println("Despertar por temporizador"); break;
        case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.println("Despertar por pantalla táctil"); break;
        case ESP_SLEEP_WAKEUP_ULP : Serial.println("Despertar por programa ULP"); break;
        default : Serial.printf("El despertar no fue causado por el sueño profundo: %dn", wakeup_reason); break;
    }
}

void setup(){
    Serial.begin(115200);
    delay(1000); // Esperar un poco para abrir el Monitor Serial
    ++bootCount;
    Serial.println("Número de inicio: " + String(bootCount));
    print_wakeup_reason();
    esp_sleep_enable_ext1_wakeup(BUTTON_PIN_BITMASK, ESP_EXT1_WAKEUP_ANY_HIGH);
    Serial.println("El ESP32 se dormirá en ahora");
    esp_deep_sleep_start();
}

void loop(){
    // Esto no se va a llamar
}

Una vez que se sube el sketch, abre tu monitor serial y establece la velocidad de baudios en 115200 bps. Al presionar el botón, se mostrará el contador de arranque y el motivo del despertar en el monitor serial. Además, notarás que ext1 utiliza el controlador RTC para despertar al ESP32.

Explicación del código para ext1

Este código es idéntico al de ext0, con dos cambios. Al principio del código, se define la máscara de bits. Como estamos usando los pines GPIO#32 y GPIO#33 en el ejemplo, la máscara tiene 1 en sus posiciones respectivas, con 32 ceros a la derecha y 6 ceros a la izquierda.

Finalmente, ext1 se habilita como fuente de activación.

Carlos Julián

Carlos Julián es el fundador de Ingtelecto, es Ingeniero Mecatrónico, Profesor y Programador, cuenta con una Maestria en Ciencias de la Educación, creador de contenido activo a través de TikTok @carlosjulian_mx

Estos temas te pueden interesar

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Tu puntuación: Útil

Subir