Dic 062017
 

Anche se non è il reale scopo dell’articolo, oggi costruiamo un orologio con Arduino. Nei due articoli precedenti abbiamo visto come usare un joystick per controllare una semplice interfaccia utente e come utilizzare un modulo RTC per tenere in memoria l’orario e data odierna. Oggi mettiamo insieme i due articoli ed aggiungiamo un display LCD HD44780 compatibile. Quel che andiamo a fare è perciò un vero e proprio orologio che tiene traccia dell’orario grazie al modulo RTC, permette di settare l’orario grazie al joystick e le fasi sia di settaggio che visualizzazione delle informazioni avvengono grazie al display LCD. Ovviamente il nostro intento non è quello di fare un orologio con Arduino, certo il gusto di farlo per dire “l’ho fatto io” non ha prezzo e questo è indubbio, ma il vero fine è quello di mostrare come possiamo creare un’interfaccia utente svincolata dal PC e che permetta di interagire con il resto del sistema, in questo caso un modulo RTC da cui andiamo a leggere l’ora ed eventualmente modificarla. 

I collegamenti

Sul blog ci sono già tutte le informazioni necessarie, ma come sempre vi ho riportato qui le informazioni più utili per fare i collegamenti senza saltare da una pagina all’altra. Vediamo di ripassare il ccolegemento del display:pinoutlcd

  1. Vss  -Massa
  2. Vcc – Generalmente 5V
  3. Vee – Controllo contrasto
  4. R\S – 0 per inviare comandi, 1 per inviare dati
  5. R\W – 0 per scrittura, 1 per lettura
  6. E – Inizio ciclo di lettura o scrittura
  7. D0
  8. D1
  9. D2
  10. D3
  11. D4
  12. D5
  13. D6
  14. D7
  15. Vcc – Vcc retroilluminazione (se presente)
  16. Vss – Massa retroilluminazione (se presente)

I pin 1, 5 (R\W) e 16 sono connessi a massa, il 2 e 15 ai 5V. Il pin 3 è collegato ad un resistore variabile che mi permette di modificare il contrasto dei caratteri, cosa pressochè insispensabile quindi non pensiate di poterne fare a meno. I pin 4, e 6 sono stati collegati rispettivamente ai pin 8 e 7 di Arduino. Per finire i pin 11, 12, 13 e 14 sono collegati rispettivamente ai pin 6, 5, 4 e 3 di Arduino.

Visti i collegamenti del display, rispetto al precedente articolo ho spostato il pin collegato al pulsante del Joystick dal pin 4 al 12, per il resto sia il joystick che il modulo rtc sono collegati esattamente come i precedenti articoli. Per completezza vi lascio qui sotto uno schema riassuntivo che riassumene tutto quello che abbiamo sinora detto., non dovete far altro che copiarlo.

Il software

Se l’hardware è solo un rimescolamento di cose già viste, la parte più complessa resta il software, ambito in cui noto esserci le maggiori difficoltà da parte dei lettori di questo blog. Rispetto a quanto abbiamo già visto dobbiamo solamente aggiungere la parte che permette la selezione dell’ora / data da memorizzare nel modulo RTC. Vediamo subito il listato completo che poi andiamo ad analizzare in alcune sue parti:

#define asseX A0 
#define asseY A1 
#define PressPin 12
#define Menu_UP 1
#define Menu_DOWN 2
#define Menu_LEFT 4
#define Menu_RIGHT 8
#define Menu_PRESS 16
#include <LiquidCrystal.h>
#include <SPI.h>
struct MenuItem
{
 unsigned char Previous;
 unsigned char Next;
 unsigned char Child;
 unsigned char Parent;
 unsigned char Fire;
 char testo[40];
};
unsigned char index; 
 int xVal, yVal,KVal;
 unsigned char buttonVal;
