Szélsebességmérő

Kezdetben nem szándékoztam külön “dedikált” szélsebességmérőt készíteni, csak a szélcsend, szellőcske és erős szél fokozatok megkülönböztetésére lett volna szükségem. Vásároltam egy szélkereket, és kísérletezgetni kezdtem vele, és közben történt egy szerencsétlen esemény: kiderült, hogy szélsebességmérő reed reléje, vagy annak elhelyezése hibás. Eleinte négy kontaktus jelet adott egy fordulatra. Aztán az egyik alkalommal már csak kettőt. Elkezdtem alaposabban megnézni, és kiderült, hogy időnként hármat. Ha megütögettem, akkor ismét csak kettőt. Végül arra következtettem, hogy eredetileg kettő volt a szándék, csak a mágnest túl távolra építették be, és időnként a “térerő” akkor is elereszti a reed kontaktusokat, amikor nem kellene. Arra az elhatározásra jutottam, tekintve hogy a reed relé élettartama is korlátozott, hogy hall szenzorra cserélem. Meg is rendeltem, de még nem érkezett meg. Míg várakozok, volt időm elgondolkodni, hogy mit is várok a szélsebesség mérő berendezéstől. Rájöttem, hogy nem is egyszerű dolog megfogalmazni az elvárásaimat, és arra jutottam, hogy kicsit jobban meg kell ismernek ehhez a környezetem széljárását. Mégis csak érdemes szélsebesség mérőt építeni és megfigyelni az eseményeket. Persze nem állhatok mellette órákon át, így valamilyen értelmes adatot kellene előállítani, ami jellemzi a szél tulajdonságait, amit később felhasználhatok egy másik berendezés építélsekor (automata locsolórendszer). Hosszas gondolkodás után a következő adatokat, információkat tartottam hasznosnak:

  1. Pillanatnyi szélsebesség. Ezt nem is olyan egyszerű mérni ezzel a szélkerékkel, mert egy kisebb szellőcske éppenhogy csak forgatja a kereket, ami egy fordulat alatt két kontaktus jelet ad. Rögvest felmerül a kérdés, hogy mit tekintek szélcsendnek. Önkényesen úgy döntöttem, 5 másodperc lesz ez a határ. Ha 5 másodperc alatt egyetlen jelet sem kapok, akkor az szélcsend. Közben persze lehet, hogy a szélkerék megmozdul, de kevesebb mint fél fordulatot tesz, vagy hosszabb mint 5 másodperc alatt ad kontaktusjelet, de ezt már nulla sebességnek tekintem. Mindazonáltal a pillanatnyi szélsebességnek nincs túl nagy jelentősége. Ha nagy orkán fúj, akkor esetleg érdekes lehet tudni, mekkora volt a szél egy adott pillanatban. Azonban teljes szélcsendben egy madár szárnycsapása is szélvihart okozgat, így ez tényleg csak “érdekes” adat, de nem sok mindenre jó.
  2. Széllökés. Szerintem a pillanatnyi szélsebesség nem hasznos, és a szél természetéről többet árul el a tartós légmozgás. Nehezen döntöttem el, hogy mennyi is a “tartós”! Végül úgy gondoltam 10 másodperces légmozgás már tényleg szél, így ekkora időegységben a levegő mozgását már szélnek tekintem. Azonban ez az időegység még mindig nem sokat árul el, így ezt széllökésnek neveztem el. Ennek már a maximumát is érdemes megjegyezni.
  3. Átlagos szélsebesség. Nagy kérdés, hogyan is átlagoljak. Ha egy teljes napot vennék alapul, az nem biztos, hogy értelmezhető információt ad. Pl. ha egy órát fúj a szél 10 km/h-val, aztán a fennmaradó 23 órában beáll a teljes szélcsend, akkor az átlag kb. 0,4 km/h, ami elhanyagolható, mintha nem is fújt volna a szél. Ezért arra gondoltam többet mond, ha csak arra az időre átlagolok, amikor fújt a szél.
  4. Szélfújás időtartama egy nap alatt. Ha az előző pont szerint az átlag szélsebességet a szeles időszak idejére akarom képezni, akkor mérnem kell azt az időtartamot, amikor fújt a szél. Értelmes adat természetesen a szélcsend időtartama is.

Sajnos az átlagsebességek, maximum értékek meghatározásakor választanom kell egy törlési időpontot, amikor újra indítom az értékek meghatározását. Ez logikusan minden nap éjfél lehetne. Ekkor azonban időmérés is szükséges. Nagy dilemma, hogy megéri-e ezért egy RTC óra modult beépíteni. Úgy döntöttem, hogy nem (épp nincs felesleges óra modulom, így ez könnyű döntés volt). Azonban az Arduino tartalmaz egy viszonylag pontos órajelet, tehát az éjfél meghatározása nem lehetetlen. Olyan megoldást választottam, ami szerint percenként növelek egy változót, és he ez elérte az 1440-et (24 óra telt el), akkor a változó értékét nullázom az összes tárolt szélsebesség adattal együtt. Azonban biztosítani kell valahogyan, hogy ez a nullázás pont éjfélkor legyen. Ehhez csináltam a programban egy óra beállító programrészt. Az éjfél óta eltelt időt ebből a változóból könnyű kiszámolni és folyamatosan kiírni a képernyőre. Már csak azt kellett megoldani, hogy az éjfél óta eltelt percek számát valahogyan be lehessen állítani. Szerintem elég kényelmes megoldást sikerült megvalósítani, egyetlen nyomógombbot használok ehhez. Azt a nyomógombot, amivel egyébként a mért adatok kijelzését is lehet változtatni az igen kicsi méretű 2×16 kerekteres LCD kijelzőn. Nagyobb kijelzőt egyenlőre nem akartam pazarolni erre a feladatra.

Végül a program a következőket valósítja meg: méri a szélsebességet egy fordulat ideje alapján, méri a fenti definíció szerinti széllökést, megjegyzi ennek maximumát, amit éjfélkor nulláz. A körbe forgások kontaktusjeleiből meghatározza a szeles időtartamot, amit szintén éjfélkor nulláz. A szeles időtartamból és a kontaktusszámból adódik egy osztással az átlagsebesség, ami nyilvánvalóan azzal az időtartammal együtt értelmezhető, mikor fújt a szél. Pl. ha az átlag szélsebesség 10 km/h, és aznap 24 órán át fújta szél, akkor ez egy szeles nap volt. Ha az átlag sebesség 10 km/h de aznap csak 10 percet volt szélfújás, akkor ez a nap valójában szélcsendes volt. Tehát az adatokat értelmezni kell. Ez a szoftver láthatóan mellőzi a mesterséges intelligenciát. Ezért is teszem közzé ingyen, értéke a szélcsendes nap átlagsebességéhez konvergál. Tehát nulla!

Az alábbi példa program még nem működik megépített végleges megoldásként. Ez egy “deszka” modell. Ténylegesen működik, hajszárító szélsebességét méri, de még nem szolgáltat adatokat valós szélsebességekről. ehhez meg kell várni a hall szenzorokat, azok beépítését, és azt, hogy legyőzzem tériszonyomat és felmásszak a tetőre.

Azonban a működőképesség tesztelt, talán lényegi hibák sincsenek benne. A működési elve kicsit hasonlít a prellmentesítésnél ismertetett harmadik mintapéldában olvasható megoldással. A millis() függvényt használom, ami a mikrovezérlő elindítása óta eltelt milliszekundumokat adja vissza. Azt hiszem ez egy olyan memória változó, amit egy ezred másodpercenként keletkező belső megszakítás növel. Ez “atombiztosan” minden ezred másodpercben megtörténik, így lehet rá alapozni. Azt nem tudom, hogy ha megszakításokat használnék, akkor kihagyná-e a számlálást a vezérlő, de nem használok megszakítást. Mivel a a változó által visszaadott értéket figyelem, nem kritikus az egyes programrészek végrehajtása, sehol nem használok delay() függvényt. Illetve egy helyen, az órabeállításnál használok, de ott semmi más nem működik a sebesség mérés sem. Nem kell passzióból naponta többször is időpontot állítgatni, és minden rendben lesz. Ezzel a technikával minden párhuzamosan zajlik, figyelme a nyomógombot (prellmentesítem), figyelem és feldolgozom a szélkerék kontaktus adatait, mérem az eltelt időt, még az LCD háttérvilágítását is időzítem, ne világítsomn egész nap. Mindez egyszerre, és semmi nem sérül.

