Ennek a "projektnek" egy kedves barátommal együtt vágtunk neki 2017 magasságában ( most amikor ezt írom 2021 van, amiből látszik, milyen rendkívül bonyolult probléma megoldásáról volt szó ... mennyire elszerencsétlenkedtük az időt és húztuk mint a rétestésztát :-). Az alapgondolat egy újszerű, mégis modern kialakítású óra elkészítése volt. Egyértelműen adódott a gondolat, hogy a modern technikát ( Arduino, mi más ? :-) felhasználva hozzuk létre. Hosszas agyalgás után olyan dizájnban állapodtunk meg, amiben kettős körív mentén vannak elhelyezve a LED -ek. Természetesen egyenként programozható RGB LED -ekről van szó. Felmerült ugyan a készen kapható LED -szalagok alkalmazása, de nagyon sok kötöttséget jelentettt volna, elsősorban a behatárolt fizikai méretekből adódóan. A diszkrét LED -ekkel viszont miénk volt a pálya, úgy alakítottuk ki, ahogy szerettük volna.
Úgy állapodtunk meg, hogy az óratest kialakítását a barátom végzi, én pedig az áramkört valósítom meg, ill. megcsinálom az Android -os alkalmazást is hozzá. Nos igen, ez egy kicsit magyarázatra szorul, ezért kifejtem kicsit részletesebben:
Nyilvánvaló, hogy egy óra működése során a legfontosabb szempont a pontosság. Ez két szempontból is csorbát szenvedhet. Az egyik az áramköri elemek "tökéletlensége". Hasonlóképpen pl. egy hagyományos kakórához, az idő időnként elmászik. Ennek a mértéke lehet kisebb, vagy annyira nagy, hogy már zavaróan befolyásolja a használatot. Másrészt figyelembe kell venni a téli-nyári időszámítást is, ill. az azokra való áttérés lehetőségét.
Mindezen okokból elengedhetetlen a pontosságról, ill. a pontos idő beállításának a lehetőségéről gondoskodni. Kivétel, ha eleve folyamatosan kapja az óra a pontos időt. Erre napjainkban már több lehetőség is van. Az egyik a DCF jel vétele, aminek lényege, hogy egy Frankfurtból sugárzott 77,5 kHz -es jellel adják a pontos időt, amit megfelelő eszköz segítségével venni lehet. Nagy hátránya, hogy ha nem tudjuk biztosan hol lesz felhasználva az óra ( márpedig nálunk ez volt a helyzet ), nem garantálható a jel biztonságos vétele, így az óra pontossága sem. Másik mód az internet felhasználása NTP ( Network Time Protokol ) segítségével. De sajnos nincs mindenhol internet. A GPS műholdak is szorgalmasan szórják a pontos időt, amit viszonylag egyszerűen lehet venni ... ha megfelelő helyen vagyunk. Azt hiszem kitalálta a kedves olvasó: ismeretlen helyen ez sem garantálható :-(.
Maradt tehát, hogy olyan óraáramkört ( RTC ) használjunk, ami megfelelő pontossággal tartja az időt, másrészt pedig biztosítani kell az óra pontos idejének beállítását is, vagy máshogy fogalmazva az idő beállíthatóságát. Erre szolgál a fent említett Android -os alkalmazás.
Mindezek figyelembe vételével lassan össze is állt a kép arról, hogy mire lesz szükség az órához:
Néhány fontos dolog az RTC áramkörrel kapcsolatban. Az egyik, hogy többféle is létezik. Utána olvasgatva egyértelműen a fenti típust ajánlják, mert eléggé megbízható, és elfogadható pontosságú ( nem mellesleg van benne egy 4 k -s EEPROM is, ami jelen helyzetben tök fölösleges, de adott esetben jól jöhet ). A másik fontos dolog, hogy a DS3231 panel alapvetően nem sima CR2032 gombelemre van kitalálva hanem 3,6 V -os LIR2032 lítium gomb akkura, mert van benne egy töltő áramkör, ami folyamatosan tölti. Így az akkumulátor kapocsfeszültsége 4,2 V körül jár, ami egy CR2032 számára nem sok jóval kecsegtet. Ezért el kell távolítani a panelről ezt a töltő áramköri részt. A következő videóban szépen el van magyarázva hogyan. Mondjuk én nem az ellenállást távolítottam el, hanem elvágtam egy vezetéket, de tökmindegy. A modifikáció természetesen azt is jelenti, hogy időnként cserélni kell a gombelemet, de egy elem - állítólag - több év működést is kibír. A Youtube videó: DS 3231 RTC | Disable Battery Charging circuit.
Az áramkör kapcsolási rajza itt látható:
Sok kommentet talán nem igényel. Minden +5V -ról működik szerencsére, ami USB csatlakozóból szuperul kinyerhető ( 2 x 60 db LED megtáplálása kicsit zűrös lehet, de egyszerre sosem világít annyi, tehát a gyakorlatban nem lehet probléma ). A BT modul bemenete ( Rx ) többek egybehangzó véleménye szerint nem tolerálja az 5 V -ot, ezért az Arduino -tól érkező jel egy elenállás osztón van megjáratva. A három nyomógomb az óra idejének beállításához kell. Mint látható a 2 LED kör vezérléséhez csupán 2 kimenetre van szükség ( tulajdonképpen 1 -el is megvalísítható lenne kis szoftveres trükközéssel ... de inkább nem trükköztem :-). Az RTC modul I2C buszt használ, és csak két analóg portot foglal az Arduino -n.
Az elvi rajz elkészítése után a következő nagy feladat a LED -ek elhelyezése, azaz a NYÁK panelek megtervezése és elkészítése volt. Ehhez egy Sprint Layout nevezetű egyszerű NYÁK rajzoló programot használtam. A jobb anyagfelhasználás miatt a kört négy cikkre osztottam. A négy cikk teljesen megegyező részekből épül fel:
Készen így néznek ki ( elkészítésükben egy másik kedves barátom segített, ezúton is köszönet érte ! ):
Deszkapanelen pedig így:
Működés közben nehéz volt róla normális fényképet készíteni, az alábbi a jobbak közül való. A belső íven levő piros LED jelképezi a kismutatót, a külső ív kék LED -je a nagymutatót, a zöld LED -ek pedig a másodperc mutatót. Így állóképen kicsit fikciós a mutatók beazonosítása, de élőben nem olyan vészes a helyzet :-)
Az óra mellett látható az Androidos alkalmazás első verziója is, mint később láthatod ez azóta sokat változott.
Ezután az óra végleges összeállítása következett. Mint említettem a mechanikai munkák nagy részét a barátom végezte, van erről is kép:
Az óra doboz hátsó részét már jómagam farigcsáltam:
Ezután behelyeztem az áramköri elemeket:
Majd a LED NYÁK -okat is, és összehuzaloztam. Az óraállításhoz szükséges nyomógombokat mikrokapcsolók beépítésével oldottam meg, amiket a jobb felső szélen látni:
Közelebbről a mikrokapcsolók:
Tisztázás után valamellyest javult a látvány:
Az áramkör egy picit közelebbről ( mint - remélhetőleg - látható háromféle USB aljzatot is beépítettem. Inkább több legyen mint kevesebb, nem igaz ? :-)
Természetesen nem maradt pucéran, le lett fedve egy kör alakú lappal:
Elképzelésünk szerint az előlapon tejfehér plexi lapot használunk, mert az jobban kihozza a LED -ek "mutató jellegét". Ennek a kivágásáról is készült kép ( itt még nem tejfehér, mert rajta van a kék védőfólia ... ami a fényviszonyok miatt a képen inkább szürkének látszik ):
És most térjünk át a szoftver(ek)re.
Két részből áll. Az egyik magát az órát működtető Arduino -s rész, a másik pedig az Androidos alkalmazás. Egyikről sem írnék túl sokat, mert mindkét program igen nagy hasonlóságot mutat a Nyomógomb naplózó kütyüével, melyet elég részletesen bemutattam itt az oldalamon. Az óra programja attól elsősorban abban különbözik, hogy nem kell adatokat naplózni ill. küldözgetni hanem csak a LED -eket vezérelni. Tehát mondhatni egyszerűbb. Íme:
/*
* Ez a program a 2018-2021 között készült LED -es óra programja.
* 2 x 60 db RGB LED -del mutatja a pontos időt. A LED -ek egy kisebb és egy nagyobb koncentrikus kör mentén helyezkednek el.
* A belső kör jelképezi az óra mutatót, a külső pedig a percmutatót.
* A pontos időt egy DS3231 RTC adja. Lehetőség van külső eszközről bluetooth -on rátölteni és lekérni az időt a lenti
* protokol szerint.
*
*/
/* Az rtc kezelő alapprogram innen származik:
http://tronixstuff.com/2014/12/01/tutorial-using-ds1307-and-ds3231-real-time-clock-modules-with-arduino/
*/
/* A 3 db gomb prellmentesített kezelésével ez alapján van megoldva:
https://www.arduino.cc/en/tutorial/debounce
*/
#include "Wire.h"
#include <Adafruit_NeoPixel.h>
#include <SoftwareSerial.h> // Ez a BlueTooth soros kommunikációjához kell
#define DS3231_I2C_ADDRESS 0x68
#define PIN_1 8 // Belső PL9823 LED sor ezen a pin -en van kezelve
#define PIN_2 9 // Külső PL9823 LED sor ezen a pin -en van kezelve
#define NUMPIXELS 60 // Hány darab PL9823 LED van kezelve. Számozásuk 0 -tól kezdődik
#define rxPin 2 // Define SoftwareSerial rx data pin , azaz a BlueTooth TX, D2
#define txPin 3 // Define SoftwareSerial tx data pin , azaz a BlueTooth RX, D3
Adafruit_NeoPixel pixels_1 = Adafruit_NeoPixel(NUMPIXELS, PIN_1, NEO_RGB + NEO_KHZ800); // PL9823 LED -ekhez kell
Adafruit_NeoPixel pixels_2 = Adafruit_NeoPixel(NUMPIXELS, PIN_2, NEO_RGB + NEO_KHZ800); // PL9823 LED -ekhez kell
SoftwareSerial BlueTooth(rxPin, txPin); // create instance of SoftwareSerial , ez a BlueTooth soros kommunikációjához kell
byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
byte byteRead; // Soros port (bluetooth) beolvasáskor ide olvasunk
byte ByteSorszam; // Megadja, hogy hányadik byte beolvasásánál tartunk a BT modul felől
byte EV1; // Soros port (bluetooth) beolvasáskor ide kerül az Android eszköztől kapott év első byte -ja
byte EV2; // Soros port (bluetooth) beolvasáskor ide kerül az Android eszköztől kapott év második byte -ja
byte HON; // Soros port (bluetooth) beolvasáskor ide kerül az Android eszköztől kapott hónap érték
byte NAP; // Soros port (bluetooth) beolvasáskor ide kerül az Android eszköztől kapott nap érték
byte NA_; // Soros port (bluetooth) beolvasáskor ide kerül az Android eszköztől kapott hét napja ( Appinventor: Weekday, Arduino: dayOfWeek )
byte ORA; // Soros port (bluetooth) beolvasáskor ide kerül az Android eszköztől kapott óra érték
byte PER; // Soros port (bluetooth) beolvasáskor ide kerül az Android eszköztől kapott perc érték
byte MAS; // Soros port (bluetooth) beolvasáskor ide kerül az Android eszköztől kapott másodperc érték
int MODE = 0; // A gombok kezelésénél használjuk. 0 - nem aktív, 1 - óra állítása, 2 - perc állítása, 3 - másodperc állítása
int A_buttonState = LOW; // the current reading from the input pin. Az "=LOW" -t már én írtam hozzá
int A_lastButtonState = LOW;
unsigned long A_lastDebounceTime = 0; // the last time the output pin was toggled
unsigned long A_debounceDelay = 500; // 1000 volt
int B_buttonState = LOW; // the current reading from the input pin. Az "=LOW" -t már én írtam hozzá
int B_lastButtonState = LOW;
unsigned long B_lastDebounceTime = 0; // the last time the output pin was toggled
unsigned long B_debounceDelay = 20;
int C_buttonState = LOW; // the current reading from the input pin. Az "=LOW" -t már én írtam hozzá
int C_lastButtonState = LOW;
unsigned long C_lastDebounceTime = 0; // the last time the output pin was toggled
unsigned long C_debounceDelay = 20;
byte A_gomb_fazis = 0; // Megadja, hogy melyik óraállítási fázisban vagyunk:
// 0 - egyik sem
// 1 - órát állítunk
// 2 - percet állítunk
// 3 - másodpercet állítunk
byte B_gomb_fazis = 0; // Megadja, hogy felfelé vagy lefelé állítunk:
// 0 - egyik sem
// 1 - felfelé
byte C_gomb_fazis = 0; // Megadja, hogy felfelé vagy lefelé állítunk:
// 0 - egyik sem
// 1 - felfelé
int gyors = 200; // PL9823 LED -ekhez
// byte masodperc_tomb[60][3] = {0};
byte tomb_1[61][3] = {0}; // A LED -eknek 61 elemű tömböket foglalunk, hogy könnyebb legyen címezni ( 1-60 )
byte tomb_2[61][3] = {0};
byte puffer[4] = {0};
int second_buffer;
byte kismutato; // A 24 órás formátumú "hour" a megjelenítéskor ezt a LED sorszámot jelenti
byte km_korr; // A percmutató állásától függő óramutató korrekció
// byte nm_korr; // A nagymutató (percmutató) korrigált értéke ( 0 perc esetén 60 )
int MitAkar = 0; /* Az Android eszköztől kapott byte, ami megadja, hogy mit szeretne tőlünk
* - KÜLDHETI A PONTOS IDŐT ( a = 97 )
* - LEKÉRDEZHETI A MI IDŐNKET ( b = 98 )
*/
/* A következőkben megadjuk az óra egyes komponenseinek az R-G-B intenzitását ( 0 - 255 ) : */
byte OraMutato_R = 150;
byte OraMutato_G = 0;
byte OraMutato_B = 0;
byte PercMutato_R = 0;
byte PercMutato_G = 0;
byte PercMutato_B = 150;
byte MasPercMutato_R = 0;
byte MasPercMutato_G = 40;
byte MasPercMutato_B = 0;
byte SegedOsztas_R = 2;
byte SegedOsztas_G = 2;
byte SegedOsztas_B = 0;
/* ======================================================================================================================================================================= */
void setup()
{
Wire.begin();
Serial.begin(9600);
BlueTooth.begin(9600); // Ezen megy a BlueTooth kommunikáció
// set the initial time here:
// DS3231 seconds, minutes, hours, day, date, month, year
pinMode(5, INPUT); // Állító gombok
pinMode(6, INPUT);
pinMode(7, INPUT);
pixels_1.begin(); // This initializes the NeoPixel library.
pixels_2.begin(); // This initializes the NeoPixel library.
}
/* ======================================================================================================================================================================= */
void loop()
{
gomb_kezeles(); // Az A, B és C gombok alapján beállítja az RTC idejét
bt_kezeles(); // Ha jön a soros porton valami, beolvassuk
readDS3231time(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);
// --------------- ITT KÖVETKEZIK A LED -EK KEZELÉSE ----------------------------------------------------------------------- :
// Elvileg elgendő volna csak akkor foglalkozni vele, ha másodperc váltás történt, de a gombokkal történő beállításnál azt szeretnénk,
// ha az éppen állítandó "mutató" gyorsan színt váltana, hogy jól látható legyen melyiket állítjuk
for(int i=1; i<=60; i++) // Először nullázunk mindent
{
tomb_1[i][0] = 0;
tomb_1[i][1] = 0;
tomb_1[i][2] = 0;
tomb_2[i][0] = 0;
tomb_2[i][1] = 0;
tomb_2[i][2] = 0;
}
// Azután beállítjuk az óra alapmintáit (osztásokat), tehát ami a mutatóktól függetlenül mindig a háttérben látható ( már ha épp egy mutató ki nem takarja )
for(int i=5; i<=60; i=i+5)
{
tomb_1[i][0] = SegedOsztas_R ;
tomb_1[i][1] = SegedOsztas_G ;
tomb_1[i][2] = SegedOsztas_B ;
}
for(int i=15; i<=60; i=i+15)
{
tomb_2[i][0] = SegedOsztas_R ;
tomb_2[i][1] = SegedOsztas_G ;
tomb_2[i][2] = SegedOsztas_B ;
}
// Ezután a mutatókat állítjuk be ( a másodpercet ( second ) a végére hagyjuk, hogy a másodperc mutató "eltakarja" a másik kettőt ha rájuk ér ).
// A perc beállításánál az RTC 0-59 miatt speciális eset a 0 perc, mert annak a tömbünk 60 -as eleme felel meg. Ezt itt kezeljük le:
if ( minute == 0 ) minute = 60;
if ( MODE == 2 ) // Ha PERC változtatási üzemmódban vagyunk villogtatjuk a nagymutatót
{
tomb_2[minute][0] = random ( 0, 200 );
tomb_2[minute][1] = random ( 0, 200 );
tomb_2[minute][2] = random ( 0, 200 );
// pixels_2.setPixelColor(nm_korr-1, pixels_2.Color( random ( 0, 200 ), random ( 0, 200 ),random ( 0, 200 )));
pixels_2.setPixelColor(minute-1, pixels_2.Color( random ( 0, 200 ), random ( 0, 200 ),random ( 0, 200 )));
pixels_2.show();
}
else
{
tomb_2[minute][0] = PercMutato_R;
tomb_2[minute][1] = PercMutato_G;
tomb_2[minute][2] = PercMutato_B;
}
// Az óra mutató speciálisan viselkedik, hiszen az RTC 0-23 órát számol amit le kell képeznünk a 0-12 felületre
if (( hour == 0 ) || ( hour == 12 )) kismutato = 60 ;
if (( hour == 1 ) || ( hour == 13 )) kismutato = 5 ;
if (( hour == 2 ) || ( hour == 14 )) kismutato = 10 ;
if (( hour == 3 ) || ( hour == 15 )) kismutato = 15 ;
if (( hour == 4 ) || ( hour == 16 )) kismutato = 20 ;
if (( hour == 5 ) || ( hour == 17 )) kismutato = 25 ;
if (( hour == 6 ) || ( hour == 18 )) kismutato = 30 ;
if (( hour == 7 ) || ( hour == 19 )) kismutato = 35 ;
if (( hour == 8 ) || ( hour == 20 )) kismutato = 40 ;
if (( hour == 9 ) || ( hour == 21 )) kismutato = 45 ;
if (( hour == 10 ) || ( hour == 22 )) kismutato = 50 ;
if (( hour == 11 ) || ( hour == 23 )) kismutato = 55 ;
// Másrészt a számlapon két egész óra között 5 osztás van, és az a percmutató állásától függ, hogy ezek közül melyikben kell lennie.
if (( minute >= 0 ) && ( minute < 12 )) km_korr = 0 ;
if (( minute >= 12 ) && ( minute < 24 )) km_korr = 1 ;
if (( minute >= 24 ) && ( minute < 36 )) km_korr = 2 ;
if (( minute >= 36 ) && ( minute < 48 )) km_korr = 3 ;
if (( minute >= 48 ) && ( minute < 60 )) km_korr = 4 ;
if ( minute == 60 ) km_korr = 0 ;
// Sajnos még mindig maradt speciális eset, mégpedig amikor legfelül a 12 -esen ( vagy 0 -n ) áll a kismutató. Ekkor ha megnövelnénk a korrekcióval, akkor
// 61, 62, 63, 64 adódna, ami nyilván megengedhetetlen az 1-60 tömbelemeknél. Ezt tahát külön kell lekezelnünk:
if (( kismutato == 60 ) && ( km_korr != 0 )) kismutato =0;
if ( MODE == 1 ) // Ha ÓRA változtatási üzemmódban vagyunk villogtatjuk a kismutatót
{
tomb_1[kismutato+km_korr][0] = random ( 0, 200 );
tomb_1[kismutato+km_korr][1] = random ( 0, 200 );
tomb_1[kismutato+km_korr][2] = random ( 0, 200 );
pixels_1.setPixelColor(kismutato+km_korr-1, pixels_1.Color( random ( 0, 200 ), random ( 0, 200 ),random ( 0, 200 )));
pixels_1.show();
}
else
{
tomb_1[kismutato+km_korr][0] = OraMutato_R;
tomb_1[kismutato+km_korr][1] = OraMutato_G;
tomb_1[kismutato+km_korr][2] = OraMutato_B;
}
// Hasonlóan a percmutatónál levő megoldáshoz, a másodperc beállításánál az RTC 0-59 miatt speciális eset a 0 másodperc,
// mert annak a tömbünk 60 -as eleme felel meg. Ezt itt kezeljük le:
if ( second == 0 ) second = 60;
if ( MODE == 3 ) // Ha MÁSODPERC változtatási üzemmódban vagyunk villogtatjuk a másodperc mutatót
{
tomb_1[second][0] = random ( 0, 200 );
tomb_1[second][1] = random ( 0, 200 );
tomb_1[second][2] = random ( 0, 200 );
tomb_2[second][0] = random ( 0, 200 );
tomb_2[second][1] = random ( 0, 200 );
tomb_2[second][2] = random ( 0, 200 );
pixels_1.setPixelColor(second-1, pixels_1.Color( random ( 0, 200 ), random ( 0, 200 ),random ( 0, 200 )));
pixels_1.show();
pixels_2.setPixelColor(second-1, pixels_2.Color( random ( 0, 200 ), random ( 0, 200 ),random ( 0, 200 )));
pixels_2.show();
}
else
{
tomb_1[second][0] = MasPercMutato_R;
tomb_1[second][1] = MasPercMutato_G;
tomb_1[second][2] = MasPercMutato_B;
tomb_2[second][0] = MasPercMutato_R;
tomb_2[second][1] = MasPercMutato_G;
tomb_2[second][2] = MasPercMutato_B;
}
if ( second != second_buffer ) // Végül pedig kigyújtjuk a fentiekben beállított LED -eket, de csak akkor, ha lépett a másodperc
{
for(int i=1; i<=60; i++)
{
pixels_1.setPixelColor(i-1, pixels_1.Color(tomb_1[i][0], tomb_1[i][1], tomb_1[i][2])); // Azért kell -1, mert a LED -ek megjelenítési sorszáma 0 -val indul,
pixels_1.show(); // a tömbjeink pedig 1 -el.
pixels_2.setPixelColor(i-1, pixels_2.Color(tomb_2[i][0], tomb_2[i][1], tomb_2[i][2]));
pixels_2.show();
}
}
second_buffer = second;
}
/* ======================================================================================================================================================================= */
/* ====================================================== FÜGGVÉNYEK: ==================================================================================================== */
void setDS3231time(byte second, byte minute, byte hour, byte dayOfWeek, byte
dayOfMonth, byte month, byte year)
{
// sets time and date data to DS3231
Wire.beginTransmission(DS3231_I2C_ADDRESS);
Wire.write(0); // set next input to start at the seconds register
Wire.write(decToBcd(second)); // set seconds
Wire.write(decToBcd(minute)); // set minutes
Wire.write(decToBcd(hour)); // set hours
Wire.write(decToBcd(dayOfWeek)); // set day of week (1=Sunday, 7=Saturday)
Wire.write(decToBcd(dayOfMonth)); // set date (1 to 31)
Wire.write(decToBcd(month)); // set month
Wire.write(decToBcd(year)); // set year (0 to 99)
Wire.endTransmission();
}
/* -------------------------------------------------------------------------------- */
void readDS3231time(byte *second,
byte *minute,
byte *hour,
byte *dayOfWeek,
byte *dayOfMonth,
byte *month,
byte *year)
{
Wire.beginTransmission(DS3231_I2C_ADDRESS);
Wire.write(0); // set DS3231 register pointer to 00h
Wire.endTransmission();
Wire.requestFrom(DS3231_I2C_ADDRESS, 7);
// request seven bytes of data from DS3231 starting from register 00h
*second = bcdToDec(Wire.read() & 0x7f);
*minute = bcdToDec(Wire.read());
*hour = bcdToDec(Wire.read() & 0x3f);
*dayOfWeek = bcdToDec(Wire.read());
*dayOfMonth = bcdToDec(Wire.read());
*month = bcdToDec(Wire.read());
*year = bcdToDec(Wire.read());
}
/* -------------------------------------------------------------------------------- */
void displayTime() // Csak tesztelésnél használjuk
{
byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
// retrieve data from DS3231
readDS3231time(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);
// send it to the serial monitor
Serial.print(hour, DEC);
// convert the byte variable to a decimal number when displayed
Serial.print(":");
if (minute<10)
{
Serial.print("0");
}
Serial.print(minute, DEC);
Serial.print(":");
if (second<10)
{
Serial.print("0");
}
Serial.print(second, DEC);
Serial.print(" ");
Serial.print(dayOfMonth, DEC);
Serial.print("/");
Serial.print(month, DEC);
Serial.print("/");
Serial.print(year, DEC);
Serial.print(" Day of week: ");
switch(dayOfWeek){
case 1:
Serial.println("Sunday");
break;
case 2:
Serial.println("Monday");
break;
case 3:
Serial.println("Tuesday");
break;
case 4:
Serial.println("Wednesday");
break;
case 5:
Serial.println("Thursday");
break;
case 6:
Serial.println("Friday");
break;
case 7:
Serial.println("Saturday");
break;
}
}
/* -------------------------------------------------------------------------------- */
// Convert normal decimal numbers to binary coded decimal
byte decToBcd(byte val)
{
return( (val/10*16) + (val%10) );
}
/* -------------------------------------------------------------------------------- */
// Convert binary coded decimal to normal decimal numbers
byte bcdToDec(byte val)
{
return( (val/16*10) + (val%16) );
}
/* -------------------------------------------------------------------------------- */
void bt_kezeles()
{
/* Ezt a függvényt kell gyúrnunk hogy az Android -os eszközzel megfelelő legyen a kommunikáció
*
* Először is meg kell állapítanunk, hogy az Android -os eszköz mit akar.
* - KÜLDHETI A PONTOS IDŐT ( a = 97 )
* - LEKÉRDEZHETI A MI IDŐNKET ( b = 98 )
*
* Ezeket úgy jelzi, hogy elküldi a kívánt dolog kódját
*/
MitAkar = 0; // Tiszta lappal indulunk
if ( BlueTooth.available() ) // Ha van mit olvasni
{
MitAkar = BlueTooth.read(); // Beolvasunk egy karaktert
Serial.print ( " - Eszköztől kapott byte : " );
Serial.print(MitAkar);
switch(MitAkar)
{
case 97: Serial.println ( " ( küldené a pontos időt )" );
break;
case 98: Serial.println ( " ( kéri a mi időnket )" );
break;
default:
Serial.println(" ( ? )");
}
}
// Elvileg a MitAkar csak a fenti négy érték valamelyikét veheti fel mert azt kaphatjuk csak az Android eszköztől.
// Ezeket az eseteket a következő négy if feltétellel kezeljük le:
if ( MitAkar == 97 ) // 'a', AZ ANDROID -OS ESZKÖZ KÜLDI A PONTOS IDŐT
{
/* A BT modul felől soros porton érkezik a pontos idő a következő formátumban:
1. byte: " + " a byte sorozat elejét jelző karakter
2. byte: ÉV első byte
3. byte: ÉV második byte. Hogy megkapjuk a helyes évet ezt 256 -tal kell szorozni, majd hozzáadni a 1. byte -ban kapott értéket
4. byte: HÓNAP
5. byte: NAP
6. byte: HÉT NAPJA
7. byte: ÓRA
8. byte: PERC
9. byte: MÁSODPERC
10. byte: " - " a byte sorozat végét jelző karakter
*/
ByteSorszam = 1; // Megadja, hogy hányadik byte beolvasásánál tartunk a BT modul felől
if ( BlueTooth.available() ) // Ha van mit olvasni
{
byteRead = BlueTooth.read(); // Beolvasunk egy karaktert
// Serial.write(byteRead);
if ( byteRead == 43 ) ByteSorszam = 2 ; // Továbblépünk a ciklusból ha '+' jött
}
if ( ByteSorszam == 2 ) // Ha az előbb beolvastuk az induló "+" karaktert, akkor beolvassuk a többi byte -ot is
{
while ( ByteSorszam == 2 ) // Ebben a ciklusban beolvassuk az ÉV érték első byte -ját
{
if ( BlueTooth.available() ) // Ha van mit olvasni
{
byteRead = BlueTooth.read(); // Beolvasunk egy karaktert
// Serial.write(byteRead); // Vissza echo -zzuk
EV1 = byteRead;
ByteSorszam = 3 ; // Továbblépünk a ciklusból
}
}
while ( ByteSorszam == 3 ) // Ebben a ciklusban beolvassuk az ÉV érték második byte -ját
{ // A helyes évet úgy kapjuk, ha ezt 256 -tal kell szorozzuk, majd hozzáadjuk az EV1 -ben kapott értéket
if ( BlueTooth.available() ) // Ha van mit olvasni
{
byteRead = BlueTooth.read(); // Beolvasunk egy karaktert
// Serial.write(byteRead); // Vissza echo -zzuk
EV2 = byteRead;
ByteSorszam = 4 ; // Továbblépünk a ciklusból
}
}
while ( ByteSorszam == 4 ) // Ebben a ciklusban beolvassuk az HONAP értékét
{
if ( BlueTooth.available() ) // Ha van mit olvasni
{
byteRead = BlueTooth.read(); // Beolvasunk egy karaktert
// Serial.write(byteRead); // Vissza echo -zzuk
HON = byteRead;
ByteSorszam = 5 ; // Továbblépünk a ciklusból
}
}
while ( ByteSorszam == 5 ) // Ebben a ciklusban beolvassuk a NAP értékét
{
if ( BlueTooth.available() ) // Ha van mit olvasni
{
byteRead = BlueTooth.read(); // Beolvasunk egy karaktert
// Serial.write(byteRead); // Vissza echo -zzuk
NAP = byteRead;
ByteSorszam = 6 ; // Továbblépünk a ciklusból
}
}
while ( ByteSorszam == 6 ) // Ebben a ciklusban beolvassuk a "HÉT NAPJA" értékét
{
if ( BlueTooth.available() ) // Ha van mit olvasni
{
byteRead = BlueTooth.read(); // Beolvasunk egy karaktert
// Serial.write(byteRead); // Vissza echo -zzuk
NA_ = byteRead;
ByteSorszam = 7 ; // Továbblépünk a ciklusból
}
}
while ( ByteSorszam == 7 ) // Ebben a ciklusban beolvassuk az ÓRA értékét
{
if ( BlueTooth.available() ) // Ha van mit olvasni
{
byteRead = BlueTooth.read(); // Beolvasunk egy karaktert
// Serial.write(byteRead); // Vissza echo -zzuk
ORA = byteRead;
ByteSorszam = 8 ; // Továbblépünk a ciklusból
}
}
while ( ByteSorszam == 8 ) // Ebben a ciklusban beolvassuk az PERC értékét
{
if ( BlueTooth.available() ) // Ha van mit olvasni
{
byteRead = BlueTooth.read(); // Beolvasunk egy karaktert
// Serial.write(byteRead); // Vissza echo -zzuk
PER = byteRead;
ByteSorszam = 9 ; // Továbblépünk a ciklusból
}
}
while ( ByteSorszam == 9 ) // Ebben a ciklusban beolvassuk a MÁSODPERC értékét
{
if ( BlueTooth.available() ) // Ha van mit olvasni
{
byteRead = BlueTooth.read(); // Beolvasunk egy karaktert
// Serial.write(byteRead); // Vissza echo -zzuk
MAS = byteRead;
ByteSorszam = 10; // Továbblépünk a ciklusból
}
}
while ( ByteSorszam == 10 ) // Ebben a ciklusban beolvassuk a záró " - " karaktert ( de nem csinálunk vele semmit )
{
if ( BlueTooth.available() ) // Ha van mit olvasni
{
byteRead = BlueTooth.read(); // Beolvasunk egy karaktert
// Serial.write(byteRead); // Vissza echo -zzuk
ByteSorszam = 1 ; // Továbblépünk a ciklusból
setDS3231time(MAS, PER, ORA, NA_, NAP, HON, EV2*256+EV1-2000) ; // Végül a beolvasott időt elküldjük az RTC -nek.
} // Formátum: void setDS3231time(second, minute, hour, dayOfWeek, dayOfMonth, month, year)
} // A DS3231 az évnek csak a második 2 számjegyét várja, ezért kell kivonni a kiszámolt év értékéből ( EV2*256+EV1 ) 2000 -et.
Serial.print ( " - Eszköztől kapott idő: " ); // Ezek csak akkor íródnak ki ha az Android eszköz küldi nekünk a
Serial.print ( " - EV_1=" ); Serial.print ( EV1 ); // pontos időt, ritkán történik ezért benne hagytam a kiírásokat ... teszteléshez jól jöhet
Serial.print ( " - EV_2=" ); Serial.print ( EV2 );
Serial.print ( " - HON=" ); Serial.print ( HON );
Serial.print ( " - NAP=" ); Serial.print ( NAP );
Serial.print ( " - NA_=" ); Serial.print ( NA_ );
Serial.print ( " - ORA=" ); Serial.print ( ORA );
Serial.print ( " - PER=" ); Serial.print ( PER );
Serial.print ( " - MAS=" ); Serial.println ( MAS );
// Serial.print ( " ----------- EV_szamolt=" ); Serial.println ( EV2*256+EV1 );
}
} // Eddig tart az első funkció, azaz amikor az Android -os eszköz leküldi nekünk a pontos időt
if ( MitAkar == 98 ) // 'b', AZ ANDROID -OS ESZKÖZ LEKÉRDEZI A PONTOS IDŐT
{
byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
readDS3231time(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);
if ( hour > 12 ) hour = hour-12; // Az óra kijelzése 0-12 -es, így célszerű ha az androidos eszközön sem 0-24 -ben van mutatva
BlueTooth.write("20");
BlueTooth.print(year, DEC);
BlueTooth.write(".");
BlueTooth.print(month, DEC);
BlueTooth.write(".");
BlueTooth.print(dayOfMonth, DEC);
BlueTooth.write(" ");
BlueTooth.print(hour, DEC);
BlueTooth.write(":");
BlueTooth.print(minute, DEC);
BlueTooth.write(":");
BlueTooth.print(second, DEC); // Eddig tartott a dátum és idő elküldése
// A következő kiírást is bennehagytam, teszteléshez jó jöhet:
Serial.print(" - Eszköznek elküldve : ");
Serial.print(hour, DEC);
// convert the byte variable to a decimal number when displayed
Serial.print(":");
if (minute<10) Serial.print("0");
Serial.print(minute, DEC);
Serial.print(":");
if (second<10) Serial.print("0");
Serial.print(second, DEC);
Serial.print(" ");
Serial.print(dayOfMonth, DEC);
Serial.print("/");
Serial.print(month, DEC);
Serial.print("/");
Serial.print(year, DEC);
Serial.println(" Day of week: none "); // Ezt nem íratjuk ki mert külön számoló rész tartozna hozzá
} // Eddig tart a második funkció, azaz amikor az Android -os eszköz lekérdezi tőlünk a pontos időt és a memória adatokat
// Csak a fenti két eset lehetséges, ha a négy kódon kívül valami más érkezik akkor nem reagálunk
}
/* -------------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------------- */
void gomb_kezeles()
{
int A_gomb = digitalRead( 5 ); // Ennek itt kell lennie, mert a függvény elején kell mindig beolvasnunk
int B_gomb = digitalRead( 6 ); // Ennek itt kell lennie, mert a függvény elején kell mindig beolvasnunk
int C_gomb = digitalRead( 7 ); // Ennek itt kell lennie, mert a függvény elején kell mindig beolvasnunk
if (A_gomb != A_lastButtonState) // Ha változott a gomb állapota ciklus legutóbbi futása óta, (újra)indítjuk a számlálót
{
A_lastDebounceTime = millis();
}
if ((millis() - A_lastDebounceTime) > A_debounceDelay) // Ha a számláló elérte a beállított küszöböt
{
if (A_gomb != A_buttonState) // Csak akkor foglalkozunk bármivel, ha a gomb állapota változott.
// Tulajdonképpen a "buttonState" is "LastbuttonState", mert
// ebben tároljuk a gomb legutóbbi állapotát
{
A_buttonState = A_gomb; // itt
if (A_buttonState == HIGH)
{
if ( A_gomb_fazis < 3 )
{
A_gomb_fazis++;
}
else
{
A_gomb_fazis = 0;
}
MODE = A_gomb_fazis;
}
}
}
A_lastButtonState = A_gomb;
if ( A_gomb_fazis != 0 ) // Ha az A gombbal ki lett választva valmelyik üzemmód, akkor figyeljük a B és C gombot
{
if (B_gomb != B_lastButtonState) // Először nézzük a B gombot
{
B_lastDebounceTime = millis();
}
if ((millis() - B_lastDebounceTime) > B_debounceDelay)
{
if (B_gomb != B_buttonState)
{
B_buttonState = B_gomb;
if (B_buttonState == HIGH)
{
B_gomb_fazis = 1;
}
else
{
B_gomb_fazis = 0;
}
}
}
B_lastButtonState = B_gomb;
if (C_gomb != C_lastButtonState) // Utána nézzük a C gombot
{
C_lastDebounceTime = millis();
}
if ((millis() - C_lastDebounceTime) > C_debounceDelay)
{
if (C_gomb != C_buttonState)
{
C_buttonState = C_gomb;
if (C_buttonState == HIGH)
{
C_gomb_fazis = 1;
}
else
{
C_gomb_fazis = 0;
}
}
}
C_lastButtonState = C_gomb;
} // Eddig tartott maga a gomb kezelés
// Itt kezdődik a beolvasott gomb állapotok alapján az RTC beállítás
if ( (B_gomb_fazis == 1) or (C_gomb_fazis==1) ) // Csak akkor csinálunk bármit is, ha fel vagy le kell állítanunk az órát/percet/másodpercet
{
readDS3231time(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year); // Kiolvassuk a pillanatnyi időt
if ( (A_gomb_fazis == 1) and (B_gomb_fazis==1) )
{
if ( hour < 23 ) hour++; else hour=0; // A nem valós értékeket ki kell kiszöbölni, mert az RTC megesz mindent, csak persze hülyén működik
B_gomb_fazis=0;
}
if ( (A_gomb_fazis == 1) and (C_gomb_fazis==1) )
{
if ( hour > 0 ) hour--; else hour=23;
C_gomb_fazis=0;
}
if ( (A_gomb_fazis == 2) and (B_gomb_fazis==1) )
{
if ( minute < 59 ) minute++; else minute=0;
B_gomb_fazis=0;
}
if ( (A_gomb_fazis == 2) and (C_gomb_fazis==1) )
{
if ( minute > 0 ) minute--; else minute=59;
C_gomb_fazis=0;
}
if ( (A_gomb_fazis == 3) and (B_gomb_fazis==1) )
{
if ( second < 59 ) second++; else second=0;
B_gomb_fazis=0;
}
if ( (A_gomb_fazis == 3) and (C_gomb_fazis==1) )
{
if ( second > 0 ) second--; else second=59;
C_gomb_fazis=0;
}
setDS3231time(second, minute, hour, dayOfWeek, dayOfMonth, month, year) ; // Visszaírjuk a megváltoztatott időt
}
}
A másik rész az Android -os alkalmazás, amit ezúttal is az App Inventor segítségével állítottam össze. Ezúttal nem kommentezem, de ha valami nem érthető ismétcsak javaslom átnézésre a Nyomógomb naplózó leírását. Vagy keress meg, szívesen elmagyarázom. A Designer -ben létrehozott fő képernyő így néz ki:
Működés közben az Android -os eszközön látható képernyőket mutatják az alábbi képek. A felső sorban van az Android -os eszköz saját ideje, alatta az óra ideje, tehát amit az óra bluetooth -on átküldött az Android -os eszköznek. Nyilván ha még nem jött létre a BT kapcsolat akkor itt csak kérdőjeleket láthatunk. Ha nincs bekapcsolva az Android -os eszközön a bluetooth akkor a felső gomb pirossal hívja fel erre a figyelmet. Az alkalmazásból nem lehet bekapcsolni ( még, de dolgozom a problémán :-). Ha engedélyezett a bluetooth ( felső gomb zöld ) akkor össze kell kapcsolódni az eszközzel. Ezt a második ( egyelőre piros ) gombra bökve lehet megtenni. A felkínált eszközök közül a HC-06 -ot kell választani. Ha minden jól megy ez is bezöldül, és fent a második sorban megjelenik az órától kapott idő ( ha nem látnánk magán az órán :-). Ha úgy ítéljük hogy ez az idő nem pontos, a harmadik gomb megnyomásával kezdeményezhetjük az Android -os eszköz ( nyilvánvalóan pontos ) idejének átküldését az órára. Az átküldés megtörténtét egy üzenet jelzi, ill. a felső második sorban ellenőrizhető.




Végezetül következzen maga a "program", vagyishát a program blokkok. Nem bonyolult ezért nem kommenteztem. A Nyomógomb naplózóéhoz hasonlóan itt is egy időzítő adja a lényegi részt ami 1 másodpercenként fut le. Az eszköz ennyi időközönként küld egy 98 ( azaz "b" ) karaktert az órának, ami abból tudja, hogy vissza kell küldenie az idejét. Ezáltal tud frissülni a második felső sorban látható óra idő ( a szemléletesség kedvéért 0-12 formában kiírva ). Ha a felhasználó az óra pontosítását szeretné, azazt megnyomja a 3. gombot, azt egy 97 kód ( azaz "a" karakter ) küldésével jelzi az Android eszköz. Ennek hatására az óra "vételi módba" áll, az Android küldi az időt, az óra pedig megkapva azt beírja az RTC -be és onnantól azt használja. Tehát a program:
Végezetül még egy kép, a - majdnem - kész óráról. Ha lesz végleges kép azt is felteszem, addig türelmet kérek :-)
Remélem hasznos és érdekes volt.