Archivi tag: Tiny RTC I2C

Utilizzare un orologio RTC con Arduino – Modulo Tiny RTC I2C – Visualizzazione su display I2C

Continuo la serie di post dedicati all’uso dell’RTC con integrato DS1307, in questa lezione viene suggerito come visualizzare su un display 16×2 Hitachi HD44780 1602 con modulo I2C PCF8574T: giorno della settimana, data e ora.
L’obiettivo che si vorrà raggiungere nei prossimi tutorial sarà quello di realizzare un timer programmabile da utilizzare in diverse esercitazioni di automazione.

Lo Schema di collegamento è il seguente:

Allego lo Sketch generale in cui ho inserito commenti di spiegazione sulle varie parti del codice e sull’utilizzo di specifiche funzioni.

Per quanto riguarda la libreria LiquidCrystal_I2C vi rimando alla lezione:
Utilizzo dell’LCD 16×2 Hitachi HD44780 1602 con modulo I2C PCF8574T

/* Prof. Maffucci Michele
   15.01.2020
   Orologio - v01
*/

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

// La data e l'ora funzionano usando un RTC DS1307 collegato tramite I2C e Wire lib
#include "RTClib.h"

RTC_DS1307 rtc;

char stringa1[8]; // per memorizzare la stringa che include la data (dimensione massima data: 8 caratteri)
char stringa2[6]; // per memorizzare la stringa che include l'ora (dimensione massima ora: 6 caratteri)

// Variabile per la verifica della cancellazione del display
int chiaveCancella = 0;

// Array multidimensionale costituito
// da 7 righe (giorni della settimana)
// 4 colonne (le lettere che compongono il giorno più il carattere null con cui deve terminare una stringa)

char giornoDellaSettimana[7][4] = {"Dom", "Lun", "Mar", "Mer", "Gio", "Ven", "Sab"};

// Inizializzazione della libreria in cui è descritta la modalità di utilizzo dei pin dell'LCD,
// impostazione dell'indirizzo dell'LCD 0x27 di 16 colonne e 2 linee

LiquidCrystal_I2C lcd(0x27, 16, 2);

void setup () {

  lcd.begin();      // inizializzazione dell'LCD
  lcd.backlight();  // attivazione della retroilluminazione

  if (!rtc.isrunning()) {
    lcd.setCursor(0, 0);
    lcd.print("RTC non funzionante!");
    // la riga che segue permette di impostare data e ora prendendo l'informazione
    // dal computer a cui è collegato Arduino
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
    // la riga che segue permette di impostare
    // esplicitamente da parte dell'utente data e ora
    // Gennaio 21, 2014 alle 3 del pomeriggio ybisognrà scrivere:
    // rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
  }
}

void loop () {

/* Controllo del funzionamento dell'RTC all'interno del loop

   Il controllo della disconnessione dell'RTC viene effettuato anche all'interno del loop,
   se ciò non venisse effettuato, una disconnessione dell'RTC non permetterebbe la visualizzazione
   del messaggio "RTC non risponde", ma verrebbero visualizzati valori numerici errati.
   La successiva connessione dell'RTC farà riapparire data e ora, ma in una modalità non allineata,
   in cui saranno mostrati i valori numerici derivanti dalla precedente disconessione.
*/

  if (!rtc.isrunning()) {
    lcd.setCursor(0, 0);
    lcd.print("RTC non risponde");
    lcd.setCursor(0, 1);
    lcd.print("                ");
    chiaveCancella = 1;
  }

  else
  {
    if (chiaveCancella == 1) {
      
      /* La cancellazione del display avviene solamente una sola volta, solo se si è verificata
         una precedente disconnessione dell'RTC. L'azione è necessaria perché in fase
         di riconnessione dell'RTC appaiono sul display numeri non coerenti.
         La cancellazione potrebbe essere effettuata  direttamente nel corpo della prima if,
         ma ciò causerebbe un flikering del testo.
      */
      
      lcd.clear();
      chiaveCancella = 0;
    }

    DateTime now = rtc.now();

    lcd.setCursor(0, 0);

    // estrae dall'array giornoDellaSettimana il nome del giorno
    lcd.print(giornoDellaSettimana[now.dayOfTheWeek()]);
    lcd.setCursor(5, 0);

    /*
      int sprintf(char *str, const char *format, ...);
      ha lo stesso funzionamento della printf, con la differenza che
      l'output non sarà visualizzato sullo schermo (standard output), ma
      immagazzinato nel vettore str

      %d è uno dei possibili specificatori di formato che può essere usato nella sprintf
      ha il compito di indicare alla funzione (la sprintf) il tipo della variabile che deve essere
      visualizzata, in questo caso con d indichiamo decimale.
      Con %02d si specifica la stampa di solo due numeri decimali.
    */

    sprintf(stringa1, "%2d/%02d/%d", now.day(), now.month(), now.year());
    lcd.print(stringa1);
    lcd.setCursor(0, 1);
    sprintf(stringa2, "%02d:%02d:%02d", now.hour(), now.minute(), now.second());
    lcd.print(stringa2);
    delay(1000);
  }
}

