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.