Megmértem az óra pontosságát is. Meglepően pontos volt. A mobiltelefonomhoz állítottam az időpontot, és megnéztem egy nap múlva. Szemmel nem tért el egyetlen másodpercet sem. Nem kritikus az, hogy pont éjfélkor nullázza a program a gyűjtőket, így elég lesz havonta beállítani az időt. Ez szinte tökéletes jelen esetben. A program egy LCD kijelzőn kijelzi a mért és számított adatokat. Egyetlen nyomógombbal lehet váltani, hogy a kijelzőn mit jelezzen ki. Néhány fotó a kijelzett állapotokról:

Bocs a minőségét, az LCD az Arduino UNO-ről kapta a tápot (az meg a PC-ről), és ilyenkor a kontraszt nem tökéletes. Ráadásul a fejlesztő panelemen csak 4 soros LCD van, de úgy írtam meg a programot, hogy 2×16-os kijelzőn működjön. A 2×16-os kijelző esetén a kijelző címét át kell állítani a harmadik rogramsorban!!

Az LCD háttérvilágítása a gombnyomásra bekapcsol, és egy percig világít az utolsó nyomógomb lenyomás után. A kijelző tartalmat csak akkor lépteti a nyomógomb, ha világít a kijelző. Amikor a program az időt jelzi ki, a nyomógomb folyamatos nyomva tartásával lépteti az órák illetve percek értékét, és azt állítja be órának illetve percnek, amit a a nyomógomb elengedésekor látunk a kijelzőn. Az óra beállítást értelem szerűen két lépésben lehet elvégezni, először az órákat (0-24-ig) illetve egy gombnyomással átváltunk a percek beállító képernyőre, és ekkor állíthatjuk be a perceket (0-59-ig).

És íme a forráskód:

#include <Wire.h>    //I2C library 
#include <LiquidCrystal_I2C.h>  //I2C LCD kezelő könyvtár
LiquidCrystal_I2C lcd(0x3F, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);  //LCD paraméterek megadása, a 4 soros LCD-m címe 3F

bool elozo;                        //segéd változó előző szélkerék kontaktus állapot tárolására
bool most;                         //segéd változó a pillanatnyi szélkerék kontaktus állapot tárolására        
long ido_tmp=millis();             //segéd változó egy fordulat idejének a méréséhez
long nemforog_ido=millis();        //segéd változó amivel mérjük az időt akkor, ha nincskontaktus, mert nem fúj a szél
long szelkerek_ido=millis();       //segéd változó, amivel a szélkerék pillanatnyi sebesség frissítés 1 másodperces idejét mérem
long ido_tmp_10=millis();          //segéd változó, amivel a 10 másodperces széllökés méréshez használok
long kijelzo_valtas_ido=millis();  //segéd változó, amit a 1 másodperces kijelző tartalom frissítéshez használok
bool frissit=true;                 //true értéke esetén azonnal befrissíti a kijelző érékét, ha false akkor csak másodpercenként
long percidozito=millis();         //segéd változó az egy perces időzítéshez, percenként növeljük az ejfel változó értékét
byte adat_index=0;                 //az éppen aktuálisan kijelzett adat indexe. Minden ciklusban 1-el növelem, 7 után nullázom
byte ora;                          //segéd változó a szeles és szélmentes órák kiírásához, és az idő beállításhoz
byte perc;                         //segéd változó a szeles és szélmentes percek kiírásához, és az idő beállításhoz
long nyomva_ido=millis();          //setup gomb nyomvatartásának figyeléséhez a prellmentesítés érdekében 50msec időzítéshez
long elenged_ido=millis();         //a nyomógomb elengedési idejének figyeléséhez a prellmentesítés érdekében 50msec időzítéshez
bool nyomva;                       //nyomógom nyomva jelzésére, prellmentesítéshez kell
bool elengedve;                    //nyomógomb elengedve jelzésére, prellmentesítéshez kell
bool setup_mod;                    //ezzel jelzem, ha megnyomta a nyomógombot, és elkezdődik a setup folyamat (kijelzett érték beállítás, idő beállítás)
long vilagitas_ido=millis;         //az lcd kijelző háttérvilágításának időzítésére használom. bármilyen gombnyomástól számított
                                   //60 másodperccel kikapcsol a kijelző háttérvilágítása 
bool lcd_vilagitas;                //ez jelzi, hogy az lcd kijelző háttérvilágítása be van kapcsolva, vagy ki van kapcsolva
byte imp=0;                        //ezt a második felfutóél jelzésére használom, hogy egy fordulat idejét tudjam megmérni
int imp_10sec=0;                   //a 10 sec alatt beérkezett impulzusok száma
long imp_atlag=0;                  //az egy nap alatt beérkezett impulzuok száma a napi átlagsebesség méréshez
int ford_ido=0;                    //egy fordulat idejét milisecundumban, pillnatnyi sebesség méréséhez
bool all=false;                    //ez a változó jelzi, ha áll a szélkerék és nem kell kiszámolni a pillanatnyi szélsebességet, mert éppen nulla
float pill_szelseb=0;              //két impulzus közt eltelt időből kiszámított pillanatnyi szélsebességet
float napi_atlag_seb=0;            //napi átlag sebesség, amit a napi impulzusszámból és a szeles idő másodpercekben mért értékéből számolok ki
float szellokes;                   //a széllökés 10 másodpercre számított értéke
float szellokes_max;               //a széllökés maximumát tárolja. Naponta egyszer kel nullázni, és így kezdődik minden nap éjfélkor a maximum meghatározása
                                   //Az éjfélkor történő nullázásról egy ejfel navű változó gondoskodik, ami 1440-nél nullázódik. 1440 perc van egy nap
                                   //ez így nem valódi pontos óra, a processzor kavarcz órajelén alapul, nem túl pontos, időnként be kell állítani
int szeles_ido=0;                  //A szeles idő másodpercben mért értéke a nap során
int szel_mentes_ido;               //a szélmentes idő másodpercben mért értéke a nap során
int ejfel=0;                       //az éjfél óta eltelt idő percekben


void setup() {
  Serial.begin(9600);
  Serial.println("Indul...");
  pinMode(2,INPUT);            //2-es kivezetésre kötöttem a kontaktus jelét             
  digitalWrite(2,HIGH);        //felhúzó ellenállás bekapcsolása    
  elozo=digitalRead(2);        //máris vezünk egy mintát a bemenetből
  pinMode(3,INPUT);            //3-es kivezetésre kötötjük a nyomógombot             
  digitalWrite(3,HIGH);        //felhúzó ellenállás bekapcsolása    
  lcd.begin(20,4  );           //LCD inicializálása
  lcd.clear();
  lcd.backlight();             //háttérvilágítás bekapcsolása
  lcd_vilagitas=HIGH;          //ezzel jelezzük, hogy világít a háttérvilágítás, csak akkor lépteti a program a kijelzett adatot
                               //a képernyőn, ha világít a háttérvilágítás (az első gombnyomás 60 másodpercre bekapcsolja)
}

