Hőmérséklet adatgyűjtő

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) );
}

Mennyire volt hasznos amit olvastál? Értékelés után szövegesen is leírhatod megjegyzéseidet és véleményedet!

Kattints egy csillagra az értékeléshez!

Szövegesen is leírhatod véleményedet! Ha kérdésed van, ne felejtsd el megadni az email címedet!