RTC, alarma y modos sleep [SOLUCIONADO]

Hola.

Necesito medir temperaturas y humedades de dos sensores DHT22; uno situado en el exterior y otro en el interior. Lo tengo todo montado y funcionando, en principio enviando datos por el puerto serie cada 10 minutos; en cuanto tenga un lector de tarjetas micro SD que estoy esperando almacenará los datos en la tarjeta. El problema es que en su ubicación definitiva no hay electricidad, por lo que estoy a la espera de un par de baterías que acabo de comprar, una que esté alimentando a Arduino y la otra cargándose, para que el tiempo sin alimentación sea mínimo.

Por los requerimientos de autonomía tengo que pensar en las estrategias para reducir consumo. Tengo lo siguiente:

  1. Uso de un Arduino nano. Actualmente tengo un Arduino Uno R3.
  2. Poner al micro en modo sleep y despertarlo cada 10 minutos.
  3. Montar al micro sólo, standalone, con la circuitería mínima (sin reloj externo).
  4. Bajar la frecuencia del reloj.

El caso es que he investigado sobre el punto 2 y no lo tengo claro. Tengo un DS3231, y creo que puedo configurar alarmas. Lo que no sé es si esta alarma que configure en el DS3231 me despertaría al micro de un modo sleep. No sé si es como la hora, que la programo e independientemente del micro, si tiene alimentación el reloj no la pierde. Me imagino que la alarma será igual pero no lo sé,

Para el reloj uso la librería Wire.h. He visto que para alarmas está la librería TimeAlarms, con la siguiente función:

Alarm.timerRepeat(seconds, function)

Mi duda es; ¿despierta esta función a Arduino de un modo sleep?

seconds serían 600, ¿y function sería la rutina donde leo los sensores, guardo los datos y vuelvo a poner a Arduino en modo sleep?

Por otro lado, para el modo sleep he visto la librería narcoleptic.h, muy sencilla de usar, simplemente escribiendo Narcoleptic.delay(500), y configura un Power Down, que es justamente lo que creo que necesito. ¿Es la más recomendable?

Muchas gracias por la ayuda.

Un saludo.

  1. Bajar la frecuencia del reloj.

Cuando veas todo lo que puedes reducir consumo sin hacer eso.. veras que no hace falta

El caso es que he investigado sobre el punto 2 y no lo tengo claro. Tengo un DS3231, y creo que puedo configurar alarmas. Lo que no sé es si esta alarma que configure en el DS3231 me despertaría al micro de un modo sleep. No sé si es como la hora, que la programo e independientemente del micro, si tiene alimentación el reloj no la pierde. Me imagino que la alarma será igual pero no lo sé,

No entiendo, dijiste que no usarías el RTC ahora si.
Veamos. a menos que quieras datos precisos, entonces si necesitas RTC. Sino despertarlo cada 10 min es tarea del sleep.
Aca decides tu.
He visto que mucha gente espera despertarlo con la alarma del DS3231 o DS1307 y tiene problemas para lograrlo.
El RTC tiene bateria y cuando se queda sin energía funciona con la batería de 3.3V. CR3202 o algo asi.

Alarm.timerRepeat(seconds, function)

Esta es una librería para generar alarmas en el arduino, pero no despierta el arduino.

Mi duda es; ¿despierta esta función a Arduino de un modo sleep?

No.

seconds serían 600, ¿y function sería la rutina donde leo los sensores, guardo los datos y vuelvo a poner a Arduino en modo sleep?

No entiendo. supongo que hablas de 10 minutos. Eso reservado para sleep.
El RTC solo te estampara la hora en la SD.

Por otro lado, para el modo sleep he visto la librería narcoleptic.h, muy sencilla de usar, simplemente escribiendo Narcoleptic.delay(500), y configura un Power Down, que es justamente lo que creo que necesito. ¿Es la más recomendable?

No la conozco, pruebala y nos cuentas.

CONSEJO: no hagas 10 cosas simultáneas.
Toma una tarea la pruebas y analizas pros y contras. Luego otra y asi.
AL final cuando tengas todo resuelto ensamblas un código que satisfaga todo.
Okay?

No entiendo. supongo que hablas de 10 minutos. Eso reservado para sleep.
El RTC solo te estampara la hora en la SD.

No me he explicado bien. De lo que puedo prescindir es del cristal externo, y usar el interno. El RTC me hace falta porque necesito saber a qué horas tomo las medidas.

He visto que mucha gente espera despertarlo con la alarma del DS3231 o DS1307 y tiene problemas para lograrlo.

