Come realizzare un simpatico gadget per ricordarci delle date importanti

Se anche tu hai difficoltà a ricordare le date degli appuntamenti e sei un tipo creativo e non ti accontenti dei classici promemoria via cellulare, tablet o computer, ma vuoi realizzare qualcosa di insolito, puoi sfruttare la tua passione per l’elettronica e la tua voglia di costruire per realizzare questo progetto di promemoria con Arduino.

Avendo già le basi per poter lavorare con l’integrato RTC DS1307 puoi sfruttare la ram non volatile di questo chip per memorizzare circa 18 date e permettere, ad Arduino, di avvisarti tramite display, led rgb, twitter o quello che ti viene in mente!!

L’idea è quella di utilizzare l’RTC per memorizzare il dato di tempo e sfruttare i 56 byte di flash ram aggiuntive per memorizzare le date degli avvenimenti. Arduino ha il compito di comparare le date e di agire di conseguenza pilotando le uscite digitali.

Parto dal presupposto che la data e l’ora del RTC sia già stata impostata come spiegato nel precedente tutorial.
Per memorizzare le date degli avvenimenti possiamo usare un array di 3 byte (Giorno Mese ed Anno), il codice è il seguente:

//inserisco la libreria per il bus I2C
#include <Wire.h>

//Variabile per memorizzare l'indirizzo della ram del DS1307
byte IndRam = 0x00;
//array per la data (G M A)
byte Data[3];

void setup()
{
  //init bus I2C
  Wire.begin();
  //init seriale
  Serial.begin(9600);

  //imposto l'indirizzo iniziale della user ram
  IndRam = 0x08;

  //avvio la comunicazione con il DS1307
  //indirizzo 0x68
  Wire.beginTransmission(0x68);
  //specifico l'indirizzo a cui voglio accedere
  Wire.write(IndRam);

  //imposto la data 25/04/12 in esadecimale
  Data[0] = 0x19;  //giorno 25
  Data[1] = 0x04;  //mese   04
  Data[2] = 0x0C;  //anno   12

  //scrivo i 3 byte negli indirizzi 0x08 0x09 0x0A
  Wire.write(Data,3);

  //chiudo la comunicazione
  Wire.endTransmission();

}

void loop()
{}

Nel datasheet del DS1307 la ram aggiuntiva inizia all’indirizzo 0x08 e finisce all’indirizzo 0x3F e, sapendo che per memorizzare una data servono 3 byte (G M A), devi sommare alla variabile IndRam valori multipli di 3.
Ad esempio per memorizzare la seconda data IndRam varrà 0x08 + 3 per la terza data 0x08+6 e cosi via fino a 0x08+51, ottenendo così 18 date memorizzabili.

LocazioneValore variabile IndRam
10x08 (8)
20x0B (11)
30x0E (14)
40x11 (17)
50x14 (20)
60x17 (23)
70x1A (26)
80x1D (29)
90x20 (32)
100x23 (35)
110x26 (38)
120x29 (41)
130x2C (44)
140x2F (47)
150x32 (50)
160x35 (53)
170x38 (56)
180x3B (59)

Una volta memorizzate le date che ci interessano, è necessario scrivere del codice che confronti questi valori con quelli dell’RTC e di conseguenza eseguire delle operazioni di output.

Per primo recupero il giorno, mese e anno dell’RTC a partire dall’indirizzo  0x04

RAM ds1307

e poi recupero i dati della user ram per eseguire il confronto quindi:


void loop()
{
  IndRam = 0x04;

  //setto l'indirizzo da ddove iniziare
  //a leggere
  Wire.beginTransmission(0x68);
  Wire.write(IndRam);
  Wire.endTransmission();

  //recupero 3 byte G M A
  Wire.requestFrom(0x68, 3);
  byte Giorno = Wire.read();
  byte Mese = Wire.read();
  byte Anno = Wire.read();

  Serial.print(Giorno, DEC);
  Serial.print("/");
  Serial.print(Mese,DEC);
  Serial.print("/");
  Serial.println(Anno, DEC);

  //recupero tutte le date memorizzate nella user ram
  //e le confronto con quella attuale dell'RTC

  for( byte Ciclo = 0; Ciclo < 54; Ciclo += 3)
  {
    IndRam = 0x08 + Ciclo;
    //Serial.println(IndRam, HEX);

    Wire.beginTransmission(0x68);
    Wire.write(IndRam);
    Wire.endTransmission();

    Wire.requestFrom(0x68, 3);
    byte memGiorno = Wire.read();
    byte memMese = Wire.read();
    byte memAnno = Wire.read();

    //confronta i dati

    if(Giorno == memGiorno && Mese == memMese && Anno == memAnno)
    {
      //trovata data
      //fai qualcosa
      Serial.println("Data OK");
    }

    delay(10);
  }

  delay(1000);
}

Il codice controlla ciclicamente se la data impostata nell’RTC è presente nella user ram in caso affermativo viene printata la stringa Data OK.
Il codice ha un problema, non tiene conto dell’andamento temporale, infatti viene fatto un controllo di valori che non permette di verificare se una data è precedente o successiva a quella impostata.
Il compito del codice dovrebbe essere quello di avvisarci degli avvenimenti futuri mentre dovrebbe escludere quelli passati.

Per poter confrontare le date tra di loro mi sono avvalso della libreria Time che tra le varie funzioni permette di restituire il numero di secondi trascorsi tra il 1 gennaio 1970 e una data definita.
Ottenendo un numero progressivo posso confrontare due date e capirne l’ordine temporale.

Installare e utilizzare la libreria Time

