Arduino y el modo Sleep
Material requerido.
Arduino Nano | |
Una Protoboard mas cables. | |
Un multímetro digital. |
Saliendo del modo Sleep
Normalmente, hay muchos proyectos en los que hay que realizar acciones periódicas de tarde en tarde, como tomar lecturas de sensores o enviar mensajes a un servidor central, pero la mayor parte del tiempo nuestro Arduino está haciendo lo que mejor nos sale a todos: Nada.
Sin embargo nada, en un procesador, nada sigue suponiendo miles de instrucciones por segundo que consumen una energía preciosa, cunado funciona a baterías (En buena parte de los casos) y por eso dedicamos los capítulos anteriores a considerar como reducir ese consumo cuando no necesitamos hacer ese nada.
En la sesión previa vimos cómo poner nuestro Arduino en modo de animación suspendida, reduciendo al mínimo su consumo y vimos como entrar en ese modo Sleep, que tan buen resultado nos daba a la hora de estirar las baterías.
Pero en esa sesión usamos un Watch Dog Timer (De los que aún no hemos hablado) para despertar a la bella durmiente cada 8 segundos. Sin embargo en un proyecto que requiera tomar lecturas cada media hora, en mitad de la sabana africana a 300 Km del enchufe más cercano, despertar nuestro MCU cada 8 segundos se me antojó un lujo excesivo y derrochón, cuando tenemos que estrujar al máximo la capacidad de la batería.
Por poco que Arduino consuma al despertar y volver a dormirse es más que nada, y cuando hay que escatimar con el consumo, hay que hacerlo hasta el final, así que nada de despertar sin necesidad, que el invierno es largo.
Y para eso hay un modo de dormir nuestro procesador especialmente útil, que es el de duérmete sin fecha de despertar, pero levántate cuando recibas una interrupción, es decir cuando ocurra algo que estamos esperando.
Imagínate un contador de lluvia en la Sabana Africana, ¿Por qué despertar cada poco para ver si llueve y gastar una energía valiosa sin necesidad?
Hiberna hasta que tus detectores de lluvia te den una señal. Despierta entonces y toma la medida del modo que sea, e inmediatamente a hibernar de nuevo hasta que vuelva a llover (Lo que puede suponer meses).
Este problema nos lo planteaba uno de nuestros lectores y me hizo pensar mucho al respecto. Me encanto el proyecto y el problema era precisamente ahorrar batería al máximo hasta que hubiera lluvia (Si no entendí mal) y despertar cuando llegara para medir el caudal.
Esta pequeña sesión va de como estrujar al máximo nuestro tiempo de vida a baterías, despertando solo cuando sea imprescindible mediante una interrupción.
Arduino y las interrupciones
Comentamos en la sesión previa, que podíamos despertar a nuestro Arduino durmiente, mediante uno de 3 eventos posibles:
La idea de esta sesión es montar un pequeño prototipo de como dormir nuestro Arduino y usar una interrupción hardware externa para despertarlo, hacer lo que corresponda, y vuelta dormir hasta la próxima interrupción.
Naturalmente, para seguir este capítulo, es conveniente que hayas leído el capítulo anterior Ahorrando energía, y que estés cómodo con las interrupciones, que ya vimos en una sesión anterior.
Si no es así, empieza por leer esas sesiones, porque lógicamente en esta no vamos a volver a lo mismo (Soy vago, lo siento, aunque poco) pero es importante que os suenen los conceptos que vamos a usar, o de lo contrario todo esto va a ser un poco complicado de seguir.
En la sesión en que hablamos de las interrupciones hicimos este circuito:
En el usábamos un sencillo pulsador para disparar una interrupción en Arduino. Como el Arduino UNO y el Nano comparten el mismo procesador, todo el tema de interrupciones y pines son exactamente lo mismo que vimos allí y podemos montar este mismo circuito sobre un Nano.
Con este sencillo ejemplo podemos ilustrar el uso de una interrupción hardware y de cómo usarla para despertar al Belloduino durmiente.
Tenemos un inconveniente, no obstante, y es que como vamos a utilizar un mensaje por la puerta Serial cuando recibamos una interrupción, no lo tenemos fácil para medir el consumo real, porque no podemos hacerlo sin romper un cable USB y cortar los hilos de alimentación.
Pero seguro que en un caso real podréis encontrar el modo de medir en vivo los consumos precios.
El programa de control
De nuevo, el programa es de lo más sencillo. Empezamos con include y definiciones:
#include "LowPower.h" const int wakeUpPin = 2; int contador = 0; int n = contador ;
Vamos a usar la interrupción del pin 2 y por eso la definimos en un const, y luego un par de variables que usaremos para contar el número de pulsos que recibimos que va incrementando contador, y usaremos n para imprimir su valor cuando cambie.
void setup() { Serial.begin(9600); pinMode( wakeUpPin, INPUT); }
Aquí ponemos a 9600 baudios el puerto serie, pero habría que hacer pruebas y medir consumos, para tener claro si compensa subir la velocidad de comunicación al máximo.
Los que van a por nota, probablemente tengan la tentación de usar el pin 2 en modo INPUT_PULLUP para ahorrarse la resistencia (Bien, la vagancia se llama eficiencia cuando quieres alabarla) pero no es una buena idea en este caso.
Como ese modo pone a 5V mediante una resistencia interna de pullup es una buena opción en la mayor parte de los casos, pero no estoy tan seguro de que lo sea en este, porque al poner a dormir Arduino, casi con seguridad también se pondrá a dormir la tensión de pullup y no estoy seguro de que al despertar todo vaya como es debido.
A diferencia de la sesión de las interrupciones aquí queremos controlar el Attach y Dettach de la interrupción en el programa principal:
void loop() { attachInterrupt( 0, ServicioBoton, FALLING); LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF); detachInterrupt(0); if (n != contador) { Serial.println(contador); n = contador ; Serial.flush(); } }
La primera línea del loop() engancha una interrupción llamada ServicioBoton que se dispara cuando usamos el pulsador, y que simplemente incrementa contador, para saber cuántas veces se ha pulsado.
La segunda línea, que ya conocemos de la sesión previa, pone nuestro Arduino Nano a dormir a pierna suelta y va a seguir así hasta que reciba una interrupción.
Cuando detecte una interrupción hardware en el pin 2, despierta súbitamente, Incrementa el valor de contador y sigue el ciclo normal del programa, donde se encuentra lo primero con un dettachInterrupt.
Es decir, desautorizamos las interrupciones hasta que hagamos nuestros deberes, que en este caso son simplemente comprobar si contador (Que se incrementó con la interrupción) es diferente de n (El valor último registrador de contador) y si es así imprime el valor de contador.
Fijaros que no estamos haciendo un debouncing del pulsador. Sería una muy mala idea hacerlo aquí con un delay, porque el tiempo que durase el delay, nuestro procesador está activo y consumiendo y eso es algo que no estamos dispuestos a tolerar (Si ahorramos, ahorramos hasta el final) Vamos a dejar la señal sin filtrar por ahora a ver que pasa.
Fijaros también que cuando mandamos un valor a la puerta serie, usamos un flush() al final para asegurarnos de que se vacía la cola serie, de lo contrario, nunca parece que llega a imprimirse nada (Es que la puerta serie es un asco de lenta comparativamente hablando)
Si corremos el programa veréis que cada vez que pulsamos el botón Arduino despierta e imprime el valor de contador en la puerta serie, y sorpresa sorpresa, no es necesario hacer el debouncing, porque a pesar de los rebotes imprimir lleva mas tiempo de lo que tarda el rebote.
Y por último, al volver al inicio del loop, reinstalamos las interrupciones y entramos en sueño profundo de nuevo (O Stasis como dicen en las pelis de Ciencia Ficción, mis favoritas, claro)
Aquí os dejo un mini video con el resultado:
Resumen de la sesión
<