Arduino (IoT): Simple Tutorial Arduino Durmiente: Parte 2

Arduino Honduras Santiapps Marcio Valenzuela

Tutorial RTC: Arduino Durmiente -Parte 2

En el tutorial anterior aprendimos sobre dormir la Arduino.  Usamos eventos externos como un interrupt en un pin, (Pin2=INT0) o recepción de data Serial (la cual mandamos del Rx al Pin2(INT0) y la usamos como interrupt externo.

Ahora veremos como podemos usar eventos internos al MCU para despertarlo, como timers.

Requisitos:

  1. Computadora (mac)
  2. Arduino UNO
  3. Arduino IDE (https://www.arduino.cc/en/Main/Software)
Arduino Tutorial Sleep Wakeup Dormir Despertar Santiapps Marcio Valenzuela
Arduino Tutorial Sleep Wakeup Dormir Despertar

Repaso de los modos de dormir:

  • SLEEP_MODE_IDLE: 15 mA (ahorra poco pero retiene muchas funciones)
  • SLEEP_MODE_ADC: 6.5 mA
  • SLEEP_MODE_PWR_SAVE: 1.62 mA
  • SLEEP_MODE_EXT_STANDBY: 1.62 mA
  • SLEEP_MODE_STANDBY : 0.84 mA
  • SLEEP_MODE_PWR_DOWN : 0.36 mA (ahorra mucho pero OJO!)

Para mayor información puede visitar (http://www.gammon.com.au/forum/?id=11497)

Repaso de las formas de despertar una MCU dormida:

  • via interrupt externo. Alguna acción externa es requerida para despertarla.
  • via el UART (USB serial interface). Una acción via el puerto serial USB puede despertarla.
  • via un timer interno. La Arduino puede ser despertada por Timer1, uno de los timers internos que corren en su chip.
  • via el watchdog timer (wdt). La Arduino solo puede ser despertada por el WDT, un timer especial que corre por aparte.

Repasemos los wakeups disponibles a cada modo de dormir según el datasheet:

Arduino Tutorial Sleep Wake up Dormir Despertar Santiapps Marcio Valenzuela
Arduino Tutorial Sleep Wake up Dormir Despertar

 

Eventos Internos

Para comprender eventos internos que puedan despertar la Arduino es necesario entender un poco sobre como funciona el MCU.  El video original esta inglés aquí () sin embargo hacemos una reseña sencilla a continuación:

Arduino Atmel Atmega328 Sleep Mode Watchdog Timer Simple Tutorial Santiapps Marcio Valenzuela
Arduino Atmel Atmega328

La Atmega tiene un counter de 16MHz que controla sus operaciones (significativamente mas lento que nuestras computadoras de escritorio, portátiles e incluso nuestros celulares).  Pero adicionalmente tiene otro counter de 128kHz que es aun MAS lento, llamado el wdt.  Este otro counter se puede usar como alarma con un timeout o alarma.  Esto también es conocido como “la mordida del perro”.  El perro guardian muerde cuando se dispara la alarma o se termina el timeout.  Al suceder esto se puede generar un interrupt o un reset del sistema.  Esto se usa comúnmente en proyectos remotos donde, si algo llegara a pasar como falta o fluctuación de energía o algo que “atasca” la operación normal del MCU, se puede resetear automáticamente.

Como podemos ver en la imagen, el reloj principal que maneja el código principal del loop (el azul), tiene la habilidad de resetear el timer wdt.  Es decir, configuramos el wat (verde) para que cuente (desde 16ms hasta 8s) hasta el timeout que deseamos.  Al llegar al valor del timeout, la alarma de dispara, o el perro muerde.  Pero desde el código principal (azul) podemos reiniciar el counter (verde) nuevamente a 0, antes que llegue a su valor de timeout.  Esto se conoce como “Patting the Dog” o darle una palmada de cariño al perro, para que no nos muerda y se vuelva a dormir!

Veremos muchos valores de estos “relojes” y del chip que tenemos que setear al usar el wdt o cualquier de los otros 3 timers internos de la atmega328.  Pero algo importante de notar es que cuando el timer se dispara y se genera un interrupt, se hace a través del ISR(), una función que veremos en los siguientes ejemplos.

Ahora veamos como despertar la Arduino via el Timer1, interno:

#include <avr/sleep.h>
#include <avr/power.h>
#define LED_PIN (13)
volatile int f_timer=0;
ISR(TIMER1_OVF_vect){
  // Set la flag llamada f_timer
   if(f_timer == 0){
     f_timer = 1;
   }
}

void enterSleep(void){
  set_sleep_mode(SLEEP_MODE_IDLE);
  sleep_enable();
  /* Deshabilitar los peripherals para reducir consumo y también para que no generen interrupts que puedan despertar la Arduino sin querer
  power_adc_disable();
  power_spi_disable();
  power_timer0_disable();
  power_timer2_disable();
  power_twi_disable();  
  // Dormir
  sleep_mode();
  // Ejecucion continua aquí luego de timer1 timeout. Es critico deshabilitar sleep aqui
  sleep_disable();
  // Rehabilitar peripherals
  power_all_enable();
}

void setup(){
  Serial.begin(9600);
  // Pin LED de Arduino 
  pinMode(LED_PIN, OUTPUT)
  // Configurar el Timer1 interno
  TCCR1A = 0x00; 
  // Resetear el timer counter register.
  //  Aquí se puede asignar un valor para reducir el timeout.
  TCNT1=0x0000; 
  // Configurar el prescaler a 1:1024 para un timeout de 4.09 segundos
  TCCR1B = 0x05;
  // Habilitar el timer overlook interrupt
  TIMSK1=0x01;
}

void loop(){
  if(f_timer==1){
    f_timer = 0;
    // Encender una LED
    digitalWrite(LED_PIN, !digitalRead(LED_PIN));
    // Dormir!
    enterSleep();
  }
}

Aquí nuevamente tenemos el setup() y la función de dormir llamada enterSleep().  En el setup() configuramos el timer según el datasheet.  En enterSleep() contiene al igual que sleepNow, el código para dormir la MCU.  loop() revisa cuando dormir.  Finalmente ISR es nueva, es una rutina que ejecuta el MCU cuando recibe un interrupt.

Finalmente veamos un ejemplo con WDT:

#include <avr/sleep.h>
#include <avr/power.h>
#include <avr/wdt.h>

#define LED_PIN (13)

volatile int f_wdt=1;

ISR(WDT_vect){
  if(f_wdt == 0)
  {
    f_wdt=1;
  }
  else
  {
    Serial.println("WDT Overrun!!!");
  }
}
void enterSleep(void){
  set_sleep_mode(SLEEP_MODE_PWR_SAVE);   // O usar SLEEP_MODE_PWR_DOWN para menor consumo
  sleep_enable();
  
  // Dormir
  sleep_mode();
  
  // Programa continua aquí luego del WDT Overrun
  sleep_disable(); // Impt prioridad deshabilitar sleep
  
  // Re-habilitar peripherals
  power_all_enable();
}

void setup(){
  Serial.begin(9600);
  Serial.println("Inicializando...");
  delay(100); // Delay requerido para el Serial

  pinMode(LED_PIN,OUTPUT);

  // Configurar WDT
  
  // Clear reset flag
  MCUSR &= ~(1<<WDRF);
  
  // Para cambiar WDE o el precaver, debemos set WDCE
  // Esto permite updates por 4 ciclos
  WDTCSR |= (1<<WDCE) | (1<<WDE);

  // Configurar nuevo watchdog-timeout prescalar
  WDTCSR = 1<<WDP0 | 1<<WDP3; /* 8.0 segundos */
  
  // Habilitar WD interrupt (note no reset)
  WDTCSR |= _BV(WDIE);
  
  Serial.println("Inicio completo");
  delay(100); // Delay requerido para el Serial
}

void loop(){
  if(f_wdt == 1)
  {
    // Encender LED
    digitalWrite(LED_PIN, !digitalRead(LED_PIN));
    
    // Set la flag
    f_wdt = 0;
    
    // Entrar modo de dormir
    enterSleep();
  }
  else
  {
    // Hacer algo o nada
  }
}

Puede que encuentren otra manera mas sencilla de usar el WDT como esta:

//Al correr, Arduino Rebooted se muestra y wat comienza a contar 8s
//Ya que ifdef, calmamos al perro y no muerde e imprime
//Arduino Running.  Luego de 1 segundo
//Si ifdef se comments, no calmamos al perro y muerde cada 8 segundos
#include &amp;lt;avr/wdt.h&amp;gt;
#define RESETWATCHDOG
void setup(){
Serial.begin(57600);
Serial.println(&amp;quot;&amp;quot;);
Serial.println (&amp;quot;-------&amp;gt;Arduino Rebooted&amp;quot;);
Serial.println(&amp;quot;&amp;quot;);
wdt_enable(WDTO_8S);
}
void loop(){
#ifdef RESETWATCHDOG
wdt_reset(); //pat the dog
#endif
Serial.println(&amp;quot;Arduino Running&amp;quot;);
delay(1000);
}

Esto lo que hace es reiniciar la MCU, no la duerme.  Todo esto depende de que queremos lograr. Con lo que hemos visto podemos dormir o reiniciar.

DORMIR.  Es para cuando queremos ahorrar energía ya que estamos corriendo nuestro proyecto de una fuente de energía limitada, como ser una batería cargada de un panel solar u otra fuente intermitente.

RESETEAR.  Esta función es util cuando tenemos un proyecto remoto y debemos asegurarnos que cualquier problema con el código o con la fuente de energía que pueda “atascar” el correr normal del main loop del programa, pueda ser resuelto reiniciando la MCU.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s