void loop() {
  /***********************************************************************************************************
   * Ezek a programrészek a szélsebességmérő kontaktusait figyelik, és mérik a forgási időt, kontaktusszámot *
   * és az eredményeket elhelyezik a megfelelő változókban.                                                  *
   **********************************************************************************************************/
  most=digitalRead(2);             //a pillanatnyi kontaktus állapot beolvasása
  if(elozo==LOW and most==HIGH) {  //felfutó él
    nemforog_ido=millis();         //minden kontaktusnál elindítjuk a nemforgás időzítőt, 5 másodperc múlva tekintjük úgy, hogy áll a szélkerék
    all=false;                     //forog a szélkerék
    imp_10sec++;                   //10 másodperces impulzus számláló növelése
    imp_atlag++;                   //napi impulzusszámláló növelése
    if (imp==0) {imp++;}           //egy fordulat idejének meghatározásához két impulzust kell leszámolni, ez volt az első
    if (imp==1) {ford_ido=millis()-ido_tmp;ido_tmp=millis();imp=0;} //megvolt a második impulzus, az egy fordulat idejének kiszámolása
                                                                    // időpillanat változó törlése következő méréshez
  }
  elozo=most;
  
  if (millis()>nemforog_ido+5000) {                             //ha 5 másodperc alatt sem ad egyetlen impulzust sem, akkor úgy tekintjük nulla a szélsebesség
    imp=0;
    pill_szelseb=0;
    all=true;
  }
  
  if (millis()>ido_tmp_10+10000) {                              // 10 másodpercenként nullázom az imp_10sec változót, kiszámolom a széllökést és tárolom a maximumát
    napi_atlag_seb=(((float)(imp_atlag)/2)*2.5)/szeles_ido;     //napi átlag a napi impulzusszámból és a szeles (amikor forog a kerék akkor növeljük másodpercenként) 
                                                                //másodpercek számából
    szellokes=((float)(imp_10sec)/20)*2.5;                      //10 másodperces széllökés értékének frissítése
    if (szellokes_max<szellokes) {szellokes_max=szellokes;}     //széllökés maximum frissítése
    imp_10sec=0;                                                //újra kezdjük a 10 másodperc alati impulzusok mérését
    ido_tmp_10=millis();                                        //elkezdődik a 10 másodperc mérése ezzel a kezdőértékkel
  }
  
  if (millis()>szelkerek_ido+1000) {                              //egy másodpercenként frissítem a pillanatnyi szélsebesség értékét,  
                                                                //ha éppen az időt állítjuk be, akkor nem  számoljuk az impulzusokat, mert ott delay() van!
    if (!all) {                                                 //nem áll a szélkerék, lehet sebességet számolni
      pill_szelseb=2.5*(1000/(float)ford_ido);                  //pillanatnyi szélsebesség kiszámítás egy körülfordulás idejéből
      szeles_ido++;                                             //növeljük a szeles idő idejét másodperc alapon mérő számlálót az átlag sebesség meghatározáshoz
    }
    else {szel_mentes_ido++;}                                   //növeljük a szélmentes idő értékét 1 másodperccel (áll a szélkerék)
    szelkerek_ido=millis();                                       //innen indul egy új egy másodperces időzítés
  }

   
  if (ejfel>=1440) {imp_atlag=0;szeles_ido=0;ejfel=0;}           //24 óra elteltével nullázzuk a napi impulzusgyüjtőt és a napi szeles idő változóját, és 
                                                                //újra indul az ejfel szamlaló, ami percenként növekszik. 24 óra 1440.

  if (millis()>percidozito+60000) {                             //egy percenként növeljük az ejfel változót
    ejfel++;                                                    
    percidozito=millis();
  }

  /*************************************************************************************************************************************
   * Az itt következő programrészek a nyomógombot figyelik. Megtörténik a prellmentesítés. Mivel a hosszú megnyomásnak                 *
   * szerepe van, a nyomógomb elengedés indítja az akciókat. Egy megnyomás bekapcsolja a háttérvilágítást (ez a nyomógomb lenyomással  *
   * törtéik nem az elengedéssel). Ha már világít a háttérvilágítás, akkor növeljük a kijelzett adat indexértékét. Ha hosszan          *
   * nyomvatartjuk az időkijelzés óra és perc felirattal jelzett állapotában, akkor az óra illetve a perc értékét növeli               *
   * ciklikusan, ha elérte a 23-at (óra) illetve az 59-et (perc), akkor 0-ról indul újra. Ha egy ilyen bállítás után                   *
   * elengedjük a gombot, akkor ezzel be is állítottuk az órát.                                                                        *
   *************************************************************************************************************************************/
  //megnyomta a nyomógombot, de még lehet, hogy prelles
  if (digitalRead(3)==LOW and nyomva==LOW) {    //megynomta a setup gombot
    nyomva_ido=millis();  //prellmentesítéshez ezzel indul az 50msec időzítése
    nyomva=HIGH;  //innen tudjuk, hogy már történt egy kontaktus, 50 msec múlva már biztosan prellmentes
    lcd.backlight();vilagitas_ido=millis();   //bekapcsoljuk a háttérvilágítást, és elindítjuk a kikapcsolási időzítést
                                          //lcd_vilagitas változót még nem billentjük be, az fogja jelezni később, hogy lehet léptetni 
                                          //a kijelzett adat indexét

    }    
  //ha 50msec mulva is nyomva tartja,akkor ez már nem prelles, biztosn nyomva van, lehet belépni a setup folyamatba
  if (digitalRead(3)==LOW and millis()>nyomva_ido+50 and nyomva==HIGH) {setup_mod=HIGH;}       //mar itt is lehetne növelni a kijelzett adat index értékét
                                                                                               //de az óra beállítás miatt az elengedéskor növelek (ha már 
                                                                                               //bilágít a háttérvilágitás, mert ha nem akkor csak azt bekapcsolom)
                                                                                               //ha 1 másodpercnél tovább nyomom, és nem az órabeállításban vagyok
                                                                                               //akkor nem fog történni semmi
  //elengedte a nyomógombot, de még lehet, hogy prelles
  if (digitalRead(3)==HIGH and elengedve==LOW and nyomva==HIGH) {elenged_ido=millis();elengedve=HIGH;}
  //már 50msec óta elenged, biztosan nem prelles, beállítási értéket váltunk, lehet várni az új megnyomásra
  if (digitalRead(3)==HIGH and elengedve==HIGH and millis()>elenged_ido+50)                    //amikor elengedjük a nyomógombot, akkor váltjuk a kijelzett értéket
                                                                                               //Ha azonban nem világított a háttérvilágítás, akkor ez a gombnyomás
                                                                                               //csak bekapcsolja azt, és nem léptet 
  {
    nyomva=LOW;
    elengedve=LOW;
    Serial.println(lcd_vilagitas);
    if (lcd_vilagitas==LOW) {lcd_vilagitas=HIGH;}  //ezzel elnyeljük az első nyomógomb lenyomást, ha nem világított a háttérvilágítás (csak bekapcsoljuk azt)
    else {                     //már világit a háttérvilágítás, lehet az adatindexet növelni
      adat_index++;
      if (adat_index>7) {adat_index=0;}            //a legutolsó adat után újra a legelső adatot fogjuk kiírni
      frissit=true;                                //azonnal frissíteni kell a képernyőt, mert gombnyomás történt
      vilagitas_ido=millis();                          //innen indul a 60 másodperc időzítés a háttérvilágítás kikapcsolására
    }
  } 

  if (millis()>vilagitas_ido+60000)   //ha 60 másodperce nem nyúlt a nyomógombhoz akkor a háttérvilágítást kikapcsoljuk 
  {
    lcd_vilagitas=LOW;
    lcd.noBacklight();
  }

  if (digitalRead(3)==LOW and setup_mod==HIGH and nyomva==HIGH and millis()>nyomva_ido+1000)   //egy másodpercig nyomvatartotta, változtatjuk az óra vagy a perc értékét
                                                                                               //elengedéskor az éjfél óta eltelt időt már be is állítottuk 
  {
    ora=ejfel/60;                               //ejfel változóból kiszáítjuk az óra értékét
    perc=ejfel-(ora*60);                        //ejfel változóból kiszámítjuk perc értékét
    if (adat_index==6) {                      //ha az éppen kijelzett adat az óra volt, akkor óra értékét fogjuk növelni
      do {
        ora++;if (ora>23) {ora=0;}
        lcd.setCursor(11,1); 
        lcd.print(ora);lcd.print("  ");       //kiírjuk az óra aktuális értékét
        ejfel=ora*60+perc;                    //minden növelési ciklusban kiszámítjuk ejfel értékét
        delay(700);
      } while (digitalRead(3)==LOW);          //addig fut a ciklus, amíg nyomjuk a gombot
    }
    if (adat_index==7) {
      do {
        perc++;if (perc>59) {perc=0;}
        lcd.setCursor(12,1); 
        lcd.print(perc);lcd.print("  ");
        ejfel=ora*60+perc;
        delay(700);
      } while (digitalRead(3)==LOW);
    }
    setup_mod=LOW;                             //kilépünk a setup módból, gombnyomás mentes álapot jut érvénre, frissül a képernyő másodpercenként az aktuálisn kijelzett adattal
    frissit=true;                              //azonnal frissíteni kell a képernyőt, mert változtattunk az idő értékén
    nyomva=LOW;                                //elengedtük a nyomógombot
    percidozito=millis();                      //innen indítjuk az egy perc időzítését az éjfel szamlaló növeléséhez
    vilagitas_ido=millis();                    //innen indul a 60 másodperc időzítés a háttérvilágítás kikapcsolására
  }

  /*******************************************************************************************************************************
   * Ebben a részben a kijelzőn megjelenő adatot frissítjük másodpercenként. Hogy mit lehet látni a kijelzőn azt az adat_index   *
   * változó dönti el, amit a nyomógomb lenyomásokkal tudunk változtatni, növelni 0 és 7 között. A 6. és 7. index érték          *
   * esetén az időt jelezzük ki, és ha ekkor megnyomjuk hosszan a nyomógombot, akkor a perc illetve az óra értéke változtatható  *
   * amit a nyomógomb elengedésekor azonnal frissítünk is.                                                                       *
   *******************************************************************************************************************************/
  if (millis()>kijelzo_valtas_ido+1000 or frissit) {  //két másodpercenként frissítjük a mért értékeket, ha éppen beállítjuk 
                                                                   //az éjfélig hátralévő időt, akkor nem
    frissit=false;  //ettől kezdve már csak másodpercenként frissítünk
    switch (adat_index) {
       case 0:         //pillanatnyi széllsebesség értékének kiírása
        lcd.setCursor(0,0);lcd.print("Pill. szelseb:  ");
        lcd.setCursor(0,1);lcd.print("            km/h");
        lcd.setCursor(poz_szam(pill_szelseb)+5,1);lcd.print(pill_szelseb,1);  //egy tizedesjegy hosszan kiírjuk a pillanatnyi szélsebességet
                                                                              //a kiírás kezdő pozicióját a szélsebesség nagyságrendje határozza meg,
                                                                              //hogy ne vándoroljon a kiírt szám, így a tizedespont egy helyben marad
        break;
       case 1:         //széllökés értékének kiírása
        lcd.setCursor(0,0);lcd.print("Szellokes:      ");
        lcd.setCursor(0,1);lcd.print("            km/h");
        lcd.setCursor(poz_szam(szellokes)+5,1);lcd.print(szellokes,1);
        break;
      case 2:         //széllökés maximum értékének kiírása
        lcd.setCursor(0,0);lcd.print("Szellokes max.: ");
        lcd.setCursor(0,1);lcd.print("            km/h");
        lcd.setCursor(poz_szam(szellokes_max)+5,1);lcd.print(szellokes_max,1);
        break;
      case 3:         //24 órás átlag sebesség (csak a szeles időszakot vesszük figyelmebe)
        lcd.setCursor(0,0);lcd.print("24h atlag seb.: ");
        lcd.setCursor(0,1);lcd.print("            km/h");
        lcd.setCursor(poz_szam(napi_atlag_seb)+5,1);lcd.print(napi_atlag_seb,1);
        break;
      case 4:         //szeles időszak idejének kiírása óra perc kijelzéssel (ennyi ideig forgott a szélkerék)
        ora=szeles_ido/3600;
        perc=(szeles_ido-ora*3600)/60;
        lcd.setCursor(0,0);lcd.print("Szeles idoszak: ");
        lcd.setCursor(0,1);lcd.print("                ");
        lcd.setCursor(7,1);lcd.print(ora);lcd.print("o ");lcd.print(perc);lcd.print("p   ");
        break;
      case 5:         //szelmentes időszak idejének kiírása óra perc kijelzéssel (ennyi ideig ált a szélkerék)
        ora=szel_mentes_ido/3600;
        perc=(szel_mentes_ido-ora*3600)/60;
        lcd.setCursor(0,0);lcd.print("Szelmentes ido- ");
        lcd.setCursor(0,1);lcd.print("szak:           ");
        lcd.setCursor(7,1);lcd.print(ora);lcd.print("o ");lcd.print(perc);lcd.print("p   ");
        break;
      case 6:         //idő kijelzése (ha hosszan nyomjuk a gombot, akkor beállíthatjuk az óra értékét)
        ora=ejfel/60;
        perc=ejfel-(ora*60);
        lcd.setCursor(0,0);lcd.print("Ido:            ");
        lcd.setCursor(6,0);
        if (ora<10) {lcd.print("0");}
        lcd.print(ora);lcd.print(":");
        if (perc<10) {lcd.print("0");}
        lcd.print(perc);lcd.print("  ");
        lcd.setCursor(0,1);lcd.print("Ora beall.:     ");
        break;
      case 7:         //idő kijelzése (ha hosszan nyomjuk a gombot, akkor beállíthatjuk a perc értékét)
        ora=ejfel/60;
        perc=ejfel-(ora*60);
        lcd.setCursor(0,0);lcd.print("Ido:            ");
        lcd.setCursor(6,0);
        if (ora<10) {lcd.print("0");}
        lcd.print(ora);lcd.print(":");
        if (perc<10) {lcd.print("0");}
        lcd.print(perc);lcd.print("  ");
        lcd.setCursor(0,1);lcd.print("Perc beall.:     ");
        break;
    }
    kijelzo_valtas_ido=millis();
  }
  
}

