Micropython: usando los modos de bajo consumo del ESP8266

Si un dispositivo basado en ESP8266 (u otro módulo o microcontrolador equivalente) tiene una fuente de alimentación permanente derivada de la red eléctrica, el consumo de corriente en funcionamiento no es un problema. Sin embargo, si ese dispositivo es portátil y depende de unas baterías para su operación, el tema del consumo es crucial para lograr la máxima autonomía posible. En este artículo veremos que opciones tenemos cuando utilizamos un ESP8266 y las aplicaremos a un proyecto que envía datos a Thingspeak usando una placa D1 Mini programada con Thonny.

Un módulo como el NodeMCU o el D1 Mini, ambos basados en el ESP8266, mientras mantienen una conexión a Internet a través de una red Wifi para enviar y recibir información puede tener un consumo promedio de aproximadamente 75 mA, con picos aún mayores en los momentos de transmisión. Si alimentamos nuestra placa con una batería de Litio tipo 18650, con este consumo tan elevado, el tiempo de funcionamiento hasta que la batería se descargue será de unas pocas horas (en realidad lo mas apropiado sería emplear otro método de comunicación, ya que Wifi consume mucha energía).

Sin embargo, en la mayoría de los casos, no es necesario que el micro funcione todo el tiempo a ese nivel de consumo. Si nuestra placa mide alguna variable, por ejemplo la temperatura, para reportarla cada 5 minutos a un servidor, no necesitamos que esté en funcionamiento pleno todo el tiempo. Podemos disminuir drásticamente el consumo y aumentar la duración de la carga de la batería si podemos “dormir el micro” para que despierte cada 5 minutos, realice la medición y reporte su valor (algo que no demora mas que unos pocos segundos) y luego vuelva a dormir.

Afortunadamente, el ESP8266 cuenta con modos de funcionamiento de bajo consumo que nos permiten apagar distintas secciones del micro y reducir notablemente el consumo de corriente, incluyendo uno que pone a dormir al micro durante un tiempo determinado. Analicemos estos distintos modos de funcionamiento.

Fig. 1. El consumo de corriente es fundamental en equipos portátiles

Modos de bajo consumo

El 8266 (que recordemos es un SoC complejo, con distintos módulos en su interior) tiene tres modos de funcionamiento de bajo consumo configurables desde el software que se diferencian entre ellos por las secciones o módulos que apagan para bajar el consumo mientras están activos. Estos son Modem Sleep, Light Sleep y Deep Sleep. La siguiente tabla muestra que módulos del SoC quedan funcionando en cada modo:

Sección Modem Sleep Light Sleep Deep Sleep
Wi-Fi NoNoNo
Clock del sistema SiNoNo
RTC SiSiSi
CPU SiPendienteNo

Veamos mas en detalle como funciona cada uno de estos modos:

Modem-sleep

En este modo, la CPU se mantiene en funcionamiento pero si el módulo Wi-Fi está configurado en modo STATION (STA) se lo desactiva cuando no hay transmisión de datos. Este modo es activado automáticamente y no es necesaria ninguna acción desde el software.

Light-sleep

Este modo es muy similar al anterior, sólo que además se apaga la CPU. Es útil en aplicaciones donde debemos mantener la conectividad y la CPU debe activarse ante la llegada de alguna información.

Si bien estos dos modos de funcionamiento bajan el consumo del ESP8266, siguen manteniendo un nivel elevado para un equipo alimentado a baterías, así que no profundizaremos en los detalles de su operación. El modo que realmente baja el consumo de manera drástica y nos permite alargar la vida de las baterías es el modo Deep-Sleep.

Deep-sleep

En este modo no sólo se apaga la CPU sino que también se desactiva el módulo Wi-Fi y se mantiene operando sólo el bloque del Reloj de Tiempo Real (RTC) que se encarga de reactivar al chip. De esta forma se logra bajar significativamente el consumo, a valores de micro Amper.

Utilizando el modo Deep Sleep

Este modo es el único que no se activa de manera automática y debe ser controlado por el programa. En Micropython, la instrucción que utilizaremos es deepsleep () que está incluida en el módulo machine.

Una vez que el micro está en modo Deep Sleep, la única forma de reactivarlo es realizando un RESET, poniendo a cero el pin RST. Esto puede lograrse manualmente (con un pulsador) o a través de un circuito que la ponga a cero. Para aquellas aplicaciones en las que queramos que el reinicio sea automático, luego de una determinada cantidad de tiempo, debemos usar una alarma del módulo RTC.

Las alarmas del RTC tienen una funcionalidad reducida en el ESP8266 y su uso prácticamente se reduce a esta aplicación, sacar al micro del modo Deep Sleep. Podemos programar una alarma (específicamente debe ser ALARM0) con un valor de tiempo, terminado el cual se produce el RESET del micro. Este RESET no es interno: cuando se produce la alarma el micro lleva a cero la salida GPIO16, por lo que debemos realizar una conexión entre RESET y GPIO 16.