Sul sito ufficiale di Arduino, seguendo questo link possiamo scaricare la libreria. Scompattiamo il file zip nella cartella Arduino 1.0->libraries. Questa operazione produrrà tre cartelle una con nome Time, DS1307RTC e TimeAlarms. Riavviamo l’IDE per aggiornare le lista delle librerie e degli esempi.

Per utilizzare la libreria nello sketch del progetto che stiamo sviluppando, possiamo inserire a mano la clausola

#include <Time.h>

oppure dal menu dell’ide eseguiamo Sketch->Import Library->Time

Per ottenere i secondi trascorsi dal 1 gennaio 1970 è disponibile la funzione now(), ma prima, giustamente bisogna settare la data di riferimento, per farlo si utilizza la funzione setTime(ora, minuti, secondi, giorno, mese, anno) quindi il codice precedente viene modificato così:

#include <Wire.h>
//includo la libreria Time
#include <Time.h>

byte IndRam = 0x00;
byte Data[3];

byte val = 0x00;

//queste variabili servono per memorizzare
//la data in secondi trascorsi dal 1 gen 1970
unsigned long UnixData = 0;
unsigned long memUnixData = 0;

void setup()
{
  Wire.begin();
  Serial.begin(9600);

  //imposto l'indirizzo iniziale della user ram
  IndRam = 0x08;

  //carico una data da ricordare nei primi
  //3 byte della user ram
  Wire.beginTransmission(0x68);
  Wire.write(IndRam);
  Data[0] = 0x05; //giorno  05
  Data[1] = 0x04; //mese    04
  Data[2] = 0x0C;  //anno   12
  Wire.write(Data,3);
  Wire.endTransmission();

  delay(100);

  //verifico la data appena impostata
  //nella user ram
  Wire.beginTransmission(0x68);
  Wire.write(IndRam);
  Wire.endTransmission();

  Wire.requestFrom(0x68, 3);
  byte data = Wire.read();
  Serial.print(data);
  Serial.print("/");
  data = Wire.read();
  Serial.print(data);
  Serial.print("/");
  data = Wire.read();
  Serial.print(data);
  Serial.println("/");

  delay(100);
}

void loop()
{
  //setto l'indirizzo per accedere alla data
  //impostata nell'RTC
  IndRam = 0x04;
  //setto l'indirizzo da dove iniziare
  //a leggere
  Wire.beginTransmission(0x68);
  Wire.write(IndRam);
  Wire.endTransmission();

  //recupero 3 byte G M A
  Wire.requestFrom(0x68, 3);
  byte Giorno = Wire.read();
  byte Mese = Wire.read();
  byte Anno = Wire.read();

  //visualizzo la data dell'RTC
  Serial.print(Giorno, DEC);
  Serial.print("/");
  Serial.print(Mese,DEC);
  Serial.print("/");
  Serial.println(Anno, DEC);

  //setto la libreria Time con la data dell'RTC
  setTime(0,0,0, Giorno, Mese, (Anno + 2000));
  //Recupero i secondi trascorsi tra la data 1 Gen 1970
  //e la data dell'RTC
  UnixData = now();
  //Visualizzo i secondi trascorsi sul Serial Monitor
  Serial.println(now());

  //recupero tutte le date memorizzate nella ram
  //e le confronto con quella attuale dell'RTC
  //trasformandole in secondi in modo da poter confrontare
  //le date

  for( byte Ciclo = 0; Ciclo < 54; Ciclo += 3)
  {
    //imposto il primo indirizzo della user ram
    //e poi proseguo ciclicamente incrementando di
    //3 posizioni
    IndRam = 0x08 + Ciclo;
    //Serial.println(IndRam, HEX);

    //setto l'indirizzo
    Wire.beginTransmission(0x68);
    Wire.write(IndRam);
    Wire.endTransmission();

    //recupero i tre byte G M A
    //dalla user RAM
    Wire.requestFrom(0x68, 3);
    byte memGiorno = Wire.read();
    byte memMese = Wire.read();
    byte memAnno = Wire.read();

    //imposto la libreria Time con la data dela user RAM
    // per convertirla in secondi
    setTime(0,0,0, memGiorno, memMese, (memAnno + 2000));
    memUnixData = now();
    //Serial.println(now());

    //confronto la data in secondi dell'RTC con la data
    //appena recuperata dalla user ram

    if(UnixData == memUnixData)
    {
      //trovata data
      //fai qualcosa
      Serial.println("TROVATA!!!");
    }

    delay(10);
  }

  delay(1000);
}

Il codice ha lo stesso comportamento di quello iniziale ma adesso c’è la possibilità di eseguire il confronto numerico grazie alla conversione in secondi. Ad esempio per escludere tutte le date passate possiamo fare questa semplice modifica


   //escludi le date passate

   if(UnixData >= memUnixData)
   {
     //trovata data
     //fai qualcosa
     Serial.println("TROVATA!!!");
   }

oppure eseguire del codice per avvisarci una settimana in anticipo:

  //avvisami una settimana prima
  //settimana in secondi 60*60*24*7 = 604800

  if((memUnixData - UnixData) <= 604800)
  {
    //trovata data
    //fai qualcosa
    Serial.println("TROVATA!!!");
  }

Avendo il modo di eseguire un confronto numerico tra le date ci offre la possibilità di realizzare ulteriori funzioni, come ad esempio sketch che gestiscono l’Arduino a seconda delle date impostate nella user ram.

Per le operazioni da eseguire quando viene trovata una data possiamo eseguire tutte le tipiche funzioni di output che può gestire Arduino.

 

   //escludi le date passate
   if(UnixData >= memUnixData)
   {
     //trovata data
     //fai qualcosa
     Serial.println("TROVATA!!!");
   }