Proiect de Duminică Autor: Robert Dima (YO8SHU)
Ceas pentru concurs cu Arduino Nano și RTC
De
ceva vreme încerc să-mi fac timp pentru un proiect destul de simplu dar în
același timp util, la care mă gâdesc de ceva vreme și nu am avut timpul necesar
să-l încep darămite să-l și duc până la capăt.
Aveam,
prin șmelțurile mele, un arduino nano care a fost achiziționat datorită
prețului și nu pentru un proiect anume, iar eu voiam ceva simplu, care să-mi
ofere și oarece satisfacție. În ideea că nu am făcut nici măcar inițerea în
arduino, teama era destulă dar nefondată. În sfârșit, mi-am pus la punct
planul, am achiziționat piesele de care mai era nevoie, și m-am apucat de
treabă, puțin copy/paste de aici, putină documentare de acolo și uite așa am încropit
și firmwareul ce urma să-l uploadez pe arduino. După ce am construit ceasul pe
breadboard și am făcut câteva debuguri la firmware totul era pregătit pentru a începe
construcția montajului final.
Astfel:
am folosit în acest proiect următoarele componente și module:
1 x Arduino Nano ,
1 x RTC cu comunicare
I2c,
1 x LCD 16X2 compatibil
HD44780,
1 x adaptor I2c la LCD,
1 x R 10K ohm,
1 x R 100K ohm,
1 x Cablaj de test cu
dimensiuni 60x80 (din acela cu găurele),
2 x Rigletă cu pini
(tată) 1x20,
1 x Rigletă cu pini
(mama) 1x20
1 x Baterie 9v
1 x cutie de tablă
refolosită (destul de mare sa-ti incapa totul in ea),
1 x comutator ON/OFF,
1 x Push Button,
Ca
și scule, am avut următoarele:
1 x ciocan,
1 x șurubelniță
„Phillip” („cruce” fară să știe mitropolitul că vrea drepturi de autor),
1 x pilă dreaptă
1 x letcon cu
pământare(ca să nu ardem microcontrolerul cand facem reparații, GRIJA MARE CA
SE ARDE),
1 x dremel cu pânză
pentru tăiat fier,
1 x cutter,
1 x ciupitor,
1 x patent,
Fludorul, sacâzul și sârmele
cred că le are toata lumea.
Să trecem la treabă:
Am
început prin a lipi adaptorul I2c la LCD pe un lcd16x2 de dimensiuni
rezonabile...mai mare decât cele standard (recuperat, nu mai știu de pe unde).
Pe placa de test am lipit baretele pentru arduino și pentru RTC conform schemei,
pe aceeași placă am lipit și firele pentru conectarea lcd-ului, LCD-ul este
unul recuperat, cu dimensiuni mari pentru o vizibilitate bună dar care este
100% compatibil cu HD44780, 16 caractere pe 2 rânduri. Apoi am lipit cele două
rezistențe ce formează divizorul de tensiune pentru a coborî nivelul măsurat la
sub 5v maxim măsurabil de arduino.
Apoi am trasat, cu un marker, forma
LCD-ului pe capacul superior al cutiei, urmând imediat și tăierea cu dremelul,
apoi am modificat și lărgit găurile de pe spatele cutiei astfel încât să iasă
mufele de alimentare și mufa USB a modulului arduino. Am punctat unde vin găurile
pentru șuruburi. După ce am montat arduino în interiorul cutiei și mi-am făcut
o idee unde și cum montez restul m-am apucat de lipit pinii pe PCB de test urmând imediat și firele aferente
conexiunilor.
După cum se vede și din schemă,
divizorul de tensiune, se leagă pe pinul A0 al modulului arduino, acesta fiind
un pin ANALOG IN ce conține un DAC cu rezoluție de 10 biti, el oferindu-i
procesorului o valoare între 0 si 1023, mai mult decât de ajuns pentru a avea o
acuratete de 0.01v în măsurarea tensiunii bateriei.
Ultima conexiune cu arduino se face pe
pinul A2 unde s-a conectat un buton push cu celălalt capăt la GND.
Alimentare din baterie se face pe pinul
VIN al modulului, iar toate perifericele se conectează pe pinul +5v de pe
arduino.
Ceasul este gata de utilizare și nu mai
este necesar decât să-i reglăm ora.
Pentru
aceasta există o porțiune din cod unde se introduce data și ora exactă, UTC, și
se „de-comentează” linia de cod semnalizată astfel dupa care facem upload, la
cod, în arduino. După ce am văzut că arduino afișează data si ora corect, vom „re-comenta”
linia de cod „de-comentată” anterior și facem iarăși upload la cod. Nefiind
necesare alte modificări în cod până când vom sincroniza iarăși ceasurile (în
funcție de modulul RTC folosit este posibil să nu mai fie necesară o astfel de
sincronizare) Butonul aprinde
retro-ilumiarea ecranului, pentru economisirea bateriei, stingându-se la 10
secunde dupa eliberarea butonului.
Baftă la construit.
Un link catre pagina proiectului de pe GitHub
Mai jos
aveti codul sursa pe care-l puteti modifica si imbunatati dupa plac.
// Ceas pentru concursurile de radioamatori
// Versiune 3.0
// YO8SHU Robert
// Connectare LCD:
//pin SDA to Arduino Analog pin A4
//pin SCL to Arduino Analog pin A5
//pin GND to Arduino GND
//pin VCC to Arduino 5v
//Conectare RTC:
//pin SDA to Arduino Analog pin A4
//pin SCL to Arduino Analog pin A5
//pin GND to Arduino GND
//pin VCC to Arduino 5v
// Conectare Divizor pin A0
// Conectare buton pin A2
//Librarii
#include <Wire.h> // include libraria Wire.h
#include <LCD.h> // include libraria LCD.h
#include <LiquidCrystal_I2C.h> // include libraria LiquidCrystal_I2C.h
#define DS1307_I2C_ADDRESS 0x68 // defineste adresa I2C folosita de RTC 0x68
LiquidCrystal_I2C lcd(0x27,2,1,0,4,5,6,7); // defineste adresa I2C folosita de LCD 0x27
// Configurare Voltmetru
int vBat = A0; //Pinul folosit pentru citirea voltajului de pe baterie, A0.
float vout = 0;
float vin = 0;
float R1 = 103000; // R1 aprox 100K, pentru acuratetea masurarii trebuie introdusa valoarea exacta!
float R2 = 10000; // R2 aprox 10K, pentru acuratetea masurarii trebuie introdusa valoarea exacta!
int value = 0;
// Configurare activare si temporizare retroiluminare
const byte BUTTON = A2; // Pinul folosit pentru butonul ce prinde retroiluminarea, A2
unsigned long buttonPushedMillis = 0; // defineste cand butonul a fost eliberat
unsigned long ledTurnedOnAt = 0; // defineste cand ledul sa aprins
unsigned long turnOnDelay = 250; // defineste cat se asteapta pana se aprinde ledul
unsigned long turnOffDelay = 10000; // defineste cat timp sta aprins ledul
bool ledReady = true; // flag for when button is let go
bool ledState = false; // for LED is on or not.
// Converteste numerele Zecimale in Binare
byte decToBcd(byte val)
{return ( (val/10*16) + (val%10) );}
// Converteste numerele Binare in Zecimale
byte bcdToDec(byte val)
{return ( (val/16*10) + (val%16) );}
// 1) Setarea datei si orei in modulul RTC
// 2) Porneste ceasul
// 3) regleaza afisarea orei in formatul 24 ore
// introduceti cifre corecte
void setDateDs1307( byte second, //ORA: // 0-59
byte minute, // 0-59
byte hour, // 1-23
byte dayOfWeek, //DATA: // 1-7
byte dayOfMonth, // 1-28/29/30/31
byte month, // 1-12
byte year) // 0-99
{Wire.beginTransmission(DS1307_I2C_ADDRESS);
Wire.write(0);
Wire.write(decToBcd(second)); // 0 to bit 7 starts the clock
Wire.write(decToBcd(minute));
Wire.write(decToBcd(hour));
Wire.write(decToBcd(dayOfWeek));
Wire.write(decToBcd(dayOfMonth));
Wire.write(decToBcd(month));
Wire.write(decToBcd(year));
Wire.write(0x10); // sends 0x10 (hex) 00010000 (binary) to control register - turns on square wave
Wire.endTransmission();}
// Citirea datei si orei din modulul RTC
void getDateDs1307(byte *second,
byte *minute,
byte *hour,
byte *dayOfWeek,
byte *dayOfMonth,
byte *month,
byte *year)
// Reset the register pointer
{Wire.beginTransmission(DS1307_I2C_ADDRESS);
Wire.write(0);
Wire.endTransmission();
Wire.requestFrom(DS1307_I2C_ADDRESS, 7);
*second = bcdToDec(Wire.read() & 0x7f); // A few of these need masks because certain bits are control bits
*minute = bcdToDec(Wire.read());
*hour = bcdToDec(Wire.read() & 0x3f); // Need to change this if 12 hour am/pm
*dayOfWeek = bcdToDec(Wire.read());
*dayOfMonth = bcdToDec(Wire.read());
*month = bcdToDec(Wire.read());
*year = bcdToDec(Wire.read());}
void setup()
{pinMode(BUTTON, INPUT_PULLUP); // Setaza butonul ca fiind Pulup
pinMode(vBat, INPUT); // Setaza pinul de masura voltaj baterie ca fiind INPUT
lcd.setBacklightPin(3,POSITIVE); // Seteaza pinul 3 din i2c to lcd ca fiind cel ce controleaza iluminatul
lcd.setBacklight(HIGH); // Aprinde iluminatul
byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
Wire.begin();
// Introduceti mai jos data si ora exacta
// Aceasta informatii se introduc doar atunci cand dorim sincronizarea ceasului cu ora excacta
// In functie de calitatea RTC-ului acest lucru nu trebuie efectuat prea des.
second = 0; // aici se introduce secunda exacta
minute = 12; // aici se introduce minutul exact
hour = 13; // aici se introduce ora exacta
dayOfWeek = 7; // aici se introduce numarul zilei din saptamana
dayOfMonth = 12; // aici se introduce ziua
month = 04; // aici se introduce luna
year = 20; // aici se introduce anul
// Pentru a introduce datele de mai sus in modulul RTC, se decomenteaza urmatoarea linie si apoi se uploadeaza codul in arduino.
// setDateDs1307(second, minute, hour, dayOfWeek, dayOfMonth, month, year);
// Dupa ce sa uploadat codul cu ora exacta reglata, se comentaza la loc linia de deasupra si se face upload la cod din nou.
// daca linia raman decomentata, la fiecare pornire a ceasului informatiile de mai sus se reintroduc in
// modulul RTC ceea ce face ca data si ora sa se reseteze la momentul uploadului initial.
// initializare LCD
lcd.begin(16, 2); // Stabileste dimensiunile LCD-ului
lcd.setCursor(0,1); // Stabileste pozitia cursorului la inceputul primului rand , Stanga sus
lcd.print(" CONTEST TIMER "); // Afiseaza textul ditre paranteze
lcd.setCursor(0,0); // Stabileste pozitia cursorului la inceputul urmatorului rand.
lcd.print("YO8SHU/P Ver. 3"); // Afiseaza textul ditre paranteze, puteti modifica textul dupa plac
delay(5000); // pauza de 5 secunde sa se poata citi textul
lcd.clear();} // sterge ecranul
void loop()
{byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
getDateDs1307(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);
BackLight(); //Apeleaza functia backlight
//Afisare DATA si ORA
UTC(); // Afiseaza ora UTC pe primul rand
if (second < 20){CFR(); } // Daca secunde < 20 afiseaza pe randul 2 ora CFR
else if (second < 30){BatVolt(); } // Altfel daca secunde < 30 afiseaza voltajul bateriei pe randul 2
else if (second < 50){ DATA(); } // Altfel daca secunde < 50 afiseaza pe randul doi data
else if (second < 60){BatVolt(); }} // Altfel daca secunde < 60 afiseaza voltajul bateriei pe randul 2
//Functie citire si afisare voltaj baterie
void BatVolt()
{value = analogRead(vBat); // citeste pinul vBat si stocheaza valoarea in value
vout =(value * 5) / 1024.0; // defineste vout ca fiind value inmulti cu tensiunea de referinta totul impartit la numarul de pasi
vin = vout / (R2/(R1+R2)); // defineste vin ca fiind vout impartit la divizorul de tensiune
if (vin<6) { // daca vin mai mic ca 6
lcd.setCursor(0,1); // stabileste cursorul pe randul 2
lcd.print("Low bat Recharge");} // afiseaza low bat reincarcati
else{ // altfel
lcd.setCursor(0,1); // cursorul pe randul 2
lcd.print("Bat. volt. "); // afiseaza: Bat. Volt.
lcd.print(vin); // afiseaza valoarea lui vin
lcd.print("V");}} // afiseaza: V
// Functie aprindere backlight
void BackLight()
{unsigned long currentMillis = millis(); // deineste surrentMillis ca find ora actuala
if (digitalRead(BUTTON) == LOW) {
buttonPushedMillis = currentMillis; // inoieste milis cand butonul a fost apasat
ledReady = true; }
if (ledReady) { // this is typical millis code here:
if ((unsigned long)(currentMillis - buttonPushedMillis) >= turnOnDelay) { // okay, enough time has passed since the button was let go.
lcd.setBacklight(HIGH); // Backlight on setup our next "state"
ledState = true; // save when the LED turned on
ledTurnedOnAt = currentMillis; // wait for next button press
ledReady = false;}}
if (ledState) { // okay, led on, check for now long
if ((unsigned long)(currentMillis - ledTurnedOnAt) >= turnOffDelay) {
ledState = false;
lcd.setBacklight(LOW);}}} // Backlight off
// functie afisare ora UTC
void UTC()
{byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
getDateDs1307(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);
//Afiseaza ORA UTC
lcd.setCursor(0,0);
lcd.print(" UTC ");
if (hour<10){lcd.print("0");}
lcd.print(hour, DEC);
lcd.print(":");
if (minute<10){lcd.print("0");}
lcd.print(minute, DEC);
lcd.print(":");
if (second<10){lcd.print("0");}
lcd.print(second, DEC);
lcd.print(" ");}
// Functie afisare ora CFR
void CFR()
{byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
getDateDs1307(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);
//Afiseaza ORA CFR
lcd.setCursor(0,1);
lcd.print(" CFR ");
if (hour+3<10){lcd.print("0");} // se inlocuieste +3 cu +2 daca se foloseste ora de iarna
if (hour+3>24){lcd.print (hour-21,DEC);} // se inlocuieste +3 cu +2 daca se foloseste ora de iarna
else {lcd.print(hour+3, DEC);} // se inlocuieste +3 cu +2 daca se foloseste ora de iarna
lcd.print(":");
if (minute<10){lcd.print("0");}
lcd.print(minute, DEC);
lcd.print(":");
if (second<10){lcd.print("0");}
lcd.print(second, DEC);
lcd.print(" ");}
// Functie afisare DATA
void DATA()
{byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
getDateDs1307(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);
//Afiseaza DATA
lcd.setCursor(0,1);
lcd.print(" ");
switch(dayOfWeek){ //AFISEAZA in loc de cifra, numele zilei
case 1: lcd.print("Lun"); break;
case 2: lcd.print("Mar"); break;
case 3: lcd.print("Mie"); break;
case 4: lcd.print("Joi"); break;
case 5: lcd.print("Vin"); break;
case 6: lcd.print("Sam"); break;
case 7: lcd.print("Dum"); break; }
lcd.print(" ");
if (dayOfMonth<10){lcd.print("0");} //Daca ZIUA mai mica ca 10 afiseaza "0" inainte
lcd.print(dayOfMonth, DEC); //Print ZIUA
lcd.print(".");
if (month<10) {lcd.print("0");} //Daca LUNA mai mica ca 10 afiseaza "0" inainte
lcd.print(month, DEC); //Print LUNA
lcd.print(".20");
lcd.print(year, DEC); //Print ANUL
lcd.print(" ");}
Link catre Github