micropython-bajo-consumo-deep-sleep

Deep sleep y bajo consumo en MicroPython

  • 5 min

El deep sleep es un modo que apaga casi todo el microcontrolador hasta que ocurre un evento de despertar.

Hasta ahora, nuestros programas funcionaban a toda máquina. El procesador estaba siempre activo, el Wi-Fi conectado y los LEDs encendidos.

Si tienes la placa enchufada a la pared, genial. Pero si quieres alimentar tu sensor con una batería (LiPo o pilas AA), tienes un problema grave:

  • ESP32 activo con Wi-Fi: ~150 mA - 240 mA.
  • Batería típica (2000 mAh): Duración aprox. 8 horas.

¿8 horas? Eso no es un dispositivo IoT, es un juguete. Para conseguir duraciones de meses o años, necesitamos usar el modo Deep Sleep (Sueño Profundo).

En este modo, el ESP32 apaga la CPU, la RAM, el Wi-Fi y Bluetooth. Solo deja encendido un pequeño reloj interno (RTC) y un coprocesador de ultra-bajo consumo.

  • ESP32 en Deep Sleep: ~10 µA (0.01 mA).
  • Duración teórica: Años.

La filosofía del despertar

Lo más importante es entender que cuando el ESP32 despierta de deep sleep no continúa donde se quedó.

Para el microcontrolador, despertar del Deep Sleep es igual que un Reset. El programa vuelve a empezar desde la primera línea de boot.py y main.py.

Por tanto, la lógica de nuestros programas de bajo consumo cambia radicalmente:

  1. Arrancar.
  2. Leer sensor.
  3. Conectar WiFi y enviar dato.
  4. Irse a dormir.
  5. (Al despertar, vuelve al paso 1).

Dormir y despertar por tiempo

Este es el caso más común: “Quiero medir la temperatura cada 10 minutos”.

Usamos la función machine.deepsleep(milisegundos).

import machine
import time

# 1. Código de "Hacer cosas"
print("Me he despertado. Voy a trabajar...")
# (Aquí leerías el sensor y enviarías por MQTT)
time.sleep(1) # Simulamos trabajo

print("Trabajo terminado. Me voy a dormir 10 segundos.")

# 2. Configurar el despertar
# Si no le pasamos argumentos, duerme para siempre hasta un reset manual.
# Al pasarle milisegundos, configuramos el RTC Timer.
machine.deepsleep(10000) 
Copied!

Saber por qué nos hemos despertado

A veces necesitamos saber si el ESP32 acaba de encenderse porque alguien pulsó el botón de Reset o porque se ha despertado del sueño.

import machine

razon = machine.reset_cause()

if razon == machine.DEEPSLEEP_RESET:
    print("¡Vengo de un sueño profundo!")
else:
    print("He arrancado por primera vez (Power On o Reset).")
Copied!

Dormir y despertar mediante un botón

Imagina un timbre inteligente. Tiene que estar dormido siempre, excepto cuando alguien pulsa el botón. No nos vale el temporizador, necesitamos una interrupción externa.

El ESP32 tiene un hardware especial en el RTC para vigilar pines mientras duerme. En MicroPython usamos el módulo esp32.

import machine
import esp32
from machine import Pin

# Botón en GPIO 33 (Debe ser un pin RTC: 0, 2, 4, 12-15, 25-27, 32-39)
btn = Pin(33, Pin.IN, Pin.PULL_UP)

# Configuramos el despertar por pin (wake_on_ext0)
# Pin: El objeto pin
# Level: esp32.WAKEUP_ALL_LOW (si es pull-up y pulsamos a tierra)
#        esp32.WAKEUP_ANY_HIGH (si es pull-down y metemos 3.3v)
esp32.wake_on_ext0(pin=btn, level=esp32.WAKEUP_ALL_LOW)

print("Durmiendo... Pulsa el botón GPIO 33 para despertar.")
machine.deepsleep()
Copied!

En el ESP32 original, ext0 solo permite vigilar un solo pin. Si necesitas despertar con varios botones distintos, debes usar wake_on_ext1, que es un poco más complejo de configurar.

Conservar datos en la memoria RTC

Como hemos dicho, al despertar se reinicia todo. Las variables se borran. contador = 0.

Si queremos llevar la cuenta de cuántas veces hemos despertado, o guardar el último valor del sensor, no podemos usar variables normales. ¿Usamos la memoria Flash (archivos)? No, porque es lenta y se desgasta si escribimos cada minuto.

La solución es la Memoria RTC. Es una pequeña zona de memoria RAM (unos 8KB) que se mantiene alimentada durante el Deep Sleep.

import machine

# Accedemos a la memoria RTC
rtc = machine.RTC()

# Leemos lo que hay guardado (devuelve bytes)
datos = rtc.memory()

if not datos:
    # Si está vacío (primer arranque)
    contador = 0
else:
    # Convertimos bytes a entero
    contador = int(datos)

print(f"Esta es la ejecución número: {contador}")

# Incrementamos
contador += 1

# Guardamos en la memoria RTC (debe ser bytes)
rtc.memory(str(contador))

print("A dormir...")
machine.deepsleep(2000)
Copied!

El consumo de las placas de desarrollo

Aquí viene el jarro de agua fría. Si cargas estos códigos en un ESP32 DevKit V1 o un NodeMCU típico, y mides el consumo, verás que es de unos 10 mA, no 10 µA.

¿Por qué?

  1. Regulador de voltaje: El chip AMS1117 que traen estas placas es muy ineficiente y consume corriente incluso sin carga.
  2. Convertidor USB-Serial: El chip CP2102/CH340 también consume.
  3. LED de Power: Ese LED rojo que siempre está encendido gasta él solo unos 5-10 mA.

Para bajo consumo real Para desplegar proyectos reales, necesitas placas diseñadas para ello (como la FireBeetle de DFRobot o la EzSBC), o bien usar el chip ESP32 “desnudo” con un regulador eficiente (como el MCP1700) y sin LEDs innecesarios.

Estrategia para un nodo IoT

Para hacer un sensor de temperatura WiFi que dure un año con pilas:

  1. Activa la interfaz Wi-Fi y llama a WLAN.connect() cuanto antes.
  2. Usa IP estática (ahorra 2-3 segundos de negociación DHCP).
  3. Envía el dato por MQTT o UDP (más rápido que HTTP).
  4. Si el cambio de temperatura es pequeño, no envíes nada, solo vuelve a dormir (ahorras WiFi).
  5. Usa machine.deepsleep() el 99.9% del tiempo.

Dominar el sueño profundo es lo que convierte un “invento casero” en un dispositivo profesional.