struct MenuItem ArduinoMenu[7];
LiquidCrystal lcd(8, 7, 6, 5, 4, 3);
#include "RTClib.h"
RTC_DS3231 rtc;
bool refresh;
void setup ()
{
 #ifndef ESP8266
 while (!Serial); // Leonardo/Micro/Zero
 #endif
 Serial.begin (9600); 
 pinMode (PressPin, INPUT_PULLUP);
 lcd.begin(16, 2); // inizializzo il display a 16x2 caratteri
 
 //pinMode (PressPin, INPUT);
 //digitalWrite(PressPin, HIGH);
if (! rtc.begin()) {
 lcd.print("Erorre nmodulo RTC");
 while (1);
 }
 
 SetItemMenu(&ArduinoMenu[0],1,1,2,0,"Settaggio Orario",100);
 SetItemMenu(&ArduinoMenu[1],0,0,4,1,"Settaggio Data",100);
SetItemMenu(&ArduinoMenu[2],3,3,2,0,"Set Ora",1);
 SetItemMenu(&ArduinoMenu[3],2,2,3,0,"Set Min",2);
SetItemMenu(&ArduinoMenu[4],6,5,4,1,"Set Giorno",5);
 SetItemMenu(&ArduinoMenu[5],4,6,5,1,"Set Mese",6);
 SetItemMenu(&ArduinoMenu[6],5,4,6,1,"Set Anno",7);
index = 0;
 
 if (rtc.lostPower()) rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); // uso la data e l'ora di compilazione dello sketch
lcd.setCursor(0, 0);
 lcd.print("www.mcmajan.com");
 lcd.setCursor(0, 1);
 lcd.print(" Stefano Smania");
 // ... per 3 secondi....
 delay(3000);
 lcd.clear(); // cancello il display
 lcd.print(ArduinoMenu[index].testo);
}
void loop ()
{
 refresh=false;
 
 xVal = analogRead (asseX);
 yVal = analogRead (asseY);
 buttonVal = digitalRead (PressPin);
if(xVal<200) refresh=MenuReaction(ArduinoMenu,&index,Menu_LEFT);
 else if(xVal>800) refresh=MenuReaction(ArduinoMenu,&index,Menu_RIGHT);
 
 if(yVal>800) refresh=MenuReaction(ArduinoMenu,&index,Menu_DOWN);
 else if(yVal<200) refresh=MenuReaction(ArduinoMenu,&index,Menu_UP);
if (buttonVal == LOW) 
 {
 DateTime now=rtc.now();
 int h = now.hour();
 
 if(ArduinoMenu[index].Fire==1) {int a=GetJoyValue(0,23,now.hour());rtc.adjust(DateTime(now.year(),now.month(),now.day(),a,now.minute(),now.second()));}
 if(ArduinoMenu[index].Fire==2) {int a=GetJoyValue(0,59,now.minute());rtc.adjust(DateTime(now.year(),now.month(),now.day(),now.hour(),a,now.second()));}
if(ArduinoMenu[index].Fire==5) {int a=GetJoyValue(1,31,now.day());rtc.adjust(DateTime(now.year(),now.month(),a,now.hour(),now.minute(),now.second()));}
 if(ArduinoMenu[index].Fire==6) {int a=GetJoyValue(1,12,now.month());rtc.adjust(DateTime(now.year(),a,now.day(),now.hour(),now.minute(),now.second()));}
 if(ArduinoMenu[index].Fire==7) {int a=GetJoyValue(2017,5000,now.year());rtc.adjust(DateTime(a,now.month(),now.day(),now.hour(),now.minute(),now.second()));}
 refresh=true;
 
 }
if(refresh)
 {
 lcd.clear();
 lcd.setCursor(0, 0);
 lcd.print(ArduinoMenu[index].testo);
 lcd.setCursor(0, 0);
 }
 PrintTime(0,1);
}
void SetItemMenu(struct MenuItem * AI,unsigned char P,unsigned char N,unsigned char C,unsigned char Pr,char * testo,unsigned char Fire)
{
 AI->Previous=P;
 AI->Next=N; 
 AI->Child=C; 
 AI->Parent=Pr; 
 strcpy(AI->testo,testo); 
 AI->Fire=Fire;
 
}
int GetJoyValue(int minimo,int massimo,int def)
{
 int val=def;
 for(;;) if(digitalRead (PressPin)==LOW) break; // aspetto il rilascio del pulsante prima di uscire
 lcd.clear(); // cancello il display
 delay(150);
 bool ref=true;
 for(;;)
 {
 
 
 xVal = analogRead (asseX);
 yVal = analogRead (asseY);
 buttonVal = digitalRead (PressPin);
if(xVal<200) {val--;ref=true;}
 else if(xVal>800) {val++;ref=true;}
 
 if(yVal>800) {val+=10;ref=true;}
 else if(yVal<200) {val-=10;ref=true;}
 
 if(val<minimo) val=massimo;
 if(val>massimo) val=minimo;
 if (buttonVal == LOW) break;
 if(ref==true)
 {
 lcd.clear();
 lcd.setCursor(0, 0);
 lcd.print(val);
 }
 delay(250);
 ref=false;
 }
 
 for(;;) if(digitalRead(PressPin)==LOW) break; // aspetto il rilascio del pulsante prima di uscire
 delay(250); // debounce
 return(val);
 
}
bool MenuReaction(struct MenuItem * MenuBase,unsigned char * index,unsigned char action)
{
 struct MenuItem local;
 local=MenuBase[*index];
 if(action==Menu_UP) *index=local.Previous;
 if(action==Menu_DOWN) *index=local.Next;
 if(action==Menu_LEFT) *index=local.Parent;
 if(action==Menu_RIGHT) *index=local.Child;
 delay(500);
 return true;
}
void PrintTime(unsigned char x,unsigned char y)
{
 DateTime now=rtc.now();
 lcd.setCursor(x, y); // Move the cursor to column four, lower row
 PrintDecimal(now.hour());
 lcd.print(":"); // And print the colon
 PrintDecimal(now.minute());
 lcd.print(":"); // And print the colon
 lcd.setCursor(x+6, y); // Move to the next column
 PrintDecimal(now.second());
lcd.setCursor(x+9, y); // Move to the next column
 PrintDecimal(now.day());
 lcd.print("/");
 PrintDecimal(now.month());
 }