¿Me puedes indicar algún post, blog, artículo...donde poder investigar sobre esto? Ahora mismo no sé cómo el RTC podría despertar a Arduino de un modo sleep. He leído distintas cosas pero no me ha quedado claro.

También agradecería un artículo, ejemplo, etc., sobre la programación de los modos sleep en Arduino, y qué librerías son las más habituales y cómo usarlas.

Por ahora he replanteado ligeramente el proyecto. He pensado que con la librería narcoleptic.h y la función narcoleptic.delay(598) [598 segundos porque lo quiero tener en modo sleep 9 minutos y 58 segundos], puedo dormir al Arduino y despertarlo dos segundo antes de la medición, de los 10 minutos. Cuando despierta, una rutina espera a que los minutos sean 0 o múltiplo de 10, se toman los datos y se vuelve al modo sleep. De este modo no perderé la precisión en el tiempo en el que tomo los datos, y el micro funcionará unos 2 segundos de cada 10 minutos.

A pesar de esta reorientación del proyecto estoy interesado en aprender sobre las alarmas del DS3231 para despertar de un modo sleep y en cómo programar los modos sleep, como os he dicho antes.

CONSEJO: no hagas 10 cosas simultáneas.
Toma una tarea la pruebas y analizas pros y contras. Luego otra y asi.
AL final cuando tengas todo resuelto ensamblas un código que satisfaga todo.]

Gracias por el consejo. Realmente trabajo así, lo aprendí con la práctica, separar el proyecto en módulos e ir agregándolos conforme los voy resolviendo.

Un saludo.

Hola.

He conseguido poner en marcha el proyecto. He usado las librerías "Sodaq_DS3231.h" para configurar la alarma del DS3231 y la librería "Sleep_n0m1.h" para configurar el modo Sleep (además de "SD.h", "DHT.h" y "Wire.h" para el resto de funciones en el proyecto).

Me ha pasado una cosa. Tenía configurada una interrupción mediante attachInterrupt(0, INT0_ISR, FALLING) que se ejecutaba cuando saltaba la alarma; entraba en INT0_ISR y seguía las rutinas que tengo programadas. Después he añadido al programa el paso a modo Sleep con sleep.pwrDownMode() y sleep.sleepInterrupt(0,FALLING).

El caso es que después de haber añadido la entrada en modo Sleep el programa deja de entrar en INT_ISR. ¿Se debe esto a que después de la interrupción el micro tiene que hacer dos cosas, despertar y entrar en INT_ISR, y al final sólo hace una?

Por otro lado, estoy casi seguro que el micro entra en modo Sleep, ya que impirmí un carácter por el puerto serie después de activar el PowerDown y se ejecuta sólo una vez cada 10 minutos, pero...¿de qué modo puedo comprobar con seguridad que he entrado en el modo Sleep y estoy ahorrando energía?

Os pego el código por si os puede servir:

/*Guarda en una tarjeta micro SD la humedad y temperatura de dos sensores cada 10 minutos y mientras está en modo Power Down*/

#include <SPI.h>
#include <SD.h>
#include <DHT.h>
#include <Wire.h>
#include "Sodaq_DS3231.h"
#include <Sleep_n0m1.h>

File archivo;
char weekDay[][4] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
DateTime dt(2015, 12, 1, 18, 39, 55, 2);   //year, month, date, hour, min, sec and week-day(starts from 0 (domingo) and goes to 6 (sábado))
Sleep sleep;

#define DHT1PIN 7 //Seleccionamos el pin en el que se conectará el sensor 1
#define DHT2PIN 8 //Seleccionamos el pin en el que se conectará el sensor 2

#define DHT1TYPE DHT22 //Se selecciona el DHT22 (hay otros DHT)
#define DHT2TYPE DHT22 //Se selecciona el DHT22 (hay otros DHT)

#define DS3231_I2C_ADDRESS 0x68

/*
DHT 1 INTERIOR
DHT 2 EXTERIOR*/

DHT dht1(DHT1PIN, DHT1TYPE); //Se inicia una variable que será usada por Arduino para comunicarse con el sensor
DHT dht2(DHT2PIN, DHT2TYPE); //Se inicia una variable que será usada por Arduino para comunicarse con el sensor

void setup()
{
  Serial.begin(9600); //Se abren las comunicaciones serie
  Wire.begin();
  rtc.begin();
  rtc.setDateTime(dt); //Adjust date-time as defined 'dt' above

  PORTD |= 0x04;      /*Se configura INT0 para que acepte interrupciones*/
  DDRD &=~ 0x04;

  if (!SD.begin(4)) {
    Serial.println("initialization failed!");
    return;
  }                   //Este trozo de código se usa si queremos comprobar que la inicialización se ha llevado a cabo*/
  
  dht1.begin(); //Se inicia el sensor
  dht2.begin(); //Se inicia el sensor

  rtc.clearINTStatus();                      //Antes de habilitar las interrupciones hay que poner INT0 a HIGH
}

