bclose

El modo Sleep y las Interrupciones

Despertando por interrupción hardware

Arduino y el modo Sleep

 

 

    • Seguimos jugando con el modo sleep.
    • VAmos a ver cómo salir del modo Sleep de Arduino mediante una interrupción.
    • Montaremos un pequeño circuito que muestre el uso práctico.
 
  • Material requerido.

    Vista principal Arduino Nano

     

    ProtoboardconexionesUna Protoboard mas  cables.
    Multimetro sencilloUn 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:

     
    • Un Timer ( Un Watch Dog, es un caso particular de Timer)
    • Una interrupción hardware.
    • Una señal UART (Que también puede ser SPI o I2C por lo que tengo entendido, pero no he encontrado mucha información al respecto). 

    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:

    Esquema de conexión

    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.

    diagrama protoboard

    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.

     
    • Aunque al subir la velocidad serie consumirá un poco más, también nos obliga a tener encendido el chip más tiempo (115200 / 9600 = 12 veces más tiempo) y casi con certeza que el aumento de velocidad compensa de sobraPero te fíes de mi y compruébalo si tienes necesidad de ello. 

    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.

     
    • Claro está, que esta teoría, como tantas otras, debería pasar la prueba de la realidad para salir de dudas. Si alguno hacéis la prueba me encantaría saber el resultado

    A diferencia de la sesión de las interrupciones aquí queremos controlar el Attach y Dettach de la interrupción en el programa principal: Prog_146_1

    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.

     
    • Este sería un buen caso en que podríamos usar un filtro RC para evitar los rebotes, pero esa es otra historia. 

    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

     

     

      • Hemos visto el que podemos dormir a Arduino indefinidamente y despertarlo cuando se produce una interrupción hardware.
      • La ventaja de este método es que no desperdiciamos energía en despertar sin necesidad.
      • Cualquier interrupción hardware le saca del modo letárgico y luego podemos ponerlo a dormir de nuevo 

    (21) Comments

    • Hola Juan,
      Te transcribo el mensaje exacto de nuevo:
      Hola Juan,
      Gracias como siempre por el esfuerzo que haces es explicar las cosas claramente. Tengo una duda, normalmente se pone una resistencia para hacer pullup de un pulsador de 10K, veo que usas una de 330ohm, ¿es por algo particular?. Gracias

      • Hola Jose, En principio un pull up es para mantener la linea en tension alta y normalmente conviene usar una resistencia alta para evitar consumos inutiles, de hecho 330 es mas bien poco y convendria usar al menos 1k o 10k para obligar precisamente a un consumo minimo

        Pero ya sabes, ese dia tendria una de 330 a mano y me puese a buscar mas

    • Hola Juan,
      Disculpa que te pregunte de nuevo por lo mismo. El comentario que te hago de la resistencia de Pullup, la respuesta parece como si se hubiera cruzado con otra pregunta distinta o yo por lo menos no la he entendido.

      Gracias y saludos

      • Hola Jose, porfa repiteme la pregunta entonces

    • Hola Juan,
      Gracias como siempre por el esfuerzo que haces es explicar las cosas claramente. Tengo una duda, normalmente se pone una resistencia para hacer pullup de un pulsador de 10K, veo que usas una de 330ohm, ¿es por algo particular?. Gracias

      • No creo que pueds modificar el alcance, o por lo menos yo no se como

      • Luis A. Garcia

      Hola Juan,
      Primero felicitarte por tus tutoriales, muy buenos.
      Yo estoy empezando a manejar el Arduino Uno con un GSM900, el proyecto consiste controlar el estado de 4 entradas digitales del Arduino Uno (High a low y low a high) estoy usando los pines 10, 11 12 y 13. Al cambiar el estado en una de las entradas realiza una función determinada, envía un mensaje y realiza una llamada.
      Lo que deseo (no logro hacerlo) es que mientras no haya cambio de estado en los pines el Arduino quede en modo sleep, cuando sucede un cambio de estado en cualquiera de los pines de entrada realice su función determinada y después que termine, vuelva a modo sleep a esperar otro cambio de estado en cualquiera de los pines y así sucesivamente. Gracias de antemano

      • Hola Luis, lo más probable es que tengas delays dentro de la interrupción. Yo he probado a realizar una llamada utilizando la función que tenemos en la sesión del GSM900 sin meterle un delay ni colgar la llamada y activando la interrupción con un pulsador botón y parece que ha ido bien la cosa. Un saludote.

    • Se me había olvidado comentar que utilizo el shield 2 GSM de Arduino también (por si afecta en la utilización del pin 2).

      • Hola Carlos, pues por lo que leo ese shield utiliza los pines 2 y 3 del Arduino para la comunicación serie. Así que probablemente por ahí viene el problema. Un saludote.

    • Hola, tengo un problema a la hora de despertar a Arduino. Lo primero de todo es que, una vez está dormido, no lo puedo despertar inmediatamente, sino que tengo que esperar un cierto tiempo para poder volver a hacerlo (no entiendo el motivo, ya que una vez la entrada detecta el cambio de estado se debería despertar). A parte de eso, el problema es que a veces va y a veces no (introduciéndole una señal de 5V a la entrada 2). No se por donde puede venir el fallo… A ver si puedes arrojarme un poco de luz con este asunto.
      Gracias de antemano.

    • Otra posible configuración sería intentar despertar al arduino a partir de una señal de radio (NRF2401). Una estación central, ¿podría “llamar” al arduino, despertarlo y que este midiera, contestara y se pusiera de nuevo a dormir? ¿Para ello habría que definir la radio como: “RF24 radio(2,10);” para la interrupción 0?
      Como veis, esto de las interrupciones y el modo “sleep” me interesa, pero creo que voy algo perdido…

      • Hola Jaime, no he revisado los tutos del NRF y los tengo un poco oxidades, pero si podemos generar una interrupcion en el pin 2 seria suficiente para garantizar que arduino despierta

    • Hola,
      a pesar de que soy un novato en temas electrónicos, este tutorial me parece superinteresante.
      Las interrupciones por hardware me han quedado muy claras, sin embargo, en el tema de los timers, tengo mis dudas. Mi idea es montar algo así como una estación meteo que mida cada 20 minutos.
      Existe alguna posibilidad de poner a “dormir para siempre” al alduino y que se despierte a los 20 minutos, en vez de hacer un bucle del tipo:
      “for (int i = 0 ; i < 151 ; i++)
      LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);"

      Si se pudiera incluir una interrupción asociada a la librería "TimeAlarms" (explicada aquí: http://www.prometec.net/relojes-rtc/), y una orden del tipo: "Alarm.timerRepeat(1200, Funcion);" sería perfecto, pero me temo que si el Arduino está dormido, las alarmas no funcionen…

      ¿Algún consejo?

      • Seta43

      Hola

      He estado viendo tu artículo que me ha gustado.
      Hace 3 años realice un estudio sobre como reducir el consumo en el ATMEGA328.
      http://www.seta43.netau.net/arduraa.html
      http://www.seta43.netau.net/ardurab.html
      Espero que te sirva.

      Un saludo
      Juan

      • Hola Juan.

        Muy interesantes tus pruebas. Gracias por compartir.

        Saludos.

    • Hola a todos.

      Lo primero felicitarte Admin por este nuevo tutorial, que para los que andamos pensando en proyectos de cierta autonomía, es casi de obligado cumplimiento ir pensando en el modo sleep.

      David: con respecto al proyecto que planteas, mejor crea un nuevo debate en el apartado proyectos y allí debatimos sobre el tema. Explica lo que pretendes crear o necesitas, lo vamos analizando y centrándolo en la medida de lo posible.

      Saludos.
      Man (FlyMan)

      • Juan David

      Muchas gracias, al ver el programa descargado ya me quedó todo claro… me alegra saber que mi lógica no me engañaba… y nuevamente felicidades por tu blog y tu tienda..!!

      No se si éste es el medio pero aprovecho para preguntarte sobre un pqueño proyecto que estoy haciendo: Quiero hacer un detector del nivel de agua de un depósito, incluso calcular los litros, por internet encuentro infinidad de cosas pero siempre caen en lo mismo, sensor de ultrasonido, boyas, contactos a lo largo de la vertical del depósito (pocos preciso),… pero siendo agua y de un depósito que está montado en un vehículo (una furgoneta camperizada), el ultrsónico se moja, y el resto de medios me dificultan saber los listros aproximados…

      ¿Se te ocurre algún medio medianamente económico?

      Existen ultrasónicos impermeables… de 80€ para arriba, con éso me toda la gama Arduino.
      Existen sensores capacitivos con forma de varillas… 120€ para erriba. La gama Arduino y la Raspberry.

      Estoy investigando con los sensores infrarojos… pero claro, como es agua igual me mide el fondo del depósito..!!

      Si mis preguntas son mucho pedir, no me hagas caso y yo sigo igual de contento de seguir tu blog…

      Muchas gracias..!!

      • Hola Juan,

        Un placer ayudar si puede, Tienes FlyMan por el foro y la pagina que estaba haciedo un proyecto para control de un aljibe en una finca rustica, que ha empleado sensores ping de ultrasonidos para medir el nivel, pero con agua inmovil claro, no en una furgoneta y si el lee esto seria el mas indicado para contrate su experiencia.

      • Juan David

      Hola, no soy ningún experto programador por lo que discúlpame si mi siguiente pregunta resulta absurda:
      Veo que has inicializado las variables “n” y “contador” con valor 0, pero no veo dónde se realiza el incremento en la variable “contador”. Supongo que será en alguna de las 3 primeras instrucciones del loop() (attachInterrupt, LowPower.powerDown, detachInterrupt) ya que si en el “if” la variable “n” es distinto de “contador” se imprime el valor en pantalla.
      Y por otro lado… ¿Por qué es necesario el “if”, Arduino se pone a dormir en cada vuelta del loop() .. no? si es así, indicando simplemente “Serial.println(contador)” ya imprimiría en pantalla.
      Gracias y felicidades por el blog… lo sigo casí a diario a ver si hay algo nuevo..!

      • Hola Juan, no te disculpes por preguntar que mas de na vez meto la pata y por eso os digo que no os fieis demasiado de mi.

        Veo que me he olvidado de incluir en el listado (Aunque si est en el programa descargable) la funcion
        void ServicioBoton()
        { contador++ ;
        }
        Que es la que se ejecuta cada vez que hay una interrupcion e incrementa contador ¿vale?
        La razon del if que preguntas es simplemente que nuestro arduino puede ser muy rapido y si mantienes pulsado el boton sufientemente al final podria imprimir varias veces el mismo resultado. Pero tienes razon en que se dormiria antes por lo que probablemente se podria eliminar sin que se notara la diferencia, pero escribi el programa pensando en la claridad mas que en la rapidez

    Give a Reply

    WordPress Anti-Spam by WP-SpamShield