/******************************************************************************************
 * Ez a függvény a kijelzés kezdő poziciójához ad vissza egy eltolási értéket a kijelzett *
 * szám nagyságától függően                                                               *
 ******************************************************************************************/
byte poz_szam(byte adat)  //Az adat változóba beolvasott szám nagyságától függően 0,1,2-t ad vissza a kiírás könnyítéséhez
{
    if (adat<100) {                              //ha kisebb mint 10, akkor kettővel vagy eggyel kell eltolni a számot
      if (adat<10) {return 2;} else {return 1;}  //ha tiznél kisebb akkor eggyel kell eltolni
    } else {return 0;}                           //ha 100-nál nagyobb akkor nincs eltolás
}

Sok sikert a megépítéshez!

Szélsebességmérő

Szeretem egy-egy érzékelő használata előtt megérteni, hogy hogyan is működik, és ez a mérni kívánt fizikai jellemzők tulajdonságaira is igaz. Nyilván nem fogok kiselőadást írni a meteorológia rejtelmeiről, de meg szeretném tudni, mire számíthatok.

Ez különösen fontos lesz akkor, amikor majd beüzemelem a mérőeszközt. Ez a szerkezet ugyanis sok más szenzorral ellentétben, nem írja ki, hogy mennyi a mért érték. Két kontaktus jelet ad minden fordulatnál (megszakítja a zárt kontaktust, vagy zárja azt, ahogy tetszik) és vagy elhisszük a leírásban megadott egyetlen adatot, vagy sem. A leírást azt állítja, hogy 2,5 km/h szélsebességnél egyet fordul egy másodperc alatt. Nyilván ez egy erősen közelítő érték. A szél szinte soha nem fúj egyenletesen, házak közelében örvények keletkeznek, széllökések jönnek mennek. Elhiggyük? Vajon ez azt is jelenti, hogy 25km/h esetén 10-et fordul másodpercenként? Csupa jó kérdés. Nézzük megelső körben, hogy melyik lényeges kérdés ezek közül?

Magyarországon elég sok statisztika áll rendelkezésre a szelek természetéről. Találtam is egy térképet az átlagos szélsebességekkel:

Ez a térkép innen származik: https://www.met.hu/eghajlat/magyarorszag_eghajlata/altalanos_eghajlati_jellemzes/szel/