void configurar_alarma(int minutos, int hora)
{
  if (minutos < 10) {      //Mediante este conjunto de if defino los minutos y la hora de la siguiente interrupción
    minutos = 10;
  }
  else if (minutos < 20) {
    minutos = 20;
  }
  else if (minutos < 30) {
    minutos = 30;
  }
  else if (minutos < 40) {
    minutos = 40;
  }  
  else if (minutos < 50) {
    minutos = 50;
  }
  else if (minutos < 60) {
    minutos = 0;
    if (hora == 23){
      hora = 0;
    }
    else {
      hora++;
    }
  }
  rtc.enableInterrupts(hora,minutos,0);      //Defino la interrupción cada 10 minutos
}

void loop()
{ 
  DateTime now = rtc.now(); //get the current date-time
  //float nueva_hora, minutos_en_horas;

  configurar_alarma(now.minute(),now.hour());

  sleep.pwrDownMode(); //set sleep mode
  sleep.sleepInterrupt(0,FALLING); //(interrupt Number (en nuestro caso es INT0 y vale 0), interrupt State)

  float h_int = dht1.readHumidity();    //Se lee la humedad
  float t_int = dht1.readTemperature(); //Se lee la temperatura
  float h_ext = dht2.readHumidity();    //Se lee la humedad
  float t_ext = dht2.readTemperature(); //Se lee la temperatura

  archivo = SD.open("HHVV.txt", FILE_WRITE); //Se abre el fichero de trabajo. Es importante saber que sólo puede haber un 
                                             //fichero abierto, por lo que hay que cerrar el anterior antes de abrir uno nuevo. El fichero debe tener nombre corto   
  now = rtc.now();

  if (archivo) {                             //El IF se ejecuta si el fichero se ha abierto correctamente
    archivo.print(now.date());
    archivo.print("/");
    archivo.print(now.month());
    archivo.print("/");
    archivo.print(now.year());
    archivo.print(",");
    archivo.print(now.hour());
    if (now.minute() == 10) {      //Este conjunto de if es un modo cutre de expresar los minutos como horas. Debería reprogramarse y que se calculara
      archivo.print(".16,");
    }
    else if (now.minute() == 20) {
      archivo.print(".33,");
    }
    else if (now.minute() == 30) {
      archivo.print(".50,");
    }
    else if (now.minute() == 40) {
      archivo.print(".66,");
    }  
    else if (now.minute() == 50) {
      archivo.print(".83,");
    }
    else if (now.minute() == 0) {
      archivo.print(".00,");
    }
    archivo.print(h_int);
    archivo.print(",");
    archivo.print(t_int);
    archivo.print(",");
    archivo.print(h_ext);
    archivo.print(",");
    archivo.println(t_ext);
  }
  
  archivo.close();        //Se cierra el fichero

  rtc.clearINTStatus();  //Antes de habilitar de nuevo las interrupciones hay que poner INT en HIGH
}

Un saludo y gracias.

El caso es que después de haber añadido la entrada en modo Sleep el programa deja de entrar en INT_ISR. ¿Se debe esto a que después de la interrupción el micro tiene que hacer dos cosas, despertar y entrar en INT_ISR, y al final sólo hace una?

No te parece obvio que asi sea? claro que cuando lo pones en sleep solo espera la acción que lo despierta y todo lo demas es ignorado. Asi que una alternativa sería que tu alarma tmb lo despierte del sleep y que dure lo suficiente como para luego de despierto activar la INT correspondiente, o simplemente lo despiertas miras el estado del pin de alarma y si están en el nivel que corresponde actúas en consecuencia.

Por otro lado, estoy casi seguro que el micro entra en modo Sleep, ya que impirmí un carácter por el puerto serie después de activar el PowerDown y se ejecuta sólo una vez cada 10 minutos, pero...¿de qué modo puedo comprobar con seguridad que he entrado en el modo Sleep y estoy ahorrando energía?

Fácil, pones el multímetro en modo amperímetro y mides como cae la corriente que consume el Arduino. Mejor que eso nada.

Muchas gracias por tu codigo... sera de mucha ayuda!

