bajo-consumo-deep-sleep-nanoframework

Deep sleep y bajo consumo en nanoFramework

  • 5 min

El bajo consumo en nanoFramework es el conjunto de técnicas para reducir la energía que gasta el dispositivo cuando no necesita estar activo.

Nuestro dispositivo ya se conecta al Wi-Fi, lee sensores y guarda datos, pero todas esas funciones tienen un coste: el consumo de energía.

Pero tiene un problema grave: El consumo de energía. Un ESP32 con el Wi-Fi encendido consume entre 150mA y 240mA. Si lo conectas a una batería estándar de 2000mAh, se agotará en menos de 10 horas.

Para crear dispositivos que duren meses o años, necesitamos que el chip pase el 99% de su vida “muerto”, y solo despierte unos segundos para trabajar. Esto se llama Deep Sleep.

En esta entrada aprenderemos a usar las APIs de energía de nanoFramework para dormir a nuestro pequeño amigo y despertarlo solo cuando sea necesario.

Para este tutorial necesitas instalar el paquete NuGet específico de tu hardware. Para ESP32 es: nanoFramework.Hardware.Esp32

Diferencia entre Thread.Sleep y deep sleep

Conviene no confundir estos dos conceptos:

  1. Thread.Sleep(1000): suspende el hilo, pero el sistema continúa activo y puede ejecutar otros hilos.
  2. Deep Sleep: apaga la CPU y la radio, pierde la RAM normal y mantiene únicamente los dominios necesarios para despertar.

La diferencia puede ser enorme, aunque una placa de desarrollo completa suele consumir bastante más que el chip aislado por el regulador, el conversor USB y los LED.

Ciclo de vida del deep sleep

Al despertar de deep sleep, el programa arranca de nuevo desde Main().

Cuando el ESP32 despierta:

  1. NO continúa en la línea de código siguiente a donde se durmió.
  2. empieza desde el principio del Main().
  3. Todas las variables de memoria RAM se han perdido.

Por eso, la estructura de un programa de bajo consumo es diferente a un bucle infinito.

public static void Main()
{
    // 1. Inicializar hardware
    // 2. Leer sensores
    // 3. Conectar Wi-Fi y enviar datos
    // 4. Configurar el despertador
    // 5. ¡A dormir! (El programa muere aquí)
}
Copied!

Despertar mediante temporizador

Es el caso más típico: “Mide la temperatura cada 10 minutos”.

Usaremos la clase Sleep del espacio de nombres nanoFramework.Hardware.Esp32.

using System;
using System.Diagnostics;
using System.Threading;
using nanoFramework.Hardware.Esp32; // <--- Necesario

public class Program
{
    public static void Main()
    {
        Debug.WriteLine($"¡Buenos días! Hora de trabajar: {DateTime.UtcNow}");

        // --- AQUÍ HARÍAS TU TRABAJO ---
        // Leer sensor...
        // Enviar MQTT...
        // Guardar en SD...
        // Simulamos trabajo con una pausa
        Thread.Sleep(2000); 
        
        Debug.WriteLine("Trabajo terminado. Configurando alarma...");

        // Configurar el despertar: Dentro de 10 segundos
        // TimeSpan define cuánto tiempo queremos dormir
        Sleep.EnableWakeupByTimer(TimeSpan.FromSeconds(10));

        Debug.WriteLine("Entrando en Deep Sleep. ¡Hasta luego!");

        // Iniciar el sueño profundo
        Sleep.StartDeepSleep();

        // --- ESTA LÍNEA NUNCA SE EJECUTARÁ ---
        Debug.WriteLine("Esto es imposible de leer.");
    }
}
Copied!

Si ejecutas esto, verás que el dispositivo arranca, trabaja 2 segundos y se apaga. 10 segundos después, vuelve a imprimir “¡Buenos días!”. Es el “Día de la Marmota” constante.

Despertar mediante un GPIO

Imagina un timbre inteligente o un botón de emergencia. El dispositivo debe estar dormido indefinidamente hasta que alguien pulse el botón.

// Configurar despertar por PIN
// Pin: GPIO 33 (Por ejemplo)
// Nivel: SleepWakeupGpioPin.WakeupGpioPinLow (Despierta cuando se conecta a GND)
Sleep.EnableWakeupByPin(Sleep.WakeupGpioPin.Pin33, 0);

Debug.WriteLine("Durmiendo hasta que alguien pulse el botón en GPIO 33...");
Sleep.StartDeepSleep();
Copied!

Ojo con los pines RTC: En Deep Sleep, solo unos pocos pines especiales (los pines RTC) pueden despertar al chip. En el ESP32, estos suelen ser los GPIO 0, 2, 4, 12-15, 25-27, 32-39. Si intentas usar un pin normal, no funcionará. Revisa el pinout de tu placa.

Consultar la causa del despertar

A veces tu dispositivo puede despertar por varias razones (por el temporizador O por el botón). Necesitamos saber qué ha pasado para actuar en consecuencia.

Sleep.WakeupCause causa = Sleep.GetWakeupCause();

switch (causa)
{
    case Sleep.WakeupCause.Timer:
        Debug.WriteLine("Me he despertado por el Timer. Toca medir temperatura.");
        break;
        
    case Sleep.WakeupCause.Ext0: // GPIO
        Debug.WriteLine("¡Alguien ha pulsado el botón! Enviando alerta.");
        break;
        
    case Sleep.WakeupCause.Undefined:
        Debug.WriteLine("Es el primer arranque (Power On / Reset).");
        break;
}
Copied!

Conservar datos entre despertares

Como dijimos, al dormir se borra la RAM. Si quieres contar cuántas veces te has despertado, no puedes usar una variable int contador = 0; al principio del programa, porque siempre se reiniciará a 0.

Tienes dos opciones:

  1. Flash/SD: Usar lo que aprendimos en el capítulo anterior (lento, desgaste de memoria).
  2. Memoria RTC: El ESP32 tiene una pequeña memoria (8KB) que NO se borra durante el Deep Sleep.

Una opción sencilla es guardar el estado necesario en un archivo pequeño en la flash interna (I:\estado.json). Hazlo con moderación para no desgastarla.

// Ejemplo conceptual
var estado = ConfigurationManager.CargarEstado();
estado.ContadorDespertares++;
ConfigurationManager.GuardarEstado(estado);

Sleep.StartDeepSleep();
Copied!

Estrategias de ahorro

Para que la batería dure de verdad, sigue estas reglas:

  1. Trabaja rápido: Cuanto menos tiempo esté el chip despierto, mejor.
  2. Conecta el Wi-Fi al final: No enciendas la radio nada más arrancar. Primero lee los sensores. Si el valor no ha cambiado, quizás no hace falta enviarlo. ¡Vuelve a dormir y ahórrate la conexión Wi-Fi!
  3. Usa IP Estática: El DHCP tarda varios segundos en negociar. Usar IP fija ahorra tiempo de radio encendida.