En una placa D1 mini, que es la que usaremos en la pruebas mas adelante, la conexión es como sigue (GPIO16 está mapeado en D0):

Fig. 2. Conexión de RESET de una D1 mini

La programación de la alarma puede verse en el siguiente programa de ejemplo, que enciende brevemente el LED del D1 Mini, y luego activa el modo Deep Sleep:

from machine import Pin, RTC
from time import sleep

led = Pin (2, Pin.OUT)

rtc = machine.RTC()

led.off ()  #Prende
sleep (1)
led.on ()  #apaga

print ("Me voy a dormir")
sleep (5)

#Configura una alarma para resetear y despertar al micro
rtc.irq(trigger=rtc.ALARM0, wake=machine.DEEPSLEEP)
rtc.alarm(rtc.ALARM0, 10000)

machine.deepsleep()

Analicemos su funcionamiento:

En la línea 1 se incluyen las clases Pin y RTC del módulo machine y en la línea 2 la función sleep del módulo time.

En la línea 4 creamos un objeto para el LED de la D1 mini, en el pin 2 y en la línea 6 un objeto de la clase RTC.

Entre las líneas 8 a 10 prendemos y apagamos el LED.

En las líneas 12 y 13 se imprime un mensaje de aviso antes de entrar al modo Deep Sleep y se hace un retardo de 5 segundos. Este retardo es útil porque si no estuviera sería difícil retomar el control de la placa desde el IDE para modificar el programa.

En la línea 16 configuramos una interrupción asociada al RTC. Esto es parecido a asociar una interrupción a un pin GPIO, como vimos en un artículo anterior. En esta línea se indica que el RTC produce una interrupción, que su origen es la Alarma 0 (ALARM0) y que la acción que se realizará al producirse es sacar al micro del modo Deep Sleep).

A continuación, en la línea 17 se configura la Alarma 0 del RTC para que se produzca en 10 segundos (10.000 milisegundos).

Finalmente en la línea 19 se lleva el módulo al modo Deep Sleep.

Luego de los 10 segundos especificados en la Alarma 0, el micro pondrá a cero GPIO 16 (D0), producirá un RESET y el programa se reiniciará.

En la consola podremos ver el anuncio “Me voy a dormir” y a los 10 segundos notaremos que aparecen muchos caracteres extraños, indicando que el módulo se ha reseteado y el programa se reinicia.

Fig. 3. Consola de Thonny al producirse el RESET

Importante

Para que el programa se pueda reiniciar debemos guardarlo en la memoria de la placa con el nombre de “main.py”.

Ejemplo enviando datos a Thingspeak

Apliquemos lo que hemos visto hasta aquí a un caso práctico, retomando el proyecto del medidor de temperatura y humedad que reporta la información a un canal de Thingspeak que les presenté en este artículo.

Para esta prueba voy a utilizar una placa D1 mini conectada como muestra la fig. 2 de mas arriba, agregando el sensor de humedad (la conexión del sensor también pueden verla en el artículo anterior).

El programa original, sin utilizar el modo de bajo consumo es el siguiente:

#Envia temperatura y humedad a Thingspeak 

import network, time, urequests 
from dht import DHT11 
from machine import Pin 

def conectaWifi (red, password):       
    global miRed       
    miRed = network.WLAN(network.STA_IF)            
    if not miRed.isconnected():                    #Si no está conectado…           
        miRed.active(True)                              #activa la interface           
        miRed.connect(red, password)         #Intenta conectar con la red           
        print('Conectando a la red', red +"…")           
        timeout = time.time ()           
        while not miRed.isconnected():           #Mientras no se conecte..               
            if (time.ticks_diff (time.time (), timeout) > 10):                   
                return False       
    return True 

sensorDHT = DHT11 (Pin(5)) 

if conectaWifi ("nombre", "clave"):     
    
    print ("Conexión exitosa!")     
    print('Datos de la red (IP/netmask/gw/DNS):', miRed.ifconfig())            

    url = "https://api.thingspeak.com/update?api_key=*************"            

    while (True):         

        time.sleep (60)         
        sensorDHT.measure ()         

        temp=sensorDHT.temperature ()         
        hum=sensorDHT.humidity()         

        print ("T={:02d} ºC, H={:02d} %".format (temp,hum))         

        respuesta = urequests.get(url+"&field1="+str(temp)+"&field2="+str(hum))               
        print(respuesta.text)         
        print (respuesta.status_code)         
        respuesta.close ()   

else:        

    print ("Imposible conectar")        
    miRed.active (False)

Veamos cuanto consume la placa con este programa

Fig. 4. Consumo sin usar Deep Sleep