void PrintDecimal(int num)
 {
 if (num<10) lcd.print(0);
 lcd.print(num); 
 }

Ok, lo ammetto, rispetto il solito è lunghissimo, ma credo che per una volta ne valga la pena perchè questo listato può essere la base di qualunque sistema in cui l’interfaccia utente viene gestita da un display ed un piccolo joystick o se preferite dei pulsanti singoli (esistono dei moduli pronti con il display ed i pulsanti già montati). Rispetto allo scorso articolo, una delle funzioni più importi che ho aggiunto e la GetJoyValue che richiede all’utente di inserire un valore numerico compreso fra un valore minimo e massimo, potendo fornire il valore base da cui partire. Fra le cose importanti da notare c’è il ciclo for all’inizio e alla fine che attende che il pulsante del joystick venga rilasciato. Perchè? Perchè voi entrate in questa funzione premendo il pulsante e se non lo rilasciate in tempo la pressione verrà interpretata come comando di settaggio del valore per cui la funzione tornerà alla funzione chiamante. Allo stesso modo in uscita viene usato il pulsante per settare il valore desiderato, ma non attendete il rilascio del pulsante accade che una volta tornati alla funzione chiamante viene riletta la pressione e tornate nuovamente nel settaggio del valore. A peggiorere il tutto c’è il fatto che alla pressione e rilascio del pulsante ci sono dei fenomeni fisici che possono portare a false letture per cui viene utilizzato un certo ritardo per ridurre al minimo questa evenienza (debounce). Il resto di fatto lo abbiamo già visto nei precedenti articoli a cui vi rimando per i relativi approfondimenti.

Conclusioni

Bene, spero che questo articolo vi possa essere stato utile. Abbiamo visto come creare una semplice interfaccia utente che utilizza un display lcd ed un joystick. Certamente si possono fare cose più sofisticate, ma credo che questo tipo di approccio possa soddisfare il 90% dei comuni utilizzi. In un prossimo articolo completerò un progetto richiestomi da un utente, ossia la realizzazione di un caricabatterie a corrente costante. Ora vi lascio con un breve video in cui vi faccio vedere il sistema durante il funzionamento. Scusate se parlo a bassa voce, lo so che sembro un prete durante la confessione, ma questi video li faccio quasi sempre quando i bimbi dormono e non urlano in sottofondo 🙂