Di seguito riprendo quanto già inserito nei commenti:

char giornoDellaSettimana[7][4] = {"Dom", "Lun", "Mar", "Mer", "Gio", "Ven", "Sab"};

Definisce un Array multidimensionale costituito da 7 righe, i giorni della settimana e 4 colonne, le lettere che compongono il giorno (3 lettere) più il carattere null con cui deve terminare una stringa.

...
 if (!rtc.isrunning()) {
    lcd.setCursor(0, 0);
    lcd.print("RTC non risponde");
    lcd.setCursor(0, 1);
    lcd.print("                ");
    chiaveCancella = 1;
  }
...

Il controllo della disconnessione dell’RTC viene effettuato anche all’interno del loop, se ciò non venisse eseguito, una disconnessione dell’RTC non permetterebbe la visualizzazione del messaggio “RTC non risponde”, ma verrebbero mostrati valori numerici errati. La successiva connessione dell’RTC farà riapparire data e ora, ma in una modalità non allineata, in cui saranno mostrati i valori numerici derivanti dalla precedente disconessione.

...
  else
  {
    if (chiaveCancella == 1) {
      
      /* La cancellazione del display avviene solamente una sola volta, solo se si è verificata
         una precedente disconnessione dell'RTC. L'azione è necessaria perché in fase
         di riconnessione dell'RTC appaiono sul display numeri non coerenti.
         La cancellazione potrebbe essere effettuata  direttamente nel corpo della prima if,
         ma ciò causerebbe un flikering del testo.
      */
      
      lcd.clear();
      chiaveCancella = 0;
    }
...

La cancellazione del display avviene una sola volta, solo se si è verificata una precedente disconnessione dell’RTC. L’azione è necessaria perché in fase di riconnessione dell’RTC appaiono sul display numeri non coerenti. La cancellazione potrebbe essere effettuata direttamente nel corpo della prima if, ma ciò causerebbe un flikering del testo così come appare nell’immagine che segue:

sprintf(stringa1, "%2d/%02d/%d", now.day(), now.month(), now.year());

int sprintf(char *str, const char *format, …); ha lo stesso funzionamento della printf, con la differenza che l’output non sarà visualizzato sullo schermo (standard output), ma immagazzinato nel vettore str.

%d è uno dei possibili specificatori di formato che può essere usato nella sprintf ha il compito di indicare alla funzione (la sprintf) il tipo della variabile che deve essere visualizzata, in questo caso con d indichiamo decimale. Con %02d si specifica la stampa di solo due numeri decimali.

Per i miei allievi:

Esercizio 1

Modificare lo sketch proposto in modo che la retroilluminazione dello schermo venga spenta dopo 15 secondi e la pressione di un pulsante la riattivi.

Esercizio 2

Modificare lo sketch realizzato al punto 2 inserendo anche un sensore DHT11 che mostra temperatura ed umidità dell’ambiente.

Utilizzare un orologio RTC con Arduino – Modulo Tiny RTC I2C

Scrivo questo post come base di partenza per le sperimentazioni che farò svolgere ai miei studenti nei prossimi giorni sull’uso degli RTC. L’acronimo RTC si riferisce a Real Time Clock, un orologio in tempo reale. Un RTC ed è alla base di moltissime automazioni in cui è indispensabile gestire una serie di processi in funzione di una specifica data e ora, si pensi ad esempio alla semplice gestione dell’illuminazione di un locale oppure alla realizzazione di un data logging o ancora la gestione del condizionamento automatico di un locale in funzione della data e dell’ora.
Ricordo ai miei studenti che nella sessione ordinaria del 2018 dell’esame di maturità per l’indirizzo elettronica ed elettrotecnica, articolazione di automazione, si faceva riferimento ad un sito archeologico in cui erano presenti due locali affrescati in cui era necessario controllare il numero massimo di persone presenti, gestire la temperatura in maniera diversa nel periodo invernale ed estivo ed inoltre doveva essere controllata anche l’umidità relativa.
Erano presenti inoltre domande in riferimento all’illuminazione massima all’interno dei locali ed altri quesiti a cui darete risposta durante le esercitazioni di laboratorio nelle prossime settimane.

Cito parte del testo dell’esame di maturità:


a) la temperatura deve essere mantenuta tra i 6°C e i 12°C nel periodo invernale e tra i 20°C e i 24°C nel periodo estivo; quando la temperatura scende al di sotto dei valori minimi si attivano delle piccole piastre riscaldanti, poste a 1 metro dal pavimento, fino al raggiungimento della temperatura media, mentre se la temperatura è troppo elevata si attiva il sistema di aerazione che al raggiungimento della temperatura media si disattiva;