Como se puede ver, el consumo promedio en reposo, cuando no transmite es de unos 75 mA y aumenta en los breves momentos en que se envían los datos al servidor.

Ahora modifiquemos el programa agregando la llamada a deepsleep, para que quede como el siguiente:

#Envia temperatura y humedad a Thingspeak cada 1 minuto

import network, time, urequests
from dht import DHT11
from machine import Pin, RTC

def conectaWifi (red, password):
    global miRed
    miRed = network.WLAN(network.STA_IF)
    if not miRed.isconnected():              #Si no está conectado…
        miRed.active(True)                   #activa la interface
        miRed.connect(red, password)         #Intenta conectar con la red
        print('Conectando a la red', red +"…")
        timeout = time.time ()
        while not miRed.isconnected():           #Mientras no se conecte..
            if (time.ticks_diff (time.time (), timeout) > 10):
                return False
    return True
 
sensorDHT = DHT11 (Pin(5))
rtc = machine.RTC()

if conectaWifi ("--red--", "--pass--"):
    print ("Conexión exitosa!")
    print('Datos de la red (IP/netmask/gw/DNS):', miRed.ifconfig())

    url = "https://api.thingspeak.com/update?api_key=****************"  

    #Medir temperatura y humedad
    sensorDHT.measure ()  
    temp=sensorDHT.temperature ()  
    hum=sensorDHT.humidity()  
    print ("T={:02d} ºC, H={:02d} %".format (temp,hum))  

    #Enviar a ThingSpeak
    respuesta = urequests.get(url+"&field1="+str(temp)+"&field2="+str(hum))      
    
    print (respuesta.text)  
    print (respuesta.status_code)  
    respuesta.close ()  

    #Configura una alarma para resetear y despertar al micro  en 1 minuto
    rtc.irq(trigger=rtc.ALARM0, wake=machine.DEEPSLEEP)  
    rtc.alarm(rtc.ALARM0, 60000)  

    #A dormir  
    machine.deepsleep()

else:

    print ("Imposible conectar") 
    miRed.active (False)

Si probamos el mismo circuito, vemos que cuando la placa no transmite (es decir, la mayor parte del tiempo) el consumo disminuye a 175 uA, es decir unas 400 veces menos!

Fig. 5. Consumo con Deep Sleep

Para hacer una comparación de la duración de la bateria en un caso y en el otro en una situación mas real, armé el prototipo que se muestra a continuación. Sobre una base doble monté una D1 Mini, un Battery Shield (una placa sumamente útil que analizaré en un próximo artículo) y a falta de un shield con el DHT11 soldé el mío sobre una proto shield, que aproveché para hacer la unión entre D0 y RST.

Fig. 6. Prototipo para las pruebas de batería

La batería no tiene mucha capacidad de carga, pero la intención no es lograr un período prolongado de funcionamiento sino comparar la duración de la misma cuando se usa el modo de bajo consumo y cuando no se lo utiliza.

Con el primer programa, sin emplear el modo de bajo consumo, la duración fue de poco menos de 3 horas, enviándose 148 reportes a Thingspeak.

Con el segundo programa, que si aprovecha el modo Deep Sleep, la duración de la misma carga fue de 27 horas, durante las que se enviaron 1602 reportes. Esto implica una duración de la batería casi 10 veces superior.

Esto demuestra que el uso del modo Deep Sleep es fundamental si queremos lograr una autonomía de días o aún meses en un equipo funcionando a baterías.

Otros consumos

Debemos tener presente algo sumamente importante: El modo Deep Sleep afecta sólo al consumo del ESP8266 y no al resto del circuito que lo rodea. La D1 mini contiene, entre otras cosas, un driver CH340, cuyo consumo se mantiene y en este ejemplo el sensor de humedad que le hemos conectado también sigue drenando corriente de la batería aunque el micro esté en bajo consumo.

Si queremos disminuir aún mas el consumo y hacer durar mas la batería, deberíamos elegir otro módulo basado en ESP8266 que no tenga driver USB e incluir un circuito adicional que corte la alimentación del DHT11 y cualquier otro elemento adicional que tenga el circuito.

Conclusión

En este artículo vimos los modos de bajo consumo del ESP8266 analizando mas en profundidad el modo Deep Sleep. Vimos también las instrucciones necesarias para utilizarlo en Micropython y las conexiones que debemos realizar en el circuito, usando como ejemplo una placa D1 Mini. A través de un ejemplo mostramos la enorme diferencia de consumo entre el modo normal y el modo Deep Sleep, poniendo en evidencia la importancia de utilizar esta funcionalidad en un equipo portátil.

Como siempre, espero que la información les haya sido de utilidad para sus proyectos en Micropython. Cualquier duda o sugerencia pueden dejarla mas abajo en la sección de comentarios.

Deja un comentario

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.

A %d blogueros les gusta esto: