Tartalom:
- DS3231 RTC óra beállítása és idő lekérdezése
- Adatok írása SD kártyára
- SD kártya kivétele a foglalatból működés közben
Ebben a “tanuló programban” egy Arduino Uno R3, egy LCD kijelző, DS3231 RTL óra és egy SD kártya illesztő illetve egy SD kártya, volt a főszereplő.
A program az SD kártyára folyamatosan rögzíti a hőmérsékletet 30 percenként. Egy futási eredmény így néz ki. (PC-vel kiolvasva a kártyát):
Datum;Ido;Homerseklet;
2016.07.21;06:04;22.75;
2016.07.21;06:34;23.00;
2016.07.21;07:04;23.00;
2016.07.21;07:34;23.00;
2016.07.21;08:04;23.00;
2016.07.21;08:34;23.25;
2016.07.21;09:04;23.25;
A file dátuma alapértelmezetten 2000.01.01 0:00, a rendszer nem kezeli. Az állomány kiterjesztése CSV, így közvetlenül Excel-el megnyitható és feldolgozható.
Sokat küzdöttem azzal, hogy működés közben ki lehessen venni a kártyát, és az adatok rögzítése folytatódjon, ha visszadugom a helyére. Nem gondoltam ugyanis arra, hogy ez nem probléma. Tudni kell, hogy az SD kártyák példa programjaiban mindenütt úgy használják az SD kártyát, hogy a tápfeszültséget a kártyának az Arduino egyik kivezetés adja. Kimenetnek programozzák, HIGH szintet állítanak be, és az SD kártya innen kapja a tápfeszt. Olyan kicsi az SD kártya áramfelvétele, hogy ez így elméletben jó. Nekem azonban ez a megoldás nem működött. Rengeteget vacakoltam vele, míg rájöttem, hogy direktben a tápra kell kötni az SD modult, különben nem lehet a kártyát használni. Az okot azóta sem sikerült kiderítenem. Több SD modult is kipróbáltam, sehogyan sem működött hibátlanul. A kártya adatait le lehetett kérdezni, de íráskor már hibát dobott a függvény.
Úgy gondoltam, ha a tápfeszt nem lehet lekapcsolni a kártyáról, akkor biztosan nem lehet kivenni. Az első elképzelésem az volt, hogy a kártyát minden egyes írás előtt megnyitom, és ezzel biztosítom, hogy a visszahelyezett kártyát is kezelje program, ha esetleg kivettem, és közben nem volt a helyén, amikor a program írni akart rá. Azonban ez a módszer egyáltalán nem működött, amit nem igazán értettem. Arra jutottam, hogy a kártya kezelő programja hibás ebben a tekintetben. A kártyát egyszer lehet megnyitni egy programon belül és kész. Valamelyik idegen nyelvű fórumon olvastam, hogy ebbe a problémába mások is beleütköztek. Tovább kísérleteztem, és próbálkoztam azzal, hogy csak az SD.begin() függvényt hívtam meg újra és újra, de ez sem működött. Megdöbbentő módon egyedül az a megoldás működött a gyakorlatban, hogy a setup részben SD.begin()-el megnyitom a kártyát és levizsgálom létezik-e a file. Ha nem akkor létrehozom. A loop-ban már csak minden különösebb előkészítés nélkül írok a kártyára amikor kell. Esetemben fél óránként került erre sor. Azonban kísérletezgetés közben a gyakoriságot 5 másodperce állítottam, és kb. 20-30 alkalommal hosszabb rövidebb időre kivettem a kártyát. És láss csodát, minden hiba nélkül működött a kártyára a file írása. Nyilván előfordult, hogy egy vagy több file írást nem tudott a program végrehajtani, hiszen nem volt a kártya a helyén. Azt, hogy belül mi történt, nem tudom, de hiba nélkül folytatta a file írását a program. Szóval szerintem ez működik, lehet használni. Ebből az a tanulság, ha nincs probléma, akkor nem kell megoldani.
Annak aki SD kártya használatra adja a fejét, gondolnia kell a memória igényre. Az SD kártya kezelésére található könyvtárak mindegyike memóriazabáló. Legalább 10kbyte-ot elvesz a rendelkezésre álló flash memóriából. Ez ebben a programban nem okozott problémát, de sikerült olyan adatgyűjtő programot írnom, ahol sajna nem fértem bele a 32Kbyte-ba. Erre megoldásként javasolni tudom a külön datalogger modult, ami gyakorlatilag ugyanannyiba kerül, mint egy SD kártya modul. Ez azonban tartalmaz egy ATmega mikrovezérlőt, és önmagában tartalmazza a leírása szerint a szükséges programokat, és használatához már csak I2C kapcsolat szükséges. Már megérkezett aliexpressz-ről, de nem volt még időm kipróbálni. Így néz ki:
forrás: https://www.aliexpress.com/item/32959138610.html?spm=a2g0s.9042311.0.0.27424c4d81zskG
A program többi része a DS3231 RTC óramodul leírásában található programmal azonos . Nem is másoltam a forrásba, ha valaki ki akarja próbálni a programot, akkor onnan ide kell másolgatni a hiányzó függvényeket. A fordító jelezni fogja, hogy mi hiányzik. A forrás sorainak számát nagyon megnöveli, hogy az RTC óra lekérdezését egy függvénykönyvtár alapján megírtam magamnak. Ha ez valakit zavar, a számtalan előre megírt függvénykönyvtár valamelyikét érdemes használni, és sokkal rövidebb lesz a kód. Sőt, ha az RTC óra modul leírásánál használt könyvtárat telepíti, akkor valószínűleg változtatás nélkül futni fog az itt található kód, csak a program elején “#include”-olni be kell a függvénykönyvtárat.
A kód:
#define CLOCK_ADDRESS 0x68 //a modulon található óra IC címe az I2C buszon. A cím vezetékek átforrasztásával //változthat #include <Wire.h> #include <LiquidCrystal_I2C.h> //I2C LCD kezelő könyvtár LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); //LCD paraméterek megadása #include <SD.h> #define SD_POWER 8 File temp_adat; int masodperc,perc,ora,nap,honap,ev,het_napja; bool h12=true; //ha true, akkor 12 órás üzemmód, false esetén 24 órás üztemmód bool PM; //12 órás üzemmód esetén a délelőtt délután jelzése fals=AM, true=PM float temperature; long lepes=millis(); long rogz_ido=millis(); long light_ido=millis(); int elozoperc=100; String datum; String ido; String filenev=String("TEMP_1.CSV"); byte letrehozas=0; String error=String(""); void setup() { pinMode(2, INPUT); Wire.begin(); // I2C busz használat indítása /* setClockMode(false); //true értékkel 12 órás, false értékkel 24 órás üzemmód, ha átállítod az üzemmódot //ujra be kell állítani az órát ( setTime() ), mert fals eredményt ad vissza. masodperc=0;perc=34;ora=19;het_napja=7;nap=18;honap=7;ev=16; //óra beállításhoz az értékek setTime(); //óra beállítása */ getTime(); temperature=getTemperature(); date_format(); time_format(); error="SD OK!"; if (SD.begin(10)) { if (!SD.exists(filenev)) { temp_adat = SD.open(filenev, FILE_WRITE); temp_adat.println("Datum;Ido;Homerseklet;"); error="File create OK!"; } } else {error="No SD";} lcd.begin(16,2); //LCD inicializálása lcd.backlight(); //háttérvilágítás bekapcsolása lcd.print(error); delay(3000); lcd.noBacklight(); } void loop() { if (digitalRead(2)==LOW) {lcd.backlight();light_ido=millis();} //30 percenként sd kártyára írunk if (millis()>rogz_ido+1800000){ getTime(); temperature=getTemperature(); date_format(); time_format(); temp_adat.print(datum+";"+ido+";"); temp_adat.print(temperature); temp_adat.println(";"); temp_adat.flush(); lcd.backlight(); lcd.setCursor(14,1); lcd.print("+"); rogz_ido=millis(); light_ido=millis(); } //5 másodperc után kikapcsoljuk az LCD háttétvilágítását if (millis()>light_ido+5000){ lcd.setCursor(14,1); lcd.print(" "); lcd.noBacklight(); } //másodpercenként frissítjük az LCD-n az időt if (millis()>lepes+1000) { getTime(); //kiolvassuk az időt a DS3132-ből a "masodperc, perc, ora, het_napja, nap, honap, ev" változókba if (elozoperc!=perc) { date_format(); time_format(); lcd.setCursor(0,0); lcd.print(datum); lcd.print(" "); lcd.print(ido); temperature=getTemperature(); lcd.setCursor(0,1); lcd.print(temperature); lcd.print(" C"); elozoperc=perc; } lepes=millis(); } } void date_format() { datum=""; datum="20"+String(ev)+"."; if (honap<10) { datum=datum+"0"; } datum=datum+String(honap)+"."; if (nap<10) { datum=datum+"0"; } datum=datum+String(nap); } void time_format() { ido=""; if (ora<10) { ido="0"; } ido=ido+String(ora)+":"; if (perc<10) { ido=ido+"0"; } ido=ido+String(perc); } byte bcd_Dec(byte val) { // Ckonvertál bcd számból decimalisba return ( (val/16*10) + (val%16) ); } byte dec_bcd(byte val) { // Convertál decimalis számból bcd-be return ( (val/10*16) + (val%10) ); }