Áramkör és Arduino szimulátor

Tartalom:

  • Miért lehet hasznos az áramkör szimulátor program használata
  • SimulIDE áramkör szimulátor letöltése, és telepítése
  • A szimulátor használata, egyszerű bonyolultabb elektronikai alapáramkörök szimulálása (kezdőknek nagyon hasznos)
  • Arduino-val összeépített áramkörök szimulálása, kódfeltöltés és program futtatás a szimulátorban
  • Valós gyakorlatban is megépített Arduino projekt szimulálása (szélsebesség mérő)
  • Letölthető szimulátor állományok, példa Arduino program forrásokkal

————————————————————————————–

Két oka is van annak, hogy szimulátor programot kezdtem el keresni:

  • Egyrészt az Arduino-ban futó program fejlesztése nagyon nehézzé válhat, ha nem látni a változók aktuális értékét és nem lehet közvetlenül nyomon követni az éppen végrehajtott utasításokat. Márpedig az ami az Arduino-ban történik, az benne is marad, legfeljebb a soros porton, LED-ekkel, vagy egy LCD kijelzőn lehet jelzéseket küldeni magunknak a program megfelelő pontjairól. Ha azonban szimulátort használunk, a program a számítógépünkben fut, és a képernyőn minden gond nélkül megjelennek a változó értékek, és az éppen végrehajtott programsor.
  • A szimulátor programok lehetővé teszik az Arduino-t körülvevő elektronikus alkatrészek megadását is, így áramkör rajzoló funkcióval is rendelkeznek Számomra kezdetben ez volt a fontos, áramkör rajzolót kerestem, és amibe beleakadtam, az nem csak erre volt képes! Meg kellett csak barátkozni a lehetőségekkel és megismerni a működést.

Meg kell vallanom, egy-egy szimulátor program használata számomra bonyolultabb, mint az Arduino programozása. Ráadásul a szimulátor programok jelentős része fizetős. Az ingyenes eszközök nagyon buták, és szinte semmire nem használhatók, de mint kiderült számomra, vannak jól használható eszközök is, amit egy lelkes amatőr felhasználhat, és fizetni sem kell érte (2020 decemberi állapot szerint). Szorgalmasan mentegetem is a letöltött verziókat, hátha egyszer megváltozik  a helyzet. Az online világban egyik napról a másikra tűnnek el a megszokott és jól használható programok, ami a gépemen van, az talán hosszabb ideig is megmaradhat használatomban.