Azt jól lehet látni, hogy jellemzően minden zöld, tehát 2-3 km/h szélsebességre számíthatunk. Még a nagyobb átlagos szélsebességek is 5-6 km/h körül adódnak, tehát az egyetlen megadott adat a szenzorról abban a tartományban van, ami a leggyakoribb környezetünkben. Még esetleg a gyakoriságról is jól jöhet egy kis infó. Íme egy sokkal nehezebben értelmezhető grafikon:

Ráadásul ez m/sec-ben lett megadva, ami ugye 3,6-al szorzandó, hogy km/h mértékegységet kapjunk. Ez alapján szerintem már inkább a 3-6km/h az átlagos, de nem nagy a különbség, így nem igazán érdekel melyik ábrának van igaza. Ebből meg az nem derül ki, hogy mi a maximális szélsebesség. Az ábra azt sugallja, hogy 15 m/s (54 km/h) felett semmi nincs, pedig ez nem igaz. Olvastam valahol, hogy Magyarországon a szélsebesség rekord 144 km/h.

Fontos adat lenne számunkra a szélsebesség mérőről, hogy mekkora a minimális szélsebesség, amin a szélkerék megindul. Hát ezt nem adták meg, reméljük, hogy 0,5-1km/h környékén már mér. Látható az utóbbi ábrából, hogy elég csekély a 0 szélsebesség valószínűsége, tehát 1-2km/h lágy szellő mindig fújdogál. Amit a konkrét szenzorunkról érdemes tudni, hogy a háromlapátos forgó tengelyre egy mágnest szereltek, ami egy reed relét kapcsolgat. Úgy tapasztaltam, hogy egy fordulat alatt kétszer szakítja meg a folyamatosan zárt kontaktust, mégpedig 1/3-os kitöltési tényezővel (1/3 ideig zárva, 2/3 ideig nyitva a kontaktus). Egy körül fordulás alatt két felfutóélet tapasztaltam a később megtalálható mintaprogrammal. Ez bizony elég kevés. Pont abban a tartományban, ahol a jellemző szélsebességek tapasztalhatók Magyarországon, nagyon kicsi a felbontás. Ez azt eredményezi, hogy a kontaktusok számlálása esetén elég hosszú ideig kell mérni, hogy értékelhető számot kapjunk. Pl. ha egy másodperc alatt egy impulzust mérünk, akkor a szélsebesség kb. 0,6 km/h, ha egyáltalán megindul a szélkerék ekkora sebességnél. Sebaj, legfeljebb hosszú ideig fogunk mérni, és ebből az adatból készítünk átlagot.
Van azonban más lehetőség is. Ha nagyon kicsi a fordulatszám, akkor megmérhetjük egy fordulat (két impulzus felfutó éle vagy lefutó éle) között eltelt időt. Ebből egészen pontosan megtudhatjuk a pillanatnyi szélsebességet, nem kell sok impulzust begyűjteni. Ezt a technikát lehet párhuzamosan is alkalamazni, így viszonylag rövid időn belül akár három adatunk is lehet. Pillanatnyi szélsebesség, széllökés (pl. 10 másodpercig tartó átlagos szélsebesség), és átlagos szélsebesség pl. 1 perc alatt.
Nekem egyébként is csak egy nagyon elnagyolt átlagra lesz szükségem, mert csak azt akarom kideríteni, hogy a szél mennyire szárítja ki a meglocsolt földet, így a pillanatnyi sebességgel nem szándékozom foglalkozni. Ha nagyon és sokat fúj, akkor hamarabb kell újra locsolni. Ehhez nem kell pontosan mérni, nagyon-nagyon elnagyolt adatok is jók lesznek. Senkit nem érdekel, hogy éppen most mekkora a szélsebesség. Épp ezért én a példa programban egy percig mértem, és így állítottam elő egy egy-perces átlagot. Azonban kísérlet képen csináltam egy 10 másodperces mérést is, aminek az eredményét széllökésnek hívtam. Abból indultam ki, hogy egy-egy hirtelen “széllökés” esetén a szélsebességmérő 5-10 másodperc alatt sem áll le, lendületből lassulva forog. A széllökés a hajszárító produkálta. A széllökéseknek csak a maximális adata érdekel, tehát az, hogy egy adott időn belül mi volt a legmagasabb szélsebesség (ez a terv). Nehogy már ne tudjam, hiszen olyan hasznos!

Virtusból szeretném leellenőrizni, hogy vajon a megadott 2,5 km/h adat helyes-e. Ehhez elő kell venni az internetet, és beszerezni pár adatot. Mekkora erővel hat egy homorú és egy domború félgömbre a szél. Van ehhez képlet:

Egyszerűbb mint aminek látszik. “Fe” a megfújt felületre ható erő. Az a furcsa jel az egyenlőségjeltől jobbra a ró, ezt a későbbiekben q-val fogom helyettesíteni. Ez a levegő sűrűsége, ami egy konkrét szám, de mint ki fog derülni, nem is lesz rá szükség. A “Cw” a légellenállási együttható, aminek az értékét különböző alakzatokra meg lehet találni a neten. Ha félgömb homorú fele felől fúj a szél, akkor 1,4, a domború felel felől 0,4. Ez tapasztalati mérési eredmény ha nem tévedek. Beraktak egy ilyen alakzatot a szélcsatornába, megmérték az erőt egy adott szélsebességnél, és kiszámolták a fenti képletből. Az autók Cw értékét is így mérik. Egy modern autó Cw-je 0,2 – 0,3 között van. A legideálisabb cseppformánál 0,02 körüli érték. Az “A” a félgömbünk szélirányba mutatott keresztmetszetének felülete. Ez sem fog kelleni, de ez még titok. A “V” a szélsebesség.

Mivel macerás lenne a képletet a fenti formátumban megjelenítenem, az alábbi módon fogom felírni :

F=½ * q * A * C * V2

(ebben a felírásban a “*” a szorzás)

Egy szélkeréken egyszerre három félgömbre hatnak a szél által keltett erők. Mindhárom félgömböt egyszerre éri ideális esetben a szél, és a forgó mozgás addig gyorsul vagy lassul, amíg beáll egy egyensúlyi állapot, és a forgó rendszerre az erők eredője nulla.  Fel kellene írnunk egy egyenletet. Ez egyszerűbb mint gondolnánk. Tételezzük fel, hogy épp abban a helyzetben van a szélkerék, hogy az egyik félgömb a homorú felével teljes felületét mutatja a széliránybak, tehát rá maximális erő hat, ami a szél irányába mozdítja. Le is fotóztam az én szélkerekemet ebben az állapotban (piros nyilak jelzik a szélirányt):

Persze a tartórúd nem fogja engedni az egyenes vonalú elmozdulást, így kényszerből körpályán mozog, de most ezzel ne törődjünk. Azt is feltételezzük, hogy már beállt az egyensúlyi állapot, tehát a tengelyre nem hat forgatónyomaték, így a rendszer gyorsulás nélkül egyenletesen forog. Ebben a pillanatban a tengely másik oldalán két félgömb is a domború felét mutatja a szélirányba, és nem a teljes felületüket, hiszen egyik is és másik is 60 fokban elfordul a merőleges iránytól a kialakítás miatt. Most hanyagoljuk el, hogy ezek felülete egy kicsit kisebb mintha merőlegesen állnának, és azzal se foglalkozzunk, hogy egyik jobbra a másik balra fordul 60 fokkal.  Akkor lesz az eredő erő nulla, ha a tengelyre ható fogató nyomaték ebben a pillanatban nulla, vagyis a szélkerék jobb és bal oldalára ugyanakkora erő hat. Vagyis a bal oldali erő azonos a jobb oldali erőkkel:

½ * q * A * C1 * (Vsz-Vp)2 = ½ * q * 2A * C2 * (Vsz+Vp)2