b) l’umidità relativa deve essere mantenuta nel range 45% ±5% per evitare la proliferazione di muffe; tale livello viene garantito azionando per 10 minuti, se si è fuori dal range, gli aeratori o il sistema di nebulizzazione a pompa posto a muro.

L’obiettivo di questo post è quello di mostrare come gestire un RTC.

Il modulo utilizzato è un Tiny RTC I2C dotato di un integrato DS1307 I2C RTC di cui allego datasheet. Sulla scheda è presente una batteria tampone ricaricabile al litio LIR2032 che permette il funzionamento dell’RTC in assenza di alimentazione esterna, ovvero il mantenimento dell’ora e della data anche quando viene disconnesso dal microcontrollore che lo utilizza.

Il pilotaggio dell’RTC avviene mediante il protocollo I2C, che come già spiegato in precedenti lezioni, permette la comunicazione tra due o più dispositivi I2C utilizzando un bus a due fili più un terzo filo per il riferimento di massa comune a tutti i dispositivi.
Poiché il protocollo I2C utilizza 7 bit per indirizzare il dispositivo sul bus, il numero massimo di dispositivi collegabili sul bus è di 128 dispositivi, però il numero scende a 112 in quanto 16 indirizzi sono riservati.
Il bus è costituito da una linea dati SDA (Serial Data line), una line di clock SCL (Serial Clock line) e come detto sopra al filo di massa comune a tutti i dispositivi.

Per collegare il Tiny RTC I2C si faccia riferimento allo schema che segue:

Per poter pilotare il DS1307 è necessario installare una libreria specifica, ne esistono diverse quella che utilizzeremo sarà quella di Adafruit la RTClib. Installate la libreria: Sketch > Include Library > Manage Libraries

Si aprirà il gestore librerie, nel campo di ricerca inserite il testo RTClib come indicato nell’immagine che segue e procedere con l’installazione dell’ultima versione disponibile:

Al termine dell’installazione apparirà la voce INSTALLED a fianco della libreria installata

Con la libreria vengono resi disponibili una serie di sketch di esempio che potete aprire direttamente da: File > Examples > RTClib.

Aprite lo schetch: DS1307 di seguito indicato:

// Date and time functions using a DS1307 RTC connected via I2C and Wire lib
#include "RTClib.h"

RTC_DS1307 rtc;

char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};