Az egyik olyan eszköz, amit hasznosnak ítélek a SimulIDE program (https://www.simulide.com). Jelenleg ingyenes eszköz, ha jól láttam az archív állományokból 2017 óta létezik. Elég fiatalka, de évente két három új verziót is kibocsát az alkotó. Összességében nagyon jó kis program. Néha lefagy, vannak benne kisebb nagyobb működési rendellenességek, de igazán sok mindenre jó. Az a kategória, amiért ha fizetős lesz, a saját zsebemhez mérten fizetni is hajlandó leszek. Windows alatt csak le kell tölteni a programot ZIP állománykét, kibontani, és oda másolni ahová szeretnénk. Célszerű csinálni egy parancsikont, és ezzel kész is. Telepíteni nem kell.
Innen tölthető le: https://www.simulide.com/p/downloads.html

Remélem vírust, vagy kémprogramot nem tartalmaz. Legyünk óvatosak! Én rendszeresen mentek szinte naponta egy külső merevlemezre, mert egy-egy ilyen ingyenes programnál számítok rá, hogy esetleg nem csak jótékonyságból ingyenes. De azért ne legyünk túlzottan bizalmatlanok, vannak jószándékú emberek is a neten!

Tisztázzuk, mire is jó ez az áramkör szimulátor! Megrajzolhatsz vele analóg és digitális kapcsolásokat. Szinte minden gyakori áramköri elem megtalálható az alkatrészek között. Ellenállás, kondenzátor, tekercs, tranzisztor, FET tranzisztor, kapcsolók, relék, opto csatoló, 74-es és a CMOS logikai áramkörök, Arduino alaplapok, memória IC-k, és még sok más cucc. Pl. kvartz kristályt azonban nem találtam. Ezekből az alkatrészekből tetszőlegesen bonyolult kapcsolásokat lehet összeállítani, és azok működését vizsgálni valós sebességű, és lassított szimulációval. Találunk mérőműszereket és jelgenerátorokat is a tartozékok között. Arra jutottam, hogy amit fizikai valóságban megépíthetek a jelenlegi tudásommal, azt ezzel az eszközzel virtuálisan is létrehozhatom. Később mutatni is fogok példát, amik tanulásban kísérletezgetésben nagyon hasznosak lehetnek. Mivel Arduino alaplapok, sőt PIC és ATmega mikrovezérlők is szerepelnek a tartalomjegyzékben, várható, hogy programot is futtathatunk ezekben az eszközökben. Akár programot is fejleszthetünk a beépített szövegszerkesztővel. Segítheti a fejlesztést, hogy a programot lépésről lépésre hajthatjuk végre. Sajnos a program végrehajtása során a változó értékeket a program nem képes megjeleníteni. A leírás szerint csak a globális változókat lehet nyomon követni, de nekem ez sem működött. Azonban a mikrovezérlő regisztereit valóban megjeleníti futás közben, bár ezzel én általában nem sokra mentem. Talán egy későbbi verzióban változni a fog a program.

A működés titka, hogy a megírt forráskódot az Arduino IDE program fordítja le, és az általa létrehozott bináris (HEX) program állományt futtatja a szimulátor, mintha tényleg igazi Arduino alaplap lenne. Lehetséges is, egy kívülről kapott bináris állomány betöltése az Arduino-ba, ehhez a forrsákód sem kell. Természetesen ekkor nem fogjuk tudni a programot nyomon követni, csak fut, és vezérli az Arduino kivezetéseit. Ha az ATmega vagy Arduino programjait is futtatni akarod a megépített virtuális áramkörben, és a nyomon követési funkcióra is szükséged van, akkor telepítened kell a gépre az Arduino IDE programot, és be kell állítani a SimulIDE programban az Arduino IDE elérési útvonalát a gépeden. Ugyanis ezt, mint külső programot használni fogja a bináris futtatható állomány létrehozásához. De ha úgy jobban tetszik, az Arduino IDE programmal is elkészítheted a futtatható bináris program állományt, és azt töltheted be a virtuális Arduino-ba.

A program elindítása után a képernyő két vagy három fő részből fog állni:

A használat nagyon egyszerű, bal oldalon kiválasztunk egy alkatrészt, és behúzzuk középre. Ott jobb egérgombbal lehet forgatni, elhelyezni. Az alkatrész tulajdonságait (pl. ellenállás értéket) a bal oldalon lehet beállítani, ehhez kattintsunk a „Properties” fülre, majd a kiválasztott alkatrészre. Ne felejtsünk el legalább egy Groun (földelés) alkatrészt kirakni a tervező felületen. A kihelyezett mérőműszereknek, jelgenerátoroknak és feszültségforrásoknál mindig csak egy kivezetést fogunk találni, mert a másik a Ground, amit már nem kell bekötni. Kivéve az árammérő és az oszcilloszkóp. Az árammérőnek „hivatástudatból” van két kivezetése, figyelni kell a polaritásra. Az oszcilloszkópnak van invertáló és nem invertáló bemenete. Ha valaki ezt most még nem érti, nem baj, a következő mintapéldákból majd minden kiderül. Ha kiraktuk az alkatrészeket, úgy kell összehuzalozni, hogy a kivezetése fölé visszük az egérmutatót, ami alapból egy kezecske, és amikor célkeresztté alakul a kivezetés között, kattintunk az egérrel. A vonalat húzza az egérmutató után, és automatikusan helyezgeti. Ha valahol a tervező felületen kattintunk, akkor ott rögzíti a vonalat, és mehetünk tovább. Így kell eljutni a következő alkatrész kivezetéséhez, vagy egy vezetékhez, ahol a célkereszt jelzi ha kattinthatunk, és ezzel meg is történt az összeköttetés.

Gyakoroljunk egy kicsit, meg lehet barátkozni a kezelésével, bár nem egy komfortosan kialakított program. Kissé nehézkes, fölösleges kattintgatások árán juthatunk el az eredményhez, de megéri küzdeni. Ha felraktuk az alkatrészeket, és összevezetékeztük, állítsuk be az alkatrészek értékét (ellenállás, kondenzátor stb.). Egyes alkatrészeknél több paraméterrel is találkozhatunk, pl. egy tranzisztornál az alaptípus (pnp,npn), az erősítés (Gain), és a nyitófeszültség is beállítható. Ha ezekhez nem nyúlunk, akkor egy pnp átlagos 200-as erősítésű, 0,7V-os nyitófeszültségű típust kapunk. Nagyon hasznos a „Probe” alkatrész, minden bizonnyal nem véletlen, hogy ez van legfelül. Ha egy vezetékhez illetve alkatrész lábhoz érintjük a Probe komponensünk kivezetését, akkor megmutatja azon a ponton a feszültséget. Ha a feszültség gyorsan változik, akkor a Probe-ra kattintsunk jobb egérgombbal, és kapcsoljuk be a plottert, ami egy nagyon hasznos megjelenítő, a feszültség időbeni változását követhetjük vizuálisan. Egyszerre négy ponton kapcsolhatunk be plotter üzemmódot. A következő mintákban mutatok erre is példákat. A plotter feleslegessé is teszi az oszcilloszkópot, de csak akkor ha a valós sebességhez képest van lehetőségünk a működés lassítására. Már egy 50Hz-s jelet is csak akkor tudunk vizuálisan megfigyelni, ha legalább 10-ed részére lassítjuk a sebességet. Egyes esetekben viszont a lassítás kivárhatatlanná teszi a várt eredmény megfigyelését (esetleg percekig kell várni), ekkor jöhet jól az oszcilloszkóp, ami valós időben mutatja a feszültség jelalakját, de csak akkor ha periódikus, ismétlődő jelalakkal van dolgunk.

Ha egy áramkört megrajzoltunk, akkor mentsük el, hogy legközelebb is felhasználhassuk. Fent a szerkesztő felület fölötti ikonok között keressük meg a „save”, vagy „save as..” ikont, és adjunk nevet az áramkörünknek. Legközelebb az „open”-el tudjuk majd előhívni. Javaslom, hogy gyakran mentsünk. Időnként előfordulnak váratlan lefagyások, vagy egyszerűen csak eltűnik a program a képernyőről. Egyébként automatikusan is ment, és következő újraindításkor visszahozza azt az állapotot ahol tartottunk, de az utolsó egy két lépés nem szokott rajta lenni tapasztalatom szerint.

És most tényleg jönnek a példa feladatok! Azok számára, akik most tanulják az elektronikus áramköröket, elkészítettem az „Elektronikai alapok”-ról szóló írásomhoz az ott ismertetett áramkörök szimulációját. Lehet kísérletezgetni, nem kell a boltba rohangálni alkatrészekért! Ha kezdő vagy, javaslom, olvasgasd egy kicsit az elektronikai alapokat, és közben nézegess át erre az írásra is.

1. feladat: ellenállás feszültségosztó szimulálása

A kapcsolás szerkesztéséhez felhasználtam egy „Fixed volt.”, „Resistor”, „Amperimeter”, „Ground” és „Probe” komponenseket, valamint összehuzaloztam a kapcsolást. A szimuláció a tervező ablak feletti ikonsorban a kis piros bekapcsológombbal indítható el:

Amikor elindítottuk a szimulációt, a Real Speed 100%-ra fog ugrani. Ez szükség esetén lassítható, de itt nincs értele, hiszen fix egyenfeszültség esetén nincs változó feszültség, amit meg kellene figyelni. A „Probe” komponensek mutatják az adott ponton a feszültséget. Ha valaki éppen tanulja az elektronikus kapcsolásokat, és az elektronikai alapismeretekről szóló írásomról került ide, annak érdemes rákattintani valamelyik ellenállásra, és átírni az ellenállás értékét egy duplaklikket követően:

Közben a szimulációt nem kell leállítani, maradhat bekapcsolva. Azonban erre érdemes figyelni, mert bonyolultabb kapcsolásokban időnként megzavarodik, és rosszul számol értékeket. Így aztán azt javaslom, hogy egy érték módosítást követően mégiscsak legyen egy leállítás az időközben sárgára színezett főkapcsolóval, és egy újraindítás. Majdnem elfelejtettem, hogy a fix feszültségforrást a szimuláció elindítása után be kell kapcsolni a jelölő négyzetre kattintással. Sajnos a pipát nem rakja bele, azonban a szimbóluma és az összes Probe narancssárgára színeződik. Ekkor lesznek feszültség értékek is a feszültség mérők (a Probe komponenst ezentúl ennek hívom) köröcskéi mellett!

A lustább érdeklődők érdekében, hogy ne kelljen megcsinálni a kapcsolásokat, ide kattintva letölthető a kapcsolás mentett állománya: Letöltés

Elegendő ezt letölteni, és betölteni az ikonsor második ikonjával az Open Circuit (Ctrl+O)-val.

2. feladat: párhuzamos ellenállások szimulálása

Itt egy kicsivel több árammérőt raktam bele, hogy látható legyen, valóban igaz Kirchoff csomóponti törvénye. Csomópontba befolyó és onnan kifolyó áramok előjeles összege mindig nulla. Tessék itt is változtatni az feszültségforrás feszültségén és az ellenállás értékeken. Azon nem szabad meglepődni, hogy az árammérő véges pontossága miatt a felső két árammérő áramainak összege nem lesz mindig azonos az alsó árammal, de ez mérési hiba, és nem „törvénytelenség”!

Itt az áramkör mentett állománya: Letöltés

3. feladat: egyutas egyenirányító (dióda)

Ebben a kapcsolásban új dolgot tudhatunk meg a szimulátorról. Egyrészt használni kezdtünk egy újfajta elemet a „WaveGenerator”-t. Ez szinusz, négyszög, háromszög stb. jelalakok előállítására szolgál. A generátor beállító paneljét kinn is hagytam, hogy lássuk miket lehet beállítani. 50Hz frekvenciát állítottam be, mert egy 5V-os transzformátort szimulálunk ezzel a generátorral. Azt, hogy a kimenő jele váltakozó feszültség legyen, egy kis trükkel lehet elérni. Az alap jelet (Volt Base) -5V-ra állítottam, a jel amplitudót (Voltage) pedig 10V-ra. Így a kimenő szinusz jel -5V és +5V között fog váltakozni. Másik újdonság, hogy a feszülségmérő pontokon bekapcsoltam a plotter üzemmódot. Ehhez a feszültségmérő pontra jobb egérgombbal kell kattintani, és itt lehet beállítani:

Láthatóan 4 plotter csatornánk van, ennyi jelalakot nézhetünk egyszerre szinkronban. A megjelenítés alul történik, mint az az eggyel előbbi ábra alján látható is. A vonal színe megegyezik a feszültségmérő pont színével. A hálózati 50Hz váltakozását nem lehetne azonban látni valós „RealTime” szimulációs sebességnél, ezért a szimuláció sebességét jelentősen lelassítottam. Ehhez a tervező képernyő sárga négyzetrácsos felületére kell kattintani, és oldalt a „Properties” fülön tudjuk beállítani sok más paraméter mellett a sebességet:

A speed eredetileg 1000000, tehát ezzel a 10000-es értékkel pont század részére lassítottuk le. Így nézne ki a jel, ha maximális sebeséggel haladna a szimuláció:

Alább a lassított szimuláció fotóján már jól láthatóak az események:

A képen jól látható, hogy a dióda csak az egyik félhullámot engedi át, és a kimenő feszültség 0.7 V-al kisebb mint a bemenő feszültség. A diódán pont ekkora feszültség esik, amikor nyitva van és vezet. Bejelöltem sárga nyíllal, miről beszélek.

A plotter bal oldalán be lehet állítani az ábrázolási tartományt feszültség szintben, ha a jelet nyújtani akarjuk, vagy éppen kilóg a sötét ábrázolási tartományból.

A kapcsolás betöltéséhez a mentés itt tölthető le: Letöltés

4. feladat: egyutas egyenirányító puffer kondenzátorral

Ebben a kapcsolásban csak annyit változtattam, hogy az egyenirányító dióda kimenetére kötött terhelő ellenállással párhuzamosan kötöttem egy puffer kondenzátort. Amikor a dióda lezár, ez az elektrolit kondenzátor tárolja a töltéseket, és feszültséggel és ebből adódóan árammal látja el a terhelő ellenállást. Közben természetesen feszültsége csökken. Lehet kísérletezni azzal, hogy mi történik, ha lényegesen kisebb terhelő ellenállást kapcsolunk a kondival párhuzamosan, ami nagyobb áramot eredményez és így gyorsabban kifolynak a töltések a kondiból, azaz gyorsabban csökken a feszültsége:

Mint látható, 100ohm terheléssel már nagyon gyorsan csökken a feszültség. Közben megállítottam a pillanatstoppal a szimulációt (fent az ikonok között a piros nyomógomb), és az érték átírása után újraindítottam. Kicsit trükkös az ellenállás nagyságrendjének váltása. Nem lehet ugyanis a Kohm-ot átírni ohm-ra. Ha azonban 0,1-et írok az ellenállás értékhez, akkor átáll ohm-ra, és akkor már ohm-ban lehet megadni az értéket. Megaohm-ra úgy lehet átállni, hogy pl. 1000-et írok az értékhez. Lehet kísérletezgetni a kapacitás értékével, és a frekvenciával is.

Itt a kapcsolás betölthető mentése: Letöltés

5. feladat: soros kondenzátor viselkedése váltakozó feszültség esetén

Az előbbi példában láthattuk, hogy mit is csinál egy kondenzátor, ha feszültséget kapcsolunk rá. Töltéseket tárol, azaz pufferel.
Mi történik azonban akkor, ha sorba kötjük egy feszültségforrás kimenő jelével. Elvileg egyenáramon szakadás, tehát nem történik semmi. Ha azonban a feszültségforrásunk váltakozó feszültséget állít elő, érdekes módon a kondenzátor vezetővé válik. Mégpedig a frekvencia növelésével egyre jobb vezetővé. Itt a rendkívül egyszerű minta kapcsolás:

A plotteren három különböző frekvencia esetén láthatjuk az ellenállás feszültségét. 10Hz, 50Hz és 100Hz. Szimuláció közben változtattam a frekvenciát, mert így nem kellett három külön fotót csinálni. A narancssárga a jel az ellenállás feszültsége. Látható, hogy a jel nagysága a frekvencia növelésével egyre növekszik. Ez ezt jelenti, hogy a kondenzátor egyre nagyobb áramot vezet. Azt is érdemes megfigyelni, hogy a jel maximuma le van maradva a jelgenerátor maximumához képest (legjobban az 50Hz frekvenciánál látszik). Ez nem is csoda, hiszen amikor a generátor feszültsége 0V-ra csökken, akkor folyik kifelé a kondenzátorból a legnagyobb áram az ellenálláson keresztül. A tárolt töltések mindaddig fenntartják az áramot, csökkenő nagysággal, amíg el nem fogynak. Közben a generátor feszültsége megfordul, aminek hatására még gyorsabban csökken az áram, majd irányt vált. Pont ezt látjuk a képen. Amikor a sárga jel átmegy a 0V-on, akkor maximális a kondenzátorból kifolyó áram.

Itt a kapcsolás betölthető mentése: Letöltés

6. feladat: soros kondenzátor viselkedése négyszögjel esetén

Az előbb arra láttunk példát, mi történik, ha színusz jellel kapcsoljuk sorba a kondenzátort. Most nézzük meg, mit csinál, ha négyszögjelet kapcsolunk rá:

Meglepődnék, ha valaki meglepődne! Nyilván a kondenzátor csak akkor reagál, amikor a feszültség változik. Kis áram tüskéket ereszt át magán. Ez legjobban az 50Hz-nél látható. Az utolsó jel már 200Hz-s.

Itt a kapcsolás betölthető mentése: Letöltés

7. feladat: soros kondenzátor viselkedése unipoláris négyszögjel esetén

Ha Arduino áramkörökkel dolgozunk, jellemzően 5V és 0V között kapcsolgató négyszögjelekkel találkozunk. Mit lép egy kondenzátor, ha sorosan kapcsoljuk egy ilyen áramkör kimenetére:

Hát ez nagyon izgalmasan alakul. Azon nem lepődünk meg, hogy áram tüskék keletkeznek a négyszögjel fel és lefutó élénél. Azonban nem szabad nem észre venni, hogy az ellenálláson eső feszültség (ami egy kondenzátort követő áramkör bemenete is lehetne) a négyszögjel lefutó élekor negatív feszültség keletkezik. Ennek a félvezető áramkörök általában nem örülnek. Sőt, az adatlapok szerint néhány volt negatív feszültség hatására tönkre is mennek. Ne kapcsoljunk kondenzátort sorosan két áramkör közé.
Ugyanakkor érdemes tudni, hogy bizonyos esetekben erre van szükség. A HIFI hangerősítők bemenete mindig egy soros kondenzátorral kezdődik, ami leválasztja az áramkört a megelőző áramkör tápfeszültségéről. Azonban ez a kondenzátor a hangoknak megfelelő frekvenciákat átengedi. Leválasztó kondenzátornak nevezik. Az analóg technika, ez nagyon más mint a digitális világ.

Itt a kapcsolás betölthető mentése: Letöltés

8. feladat: Tranzisztoros alapkapcsolás, LED kapcsolása

Ezen a kapcsoláson megjelent egy tranzisztor. A tranzisztor lényeges tulajdonsága hogy a bázisán befolyó áram sokszorosát engedi át a kollektorán. A sokszoros, az jelen esetben 200, mert az erősítését (Gain) 200-ra állítottam be. Mivel a tranzisztor hasonlít két összeépített diódára, nem csodálkozunk, hogy van nyitó feszültsége, ami 0.7V, ez is beállítható.

A fenti áramkörben a tranzisztor bázisával sorba kötött ellenállással állítottam be a bázis áramot. Ennek 200 szorosa folyhat a kollektorban, ha egyébként az ottani alkatrészek ezt megengedik. Jelen pillanatban egy LED és egy azzal sorba kötött áramkorlátozó ellenállás található ott, és a műszer mutatja, hogy a LED éppen világít, és kb. 20mA áram folyik át rajta. Jelen pillanatban a tranzisztor túl van vezérelve, olyan nagy áram folyik be a bázisán, hogy a kollektor áram nem lehet ennek 200 szorosa, hiszen a kollektor körben lévő ellenállás ezt nem teszi lehetővé. A digitális technikában ez nagyon gyakori megoldás, ilyen túlvezérelt állapotban a tranzisztor tutira teljesen nyitva van, és a maximális áram folyik a kollektoron.

Érdemes a kapcsolásban változtatni a bázis ellenállás illetve ezáltal a befolyó áram értékét. Kb. 20mA/200=100mikroA környékén a kollektor árama, azaz a LED árama elkezd csökkenni. Az összefüggés nem lineáris, így nem pont ennél az áramnál tapasztalunk változást. A bázis ellenállás ebben a kapcsolásban 20-50Kohm-ig növelhető a kollektor áram lényeges csökkenése nélkül.

Itt a kapcsolás betölthető mentése: Letöltés

9. feladat: LED bekapcsolása tranzisztorral

Ez a kapcsolás gyakorlatilag ugyanaz, mint az előző, csak a tranzisztor bázisáramát kapcsolgatjuk egy kapcsolóval. A kapcsolót a kis négyzet alakú felületbe kattintással lehet bekapcsolni, ha elengedjük az egér bal gombját, a kapcsoló is kikapcsol. Bekerült egy ellenállás a bázis és a föld közé! Vajon miért? Érdemes kivenni ezt az ellenállást és kipróbálni, hogy mi történik. Az ellenállás nélküli furcsa működésért a valóság és a szimulátor is felelős. A tranzisztor bázisában bekapcsolt állapotban töltések gyűlnek össze. Ha kikapcsoljuk a kapcsolót, egy rövid ideig ezek a töltések még bekapcsolva tartják a tranzisztor, így a kikapcsolás késleltetve következik be. A szimulátor annyiban más mint a valóság, hogy a tranzisztor belső modelljébe elfelejtették betenni azt a belső ellenállást, ami rövid, de mérhető időn belül kiüríti a töltéseket, és a tranzisztor kikapcsol. Így a szimulátor, ha nem rakjuk be a bázis és föld közé az ellenállást, ki sem kapcsol, folyamatosan áramot kap a LED és világit. A valóság nem ilyen, igen rövid ideig tart a bekapcsolt állapot. Nagyfrekvenciás kapcsolások esetén azonban már ez is zavaró lehet, ezért a bázis-föld ellenállást a valóságban is be szokták tenni. Persze ilyenkor nem kapcsoló kapcsolgat, hanem valamilyen áramkör kimenő feszültsége, és a késleltetés megaherczes kapcsolgatási frekvencia tartományban kezd zavaró lenni. A bázisból a töltések a megelőző áramkör kimenete felé is kiürülhetnek, ezért van amikor mégsem kell oda betenni az ellenállást. Ezt az dönti el, hogy mi kapcsolgatja a tranzisztort.

Letölthető szimulációs állomány: Letöltés

10. feladat: induktív terhelés kapcsolgatása tranzisztorral

Ebben a kapcsolásban mindössze a LED-et cseréltem ki egy induktivitásra. Ilyen eszköz pl. a relé. A relé kontaktusát egy tekercs mágneses tere rántja össze. Tehát a relének van induktivitása. Amikor a tranzisztor bekapcsol és a relé áramot kap, elkezd felépülni a mágneses tér, és ez a rajta átfolyó áramot létrehozó feszültséggel ellentétes feszültséget hoz létre, ami egy pillanatra megakadályozza, hogy a tekercsen áram haladjon át. Ahogy ez az ellentétes feszültség csökken, nő az áram. Ezt a pillanatot a plotteren a lefele mutató kis tüske mutatja. Ezzel nincs probléma, mert a tranzisztor kollektorán található 5V feszültséget csökkenti. Emlékezzünk, kikapcsolt állapotban a tranzisztor kollektorán 5V feszültség van, ezért nem folyik áram az ellenálláson, hiszen rajtuk a feszültség éppen 0V. A bekapcsolás után kis idő múlva beáll az egyensúly és a tekercsen átfolyó áramot a soros ellenállás határozza meg. Az ábrán épp megállítottam a szimulációt, hogy lefotózzam a képernyőt, ezért elengedtem a kapcsolót, így nem látszik az átfolyó áram nagysága. Elárulhatom 38mA volt éppen (5V/130ohm).

Ez eddig ok! Mi van akkor, ha elengedjük a kapcsolót. A tekercs ekkor lebontja a mágneses terét, miközben igyekszik fenntartani a rajta átfolyó áramot. Ehhez azonban most is ellentétes irányú feszültség kell, ami ugyebár most éppen a tápfeszültséggel azonos irányú lesz, tehát a tápfeszhez hozzáadódik. Láthatjuk is a felfelé mutató csúcsot. Egy rövid időre itt jelentős feszültség alakulhat ki. A plotter maximális ábrázolható feszültségét 15V-ra kellett állítani, hogy a feszültség csúcs elférjen. A plotter nem tudta kirajzolni, de itt bizony még ennél is nagyobb feszültség lehet. Olyan nagy, ami egy valóságos tranzisztort tönkre is tehet. Egy átlagos tranzisztor a földre kötött emitter és a kollektor között 30-40V feszültséget visel el károsodás nélkül. Érdemes kísérletezgetni az induktivitás nagyságával, és a soros ellenállás értékével is, hogy mi történik. Meglepő dolgokat fogunk tapasztalni. Ebben a pillanatban megértheti a kapcsoló üzemű Step-Up (feszültség növelő) tápegységek működését, az aki nagyon figyelt. Az a keletkező plusz feszültséget már csak egy kondenzátorra kell vezetni egy diódán keresztül, és van egy 10-15V körüli feszültségű pontunk. A kondenzátor persze gyorsan lemerül, ha nagy a terhelés, ezért a terhelés növekedésével növelni kell a kikapcsolások számát, ami a feszültséget előállítja. És készen is vagyunk a kapcsoló üzemű tápegységgel.

Ennél a szimulációnál a sebességet alaposan lecsökkentettem, hogy látahatók legyenek a ki és bekapcsolás feszültség tüskék. A plotteren látható, hogy egy osztás 1 milisec, vagyis a tranziensek egy ezred másodpercnél lényegesen rövidebbek.

Letölthető szimulációs állomány: Letöltés

11. feladat: induktív terhelés tranzisztoros bekapcsolása túlfeszültség védelemmel

Az áramkör szinte ugyanaz, csak bekerült egy dióda, ami a tranzisztor bekapcsolt állapotában épp záró irányban van, így ekkor semmi szerepe. A bekapcsolás lefelé mutató feszültség tüskéje továbbra is látható a plotteren. Azonban kikapcsoláskor az ellentétes feszültség pont kinyitja. Nyitott állapotban esik a diódán 0.7V feszültség, ezért valami kicsike megmaradt a felfelé mutató feszültség tüskéből. A tekercs lebomló mágneses terének energiája a diódán folyik át, aminek ez természetesen meg sem kottyan (ampereket képes egy átlagos dióda rövid ideig elviselni), viszont a tranzisztorunkat megvédte a túlfeszültségtől. Jegyezzük meg: ha nincs a tekercsen dióda, a készítő idióta!

Ezen az ábrán nagyot alkottam, egyik kezemmel kapcsolgattam a kapcsolót, a másikkal megnyomtam a printscreen gombot, így látható a benyomott kapcsolónál folyó áram is.

Letölthető szimulációs állomány: Letöltés

12. feladat: LED bekapcsolása FET tranzisztorral

Hagyományos tranzisztorokat a digitális technikában már alig használnak, helyette a FET (térvezérlésű) tranzisztorok terjedtek el. Lássuk, hogy miért?! A FET tranzisztor „Source” és „Drain” kivezetésén átfolyó áramot valóban feszültséggel lehet vezérelni, és nem árammal, mint a hagyományos tranzisztornál. A „Gate” kivezetésre kötött feszültség egy elzárt térrész töltés viszonyait úgy változtatja meg, hogy áramot kezd vezetni a tranzisztor anyaga. Ha nincs feszültség a bemeneten, akkor viszont a tranzisztor irdatlanul nagy ellenállást mutat. Így ezek az áramkörök kikapcsolt állapotban gyakorlatilag nulla áramot fogyasztanak. Tökéletesek elemes táplálású berendezésekbe. Ilyen tranzisztorok alkotják az általunk nagyon is kedvelt Arduino alaplap mikrovezérlőjét is.

Nincs sok magyarázni való a működésen. Ha kipróbáljátok a kapcsolást, változtassátok a tranzisztor Gate elektródájára kötött feszültséggel sorba kötött ellenállás értékét. Tapasztalni lehet, hogy semmi hatása, hiszen ott nem folyik áram. Jó nagy értéket raktam be 1Mohm-ot, de lehetne akár 100Mohm is. Az árammérő is nullát mutat. A LED szépen világít, a FET-en is alig esik valamicske feszültség, gyakorlatilag a FET 0V-ig képes lehúzni egy áramkör kimenetét. Ez egy ideális kapcsoló elem!

Letölthető szimulációs állomány: Letöltés

13. feladat: Arduino kapcsolás szimulálása

Elérkezett az idő, hogy egy Arduino alaplapot is berakjunk az alkatrészek közé. Ez lényegileg más működés, mert itt az Arduino programját is rá kell tudni tölteni az alaplapra, és a szimulátornak ezt futtatni is kell tudni. Az alkatrészek között többféle Arduino alaplapot találunk, sőt PIC és ATmega mikrovezérlőket is. Pl. az Arduino-ba beépített ATMega328 is szerepel az alkatrész listában. Sokáig kerestem az alkatrészek között kvarcz kristályt, mert ugye az kell egy ATmega328 működéséhez. Nincs! De nem is kell, azt nem kell ráépíteni, a szimulátor enélkül is szimulálja a működést, tehát a kvarcz kristállyal és a két kondenzátorral nem kell foglalkozni.

Maradjunk azonban az Arduino-nál, az látványosabb. Rajta van a panelen a beépített LED, ha a 13-as kivezetést kapcsolgatjuk a programmal, akkor világítani fog.

Fontos tudnivaló! A program betöltésére szolgáló felületet teljes képernyős módban, jobbról balra húzva hívhatjuk elő (ha épp nincs már ott), és ide kell betöltenünk a programot, amit az Arduino-n akarunk majd futtatni. Mivel az Arduino IDE programot használja a szimulátor, meg kell adnunk annak elérési útvonalát. Töltsük be a programot az Open (Ctrl+O) ikonnal a szerkesztő felső részén látható ikonsorral. Kattintsunk jobb egérgombbal a program nevét tartalmazó fülecskére:

…és válasszuk a Set Compile Path menüpontot. Itt csak az Arduino IDE könyvtárát kell kiválasztani, nem kell belső könyvtárakba belemenni. Nálam az Arduino IDE a C:\Program files (x86) könyvtárban települt:

Ezzel készen is vagyunk.

Azonban még egy megfigyelésem van. Csak akkor fordítja le a programot a program az Arduino IDE segítségével, ha előtte az Arduino IDE programmal is megnyitottuk a forráskódot, vagy egy új program készítése során bemásoltuk a az Arduino IDE szerkesztőjébe, és elmentettük az Arduino IDE alap könyvtárába, azaz a Dokumentumok/Arduino könyvtáron belül valahová. Innen kell megnyitni az elmentett forrást a SimulIDE programmal a felső ikonsoron az „Open”-el. Ezt csak egyszer kell megtenni, és a forrásot lehet változtatni, szerkeszteni a fordítás rendben lezajlik. Ha azonban a forráskódot nem az Arduino alap könyvtárából nyitjuk meg, akkor nem fordul le a program. A fordítás a pipa ikonnal történik:

Ha a fordítás szinte azonnal lezajlik akkor valami nem stimmel. Ha minden rendben van, akkor még az egyszerű programmal is küzd pár másodpercet a program és alul a fekete mezőben kiírja, hogy Compilation OK!

Ezt követően a pipától közvetlenül balra található ikonnal feltölthetjük a szimulált Arduino-ra a program futtatható bináris állományát (Upload). Ez után már indíthatjuk is a szimulációt. A képen létható áramkör egy fényerőszabályzott világításkapcsoló programját tartalmazza.

Itt a letölthető szimulációs állomány: Letöltés

Ha betöltitek a fenti állományt, az a futtatható HEX állományt is tartalmazza, tehát azonnal elindítható a szimuláció. Azonban hasznos lehet a programot is betölteni a jobb oldali szerkesztő felületbe és azt is megnézni. Az Arduino program forráskódját és a részletes működés leírását itt találjátok!

Kipróbáltam, tényleg pont úgy működik, mint a valóságban. Direkt egy bonyolultabb programot választottam, a primitív „Blink” program nem volt számomra elegendően meggyőző. Az ábrán a LED éppen fekete, mert nem világít, de ha betöltitek a mentett szimulátor állományt, és a programot, akkor életre kel mint a Skynet. Ehhez persze ne felejtsétek el megnyomni az áramkör tervező felület tetején az ikonsoron a bekapcsoló gombot sem, ahogy Robert Brewster tette a „Gépek lázadása” epizódban. A program részletes leírását a forráskódok menüpontban találjátok a működés magyarázatával. (ez nem a Skynet, csak az oda vezető út)!

Még említést érdemel a debugger használata. Nem sokra képes ebben a szimulátorban. Soronként lehet léptetni a programot. Az ikonsoron ehhez az utolsó nyilacska szerű (Debug) gombját kell megnyomni. Kis várakozás következi, mert elő kell készíteni a terepet, és a bekapcsoló gomb kék színűre változik, de már nem kell megnyomni, fut a program, megy a szimuláció. Azonban az ikonsor megváltozik:

A szimpla nyíl (balról az első) szolgál a soronkénti léptetésre. Egy kis kék nyilacska jelzi, hogy éppen hol tartunk. A következő gomb a következő töréspontig futtatja a programot. Töréspontot a sor elején nyomott jobb klikkel lehet kitenni. Így átugorhatjuk az éppen unalmas részeket. Az ábrán már kitettem egy töréspontot:

13. feladat: soros monitor használata a szimuláció során

Sajnos úgy tapasztaltam, hogy az I2C buszt nem tudja kezelni a szimulátor. Pontosabban tudja az Arduino oldalát, de nincs I2C buszra köthető LCD kijelző. Van I2C ram és I2C párhuzamos port konverter IC, de ezek nem működnek csak a példa programok között szereplő szoftveresen szimulált I2C programmal. Így a szimulátorból nagyon nehéz adatokat kinyerni, főleg, hogy a globális változókat se lehet megjeleníteni. Ez utóbbi lehetséges, hogy csak verzió hiba, és csak várni kell, míg javítják. Láttam videót amin még működött!

Jó hír viszont, hogy a soros monitor működik. A soros monitor indításához jobb egérgombbal kell kattintani az Arduino alaplapra a tervező panelen, és elindítani:

A soros monitor sebességét nem lehet beállítani. A programban 9600 boud-ot állítottam be, és azzal működött. Egy mintaprogramot töltöttem a szimulátorba, ami fogadott is billentyű lenyomást a soros monitoron keresztül, így teljeskörűen ki tudtam próbálni. Tökéletes!

És itt egy kép a működő soros monitorról, bár ezen most csak a fogadott adatok látszanak:

Az áramkörben a nyomógombot lenyomva jött a „Bemenet állapota:0” felirat a monitoron.

14. feladat: valós, működő program szimulálása, belső eeprom stb. használata

A projektek menü pontban található egy kapcsolás, amivel egy szennyvíz szivattyú működését lehet figyelni, és ha valami baj van, akkor az áramkör figyelmeztetéseket tud küldeni, melyek segítségével megakadályozható a szivattyú idő előtti tönkremenetel. A kapcsolás azért tűnik jó bemutató programnak, mert használ hétszegmenses számkijelzőt, analóg bemenetet és a belső eeprom-ba is ír adatokat, illetve olvas is onnan adatokat. Új tulajdonságait lehet ezek segítségével felfedezni a szimulátornak. Az áramkör funkcióját és részletes működését megismerheted innen. A kapcsolási rajz:

Itt a letölthető szimulációs állomány: Letöltés

Betöltés után a program már azonnal működik a szimuláció indításakor, de esetleg érdekes lehet a forrást megnézni működés közben. Azért att a futtatható program amit a szimulátor jobb oldalai részébe kell bemásolni, lefordítani és firmware-ként feltölteni az ATmega chip-be:

#include <EEPROM.h>
#define tombmeret 10		//mekkora legyen a ramban tárolt adaok mennyisége
#define eepromkimeles 0   //ha értéke egy, akkor csak minde 5. indítás után tárolunk az eepromban, ha 0 akkor minden indításkor
#define felejtes_ido 86100  //ennyi idon belüli szivattyú indítások számát jelezzük ki. 86100sec=24 óra
byte hossz=0;
long kijelzes=millis();
byte lekerdezes_fazis=0; //lekérdezést indítottak ha nagyoibb mint nulla. egyben a lekérdezés fázisait is jelzi
byte lekerdezes_tipus=0; //darablekérdezés jelzésére
char szamjegy_db[]={'0','0','0','0'};
byte szamjegy_db_num[]={0,0,0,0};
char szamjegy_ido[]={'0','0','0','0'};
byte l=0;
byte motor_on=0;
byte motor_on_state=0;
byte szintjelzes=0;
long s1=0;	//eeprom-ból kiolvasottértékek átmeneti tárolása
long s2=0;	//eeprom-ból kiolvasottértékek átmeneti tárolása
long s3=0;	//eeprom-ból kiolvasottértékek átmeneti tárolása
byte k=0;
int inditas_db=0;
int korrekcio=0;
long ido_tmp=millis();	//a másodpercenkénti figyeléshez
long ido_tmp2=millis();	//a 100msec-es figyeléshez
long ido_tmp3=millis();	//a 100msec-es figyeléshez
long ido_tmp4=millis();	//a 100msec-es figyeléshez
long bekapcs_ido=0;	//az aktuális szivattyú működés időtartama
long ido[tombmeret]; 	//ebben mérjük a bekapcsolások között eltelt időt
long bekapcsolva[tombmeret];  //az egyes bekapcsolások ideje (szivattyú működési idő
byte tarolasjelzes[tombmeret]; //a tartalmában levo 1-es hatasara taroljuk az adatokat az epromban
byte sw_elozo_allapot=LOW;  //szivattyú működés előző ciklusban érzékelt állapota
long o_ido=0;		//tömb elemek összeadásakor használt változó
int tombindex=0;
byte villogas=0;
byte veszjelzesmemo=0;
long veszjelz_ido=millis();
byte veszjelz_tmp=0;
byte motor_megy=LOW; //szivattyú működéseklor HIGH, ha nem működik, akkor LOW
int aram_kuszob=0; //szivattyú áramküszöb értéke, mely felett működőnek tekintjük (áramváltó alap feszültsége+3+alapfesz/5)
byte motor_tularam=LOW; //szivattyú tuláram esetén HIGH, alatta LOW, Bármilyen lekérdezés LOW-ba billenti
byte szintjelzo_alapertek=0;  //ha nincs vészjelzés, akkor LOW vagy HIGH értéket vesz fel.
byte szintjelzo_on=0;

//hétszegmenses kijlző kimenetek
// szegmens betüjele:            A,  F,   B,  G,  E,  C , D
//ATmega328chip kivezetés:       13, 14,  15, 16, 17, 18, 19 
//Arduino kivezetés:             7,  8,   9,  10, 11, 12, 13
//   -       A       Szegmensek: 1   -  B,C
//  | |   F     B                2   -  A,B,G,E,D 
//   -       G                   3   -  1,3,4,6,7
//  | |   E     C                4   -  F,B,G,C
//   -       D                   5   -  A,F,G,C,D
//                               6   -  A,F,G,E,C,D
//                               7   -  A,B,C
//                               8   -  A,F,B,G,E,C,D
//                               9   -  A,F,B,G,C,D
//                               0   -  A,F,B,E,C,D
//                               forgas1   -  G
//                               forgas2   -  C
//                               forgas3   -  D
//                               forgas4   -  E
//                               telijelzés1   -  D
//                               telijelzés2   -  G
//                               telijelzés3   -  A
//                A F B G E C D 
byte szegmens[]= {0,0,0,1,0,0,0,   //0, 0
                  1,1,0,1,1,0,1,    //1, 7
                  0,1,0,0,0,1,0,    //2, 14
                  0,1,0,0,1,0,0,    //3, 21
                  1,0,0,0,1,0,1,    //4, 28
                  0,0,1,0,1,0,0 ,    //5, 35
                  0,0,1,0,0,0,0,    //6, 42
                  0,1,0,1,1,0,1,    //7, 49
                  0,0,0,0,0,0,0,    //8, 56
                  0,0,0,0,1,0,0,    //9, 63
                  1,1,1,0,1,1,1,    //f1, 70
                  1,1,1,1,1,0,1,    //f2, 77
                  1,1,1,1,1,1,0,    //f3, 84
                  1,1,1,1,0,1,1,    //f3, 91
                  1,1,1,1,1,1,0,    //s1, 98
                  1,1,1,0,1,1,1,    //s2, 105
                  0,1,1,1,1,1,1,    //s3, 112
                  1,1,1,1,1,1,1};   //ures, 119

byte pin_szegmens[] = {7,  8,  9, 10, 11, 12, 13};
// szegmens betüjele:  A,  F,  B,  G,  E,  C , D
//chip kivezetés:     13, 14, 15, 16, 17, 18, 19 



void setup()
{
  //tömbök indulú értékének feltöltése
  for (byte i=0;i<5;i++) {	ido[i]=0;bekapcsolva[i]=0;tarolasjelzes[i]=0;}
  tarolasjelzes[0]=1;
  pinMode(2,INPUT); //motor működés érzékelő bemenet //4. chip kivezetés
  pinMode(3,INPUT); //"teli" szintjelző érzékelő bemenet //5. chip kivezetés
  pinMode(4,INPUT); //indításszám lekérdezés gomb //6. chip kivezetés
  pinMode(5,INPUT); //működési idő lekérdezés gomb //11. chip kivezetés
  pinMode(6,OUTPUT);  // kijelző tizedespont 12. chip kivezetés
  pinMode(7,OUTPUT);  //13. chip kivezetés
  pinMode(8,OUTPUT);  //14. chip kivezetés
  pinMode(9,OUTPUT);  //15. chip kivezetés
  pinMode(10,OUTPUT);  //16. chip kivezetés
  pinMode(11,OUTPUT);  //17. chip kivezetés
  pinMode(12,OUTPUT);  //18. chip kivezetés
  pinMode(13,OUTPUT);  //19. chip kivezetés
  digitalWrite(6,HIGH);
  digitalWrite(7,HIGH);
  digitalWrite(8,HIGH);
  digitalWrite(9,HIGH);
  digitalWrite(10,HIGH);
  digitalWrite(11,HIGH);
  digitalWrite(12,HIGH);
  digitalWrite(13,HIGH);
  pinMode(0,INPUT);  //2. chip kivezetés gyári alapbeállítás, ha lenyomjuk a gombot, törlődnek az eeprom tartalmak 
  if (digitalRead(0)==LOW) {
    //eeprom törlés, gyári alapbeállítás
    EEPROM.write(0,0);
    EEPROM.write(1,0);
    EEPROM.write(2,0);
    EEPROM.write(3,0);
    EEPROM.write(4,0);
    EEPROM.write(5,0);
    EEPROM.write(6,0);
    EEPROM.write(7,0);
    EEPROM.write(9,digitalRead(3));       //szintjelző alapállapotának lekérdezése
    szintjelzo_alapertek=digitalRead(3);  //szintjelző alapállapot változó feltöltése
    for (byte i=0;i<10;i++) 
    {
      aram_kuszob=aram_kuszob+analogRead(A2);  //összegezzük a 10 mérés eredményét
      delay(500);
    }
    aram_kuszob=aram_kuszob/10;               //egyenirányitó átlagos nyugalmi offszet feszültségének számítása  
    aram_kuszob=aram_kuszob+3+(aram_kuszob/5);//nyugalmi offszetnél kicsival nagyobb áramküszöb számítása
    EEPROM.write(8,aram_kuszob);  //áramküszöb tárolása
    //tizedespont villogtatás, ami a gyári alapbeállítást jelzi
    digitalWrite(6,LOW);
    delay(200);
    digitalWrite(6,HIGH);
    delay(200);
    digitalWrite(6,LOW);
    delay(200);
    digitalWrite(6,HIGH);
    delay(200);
    digitalWrite(6,LOW);
    delay(200);
    digitalWrite(6,HIGH);
  }
  else
  {
    digitalWrite(6,LOW);
    delay(1000);
    digitalWrite(6,HIGH);
    aram_kuszob=EEPROM.read(8);  //tárolt áramküszöb kiolvasása
    szintjelzo_alapertek=EEPROM.read(9);  //tárolt szintjelző alapállapot kiolvasása
  }

}

void loop()
{
  if (motor_tularam==HIGH) {digitalWrite(6,LOW);} else {digitalWrite(6,HIGH);}
  //lenyomták a db lekérdezést és éppen nem fut db vagy ido lekérdezés
  if (digitalRead(4)==LOW && lekerdezes_fazis==0) {lekerdezes_fazis=1;lekerdezes_tipus=1;motor_tularam=LOW;kijelzes=millis();}
  //lenyomták az ido lekérdezést és éppen nem fut db vagy ido lekérdezés  
  if (digitalRead(5)==LOW && lekerdezes_fazis==0) {lekerdezes_fazis=1;lekerdezes_tipus=2;motor_tularam=LOW;kijelzes=millis();}
  //lenyomták az gyári alapbeállítás gombot és éppen nem fut db vagy ido lekérdezés  
  if (digitalRead(0)==LOW && lekerdezes_fazis==0) {lekerdezes_fazis=1;lekerdezes_tipus=3;motor_tularam=LOW;kijelzes=millis();}
  //lenyomták a db lekérdezést és éppen nem fut db vagy ido lekérdezés  
  if (lekerdezes_fazis>0 && millis()>kijelzes) {lekerdezes(lekerdezes_tipus);}
  //egy másodpercenként ellenőrizzuk, hogy megy e a motor és adminisztrálunk
  if (millis()>ido_tmp+1000) {inditas_szamlalo();ido_tmp=millis();}
  //1,2 másodpercre beállítjuk a vizszint vészjelzést ha bekapcsolt a vész vizszintjelző
  if (millis()>ido_tmp3+1200) {if (motor_on!=1) {szintjelzes=1;}}
  if (millis()>ido_tmp3+2400) {	ido_tmp3=millis();szintjelzes=0;k=0;}
  //Szintjelző tárolt alapértéke alapján a vészjelzés detektálása szintjelzo_on=HIGH jelzi a magas folyadékszintet 
  szintjelzo_on=LOW;
  if (szintjelzo_alapertek==LOW) {if (digitalRead(3)==HIGH) {szintjelzo_on=HIGH;}} 
  else {if (digitalRead(3)==LOW) {szintjelzo_on=HIGH;}}
  //bekapcsolt a vész szintjelző kapcsoló, sorban villogtatjuk a függőleges pálcikákat 3 másodpercenként
  if (szintjelzes==1 && szintjelzo_on==HIGH && lekerdezes_fazis==0)  
  {
    if (millis()>ido_tmp2+200) 
    {
      for (byte i=0;i<7;i++) {digitalWrite(pin_szegmens[i],szegmens[98+(k*7)+i]);}
      k++;if(k==3) {k=0;}
      ido_tmp2=millis();
    }
  }
  //éppen működik a motor, sorban körbe villogtajuk az alsó 4 szegmenst
  if (motor_on==1 && szintjelzes==0 && lekerdezes_fazis==0) 
  {
    if (millis()>ido_tmp2+200) 
    {
      //motor működik	
      for (byte i=0;i<7;i++) {digitalWrite(pin_szegmens[i],szegmens[70+(motor_on_state*7)+i]);}
      motor_on_state++;if (motor_on_state==4) {	motor_on_state=0;}
      ido_tmp2=millis();
    }			
  }
  //sem a motor nem működik, sem vész vizszint kijelzés nincs
  //kijelezzük az utolsó 24 óra indításainak számát
  if (motor_on==0 && szintjelzes==0 && lekerdezes_fazis==0) 
  {
    if (millis()>ido_tmp2+200) 
    {
      if (inditas_db==8) {villogas++;} else {villogas=1;}
      if (villogas==2) {for (byte i=0;i<7;i++) {digitalWrite(pin_szegmens[i],szegmens[119+i]);}}
      if (villogas==1) {for (byte i=0;i<7;i++) {digitalWrite(pin_szegmens[i],szegmens[inditas_db*7+i]);}}
//      for (byte i=0;i<7;i++) {digitalWrite(pin_szegmens[i],szegmens[inditas_db*7+i]);}
      if (villogas==2){villogas=0;}
      ido_tmp2=millis();
    }
  }
}	


void lekerdezes(byte lek_tip)
{
  lekerdezes_fazis++;
  veszjelzesmemo=0;
  if (lekerdezes_fazis==11) {lekerdezes_fazis=0;}
  //10.fazis kikapcsoljuk a szegmenseket
  if (lekerdezes_fazis==10) 	{for (byte i=0;i<7;i++) {digitalWrite(pin_szegmens[i],szegmens[119+i]);} kijelzes=kijelzes+1000;}
  //9.fazis kikapcsoljuk a szegmenseket
  if (lekerdezes_fazis==9) 	{for (byte i=0;i<7;i++) {digitalWrite(pin_szegmens[i],szegmens[szamjegy_db_num[3]+i]);}kijelzes=kijelzes+600;}
  //8.fazis kikapcsoljuk a szegmenseket
  if (lekerdezes_fazis==8) 	{for (byte i=0;i<7;i++) {digitalWrite(pin_szegmens[i],szegmens[119+i]);} kijelzes=kijelzes+300;}
  //7.fazis kikapcsoljuk a szegmenseket
  if (lekerdezes_fazis==7) 	{for (byte i=0;i<7;i++) {digitalWrite(pin_szegmens[i],szegmens[szamjegy_db_num[2]+i]);}kijelzes=kijelzes+600;}
  //6. fazis kikapcsoljuk a szegmenseket
  if (lekerdezes_fazis==6) 	{for (byte i=0;i<7;i++) {digitalWrite(pin_szegmens[i],szegmens[119+i]);} kijelzes=kijelzes+300;}
  //5. fazis kikapcsoljuk a szegmenseket
  if (lekerdezes_fazis==5) 	{for (byte i=0;i<7;i++) {digitalWrite(pin_szegmens[i],szegmens[szamjegy_db_num[1]+i]);}kijelzes=kijelzes+600;}
  //4. fazis kikapcsoljuk a szegmenseket
  if (lekerdezes_fazis==4) 	{for (byte i=0;i<7;i++) {digitalWrite(pin_szegmens[i],szegmens[119+i]);} kijelzes=kijelzes+300;}
  //3. fazis kikapcsoljuk a szegmenseket
  if (lekerdezes_fazis==3) 	{for (byte i=0;i<7;i++) {digitalWrite(pin_szegmens[i],szegmens[szamjegy_db_num[0]+i]);}kijelzes=kijelzes+600;}
  //első fázis, kiolvassuk az értéket és előkészítjük a megjelenítés változóit
  if (lekerdezes_fazis==2) 	
  {
    for (byte i=0;i<7;i++) {digitalWrite(pin_szegmens[i],szegmens[119+i]);} 
    kijelzes=kijelzes+1000;
    o_ido=0;
    l=0;
    if (eepromkimeles==1)
    {
      for (byte i=0;i<10;i++)
      {
        if (bekapcsolva[i]>0) {o_ido=o_ido+bekapcsolva[i];l++;}
      }
    }		
    if (lekerdezes_tipus==1) {s2=EEPROMReadlong(4);s2=s2+l;}
    if (lekerdezes_tipus==2) {s2=EEPROMReadlong(0);s2=(s2+o_ido)/60;}
    if (lekerdezes_tipus==3) {s2=analogRead(A2);s2=s2*488;s2=s2/100;}
    char_konv(s2);
    for (byte i=0;i<4;i++)
    {
      if (szamjegy_db[i]=='0'){szamjegy_db_num[i]=0;}
      if (szamjegy_db[i]=='1'){szamjegy_db_num[i]=7;}
      if (szamjegy_db[i]=='2'){szamjegy_db_num[i]=14;}
      if (szamjegy_db[i]=='3'){szamjegy_db_num[i]=21;}
      if (szamjegy_db[i]=='4'){szamjegy_db_num[i]=28;}
      if (szamjegy_db[i]=='5'){szamjegy_db_num[i]=35;}
      if (szamjegy_db[i]=='6'){szamjegy_db_num[i]=42;}
      if (szamjegy_db[i]=='7'){szamjegy_db_num[i]=49;}
      if (szamjegy_db[i]=='8'){szamjegy_db_num[i]=56;}
      if (szamjegy_db[i]=='9'){szamjegy_db_num[i]=63;}
    }
  } 
}

void inditas_szamlalo()
{
  //idő számlálók növelése. Másodperc alapon mérünk időt
  bekapcs_ido++;ido_tmp=millis();
  ido[0]=ido[0]+1;
  o_ido=0;
  s3=0;
  // sorban összeadjuk a tömbben tárolt időt, ha elértük a 24 órát (86400 másodperc), akkor megállunk
  //az utolsó 24 óra szivattyú indításainak számára vagyunk kíváncsiak
  while (o_ido<felejtes_ido && s3<tombmeret-1 && ido[s3]!=0)
  {
    o_ido=o_ido+ido[s3];
    s3++;
  }		
  inditas_db=s3-1;
  if (inditas_db==8) {veszjelzesmemo=1;}
  if (tarolasjelzes[tombmeret-1]==1 && eepromkimeles==1) //tarolásjelzés, tehát az utolsó 5 adatot összgezni és tárolni kell
                                                         //de csak akkor, ha az eeprom kimélés be van kapcsolva
  {
    //Összeadjuk a tömbben tárolt bekapcsolási időket, és közben töröljük az összeadottakat
    //a törlés csak a működés nyomnkövethetősége miatt lett beépítve
    o_ido=0;
    for (byte j=0;j<5;j++) {	o_ido=o_ido+bekapcsolva[tombmeret-j-1];bekapcsolva[tombmeret-j-1]=0;}
      //osszes szivattyú működési idő kiolvasása az eeprom-ból és hozzáadjuk az utóbbi tárolt bekapcsolási időket
    s1=EEPROMReadlong(0);
    s1=s1+o_ido;
      //Összes eddigi indításszám kiolvasása eeprom-ból és hozzáadjuk az utóbbi tárolt indítás számot
    s2=EEPROMReadlong(4);
    EEPROMWritelong(0,s1);
    EEPROMWritelong(4,s2+5); //értékek írása az eeprom-ba
    tombindex=0;
    tarolasjelzes[tombmeret-6]=1;
    tarolasjelzes[tombmeret-1]=0;
  } //if vége
  motor_lekerdezes();
    //a szivattyú indítás felfutó éle, tároljuk a tömbb 0-as indexén az utolsó indítás óta 
    //eltelt időt és tároljuk ennek változóját, töröljuk a bekapcsolási idő változóját
  if (motor_megy && !sw_elozo_allapot) {  bekapcs_ido=0;motor_on=1;}
    //szivattyú kikapcsolása (lefutó él). Minden értéket lejjebb másolunk a tömbben és
    //tároljuk a bekapcsolási idő számlálást
  if (!motor_megy && sw_elozo_allapot) 
  {
    bekapcsolva[0]=bekapcs_ido;
    motor_on=0;
    for (byte j=tombmeret-1;j>0;j--)
    {
      bekapcsolva[j]=bekapcsolva[j-1];
      ido[j]=ido[j-1];
      tarolasjelzes[j]=tarolasjelzes[j-1];
    }
    bekapcsolva[0]=0;ido[0]=0;tarolasjelzes[0]=0;
    if (eepromkimeles==0) //eeprom kimélés kikapcsolva, így minden motor működés után beírjuk az adatokat az eeprom-ba
    {
      //Összes eddigi működési idő kiolvasása eeprom-ból és hozzáadjuk a legutóbbi működési időt
      s1=EEPROMReadlong(0);
      s1=s1+bekapcs_ido;
      EEPROMWritelong(0,s1);
      //Összes eddigi indításszám kiolvasása eeprom-ból és hozzáadunk egyet
      s2=EEPROMReadlong(4);
      EEPROMWritelong(4,s2+1); //értékek írása az eeprom-ba
    }
  }
  sw_elozo_allapot=motor_megy; //fel és lefutó él érzékeléséhez
}


//Ez a funkció kiír egy 4 byte (32bit) long változót az eeprom-ba
void EEPROMWritelong(int address, long value)
{
	//szétszedjük byte-okra a long-ot
	byte four = (value & 0xFF);
	byte three = ((value >> 8) & 0xFF);
	byte two = ((value >> 16) & 0xFF);
	byte one = ((value >> 24) & 0xFF);
	//A 4 byte-os adat epromba �r�sa
	EEPROM.write(address, four);
	EEPROM.write(address + 1, three);
	EEPROM.write(address + 2, two);
	EEPROM.write(address + 3, one);
}

//Ez a funkció visszaolvas 4 byte (32bit) long változót az eeprom-ból
long EEPROMReadlong(long address)
{
	//4 bytes olvasása az eeprom-ból.
	long four = EEPROM.read(address);
	long three = EEPROM.read(address + 1);
	long two = EEPROM.read(address + 2);
	long one = EEPROM.read(address + 3);
	//4 byte long változóvá alakítása
	return ((four << 0) & 0xFF) + ((three << 8) & 0xFFFF) + ((two << 16) & 0xFFFFFF) + ((one << 24) & 0xFFFFFFFF);
}

void char_konv(int szam)
{
	String konv_string=String(szam);
	hossz=konv_string.length();
	for (byte j=0;j<4;j++) {szamjegy_db[j]='0';}
	for (byte i=0;i<hossz;i++) {szamjegy_db[4-hossz+i]=konv_string.charAt(i);}
}

void motor_lekerdezes()
{
if (!digitalRead(2) || analogRead(A2)>aram_kuszob) {motor_megy=HIGH;}  else {motor_megy=LOW;}
if (analogRead(A2)>analogRead(A1)) {motor_tularam=HIGH;}
}

Amennyiben szeretnénk megnézni a vezérlő eeprom-jának tartalmát egy egy futtatást követően, le tudjuk menteni állományba, ahonnan megnyitható és nézegethető. Ehhez kattintsunk jobb egérgombbal a vezérlőre a kapcsolási rajzon:

Így néz ki egy lementett állomány, amit néhány szivattyú indítás után csináltam:

12, 0, 0, 0, 3, 0, 0, 0, 42, 1, 255, 255, 255, 255, 255, 255
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255

15. feladat: LCD kijelző használata a szimulátorban

Sajnos a szimulátorban nincs I2C konverterrel működő LCD karakteres kijelző. Pedig ez nagyon kellene. Van azonban egy olyan megoldás, amiben az LCD kijelzőt (mert ez van: HD44780), egy I2C párhuzamos regiszter chip-hez kötjük. Ha jól értettem meg a problémát, akkor ez a chip viszont nem pont úgy működik mint az LCD kijelzőkhöz kapható I2C konverter áramkör (lásd LCD kijelzőről szóló írásom), ezért az LCD kijelzőkhöz található program könyvtárak nem működnek. A szimulátor alkotója azonban csinált egy kerülő megoldást. A szimulátor program könyvtárán belül (nálam itt: C:\SimulIDE_0.4.13-SR5_Win64\share\simulide\examples\Arduino) található egy szoftveres I2C LCD kezelő könyvtár. AZ ebben található Soft_LCD-I2C nevű almappát be kell másolni az Arduino IDE könyvtárai közé (nálam ide: C:\Program Files (x86)\Arduino\libraries ) és máris használható lesz a szoftveresen vezérelt I2C buszról működő kijelző.
Ennek bemutatására a projektekben található Ritkán működő gépek monitorozása V2.0 írásomban megvalósított feladatot választottam ki. Íme a kapcsolási rajz:

Látható a rajzon a kijelző és az azt megható I2C párhuzamos regiszter. A szoftveres I2C LCD meghatóhoz mellékelt a szimulátor készítője példa programot is, abból fejtettem meg a szükséges tudnivalókat. A kijelző használata előtt a következő kódrészletet kell a program legelejére (setup elé) beilleszteni:

#include <Soft_LCD-I2C.h>
uint8_t address = 80;
uint8_t sda = 18;
uint8_t scl = 19;
Soft_Lcd_I2C lcd( address, 16, 2, sda, scl );

Mint látható, az I2C busz SDA és SCL kivezetése bármelyik Arduino kivezetére beállítható. Itt most éppen arra állítottam be, ahol a hardveres I2C található az ATmega chip-en. A kijelző inicializálása a setup-ban: lcd.init(); Csak 2 sor 16 kerakteres kijelzőt kezel.

Fontos tudnivaló, hogy a szokásos LCD programkönyvtárakhoz képest (pl. LicuidCristal) egy kicsit más a függvények működése. Az lcd.print() függvénnyel nem lehet szöveget kiírni, helyette az lcd.printstr() függvényt kell használni. Úgy láttam az lcd.print() sem teljesen ugyanaz, vagy hibásan működik, mert bármilyen tartalomból csak az első karaktert jeleníti meg. Pl. “123” esetén csak “1” kerül ki a képernyőre. Ha azonban a változó amit ki szeretnénk vele íratni float, az jól jelenik meg. Pl. a motoráram kiírásakor az amperben kiírt áramerősség adat jó, de a zárójelbe mögé írt AD átalakítóból kinyert adat már nem. Nem is értettem egy darabig, hogyan lehet egyjegyű a számadat, miközben a bemenő feszültséget egyre feljebb tekertem.
Nem nagyok a változások, elviselhetők az esetleges hibák is, és ezért szerintem jól használható. Ha a szimulátorban fejlesztenénk, akkor majd a végleges hardverre készülő programban esetleg vissza kell a függvényeket alakítani, illetve a fent idézett “include…” programrészeket ki kell kicserélni a kiválasztott programkönyvtár hasonló soraira.
Itt a letölthető szimulációs állomány: letöltés

…és itt a forráskód, bár a szimuláció enélkül is elindítható, ha a fenti állományt betöltöd:

#include <EEPROM.h>
#include <Wire.h>          //I2C library 

#include <Soft_LCD-I2C.h>
uint8_t address = 80;
uint8_t sda = 18;
uint8_t scl = 19;
Soft_Lcd_I2C lcd( address, 16, 2, sda, scl );

#define tombmeret 10		 //mekkora legyen a ramban tárolt adaok mennyisége
#define eepromkimeles 0     //ha értéke egy, akkor csak minde 5. indítás után tárolunk az eepromban, ha 0 akkor minden indításkor
#define felejtes_ido 86100  //ennyi idon belüli szivattyú indítások számát jelezzük ki. 86100sec=24 óra
byte hossz=0;
long kijelzo_frissites=millis();
long lcd_vilagitas=millis();
char szamjegy_db[]={'0','0','0','0'};
byte szamjegy_db_num[]={0,0,0,0};
char szamjegy_ido[]={'0','0','0','0'};
byte l=0;
byte motor_on=0;
byte motor_on_state=0;
byte szintjelzes=0;
long s1=0;					//eeprom-ból kiolvasottértékek átmeneti tárolása
long s2=0;					//eeprom-ból kiolvasottértékek átmeneti tárolása
long s3=0;					//eeprom-ból kiolvasottértékek átmeneti tárolása
byte k=0;
int inditas_db=0;
int korrekcio=0;
long ido_tmp=millis();	//a másodpercenkénti figyeléshez
long ido_tmp2=millis();	//a 100msec-es figyeléshez
long ido_tmp3=millis();	//a 100msec-es figyeléshez
long bekapcs_ido=0;		//az aktuális szivattyú működés időtartama
long ido[tombmeret]; 			//ebben mérjük a bekapcsolások között eltelt időt
long bekapcsolva[tombmeret];		//az egyes bekapcsolások ideje (szivattyú működési idő
byte tarolasjelzes[tombmeret]; //a tartalmában levo 1-es hatasara taroljuk az adatokat az epromban
byte sw_elozo_allapot=LOW;	//szivattyú működés előző ciklusban érzékelt állapota
long o_ido=0;				//tömb elemek összeadásakor használt változó
int tombindex=0;
byte villogas=0;
byte veszjelzesmemo=0;
long veszjelz_ido=millis();
byte veszjelz_tmp=0;
byte motor_megy=LOW; //szivattyú működéseklor HIGH, ha nem működik, akkor LOW
int aram_kuszob=0;  //szivattyú áramküszöb értéke, mely felett működőnek tekintjük (áramváltó alap feszültsége+3+alapfesz/5)
byte motor_tularam=LOW; //szivattyú tuláram esetén HIGH, alatta LOW, Bármilyen lekérdezés LOW-ba billenti
byte szintjelzo_alapertek=0;  //ha nincs vészjelzés, akkor LOW vagy HIGH értéket vesz fel.
byte szintjelzo_on=0;
int aram_offset=0;
int motoraram=0;
int aramlimit=0;
int uts_max_motoraram=0;

//prellmentesítés segédváltozói
byte in_prev_state=0;     //bemenet előző állapota segéd változó
byte in_prell_tmp=0;      //bemenet prell figyeléshez segéd változó
long in_prell_time=0;     //bemenet prellmentesítéshez eltelt idő az első kontaktus óta, segéd változó
long out_setup_time_tmp=0;//kimenet kikapcsolási időhöz segéd változó
byte in_state=0;          //nyomógomb nyomvatartás alatt értéke 1
byte adat_valaszto=0; //az LCD kijelzett értéket határozza meg
bool k_frissit=LOW;

//hétszegmenses kijlző kimenetek
//   -       A       Szegmensek: 1   -  B,C
//  | |   F     B                2   -  A,B,G,E,D 
//   -       G                   3   -  1,3,4,6,7
//  | |   E     C                4   -  F,B,G,C
//   -       D                   5   -  A,F,G,C,D
//                               6   -  A,F,G,E,C,D
//                               7   -  A,B,C
//                               8   -  A,F,B,G,E,C,D
//                               9   -  A,F,B,G,C,D
//                               0   -  A,F,B,E,C,D
//                               forgas1   -  G
//                               forgas2   -  C
//                               forgas3   -  D
//                               forgas4   -  E
//                               telijelzés1   -  D
//                               telijelzés2   -  G
//                               telijelzés3   -  A
//                A F B G E C D 
byte szegmens[]= {0,0,0,1,0,0,0,   //0, 0
                  1,1,0,1,1,0,1,    //1, 7
                  0,1,0,0,0,1,0,    //2, 14
                  0,1,0,0,1,0,0,    //3, 21
                  1,0,0,0,1,0,1,    //4, 28
                  0,0,1,0,1,0,0 ,    //5, 35
                  0,0,1,0,0,0,0,    //6, 42
                  0,1,0,1,1,0,1,    //7, 49
                  0,0,0,0,0,0,0,    //8, 56
                  0,0,0,0,1,0,0,    //9, 63
                  1,1,1,0,1,1,1,    //f1, 70
                  1,1,1,1,1,0,1,    //f2, 77
                  1,1,1,1,1,1,0,    //f3, 84
                  1,1,1,1,0,1,1,    //f3, 91
                  1,1,1,1,1,1,0,    //s1, 98
                  1,1,1,0,1,1,1,    //s2, 105
                  0,1,1,1,1,1,1,    //s3, 112
                  1,1,1,1,1,1,1};   //ures, 119

byte pin_szegmens[] = {7,  8,  9, 10, 11, 12, 13};
// szegmens betüjele:  A,  F,  B,  G,  E,  C , D
//chip kivezetés:     13, 14, 15, 16, 17, 18, 19 


void setup()
{
  analogReference(DEFAULT);  //A tápfeszt veszi referenciának (5V)
  Wire.begin();              // I2C busz használat indítása
  lcd.init();

  //tömbök indulú értékének feltöltése
  for (byte i=0;i<5;i++) {	ido[i]=0;bekapcsolva[i]=0;tarolasjelzes[i]=0;}
  tarolasjelzes[0]=1;
  pinMode(2,INPUT); //motor működés érzékelő bemenet //4. chip kivezetés
  digitalWrite(2,HIGH);
  pinMode(3,INPUT); //"teli" szintjelző érzékelő bemenet //5. chip kivezetés
  digitalWrite(3,HIGH);
  pinMode(4,INPUT); //Kijelző világítás bekapcsoló gomb. 60 másodpercig világít, 
                    //jelenlegi hardverben nincs funkciója, mert a világítást egy külön nyomógomb direktben kapcsolja a kijelzőn
  digitalWrite(4,HIGH);
  pinMode(5,INPUT); //Kijelzett érték váltása az LCD kijelzőn és gyári alapbeállítás, ha nyomva van bekapcsoláskor
  digitalWrite(5,HIGH);
  
  // led kijelző szegmenseinek vezérlő kimeneteinek beállítása
  for (byte i=6;i<14;i++) {pinMode(i,OUTPUT);digitalWrite(i,HIGH);} //led kijelző szegmenseinek vezérlő kimenetei
  if (digitalRead(5)==LOW)     //gyári alapbeállítás
  {
    lcd.setCursor(0,0);lcd.printstr("Alapertek beall.");
    lcd.setCursor(0,1);lcd.printstr("                ");
    //eeprom törlés, gyári alapbeállítás
    for (byte i=0;i<20;i++) {EEPROM.write(i,0);}
    EEPROM.write(9,digitalRead(3));       //szintjelző alapállapotának lekérdezése
    //tizedespont villogtatás, ami a gyári alapbeállítást jelzi
    for (byte i=1;i<5;i++) {digitalWrite(6,LOW);delay(200);digitalWrite(6,HIGH);delay(200);}
  }
  lcd.clear();lcd.setCursor(0,0);lcd.printstr("Offset meres!   ");
  //10x mérünk és átlagoljuk, így megkapjuk a nyugalmi feszültségét az áramváltó egyenirányítójának
  //fontos, hogy bekapcsoláskor ne működjön a szvattyú
  for (byte i=0;i<10;i++) {aram_offset=aram_offset+analogRead(A2);delay(60);}
  aram_offset=aram_offset/10;   //egyenirányitó átlagos nyugalmi offszet átlagos feszültségének számítása
  aram_kuszob=aram_offset*10;   //nyugalmi offszetnél tízszerese a működő motor érzékelésének áramküszöbe
  lcd.setCursor(0,1);lcd.printstr("Offset:");
  lcd.setCursor(0,1);
  lcd.print((float) aram_offset*0.017);
  lcd.printstr("A (");
  lcd.print(aram_offset);
  lcd.printstr(")");
  delay(2000);
  szintjelzo_alapertek=EEPROM.read(9);  //tárolt szintjelző alapállapot kiolvasása
  uts_max_motoraram=EEPROMReadlong(15);
  lcd.setCursor(0,0);lcd.printstr("Uts.max.mot.aram");
  lcd.setCursor(0,1);lcd.printstr("torlesehez nyomd");
  delay(3000);
  lcd.setCursor(0,0);lcd.printstr("a nyomogombot!  ");
  lcd.setCursor(0,1);lcd.printstr("                ");
  delay(3000);
  lcd.setCursor(0,0);lcd.printstr("Torleshez       ");
  lcd.setCursor(0,1);lcd.printstr("hatravan:       ");
  for (byte i=5;i>0;i--) 
  {
    lcd.setCursor(9,1);lcd.print(i);
    if (digitalRead(5)==LOW)
    {
      for (byte j=15;j<20;j++) {EEPROM.write(j,0);}
      lcd.setCursor(0,0);lcd.printstr("Max aram torolve");
      lcd.setCursor(0,1);lcd.printstr("================");
      delay(2000); 
      break;
    }
    delay(1000);
  }
}

void loop()
{
  //4-es bemenetre kötött gombnyomásra bekapcsoljuk az lcd háttérvilágítását, és egy perc mulva kapcsoljuk ki
  if (digitalRead(4)==LOW) {lcd.backlight();lcd_vilagitas=millis();}
  //if (lcd_vilagitas+60000<millis()) {lcd.noBacklight();}

  //egy másodpercenként ellenőrizzuk, hogy megy e a motor és adminisztrálunk
  if (millis()>ido_tmp+1000) {inditas_szamlalo();ido_tmp=millis();}

  //1,2 másodpercre beállítjuk a vizszint vészjelzést ha bekapcsolt a vész vizszintjelző
  if (millis()>ido_tmp3+1200) {if (motor_on!=1) {szintjelzes=1;}}
  if (millis()>ido_tmp3+2400) {	ido_tmp3=millis();szintjelzes=0;k=0;}

  //Szintjelző tárolt alapértéke alapján a vészjelzés detektálása szintjelzo_on=HIGH jelzi a magas folyadékszintet 
  szintjelzo_on=LOW;
  if (szintjelzo_alapertek==LOW) {if (digitalRead(3)==LOW) {szintjelzo_on=HIGH;}} 
  else {if (digitalRead(3)==HIGH) {szintjelzo_on=HIGH;}}

  //*******************szintjelző riasztás megjelenítése led kijelzőn***********************************/
  //bekapcsolt a vész szintjelző kapcsoló, sorban villogtatjuk a függőleges pálcikákat 3 másodpercenként
  if (szintjelzes==1 & szintjelzo_on==HIGH)  
  {
    if (millis()>ido_tmp2+200) 
    {
      for (byte i=0;i<7;i++) {digitalWrite(pin_szegmens[i],szegmens[98+(k*7)+i]);}
      k++;if(k==3) {k=0;}
      ido_tmp2=millis();
    }
  }

  //******************motor működés kelzése a led kijelzőn*******************************************/
  //éppen működik a motor, sorban körbe villogtajuk az alsó 4 szegmenst
  if (motor_on==1 & szintjelzes==0) 
  {
    if (millis()>ido_tmp2+200) 
    {
      //motor működik	
      for (byte i=0;i<7;i++) {digitalWrite(pin_szegmens[i],szegmens[70+(motor_on_state*7)+i]);}
      motor_on_state++;if (motor_on_state==4) {	motor_on_state=0;}
      ido_tmp2=millis();
    }			
  }
  
  //********************indításszám kijelzése a led kijelzőn****************************************/
  //sem a motor nem működik, sem vész vizszint kijelzés nincs
  //kijelezzük az utolsó 24 óra indításainak számát, villogtatjuk ha több mint 8
  if (motor_on==0 & szintjelzes==0) 
  {
    if (millis()>ido_tmp2+200) 
    {
      if (inditas_db==8) {villogas++;} else {villogas=1;}
      if (villogas==2) {for (byte i=0;i<7;i++) {digitalWrite(pin_szegmens[i],szegmens[119+i]);}}
      if (villogas==1) {for (byte i=0;i<7;i++) {digitalWrite(pin_szegmens[i],szegmens[inditas_db*7+i]);}}
      if (villogas==2){villogas=0;}
      ido_tmp2=millis();
    }
  }

  /**************prellmentesítés a nyomógombon, és kijelzett érték paraméterének változtatása*****************/
  //első lenyomás érzékelése
  if (in_state==0 and digitalRead(5)==LOW and in_prell_tmp==0)
    {in_prell_tmp=1;in_prell_time=millis();} 
  // már 50msecv óta nyomva van, most már biztos, hogy lenyomták és nem prellezik
  if (in_state==0 and digitalRead(5)==LOW and in_prell_tmp==1 and millis()>in_prell_time+50) 
    {
      in_state=1;in_prell_tmp=0;
      //növeljük a kijelzett érték változóját, ciklikuan 0-ha, elértük a legnagyobb értéket
      adat_valaszto++;if (adat_valaszto==9){adat_valaszto=0;}
      k_frissit=HIGH; //ennek hatására azonnal ki fogja írni a kiválasztott adot (frissíti a kijelzőt)
      digitalWrite(6,HIGH);  //kikapcsoljuk a tizedespontot (túláram jelzés törlése)
    } 
  //első elengedés érzékelése
  if (in_state==1 and digitalRead(5)==HIGH and in_prell_tmp==0)
    {in_prell_tmp=1;in_prell_time=millis();} 
  // már 50msecv óta elengedve, most már biztos, hogy elengedték és nem prellezik
  if (in_state==1 and digitalRead(5)==HIGH and in_prell_tmp==1 and millis()>in_prell_time+50) 
    {in_state=0;in_prell_tmp=0;} 
        
  //***********************************kijelző frissítés******************************************/
  //Serial.println(k_frissit);
  if (millis()>kijelzo_frissites+1000 or k_frissit==HIGH)  //egymásodpercenként frissítjük a kijelzésre kerülő értéket
                                                           //vagy ha megnyomta a nyomógombot
  {
    k_frissit=LOW;
    //lcd.setCursor(0,1);lcd.printstr("                ");
    switch (adat_valaszto)
    {
      case 0:   //24 óra indításszáma
        lcd.setCursor(0,0);lcd.printstr("24H inditasszam:");
        lcd.setCursor(0,1);
        lcd.print(inditas_db);
        lcd.printstr(" db            ");break;
      case 1:   //Összes motor működési idő
        lcd.setCursor(0,0);lcd.printstr("Ossz.muk.ido:   ");
        lcd.setCursor(0,1);
        lcd.print(EEPROMReadlong(0)/60);
        lcd.printstr(" perc       ");break;
      case 2:   //Összes motor indításszám
        lcd.setCursor(0,0);lcd.printstr("Ossz.mot.indit:");
        lcd.setCursor(0,1);
        lcd.print(EEPROMReadlong(4));
        lcd.printstr(" db            ");break;
      case 3:   //árammérő offszet értéke
        lcd.setCursor(0,0);lcd.printstr("Arammero offset:");
        lcd.setCursor(0,1);
        lcd.print((float) aram_offset*0.017);
        lcd.printstr("A (");
        lcd.print(aram_offset);
        lcd.printstr(") ");break;
      case 4:   //motor működés érzékelési küszöb
        lcd.setCursor(0,0);lcd.printstr("Mot.erz.kuszob: ");
        lcd.setCursor(0,1);
        lcd.print((float)aram_kuszob*0.017);
        lcd.printstr("A (");
        lcd.print(aram_kuszob);
        lcd.printstr(") ");;break;
      case 5:   //motor túláram risztási küszöb (potméter beállított értéke)
        lcd.setCursor(0,0);lcd.printstr("Tularam.kuszob: ");
        lcd.setCursor(0,1);
        lcd.print((float) aramlimit*0.017);
        lcd.printstr("A (");
        lcd.print(aramlimit);
        lcd.printstr(") ");break;
      case 6:   //motor túláram risztási küszöb (potméter beállított értéke)
        lcd.setCursor(0,0);lcd.printstr("Tularamok szama: ");
        lcd.setCursor(0,1);
        lcd.print(EEPROMReadlong(10));
        lcd.printstr(" db            ");break;
      case 7:   //motor áramának értéke (offszet éték kivonásával)
        lcd.setCursor(0,0);lcd.printstr("Uts.max.mot.aram:");
        lcd.setCursor(0,1);
        lcd.print((float)uts_max_motoraram*0.017);
        lcd.printstr("A (");
        lcd.print(uts_max_motoraram);
        lcd.printstr(") ");break;
      case 8:   //motor áramának értéke (offszet éték kivonásával)
        lcd.setCursor(0,0);lcd.printstr("Motoraram:      ");
        lcd.setCursor(0,1);lcd.print((float) (motoraram-aram_offset)*0.017);
        lcd.printstr("A (");
        lcd.print(motoraram);
        lcd.printstr(") ");break;
    }
    kijelzo_frissites=millis();
  }
}	



void inditas_szamlalo()
{
  //idő számlálók növelése. Másodperc alapon mérünk időt
  bekapcs_ido++;ido_tmp=millis();
  ido[0]=ido[0]+1;
  o_ido=0;
  s3=0;
  // sorban összeadjuk a tömbben tárolt időt, ha elértük a 24 órát (86400 másodperc), akkor megállunk
  //az utolsó 24 óra szivattyú indításainak számára vagyunk kíváncsiak
  while (o_ido<felejtes_ido && s3<tombmeret-1 && ido[s3]!=0)
  {
    o_ido=o_ido+ido[s3];
    s3++;
  }		
  inditas_db=s3-1;
  if (inditas_db==8) {veszjelzesmemo=1;}
  if (tarolasjelzes[tombmeret-1]==1 && eepromkimeles==1) //tarolásjelzés, tehát az utolsó 5 adatot összgezni és tárolni kell
                                                         //de csak akkor, ha az eeprom kimélés be van kapcsolva
  {
    //Összeadjuk a tömbben tárolt bekapcsolási időket, és közben töröljük az összeadottakat
    //a törlés csak a működés nyomnkövethetősége miatt lett beépítve
    o_ido=0;
    for (byte j=0;j<5;j++) {	o_ido=o_ido+bekapcsolva[tombmeret-j-1];bekapcsolva[tombmeret-j-1]=0;}
      //osszes szivattyú működési idő kiolvasása az eeprom-ból és hozzáadjuk az utóbbi tárolt bekapcsolási időket
    s1=EEPROMReadlong(0);
    s1=s1+o_ido;
      //Összes eddigi indításszám kiolvasása eeprom-ból és hozzáadjuk az utóbbi tárolt indítás számot
    s2=EEPROMReadlong(4);
    EEPROMWritelong(0,s1);
    EEPROMWritelong(4,s2+5); //értékek írása az eeprom-ba
    tombindex=0;
    tarolasjelzes[tombmeret-6]=1;
    tarolasjelzes[tombmeret-1]=0;
  } //if vége
  motor_lekerdezes();
  
  //a szivattyú indítás felfutó éle, tároljuk a tömbb 0-as indexén az utolsó indítás óta 
  //eltelt időt és tároljuk ennek változóját, töröljuk a bekapcsolási idő változóját
  if (motor_megy && !sw_elozo_allapot) {  bekapcs_ido=0;motor_on=1;}
  
  //szivattyú kikapcsolása (lefutó él). Minden értéket lejjebb másolunk a tömbben és
  //tároljuk a bekapcsolási idő számlálást
  if (!motor_megy && sw_elozo_allapot) 
  {
    if (motor_tularam==HIGH) 
    {
      //ha a motor áramfelvétele nagyobb volt a működés alatt mint a potival beállított limit, akkor kigyúllad a tizedespont. 
      //Az5-os bemenetre kötött gomb lenyomása kikapcsolja
      digitalWrite(6,LOW);
      motor_tularam=LOW;
      int ta=EEPROMReadlong(10);
      ta++;
      EEPROMWritelong(10,ta);
    }
    EEPROMWritelong(15,uts_max_motoraram);
    bekapcsolva[0]=bekapcs_ido;
    motor_on=0;
    for (byte j=tombmeret-1;j>0;j--)
    {
      bekapcsolva[j]=bekapcsolva[j-1];
      ido[j]=ido[j-1];
      tarolasjelzes[j]=tarolasjelzes[j-1];
    }
    bekapcsolva[0]=0;ido[0]=0;tarolasjelzes[0]=0;
    if (eepromkimeles==0) //eeprom kimélés kikapcsolva, így minden motor működés után beírjuk az adatokat az eeprom-ba
    {
      //Összes eddigi működési idő kiolvasása eeprom-ból és hozzáadjuk a legutóbbi működési időt
      s1=EEPROMReadlong(0);
      s1=s1+bekapcs_ido;
      EEPROMWritelong(0,s1);
      //Összes eddigi indításszám kiolvasása eeprom-ból és hozzáadunk egyet
      s2=EEPROMReadlong(4);
      EEPROMWritelong(4,s2+1); //értékek írása az eeprom-ba
    }
  }
  sw_elozo_allapot=motor_megy; //fel és lefutó él érzékeléséhez
}


//Ez a funkció kiír egy 4 byte (32bit) long változót az eeprom-ba
void EEPROMWritelong(int address, long value)
{
	//szétszedjük byte-okra a long-ot
	byte four = (value & 0xFF);
	byte three = ((value >> 8) & 0xFF);
	byte two = ((value >> 16) & 0xFF);
	byte one = ((value >> 24) & 0xFF);
	//A 4 byte-os adat epromba írása
	EEPROM.write(address, four);
	EEPROM.write(address + 1, three);
	EEPROM.write(address + 2, two);
	EEPROM.write(address + 3, one);
}

//Ez a funkció visszaolvas 4 byte (32bit) long változót az eeprom-ból
long EEPROMReadlong(long address)
{
	//4 bytes olvasása az eeprom-ból.
	long four = EEPROM.read(address);
	long three = EEPROM.read(address + 1);
	long two = EEPROM.read(address + 2);
	long one = EEPROM.read(address + 3);
	//4 byte long változóvá alakítása
	return ((four << 0) & 0xFF) + ((three << 8) & 0xFFFF) + ((two << 16) & 0xFFFFFF) + ((one << 24) & 0xFFFFFFFF);
}

void char_konv(int szam)
{
	String konv_string=String(szam);
	hossz=konv_string.length();
	for (byte j=0;j<4;j++) {szamjegy_db[j]='0';}
	for (byte i=0;i<hossz;i++) {szamjegy_db[4-hossz+i]=konv_string.charAt(i);}
}

void motor_lekerdezes()
{
motoraram=analogRead(A2);
if (uts_max_motoraram<(motoraram-aram_offset)) {uts_max_motoraram=motoraram-aram_offset;}
aramlimit=analogRead(A1);
if (!digitalRead(2) || (motoraram-aram_offset)>aram_kuszob) {motor_megy=HIGH;}  else {motor_megy=LOW;}
if ((motoraram-aram_offset)>aramlimit) {motor_tularam=HIGH;}
}

16. feladat: mikor működik rosszul a szimulátor (szélsebességmérő)?!

Vannak olyan szituációk, amikor a szimulátor nem fog tudni jól működni. Erre is szeretnék példát mutatni. Készítettem egy szélsebességmérő programot. Részleteiről itt olvashatsz! Ennek a programnak a lényege, hogy egy szélkerék körbefordulási idejét kell megmérni, illetve egy adott időegység pl. 10 másodperc alatt megtett fordulatok számát kell meghatározni. Mindkét esetben a millis() függvényt használtam. Azonban a szimuláció sebessége bezavar a mérésbe. Ugyanis a szimulátor nem 100% sebességen dolgozik. A jelenségre egy nagyon gyenge teljesítményű Windows 10 alapú tableten figyeltem fel. Itt a szimuláció sebessége 50-70% körül mozgott, noha nem volt lassítás beállítva. Ekkor viszont már sokat számít, hogy mennyit késik a program végrehajtása a valós időhöz képest. A szélkerék forgását, egy órajel generátorral szimuláltam, amit 4 HZ-re állítottam. Ez a program számára 2 fordulatot jelentett 1 másodperc alatt, vagyis kb. 5km/h szélsebességet. A széllökés mérés jól működik 5km/h sebességet mutat stabilan. A pillanatnyi szélsebesség, ami az egy körül fordulás idejét méri az órajel (kontaktus) felfutó éle alapján, már össze vissza mutat. 1 km/h és 9km/h sebességet is láttam a kijelzőn. Pedig a valóságban megépített áramkör jól működött, egyenletes forgás esetén a pillanatnyi sebesség megegyezett a széllökés sebességével. Igaz, itt hajszárító biztosította az egyenletes forgást, ami biztosan nem stabil. Mondhatnánk, hogy vegyek gyorsabb gépet, de a jelenséget a laptopomon is tapasztaltam, ami viszont 100% sebességen szimulál, de néha azért 99%-ra is lemegy. Lehetséges, hogy nem csak a szimuláció sebessége a döntő ebben a kérdésben.

A kapcsolás a következő volt a szimulátorban:

Itt tölthető le a szimulációs állomány: letöltés

Ha esetleg valaki párhuzamosan a program forrására is kíváncsi, akkor itt van az is:

#include <Wire.h>    //I2C library 

#include <Soft_LCD-I2C.h>
uint8_t address = 80;
uint8_t sda = 18;
uint8_t scl = 19;
Soft_Lcd_I2C lcd( address, 16, 2, sda, scl );

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() {
  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.init();
  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.printstr("Pill. szelseb:  ");
        lcd.setCursor(0,1);lcd.printstr("            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.printstr("Szellokes:      ");
        lcd.setCursor(0,1);lcd.printstr("            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.printstr("Szellokes max.: ");
        lcd.setCursor(0,1);lcd.printstr("            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.printstr("24h atlag seb.: ");
        lcd.setCursor(0,1);lcd.printstr("            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.printstr("Szeles idoszak: ");
        lcd.setCursor(0,1);lcd.printstr("                ");
        lcd.setCursor(7,1);lcd.print(ora);lcd.printstr("o ");lcd.print(perc);lcd.printstr("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.printstr("Szelmentes ido- ");
        lcd.setCursor(0,1);lcd.printstr("szak:           ");
        lcd.setCursor(7,1);lcd.print(ora);lcd.printstr("o ");lcd.print(perc);lcd.printstr("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.printstr("Ido:            ");
        lcd.setCursor(6,0);
        if (ora<10) {lcd.printstr("0");}
        lcd.print(ora);lcd.print(":");
        if (perc<10) {lcd.printstr("0");}
        lcd.print(perc,2);lcd.printstr("  ");
        lcd.setCursor(0,1);lcd.printstr("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.printstr("Ido:            ");
        lcd.setCursor(6,0);
        if (ora<10) {lcd.printstr("0");}
        lcd.print(ora);lcd.printstr(":");
        if (perc<10) {lcd.printstr("0");}
        lcd.print(perc);lcd.printstr("  ");
        lcd.setCursor(0,1);lcd.printstr("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
}

Közben újabb hibát találtam a szimulátorban. Az lcd.print() valóban rosszul működik. Ha byte vagy int változó értékét szeretném megjeleníteni, akkor csak az első karaktert látom a kijelzőn. Próbáltam ezért a változót előtte string-re konvertálni és az lcd.printstr()-t használni, de a string konverzió sehogyan sem sikerült, illetve a string változóra meg az lcd.printstr() fordítás közben dobot hibát. Küzdök még vele, de egyenlőre idő hiányában feladtam. Ki fogom próbálni egy valós hardveren, és ekkor ki fog derülni, hogy a szoftveres LCD könyvtár hibája, vagy a szimulátoré.
Egy float típusú változó megjelenítése viszont jól működik, ezért mutatja a szélsebességet a kijelző, de pl. az óra beállításánál már csak az “egyjegyű ” időpontok jelennek meg jól. Pl. 3 óra 6 percnél jó az időpont, de ha 1 óra 2 percet látsz, az lehet, hogy 12 óra 24 perc, hiszen a második jegy nem látszik. Ez van!

Mennyire volt hasznos amit olvastál?

Kattints egy csillagra az értékeléshez!

Szövegesen is leírhatod véleményedet!