Itt a Vsz a szél sebessége, a Vp pedig a félgömbök pillanatnyi sebessége. A jobb oldali félgömb az ugye együtt mozog a széliránnyal, ezért olyan mintha kisebb sebességgel fújná a szél, a szélsebesség és a saját szélirányba történő sebességének a különbségével. Így a rá ható erő kiszámításakor ezzel a sebesség különbséggel kell számolni. A bal oldalon pont ellentétes az elmozdulás, mert a széllel szembe fordulnak a félgömbök. Tehát itt a két sebesség összegével kell számolnunk. A félgömbök felülete A, és a jobb oldalon kettő van, tehát 2A. A bal oldali domború félgömbök légellenállási együtthatója C1 (0,4), míg a jobb oldal C2 (1,4). Még nem írtam be a konkrét felületet, hátha nem is lesz rá szükség. Sajnos itt még egy lényeges korrekcióra szükség van, amit alapból nem írtam fel, nehogy érthetetlenül bonyolult legyen az egyenlet. A bal oldali félgömbök szélirányba történő elmozdulásának sebessége nem azonos a jobb oldali félgömb szélirányba történő elmozdulási sebességével, mert épp 60 fokos szögben állnak. Ha nem tévedek, akkor itt 60 fok cosinusával kell számolni, ami éppen 0,5.  Tehát a szélirányba csak a teljes sebesség felével mozogna. Így a képletünk immár pontosabb formában:

½ * q * A * C1 * (Vsz-Vp)2 = ½ * q * 2A * C2 * (Vsz+0,5 * Vp)2

Nyilván a rudazat légellenállását is elhanyagoljuk. Mivel nem vagyok matematikus, egy két ismeretlenes másodfokú egyenletet nem fogok tudni megoldani, ezért további egyszerűsítésekre lesz szükség. Kiküszöbölhetjük az egyik ismeretlent, ha beírjuk egy konkrét értékét. Pl. legyen a szélsebesség  2,5 km/h. Azért pont ennyi, mert ezt meg is adták nekünk a szélkerék katalógusában, vagyis azt, hogy 2,5 km/h szélsebesség esetén másodpercenként egyet fordul körbe. Nosza ellenőrizzük le. Helyettesítsük be a szélsebességet:

½ * q * A * C1 * (2,5-Vp)2 = ½ * q * 2A * C2 * (2,5+0,5 * Vp)2

Azt is tudjuk, hogy egy félgömb homorú oldaláról megfújva 1,4 légellenállási együtthatóval rendelkezik. Ugyanez a félgömb a domború oldalán már csak 0,4 légellenállási együtthatót produkál. Ezt is írjuk be.

½ * q * A * 1,4 * (2,5-Vp)2 = ½ * q * 2A * 0,4* (2,5+0,5 * Vp)2

Azt rögtön látjuk, hogy az egyenlet mindkét oldalát el lehet osztani q-val és A-val, sőt megszorozhatjuk 2-vel, hogy az a csúnya ½ is eltűnjön az elejéről:

1,4 * (2,5-Vp)2 = 2 * 0,4* (2,5+0,5 * Vp)2

Így már sokkal szebb, rendezzük gyorsan:

1,4 * (2,5-Vp)2 = 2 * 0,4* (2,5+0,5 * Vp)2
1,4 * (6,25-5Vp+Vp2) =0,8* (6,25+2,5Vp+0,25Vp2)
8,75-7Vp+ Vp2=5+2Vp+0,2 Vp2
3,75-5Vp+0,8V p2=0

Előkerestem egy online másodfekú megoldáskereső programot, és a következő eredményeket kaptam:
Vp1=1,66 , Vp2=0

A két eredmény közül csak az egyik ad számunkra értelmezhető megoldást, vagyis 1,66 km/h a végeredmény.

Az eredmény tükrében folytassuk az ellenőrzést. Azt már tudjuk az egyenlet megoldásából, hogy a 2,5 km/h szélsebesség hatására a félgömbjeink 1,66km/h sebességgel haladnak a körpályán. Ez elég nehezen értelmezhető hiszen nem egyenes vonalban mozognak, de ne elmélkedjünk sokat, fogjuk fel úgy, hogy légüres térben adunk az egyik félgömbnek egy 1,66 km/h kezdő lökést. Számoljuk ki, mekkora utat tenne meg ezzel a sebességgel egy másodperc alatt. Ismét segítenek a neten található online sebesség átváltó programok: 1,66km/h = 46 cm/másodperc. Vagyis 46 cm-t haladna előre a félgömbünk egy másodperc alatt. Már csak azt kell megtudnunk, körpálya esetén mennyi ez az út, vagyis a körpálya hossza kell. A félgömb közepe a tengelytől 7cm-re van (ezt el kell hinnetek nekem), vagyis a kör kerülete: (7+7)*3,14=43,96. Ennyi elhanyagolással, kerekítéssel és egyszerűsítéssel a 46cm gyakorlatilag azonos a 43 cm-el! Készen is vagyunk.

Be kel vallanom, hogy életemben először használtam másodfokú egyenlet megoldó tudásomat egy  valós gyakorlati célra. Eddig mindig csak mintapéldákat házifeladatokat oldottam meg, mert nem emlékszem, hogy szükségem lett volna rá. Szégyen bevallani, de ez a tudás éppenhogycsak megüti a gimnáziumi matematika és fizika tudásanyag alapszintjét, mégsem ment könnyen. Minden esetre hálával tartozom gimnáziumi tanáraimnak: Szabó Zsuzsa matematika, Monostori Lászlóné fizika és Borbáth Gábor anyagszerkezettan tanáraimnak, akiktől valós életben használható tudásomat megszereztem. És persze megemlíthetném még Dr. Császár Ákosné egyetemi matematika tanáromat, valamint Dr. Füzessy Zoltán fizika tanáromat, akik ezt az alaptudást használható szintre mélyítették.

Tehát az a megállapítás, hogy a szélsebesség mérő 2,5 km/h szélsebességnél egyet fordul, úgy tűnik igaz. Köszönjük meg tanáraimnak!

Még annyit kell megjegyeznem, hogy a szélkerék belsejében található reed relé nem túl biztató megoldás. Ha 2,5km/h sebességgel fúj a szél, akkor fordulatonként (másodpercenként) 2-szer kapcsol, vagyis naponta 172800-szor. Ez évente 63 millió. Egy átlagos reed relé élettartama a katalógusok szerint 100millió. Nem túl biztató. Idővel meg fogom nézni, hogy mit produkál a gyakorlatban, és ha kell valamilyen fotocellás megoldásra cserélem. Vagy veszek egy másik reed relét, de nem lesz kedvem évente a tetőre mászni, így inkább fotocella, esetleg hall szenzor (már meg is rendeltem)!!

Majdnem elfelejtettem a maximális szélsebességet! Mi lesz, ha közel 150 km/h-val fúj odakint. Hát tudom mi lesz, felborul a kerti asztalunk. A szélsebesség mérővel még nincs tapasztalatom, de tegyük fel, hogy túléli. 150 km/h esetén már 60-at fordul (biztos nem igaz így, mert ezek a rendszerek nem szoktak lineárisak lenni). Ebben az esetben a reed relé már 120-at kellene kapcsoljon másodpercenként. Vajon fogja ezt produkálni, vagy folyamatosan zárva lesz a kontaktus, mert a tehetetlenség fog győzni. Erről alig találtam adatot. Valahol annyit azért találtam, hogy a meghúzási idő 0,6ms és az elengedés 0,2ms. A 120Hz frekvencia az kb 8ms, tehát a reed relé még ekkora szélsebességnél is működni fog. Ez elég meglepő, piszokgyors. Azonban néhány szélvihar, és el is használtuk a reed relé élettartamát.
És vajon prelles a reed relé kontaktusa? Nem nem az, ezt már kipróbáltam. Olvastam, hogy a kontaktusokat higannyal futtatják be, és ezért nem prelles.

És íme a mintaprogram:

bool elozo;   //segéd változó előző kontaktus állpot tárolására        
bool most;    //segéd változó a pillanatnyi kontaktus állpot tárolására 
int imp1=0;   //segédváltozó a 10 másodperces (széllökés) impulzus számláláshoz
int imp2=0;   //segédváltozó a 1 perces (átlagos szélsebesség) impulzus számláláshoz
float szel_seb_tmp=0;   //segéd változó a szélsebesség tárolására
float szelsebesseg=0;   //a kiszámított szélsebesség 
float szellokes=0;      //a kiszámított széllökés, csak a maximális értéket tartjuk meg
long ido_tmp1=millis();  //segéd változó a 10 másodperc méréséhez
long ido_tmp2=millis();  //segéd változó az egy perc méréséhez