PS... no has tenido problemas con el 1302? yo arme un datalogger de 4 temperaturas y 2 humedades en la sd
y todo perfecto, hasta que un dia hicieron varias pruebas y nad.a.. no grabo nada.. aparentmente se quedo sin bateria... y se desconfiguro...

Tu le tines una bateria recargable?
que modelo es ??

Gracias.

-Alex.

No te parece obvio que asi sea? claro que cuando lo pones en sleep solo espera la acción que lo despierta y todo lo demas es ignorado

Después de pensarlo es obvio. Una interrupción baja el nivel del pin, despierta al micro, y cuando va a entrar en la otra interrupción el flag está bajado y no entra.

no has tenido problemas con el 1302?

Yo uso un DS3231 que me ha ido bien. El problema lo tengo con el lector de tarjetas microSD. El circuito estuvo trabajando durante tres días conectado a la red, guardando datos cada 10 minutos, y de repente dejó de funcionar. Ahora mismo es incapaz de inicializar la SD y almacenar datos. Si corro el archivo de ejemplo CardInfo, me devuelve la siguiente salida:

Wiring is correct and a card is present.

Card type: SD2
Could not find FAT16/FAT32 partition.
Make sure you've formatted the card

He formateado, vuelto a formatear, en FAT16, en FAT32, etc.

He leído en este (hilo que tiene que ver con la RAM. Extraigo el texto:

OK, problem solved. Posting it here hoping it might help someone else down the road. Long story short, it was a RAM problem (as so many of these weird problems are). I guess there was some dynamic memory allocation happening after I had used the freeRAM() function to check it. Using avr-objdump -t -j .bss xxxx.elf, I saw that most of the uninitialized memory was for the SD and Wire libraries. This definitely made sense why things broke (and in strange ways) when I brought in the Wire library. I ended up upgrading to Arduino 1.0.1 so that I could use the println(F("xxxxxx")) feature to put the text in Flash instead of RAM, and that freed up 380 bytes in the .data section (I guess I had a lot of debug messages going to the Serial Monitor!). This was enough to counteract the addition of the Wire library. I ended up using the stock SD library in Arduino 1.0.1, which is working fine so far.

I'm hoping a newer version of Arduino reports RAM usage when you compile! Using avr-size is OK, but cumbersome. I think even for newbies, it would be really useful for them to see just how much of the RAM is statically allocated. I know I would do a much better job of keeping track of RAM use if it was a bit easier to find. I think it's really key esp. for these small ATMEGA MCU's with 1k or 2k of RAM available. The various Arduino libraries don't give any indication of resource utilization (how much ROM and RAM used when compiled), so it's hard to even guess without doing trial-and-error using avr-size.

Ni siquiera lo he leído atentamente y necesito investigarlo. De hecho el puerto serie a veces indica "initialization failed", pero habiendo un problema supongo que estará relacionado con el anterior.

Con este error aún no pude probar el montaje con las baterías.

Cualquier idea es buena.

Un saludo.

El experto en fallas con SD es noter, contactalo por privado a ver si te da alguna idea.

Había tenido fallos en el conexionado, porque desconecté todo y conecté de nuevo. A parte la tarjeta microSD algunas veces se desconfigura y no hay manera. Yo uso Ubuntu y la formateé varias veces pero nada; formateé desde Windows y todo bien. Por lo demás ahora todo marcha muy bien.

He metido la pata en una cosa y no sé por qué, porque pensaba que me había documentado bien. He comprado dos baterías de 3,7 V y 5500 mAh para alimentar al Arduino a través del conector. La idea era que Arduino funcionara con una batería y mientras cargar la otra, e ir intercambiándolas. Me he encontrado con que una sola batería a 3,7 V no tira de Arduino, y voy a tener que utilizar las dos en paralelo. Quizás el error venga de que pensaba extraer el micro de la placa y hacerlo funcionar solo, pero en este caso hubiera necesitado 5 V para el lector de tarjetas y también hubiera estado mal.

Respecto a la medición de los consumos, cuando el micro pasa a Power Down no se aprecia una disminución de la corriente. Supongo que está relacionado con una mala operación por lo que comento arriba de poca intensidad para trabajar.

Un saludo y gracias por la ayuda.

Me he encontrado con que una sola batería a 3,7 V no tira de Arduino, y voy a tener que utilizar las dos en paralelo

Perdón, en serie :slight_smile:

cirulo como va? a pasaron 5 años de esto, me imagino que tu proyecto lo has resuelto bien.
Estoy intentando hacer algo similar pero midiendo otros parámetros. Podrías compartir el código que te haya funcionado mejor?
pretendo usar un arduino nano, DS3231 y un módulo para microSD..
gracias.