Un ceas Arduino cu oră și dată, fără modul RTC. O funcție simplă care ține evidența timpului.
Folosirea unui modul RTC (real time clock) este recomandată la realizarea unui proiect care se folosește de oră și dată. Modulele RTC sunt ieftine, au acuratețe bună, dar mai mult de atât continuă să funcționeze și când placa de dezvoltare nu este alimentată. Acestea folosesc o baterie tip celulă litiu pentru a ține evidența timpului utilizând curenți foarte mici. Există destul de multe biblioteci Arduino care implementează funcții pentru comunicarea cu diverse surse de timp (module RTC, servere NTP etc.). Un exemplu este biblioteca Time de Paul Stoffregen care utilizează timer-ul microcontroller-ului pentru a ține evidența timpului, dar permite și sincronizarea periodică cu o sursă de acuratețe mai mare.
Totuși, am decis să scriu o funcție ce ține evidența timpului incrementând variabile la fiecare apelare. Schița apelează această funcție în fiecare secundă, iar aceasta incrementează variabila secundelor și o afișează. Dacă s-a numărat până la 60, variabila secundelor trece la 0 și funcția avansează, incrementând minutele. Tot așa, sunt incrementate oricând este cazul variabilele pentru oră, zi, lună și an. Afișajul este actualizat doar pentru ce va fi modificat. Pentru a scrie programul am folosit o placă de dezvoltare compatibilă cu Arduino Uno căreia i-am atașat un shield cu afișaj LCD 16x2 și butoane analogice. Doar două butoane sunt necesare pentru modificarea datei și orei, așa că poți folosi butoane tactile conectate la pini de intrare digitali. În acest caz, vei modifica mai multe linii de cod, evaluând starea pinilor digitali.
Variabilele pentru oră și dată sunt globale, declarate la începutul codului. De asemenea, trei vectori stochează numărul de zile din fiecare lună, textul pentru fiecare lună și pentru zilele săptămânii.
byte hh, mm, ss; byte dd, MM, YY; byte wd; byte daysOfMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; const char* months[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; const char* wdays[7] = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
Schița conține funcții care afișează fiecare variabilă pe o poziție specifică pe ecranul LCD. Aceste funcții se găsesc în schița completă. Voi detalia funcția de numărare a timpului. La fiecare apelare, aceasta incrementează variabila secundelor ss
.
void countTime() { // count seconds ss++; if (ss < 60) { dispSec(); return; } ss = 0; dispSec(); // count minutes mm++; if (mm < 60) { dispMin(); return; } mm = 0; dispMin(); // count hours hh++; if (hh < 24) { dispHour(); return; } hh = 0; dispHour(); dd++; wd++; if (wd == 7) wd = 0; if (dd < daysOfMonth[MM]) { dispDay(); return; } dd = 0; dispDay(); MM++; if (MM < 12) { dispMonth(); return; } MM = 0; dispMonth(); YY++; if ((YY % 4) == 0) daysOfMonth[1] = 29; else daysOfMonth[1] = 28; if ((YY % 100) == 0) daysOfMonth[1] = 28; dispYear(); }
Primele linii de cod incrementează și afișează secundele. Cât timp nu au ajuns la 60, funcția se întrerupe și se revine fără a mai face altceva. Dacă secundele au ajuns la 60, li se atribuie valoarea 0, dar funcția merge mai departe. Incrementează minutele, în mod asemănător. Dacă variabila minutelor nu a ajuns la 60, se afișează minutele și se revine din funcție. Când au trecut 60 de minute funcția avansează la incrementarea orei. Orele sunt incrementate până la 24, moment în care este ajustată ziua, dar și ziua din săptămână. Ziua este incrementată până ce atinge numărul declarat în vectorul cu numărul de zile din fiecare lună. Similar, luna este incrementată de la 0 la 11. Anul nu are limită de incrementare în codul din funcție (este limitat totuși de tipul variabilei). În ceea ce privește anul, trebuie avuți în vedere anii bisecți. Dacă un an este multiplu de 4, are 366 de zile. Fac excepție anii care sunt multipli de 100, dar nu sunt divizibili cu 400, care nu sunt bisecți. Divizibilitatea cu 400 nu este verificată în codul de mai sus (mai este destul timp până în anul 2400 😉). Pentru fiecare an bisect, numărul de zile din aferent lunii februarie este actualizat în vector.
Cam atât este suficient ca funcția să poată seta corect variabilele aferente. Dar, ceasul trebuie să permită setarea datei și orei de către utilizator, deci în afara funcției. Am ales o interfață simplă, cu numai două butoane. Unul dintre butoane selectează variabila ce va fi modificată (minut, oră, zi, lună, an), iar celălalt o incrementează la fiecare apăsare. Variabila ce va fi modificată este afișată intermitent pe LCD. Când se atinge valoarea maximă, variabila se resetează (la 0 pentru minut, oră, respectiv la 1 pentru zi și lună).
Trecerea printr-o resetare a zilelor necesită reajustarea zilei săptămânii. La fel, simpla incrementare a lunii și a anului necesită această reajustare. La trecerea prin toate zilele unei luni cu un număr de zile divizibil cu 7 (februarie de 28 de zile), ziua din săptămână rămâne sincronizată. Deci, la resetarea zilelor după atingerea maximului aferent lunii (în modul de setare în care luna nu se incrementează), trebuie să adăugăm la variabila zilei din săptămână un rest calculat ca diferența dintre cel mai apropiat multiplu de 7 și numărul de zile din acea lună. Similar, se ajustează variabila zilei din săptămână la modificarea lunii cu numărul de zile din lună, respectiv la resetarea lunii, cu numărul de zile din an.
Pentru resetarea anilor am rezolvat problema ajustării zilei din săptămână limitând setarea anului între 2000 și 2055. Adică 56 de ani, din care 14 bisecți. Deci un număr total de zile divizibil cu 7, astfel încât incrementarea normală, cu 1, a zilei din săptămână să nu necesite nicio ajustare.
Schița completă conține aproximativ 300 de linii de cod și poate fi găsită pe GitHub. Variabilele și numele scurte ale lunilor și zilelor din săptămână sunt denumite în engleză, deoarece articolul poate fi citit și în limba engleză. Pasul următor este adăugarea funcției de alarmă.
Niciun comentariu :
Trimiteți un comentariu
Vă recomandăm să citiți regulamentul comentariilor înainte de a scrie un comentariu.