void setup() {
  Serial.begin(9600);
  Serial.println("Indul...");
  pinMode(2,INPUT);            //2-es kivezetésre kötöttem a kontaktus jelét             
  digitalWrite(2,HIGH);        //felhúzó ellenállás bekapcsolása    
  elozo=digitalRead(2);        //máris vezünk egy mintát a bemenetből
}

void loop() {
  most=digitalRead(2);             //a pillanatnyi kontaktus állapot beolvasása
  if(elozo==LOW and most==HIGH) {  //felfutó él
    imp1++;                        //növeljük az impulzus számlálókat
    imp2++;
  }
  elozo=most;
  if (millis()>ido_tmp1+10000) {    //letelt a 10 másodperc, értékelünk és töröljük az impulzus számlálót
    szel_seb_tmp=((imp1/2)*2.5)/10; //kiszámítjuk az impulzus számból a sebességet 10 másodpercre
    if (szellokes<szel_seb_tmp) {   //csak akkor tároljuk, ha nagyobb mint az eddigi maximum
      szellokes=szel_seb_tmp;
    }
    imp1=0;  
    ido_tmp1=millis();
  }
  if (millis()>ido_tmp2+60000) {     //letelt az egy perc kiírjuk az átlag és széllökés adatokat, töröljük az impulzusszámlálót
    szelsebesseg=((imp2/2)*2.5)/60;  //kiszámítjuk az impulzus számból a sebességet 60 másodpercre
    Serial.print("Átl. szélsebesseég uts. percben:");
    Serial.println(szelsebesseg);
    Serial.print("Széllökes:");
    Serial.println(szellokes);
    imp2=0;  
    ido_tmp2=millis();
  }
}

Ezzel azonban még nincs vége az ismerkedésnek. Sokféle módon lehet megoldani az impulzusok számlálását, pl. lehetne megszakításokat is használni. Első körben azonban ezt elvetettem, mert a megszakítások használata megérzésem szerint impulzus vesztést okozhat. Ennek oka, hogy amikor valamilyen más időigényes feladatot lát el a program, esetleg le is kell majd tiltani a megszakítást, nehogy zavart okozzon. Viszont az impulzusok loop()-ban történő számlálgatása közben elég sok idő áll rendelkezésre. Jó lenne tudni, melyik impulzus (magas/alacsony) milyen hosszú. Azaz kíváncsi lettem a kitöltési tényezőre. Írtam ehhez egy minta programot. Csak a fel és lefutó éleket, azaz a kontaktus változását figyeli:

bool elozo; //segéd változó előző kontaktus állpot tárolására
bool most;  //segéd változó a pillanatnyi kontaktus állpot tárolására        
long ido_low=millis();   //segéd változó az alacsony szint (zárt kontaktus) idejénak mérésére
long ido_high=millis();  //segéd változó az magas szint (nyitott kontaktus) idejénak mérésére
int sorszam;

void setup() {
  Serial.begin(9600);
  Serial.println("Indul...");
  pinMode(2,INPUT);            //2-es kivezetésre kötöttem a kontaktus jelét             
  digitalWrite(2,HIGH);        //felhúzó ellenállás bekapcsolása    
  elozo=digitalRead(2);        //máris vezünk egy mintát a bemenetből
}

void loop() {
  most=digitalRead(2);             //a pillanatnyi kontaktus állapot beolvasása
  if(elozo==LOW and most==HIGH) {  //felfutó él
    Serial.print(sorszam);
    Serial.print("Low idő:");
    Serial.println(millis()-ido_low); //az alacsony szint ideje a kezdőpont és a pillanatnyi idő különbsége
    ido_high=millis();                //itt kezdődik a magas szint idejémnek mérése
    sorszam++;
  }
  if(elozo==HIGH and most==LOW) {  //lefutóél
    Serial.print(sorszam);
    Serial.print("High idő:");
    Serial.println(millis()-ido_high);  //a magas szint ideje a kezdőpont és a pillanatnyi idő különbsége
    ido_low=millis();                   //itt kezdődik az alacsony szint idejének mérése
    sorszam++;
  }
  elozo=most;
}

Ime egy kiragadott futási eredmény:

151Low idő:98
152High idő:63
153Low idő:99
154High idő:54
155Low idő:98
156High idő:64
157Low idő:99
158High idő:54
159Low idő:101
160High idő:63
161Low idő:100
162High idő:54
163Low idő:99
164High idő:64
165Low idő:98
166High idő:54
167Low idő:100

A kiírt adat msec-ben értendő, de az a mintaprogramból is kiderül. Az adatokból két dolog is látható. Egyrészt a reed relé tényleg nem prelles. Másrészt a kitöltési tényező valóban nem azonos a magas és alacsony impulzus hosszaknál. A magas szint kb. fele az alacsony szintnek. Vagyis kb 1/3 – 2/3 arányt látunk, tehát ha valami hosszabb műveletet kell csinálni, akkor azt a programban a második if belsejébe kell elhelyezni, egészen pontosan a ido_low=millis(); utasítás sor mögé. Ekkor ugyanis hosszabb időnk van a következő állapotváltozásig. Ha ekkor a feladat olyan sokáig tart, hogy az élváltozás közben bekövetkezik, akkor azt az állapot változást sajnos kihagytuk, és elveszítettünk egy impulzust.

Esetleg fontos lehet, hogy a fenti kísérletben mekkora volt a szél. Ezt akár ki is számolhatjuk, mert egy fordulat az 300 msec körül van (egy impulzus pedig 50+100 msec, akkor kettő 300) Vagyis kb. 3 fordulat másodpercenként. Mivel egy fordulat 2,5 km/h, három fordulat 7,5 km/h. Egyszerű mint a pofon. Tehát egy Magyarországon átlagos szél esetén 100 msec időnk van. Mennyi ez viharban? 150 km/h / 7,5 km/h = 20. Vagyis ennek az időnek a huszad része áll rendelkezésre, azaz kb 5 msec. Ez megnyugtató! Ez alatt elég sok mindent lehet csinálni! Pl. el lehet küldeni a mérési adatokat egy másik Arduino-nak I2C buszon keresztül.

A szélsebesség mérőt egy meglévő időjárás állomás kiegészítésre akarom felhasználni. Írtam is egy demó programot, ami egy Arduino nano-ban méri a szélsebességet, és I2C buszon elküldi az adatokat az időjárás állomás master programja felé. Bár a tervem az volt, hogy a kontaktus zárt állapotában rendelkezésre álló hosszabb idő alatt küldöm el az adatokat, ez sajnos nem működött, mert programban a master adatkérésekor meghívott függvényen kívül nem tudtam adatokat írni az I2C buszra. Pontosabban írni lehetett, a program a slave-ben hiba nélkül futott, de az adatokat a master nem kapta meg. Így maradt az a megoldás, hogy amikor a master kéri az adatokat, küldi a slave. Közben persze nem számolja az impulzusokat. Szerencsére a küldés ideje jóval az impulzusok közötti időtartam alatt van. A slave programjában ki is írtam az értékét, 120mikrosec körüli érték, úgy hogy az I2C busz sebességét a lehető leglassúbbra állítottam a master programjában. Ez megnyugtató érték, még 150km/h szélsebességnél sem fogok impulzust elveszéteni (5msec két impulzus idő távolsága).

Slave programja:

#include<Wire.h>
bool elozo;               //segéd változó előző kontaktus állpot tárolására        
bool most;                //segéd változó a pillanatnyi kontaktus állpot tárolására 
int imp1=0;               //segédváltozó a 10 másodperces (széllökés) impulzus számláláshoz
int imp2=0;               //segédváltozó az átlagos szélsebesség impulzus számláláshoz
int meresido=0;           //utolsó master kiolvasás óta eltelt idő mmsec-ben
int max_imp=0;            //a maximális impulzussám tárolására (10 másodperces széllökés maximum)
long ido_tmp1=millis();   //segéd változó a 10 másodperc méréséhez
long meres_ido=millis();  //segéd változó annak megállapítására, hogy mennyi idő telt el az utolsó master kiolvasás óta 
          