void setup () {
  while (!Serial); // for Leonardo/Micro/Zero

  Serial.begin(57600);
  if (! rtc.begin()) {
    Serial.println("Couldn't find RTC");
    while (1);
  }

  if (! rtc.isrunning()) {
    Serial.println("RTC is NOT running!");
    // following line sets the RTC to the date & time this sketch was compiled
    // rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
    // This line sets the RTC with an explicit date & time, for example to set
    // January 21, 2014 at 3am you would call:
    // rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
  }
}

void loop () {
    DateTime now = rtc.now();

    Serial.print(now.year(), DEC);
    Serial.print('/');
    Serial.print(now.month(), DEC);
    Serial.print('/');
    Serial.print(now.day(), DEC);
    Serial.print(" (");
    Serial.print(daysOfTheWeek[now.dayOfTheWeek()]);
    Serial.print(") ");
    Serial.print(now.hour(), DEC);
    Serial.print(':');
    Serial.print(now.minute(), DEC);
    Serial.print(':');
    Serial.print(now.second(), DEC);
    Serial.println();

    Serial.print(" since midnight 1/1/1970 = ");
    Serial.print(now.unixtime());
    Serial.print("s = ");
    Serial.print(now.unixtime() / 86400L);
    Serial.println("d");

    // calculate a date which is 7 days, 12 hours, 30 minutes, and 6 seconds into the future
    DateTime future (now + TimeSpan(7,12,30,6));

    Serial.print(" now + 7d + 12h + 30m + 6s: ");
    Serial.print(future.year(), DEC);
    Serial.print('/');
    Serial.print(future.month(), DEC);
    Serial.print('/');
    Serial.print(future.day(), DEC);
    Serial.print(' ');
    Serial.print(future.hour(), DEC);
    Serial.print(':');
    Serial.print(future.minute(), DEC);
    Serial.print(':');
    Serial.print(future.second(), DEC);
    Serial.println();

    Serial.println();
    delay(3000);
}

Nello sketch sono presenti due righe fondamentali, che risultano commentate e che secondo necessità possiamo decommentare ed utilizzare.

Se desideriamo che l’ora e la data venga impostata non appena viene caricato sulla scheda lo sketch bisogna decommentare la riga:

rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));

che permette di leggere la data e l’ora corrente in cui sono presenti le costanti predefinite all’interno dell’IDE Arduino: DATE e TIME, che si riferiscono alla data e all’ora di compilazione dello sketch, quindi non appena compilate vengono impostate e passate all’orologio che assume queste come data e ora corrente.

Nel caso si desidera impostare manualmente l’ora e la data bisognerà decommentare la riga:

rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));

inserendo data e ora corrente, ad esempio facendo riferimento alla data 08/01/2020 ore 01:37 dovrete scrivere:

rtc.adjust(DateTime(2020, 1, 8, 1, 37, 0));

La parte restante del codice si occupa di stampare sulla Serial Monitor data e ora corrente. Nel codice è presente una parte in cui viene stampato una data futura ed il numero di secondi trascorsi da una specifica data.

Ricordate che, come specificato nella setup() la seriale è impostata con una velocità di 57600 caratteri al secondo e quindi nella finestra della Serial Monitor dovrete impostare questa velocità per visualizzare il testo:

Questo il risultato:

Per i miei studenti:

Esercizio 1

Convertire nel formato italiano data e ora ottenendo un output come quello indicato nell’immagine che segue:

Esercizio 2

Realizzare uno sketch che permette l’accensione di un led connesso al pin 3 ogni minuto nell’intervallo da 0 a 15 secondi, spento nei restanti secondi all’interno del minuto. Visualizzare sulla Serial Monitor l’ora corrente.

Esercizio 3

Fare in modo che il led dal 50° al 59° secondo incominci a lampeggiare velocemente e nell’intervallo da 0 a 15 secondi sia acceso.

Esercizio 4

Stesse specifiche dell’esercizio 4 però facendo aumentare progressivamente la frequenza di lampeggio del LED all’approssimarsi del 59° secondo.

Buon lavoro 🙂