void setup() {
  Serial.begin(9600);
  Serial.println("Slave indul...");
  pinMode(2,INPUT);               //2-es kivezetésre kötöttem a kontaktus jelét             
  digitalWrite(2,HIGH);           //felhúzó ellenállás bekapcsolása    
  elozo=digitalRead(2);           //máris vezünk egy mintát a bemenetből
  Wire.begin(8);                  // I2C kommunikáció inicializálása 8-as eszköz címmel (mivel slave, címet kell megadni)
  Wire.onRequest(master_ker);     //Ezt a funkciót hívja meg az Arduino, amikor a master adatot kér
}

void loop() {
  most=digitalRead(2);             //a pillanatnyi kontaktus állapot beolvasása
  if(elozo==HIGH and most==LOW) {  //Lefutó él, az alacsony szint hosszabb, és több idő áll rendelkezésre
                                   //Itt más feledatokat is elvégezhetne a program, ha arr szükség lenne
    imp1++;                        //növeljük a 10 secundumos szállökés impulzus számlálót (10 másodpercenként töröljük)
    imp2++;                        //növeljük az átlagsebesség impulzus számlálót (az adat küldése törli)
  }
  
  elozo=most;                       //a következő ciklus majd megállapítja, hogy volt-e változás
  if (millis()>ido_tmp1+10000) {    //letelt a 10 másodperc, értékelünk és töröljük az impulzus számlálót
    if (max_imp<imp1) {             //csak akkor tároljuk, ha nagyobb mint az eddigi maximum
      max_imp=imp1;
    }
    imp1=0;                         //töröljük imp1-et, és ezzel újra kezdjük a széllökés mérést 
    ido_tmp1=millis();
  }
}




void master_ker()   
/*************************************************************************                         
* Ez a függvény akkor indul, amikor a master adatot kér a slave-től      *
* úgy tapasztaltam, hogy a kért adatokat ebben a függvényben kell kiírni *
* az I2C buszra, különben a master nem kapja meg az adatokat.            *
* Nem foglalkoztam azzal, hogy a meres_ido változó egy long változó      *
* amibe a meres kezdetének időpontját tárolom. Ez időnként átfordul      *
* (nullázódik), és ekkor hamis értéket fog adni az iőmérés.              *
**************************************************************************/
{
  meresido=millis()-meres_ido;          //meghatározzuk, mennyi idő telt el az utolsó master lekérdezés óta
  long ido_tmp=micros();                //mérni fogjuk az I2C kommunikáció idejét mikrosecundum-ban
  Wire.write((imp2 & 0xFF));            //imp2 változó felső byte átvitele         
  Wire.write(((imp2 >> 8) & 0xFF));     //imp2 változó alsó byte átvitele
  Wire.write((max_imp & 0xFF));         //max_imp változó felső byte átvitele 
  Wire.write(((max_imp >> 8) & 0xFF));  //max_imp változó alsó byte átvitele
  Wire.write(meresido & 0xFF);          //meresido változó felső byte átvitele   
  Wire.write((meresido >> 8) & 0xFF);   //meresido változó alsó byte átvitele
  Serial.print("Elküldve (");    //kiírjuk a soros portra a fogadott byte-okat
  Serial.print(micros()-ido_tmp);Serial.print(" mikrosec):");  //I2C kommunikáció időigényének kiírása
  Serial.print(imp2);
  Serial.print(", ");
  Serial.print(max_imp);
  Serial.print(", ");
  Serial.println(meresido);
  imp2=0;                      //töröljük az átlag sebesség mérésére használt impulzustárolót
                               //lekérdezéstől lekérdezésig átlagolunk
  meres_ido=millis();          //meresi idő megállapításához a kezdő időpont tárolása;
}

…és a slave futásának eredménye a soros monitoron:

Slave indul…
Elküldve (116 mikrosec):0, 0, 317
Elküldve (116 mikrosec):145, 59, 15030
Elküldve (112 mikrosec):130, 142, 15037
Elküldve (116 mikrosec):18, 142, 15037

Master programja:

/*********************************************************************************************************************
 * Ez a demo program a szélsebességmérő Arduino program I2C buszo keresztül történő lekérdezésére készült.           *
 * A program 15 másodpercenként kérdez az I2C buszon keresztül 6 byte-ot. Első két byte a szélsebesség impulzusszáma,*
 * a 3. és 4. byte a széllökés impulzusszáma, a 5. és 6. az utolsó lekérdezés óta eltelt idő milisecundum-ban.       *
 * A beolvasott adatok alapján kiírja az előző lekérdetzés óta eltelt időben (15 másodperc) kapott impulzusok        *
 * alapján a szélsebesség átlagos értékét, valamint a 10 másodperces széllökések maximumát                           *
 *********************************************************************************************************************/
#include<Wire.h>           
int tmp1;     //segéd változó, a szélsebesség impulzusszámát olvassuk bele a slave-től 
int tmp2;     //segéd változó, a széllökés impulzusszámát olvassuk bele a slave-től
int meresido; //a slave ennyi ideig mért a legutolso adat lekérdezés óta
float szelsebesseg=0;   //a kiszámított szélsebesség 
float szellokes=0;      //a kiszámított széllökés, csak a maximális értéket tartja meg a slave, a maximum törléséről 
                        //a slave programja nem gondoskodik


void setup()
{
  Serial.begin(9600);                 // soros port inicializálása            
  Serial.println("Master indul...");
  Wire.begin();                       // I2C kommunikáció inicializálása, nincs cím, ezért master
}

void loop()
{
  TWBR = 255;                     //az I2C busz sebességének beállítása a lehető legkisebb frekvenciára (30khz)
  delay(15000);                   //15 másosdpercenként kérdezzük le az eredményt  slve-től. 
                                  //A lekérdezés elindít egy új átlagsebesség mérést a slave-nél.                           
  Wire.requestFrom(8,6);                                              // a master kér hat bájtot a slave-től
  tmp1=((Wire.read() << 0) & 0xFF) + ((Wire.read() << 8) & 0xFFFF);   //szelsebesség 2 byte fogadás (összerakva int-nek)      
  tmp2=((Wire.read() << 0) & 0xFF) + ((Wire.read() << 8) & 0xFFFF);   //szellökés 2 byte fogadás (összerakva int-nek)  
  meresido=((Wire.read() << 0) & 0xFF)+((Wire.read() << 8) & 0xFFFF); //uts lekérdezés óta eltelt iső msec-ben 2 byte-on fogadva (összerakva int-nek) 
       
  Serial.print("Slave-tol fogadott adatok:");           //kiírjuk a soros portra a fogadott byte-okat
  szelsebesseg=((tmp1/2)*2.5)/(float)(meresido/1000);   //kiszámítjuk az impulzus számból a sebességet a meresidő-re 
  szellokes=((tmp2/2)*2.5)/10;                          //kiszámítjuk az impulzus számból a sebességet 10 másodpercre
  Serial.print("Átl. szélsebesseég ");
  Serial.print((float)meresido/1000);
  Serial.print(" másodperc alatt:");
  Serial.println(szelsebesseg);
  Serial.print("Széllökes:");
  Serial.println(szellokes);
  Serial.print("Meresidő msec-ban:");
  Serial.println(meresido);
  Serial.println();
}   

… és egy futási eredmény a mesterről is:

Master indul…
Slave-tol fogadott adatok:Átl. szélsebesseég 28.73 másodperc alatt:4.91
Széllökes:17.75
Meresidő msec-ban:28728S

Slave-tol fogadott adatok:Átl. szélsebesseég 15.04 másodperc alatt:16.50
Széllökes:17.75
Meresidő msec-ban:15036

Slave-tol fogadott adatok:Átl. szélsebesseég 15.04 másodperc alatt:7.67
Széllökes:18.50
Meresidő msec-ban:15037

Slave-tol fogadott adatok:Átl. szélsebesseég 15.04 másodperc alatt:0.00
Széllökes:18.50
Meresidő msec-ban:15037