Időjárás állomás szélsebesség és csapadékmérővel V2

(beltéri egységgel)

Tartalom:

  • Az első verzió készítése és működése megtekinthető itt
  • Az új berendezés tulajdonságai (szélsebesség és csapadékmérés, rádiós átvitel beltéri egységre)
  • Elektromágneses “viharok” hatásai, hibák és javításuk
  • Adatsérülések az I2C adatátvitelben, módszerek a hibák felfedezésére
  • Hőmérséklet hatása a vezetéknélküli átvitel (433Mhz-s) vevőjére
  • Forráskódok

Már készítettem egy mai szemmel nézve primitív időjárás állomást, ami hőmérséklet, páratartalom és légnyomás adatokat mért, ezeket regisztrálta egy SD kártyán! Az adatokat két kijelzőn lehetett leolvasni. Az egyik kijelző 2×16 karakteres lcd, amit leginkább szerviz és fejlesztési célokra használtam. A fő kijelzőm egy hordó fenekébe épített 74HC595N chipekkel meghajtott hatalmas számjegyekkel rendelkező kijelző, amit jó pár méterről is le tudtam olvasni! Lényegében ennyi volt a berendezés tudása.

Kísérleteket folytattam szélsebesség és csapadék mérés tárgyában, és amikor ezek a mérőeszközök elkészültek, megérett a helyzet az időjárás állomás tovább fejlesztésére! Azonban itt kicsit nagyobbat akartam előre lépni, és szerettem volna az adatokat a lakásban is leolvasni! Ezért kitaláltam, hogy egy 433Mhz-es adóval időnként beküldöm az eredményeket egy betéri egységre. Ehhez természetesen meg kellett ismerni az általam kiválasztott RF433Mhz adó-vevő párt, ami kecsegtetően olcsó, kb. 800Ft. Kedvenc kínai webáruházamban 1500Ft-ér vettem 3 adót 5 vevőt, mert nagy terveim voltak. A kísérletek végére elkészült a beltéri egység, egy időjárás állomás szimulátor adóval.

Összességében ez a berendezés alig volt bonyolultabb az első verziónál, azonban olyan sok gyakorlati probléma jött elő, hogy több mint fél évig javítgattam, mire használható állapotba került. Rengeteg gyakorlati tapasztalatot szereztem az egyes rész rendszerek működésével kapcsolatban. És még nincs kész!!

A legfontosabb tanulsága a berendezés építésének, hogy bonyolultabb, összetettebb rendszer esetén gondolni kell a zavarvédelemre és a hőmérséklet hatásaira, ezt nem lehet kihagyni a számításból. Szinte minden problémát sikerült szoftveresen megoldani, a fél év fejlesztés alatt a hardver alig változott. Néhány zener diódát építettem be, mert arra gyanakodtam, hogy a hosszú vezetékek olyan nagy túlfeszültségeket szednek össze, ami megzavarja az eszközök működését. Tapasztalatom szerint nem ezek oldották meg a problémáimat, de legalább a zenerek beépítése miatt van remény, hogy az eszközök nem mennek tönkre. Küzdök még hálózati zavarjelek hatásaival, de már látom, hogy előbb utóbb minden problémát meg fogok tudni oldani szoftveresen. Erről később bővebben, ezek a tapasztalataim a legértékesebbek.

Új időjárás állomásom a következő fő funkciókat látja el:

  • Méri a külső hőmérsékletet, légnyomást és páratartalmat
  • Méri még a hőmérsékletet egy aknában, pincében és a szerszámos sufninak emlegetett hátsó épületben, ott ahol a „kültéri” egységet is elhelyeztem. Ezt már a régi berendezésem is tudta.
  • Méri a szélsebességet és a csapadék mennyiséget a szerszámos sufni tetején elhelyezett szélkerékkel és billenőkanalas csapadékmérővel.
  • Statisztikákat készít a legtöbb mért adatról, átlag, minimum és maximum értékeket képez, amit naponta egy alkalommal éjfélkor lenulláz. A szélsebesség esetében napi átlagsebességet számol amit arra az időszakra értelmezek, amikor fújt a szél. Másképpen fogalmazva tudom mennyi ideig volt szélcsend. Így minden napra van statisztikai adatom. Nyilván ezeket az adatokat a beltéri egység tárolja, de erről később. A statisztikai adatokat nem felejtő fram-ban tárolja, így az áramszünet vagy újraindítás nem okoz adatvesztést.
  • Egy RF433Mhz-s olcsó rádióadóval a mérési eredményeket és statisztikai adatokat időnként beküldi a beltéri egységnek. Jelenleg nincs visszajelzés arról, hogy a beltéri vevő vette az adatokat, ezért éjfél előtt sokszor megismétli a küldést. A 433Mhz-s adóvevőt eléggé megbízhatatlannak találtam, ezért nem bíztam a statisztikák készítését a beltéri egységre. Időnként órákig nem volt vétel, bár ezt a problémát megoldottam az antennák megfelelő elhelyezésével, de a bizalmam örökre elveszett! Viszont összességében megbízhatóan működik az átvitel annak köszönhetően, hogy az adatokat legalább tízszer gyakrabban küldöm mint egyébként indokolt lenne.
  • Az éjféli statisztika nullázás miatt szükség van egy beépített órára, így a kültéri egység is tartalmaz órát. Az olcsó DS3231 chip nem képes a nyári időállításra, és én alapvetően nagyon lusta vagyok, így megoldást próbáltam találni arra, hogy a beltéri egységhez szinkronizálja magát. Ezért időjárás állomásom kültéri egységén van egy 433Mhz-s vevő is (a beltéri egységen meg adó), ami óránként kap a beltéri egységtől pontos időjelzést, amihez beállítja magát. A kültérben elhelyezett 433Mhz-s vevő csak akkor működik, amikor kedve tartja (hőségben nincs vétel), de azért rendszeresen megtörténik az órák szinkronja. Állati elegáns, hogy a beltéri egységen beállított idő, pár másodpercen belül a sufniban is lemásolódik. Kár, hogy eddig még senki nem értékelte olyan nagyra, mint én! Már egyszer ténylegesen beállítottam vele az órát, amikor kiderült, hogy a beltéri egységemben program rátöltéskor elrontottam az időt! Beállítottam, és kész! Így is lehet kényelmesen élni!

Felépítés

Történelmi okokból az új időjárás állomás már két ATMega chip-et és egy Arduino nano-t tartalmaz. A dolog úgy kezdődött, hogy birkóztam a szélsebesség mérővel. Ha nagyon fúj a szél a hall szenzor kb. 20 msec időközönként ad egy felfutó élet, ekkor lehet sebességet számolni. Azonban az időjárás állomásom sokat kommunikál nagyon lassú eszközökön. Pl. egy hőmérséklet, páratartalom és légnyomás adat beküldése a beltéri egységnek 80-100msec időt vesz igénybe. Ezért eleve abban gondolkodtam, hogy a szélsebességmérő külön egység lesz. Ide elég volt egy ATMega8, alig 4kbyte a program. I2C buszon küldi az adatokat. Mivel az I2C is elég lassú egy kivezetésen jelzem a masternak (ez a tulajdonképpeni időjárás állomás), ha van szélsebesség adat, és az a lehető leghamarabb el is viszi az adatokat. Mivel mindig a hall szenzor felfutó élénél jelzek, van esély rá, hogy 20msec alatt lezajlik a kommunikáció. Természetesen a master is figyeli ezt az eseményt, és csak akkor indít időigényes feladatot, ha már a szélsebességmérővel kommunikált. Mivel a szélsebességmérő fél másodpercenként ad adatokat (addig gyűjtöget), bőven van idő. De pl. a statisztikai adatokat ennyi idő alatt sem lehet beküldeni a beltéri egységnek, mert ahhoz kellene kb. egy másodperc, ezért a masterben is alaposan szét lettek darabolva az időigényes funkciók, és egyszerre mindig csak eggyel foglalkozik. Ennek az együttműködésnek a kitalálása volt az első komoly feladat. Önmagában ez is hónapokat vett igénybe. Persze nem kapásból voltam okos, alaposan megszenvedtem ezért  a tudásért. A beltéri egységen megjelenítettem az adott napon előforduló maximális szélsebességet, és időnként 200km/h szélsebességeket olvastam le. Egy darabig eltartott, míg rájöttem, hogy a feldolgozási idők összecsúszása okozza a rossz sebesség adatok kiszámolását. Többször változtattam minden eszköz programján, volt olyan állapot is, amikor a már master számolta a szélsebességet, és a szélsebességmérő csak jelzett amikor felfutó élet talált. Ami persze hülyeség így utólag. Iszonyat időt kínlódtam, mire  kitaláltam mi a baj, és megszületett a jó algoritmus.

Az Arduino nano-ban elfogyott a flash memória, olyan nagy a lett a végén a program. Ezért kellett a harmadik eszköz. Leválasztottam a kültéri led kijelző vezérlését, és memória gazdálkodási okokból ez végzi a Dallas 18B20 onwire hőmérsékletmérők lekérdezését is. Nyilván ugyanarra az I2C buszra kötöttem, mint a szélsebességmérőt. Ezzel az eszközzel kétirányú a kommunikáció, küldöm neki a hőmérséklet, légnyomás és páratartalom, szélsebesség és csapadékmennyiség adatokat, és elkérem tőle a 4 Dallas hőmérő adatait. Utóbbiak adatait már nem is jelenítem mega kültéri kijelzőn, csak a beltéri egységen lehet nyomon követni a pince, akna és sufni hőmérsékleteket. Jelentősen változott a kijelzés algoritmusa is. Szélcsendben nem kell megjeleníteni a szélsebességet, ha nem esett eső akkor a csapadék mennyiséget sem. Ilyenkor felváltva a légnyomás és a páratartalom jelenik meg. A hőmérséklet folyamatosan látható, ez a legnagyobb számjegy (külső hőmérsékletről van szó amit a sufni oldalában mérek árnyékos helyen). Ha fújni kezd a szél, akkor kb. fél másodpercenként frissítem a szélsebességet, így folyamatosan lehet követni a kijelzőn a sebesség változását. Kivéve, ha a sebesség 1-2km/h alatt van, mert legalább egy körül fordulás kell a sebesség számításhoz, és kicsi sebességeknél nem születik fél másodpercenként sebesség adat, erre többet kell várni. 20 másodpercenként azonban, ha fúj a szél, akkor is megmutogatom a légnyomást és páratartalmat, vagy ha esett az eső, akkor a csapadékmennyiséget. Ezt az algoritmust nagy szívás volt megcsinálni. A sufniban tudtam rátölteni a programot a chip-re, és persze a szélkerék és a csapadékmérő is már a helyén volt a tetőn. Vagy nem esett az eső, vagy nem fújt a szél, vagy egyik sem, vagy mindkettő. Nem sok variáció, de mire minden megjelenítési szituációt kipróbáltam, kinőtt a szakállam. Már azt hittem minden rendben, jött egy szélroham és kiderült mégsem jó az elképzelés. Így a jól kitalált program működésének ellenőrzéséhez mindig várni kellett az időjárásra. Az időjárás tényleg szeszélyes, hetekig nem haladtam előre, mert valamit nem tudtam csak napok múlva kipróbálni. Mint említettem lusta vagyok, nem volt kedvem leszerelni a cuccot a tetőről. Egyszer elfogyott a türelmem és felmásztam a tetőre öntöttem egy kis vizet a csapadékmérőbe, és így a hónapokig tartó aszályban mégiscsak elkészültem. Közben azért kitaláltam új fejlesztési megoldásokat. Ugyanis a sufniból nem láttam a kinti kijelzőt. Ha új programot töltöttem a chip-re, akkor mindig ki kellett menni, és megnézni mi történik. Leporoltam hát a régi lassú laptopomat, tettem rá Arduino IDE-t és Team Viewert, és kényelmesen a lakásból fejlesztettem, az ablakon át néztem az eredményt. Így a logolás is megoldódott, mert olyan hibákkal kezdtem el szembe találni magam, amiknek a megoldásához napokig kellett figyelni az eseményeket! Így folyamatosan rögzítettem a sorosportról az eseményeket a putty programmal!

Íme egy kép a berendezésről még “kibelezett állapotban:

A javításhoz folyamatos kapcsolat kellett egy laptoppal, ami logolta a sorosportról érkező eseményeket, ugyanakkor a szélsebességmérőre illetve a kijelző vezérlőre STK500 programozóval lehetett programot tölteni. A képen épp a kijelző vezérlőhöz csatlakozik a programozó. Sajnos nagyon kellett némi infó a kijelző vezérlőjéből, ezért ott kivezettem a sorosporti kapcsolathoz az ATMega328 TX és RX kivezetését. A képen látható a “beizzított” soros-USB illesztő is. Szegény őskövület laptopom minden USB portját elhasználtam csúcsra járatott időszakban.

Nagy szívások

A legnagyobb szívás a sufni neoncsöves világításához kapcsolódik. Ezeket a neoncsöveket még a régi munkahelyemen szereztem felújításkor, szó szerint  kukából. Gondoltam jó lesz a sufniban. Pont úgy kapcsolódnak be, mint a „Nyolcadik utas a halál” című filmben a folyosó világítás, amikor  a „Muter” felébreszti az űrhajót. Ezért is tetszettek meg nagyon. Nosztalgiát éreztem, mert gimnazista koromban nyárimunkán voltam az “Orion” gyárban. Százával lógtak a plafonról a neoncsöves világítótestek az óriási szerelő csarnokban. A bekapcsolási folyamat tűzijátékhoz hasonlított, csak nem kerül hárommilliárdba. Ezennel jelzem az Elien filmrendezőjének, ha a további folytatáson gondolkodik, nem kell milliókat fizetnie a díszlettervezőknek, jó pénzért kölcsönadom!

Mi is a baj a neoncsővel? Ugyebár van benne egy gyújtónak nevezett cucc, ami egy ideig áramot ad a neoncső fűtőszálának, hogy felmelegedjen. Aztán amikor ez kikapcsol, egy tekercsben önindukció révén jó nagy feszültség indukálódik, ami beindítja a gázban az elektronok (vagy inonok?) áramlását és a cső világítani kezd. Ha nem gyújt be elsőre, akkor újra a gyújtó következik. Az én koszos régi csöveim 5 másodpercig villognak, mire világítani kezdenek. Na ekkor halljátok a hírekben, hogy elektromágneses támadás érte a Nato központját. Az én vagyok! Szóval nem kellett semmit csinálnom azért, hogy legyen egy igazi elektromágneses-zavar vizsgálólaborom. Ha esetleg kell valakinek, jó pénzért kölcsönadom!

A neoncső gyújtása több vezetékben is tud zavarokat indukálni. Kb. 3-4 m vezetéken lóg a BME280 I2C buszos légnyomásmérő. Az I2C busz közös a szélsebességmérő és kijelző chip-ekkel, valamint ugyanezen a buszon lóg még a 2×16 karakteres szerviz kijelző és az fram is. Az áramkörön belüli kommunikációra tervezett I2C sebességét jó alaposan lelassítottam, hogy működhessen ilyen környezetben. Jelenleg kb. 58 KHz-n ketyeg 400 KHz helyett! Úgy sejtem, hogy nagy hibát követtem el, amikor a vezetékben keletkező zavarfeszültségekre nem gondoltam. Minden világítás kapcsoláskor összefagyott minden. A szélsebességmérőmön van egy led, ami bekapcsoláskor felvillan a setup-ban 100msec-re. No ez időnként égve maradt. Csak a program újra töltésével lehetett működésre bírni az ATMega vezérlőt. Érdekes módon a chip nem ment tönkre, de a flash-ban valamit megváltoztatott a sorozatos mágneses vihar! Kénytelen voltam bújni a netet, hogy mit lehet csinálni. Lényegében nem találtam megoldást, de találtam némi utalást és egy ábrát amin zener védődiódák voltak elhelyezve, és ezt vettem át magamnak:

Ha jól értettem meg a cikket, ez szintillesztésre készült és nem zavarvédelemre, de gondoltam nekem ez is megteszi. 4.7V-os zenereket tetem a védő áramkörbe. Megmutatnám az igazán profi levegőben lógó védő-áramkörömet. Úgy gondoltam a levegőben terjedő zavarokat a levegőbe beépített védelemmel lehet megfogni. Betoltam mögé egy szürke lapot, hogy jobban latható legyen. Védelmem másik fontos tulajdonsága, hogy beleolvad környezetébe, így a zavarok nem veszik észre és nem tudnak elmenekülni előle):

Ezzel az áramkörrel elértem, hogy a vezetékben nem nőhet a feszültség 4.5-5V fölé! A kommunikációt nem zavarja a zener dióda. Természetesen az adatok sérülhetnek és tapasztalatom szerint sérülnek is, de a chip-ben a program fut tovább, és egy következő kezdeményezett I2C kommunikáció már hibátlan lehet. A fagyásokat és az égve maradó led problémáját megoldotta! Legalább is ezek a hibák megszűntek, illetve néhány hete már nem tapasztaltam.

Maradt még probléma bőven. Azt vettem észre, hogy a világítás bekapcsolásakor fals csapadékjelzések történnek. Ha átbillen a csapadékmérő, akkor a soron következő szélsebesség adat átadáskor egy külön erre fenntartott byte-ban 0 helyett 1-et küldtem át. Csak úgy röpködtek az egyesek, ha világítást kapcsoltam. Ezen a hibán részben segített, hogy a byte-ban 0 helyett 85-öt (binárisan: 01010101), és 1 helyett 170-et (binárisan: 10101010) küldtem. Mivel csak azt figyeltem, hogy a megérkezett adat egyelő-e 170-el, ritkán sérültek úgy az adatok, hogy mind a 8 bit ellentétesre változzon. Jelentősen lecsökkentek a fals jelzések amit nem értek pontosan, mert ezek is csak számok, mint a 0 és sz 1. Lehetségesnek tartom, hogy itt a körülmények tréfáltak meg, és csak azt hittem, hogy ez segített és esetleg valami mást is megváltoztattam a környezetben. Ennek a primitív megoldásnak hátránya, ha esőben kapcsolom fel a villanyt, mikor épp átbillen a mérőkanál, akkor impulzust veszíthetek. Ennek kicsi a valószínűsége! Már csak azért is, mert esőben nem megyek át a sufniba, mert út közben megázok. Probléma megoldva! De még ez is kevés volt. Rájöttem, hogy a szélsebesség adtok is megváltozhatnak, és esetleg 600km/h szélsebességek jönnek át. Erre azt találtam ki, hogy az értékes adtok végére egy jelző byte-ot helyeztem. Ennek értéke a programban 99. Miért 99? Csak! Azt figyeltem meg ugyanis, hogy amikor sérülnek az adtok, gyakorlatilag minden byte megsérül. Gyakori volt hogy csupa 0 vagy 255 érték jött, de előfordultak más értékek is és nem volt mindig minden byte egyforma. Ezzel a megoldással azt feltételeztem, hogy az értékes adatok sérülésekor a záró byte is sérül. Tehát ha nem 99 érkezik utolsónak, akkor jobb minden érkezett adatot eldobni. Ez is bevált. Viszont továbbra is voltak fals csapadék átbillenés jelzések, bár nagyon ritkán. Mondjuk hetente egy! Ez persze attól is függ, hányszor kapcsolom fel a villanyt. Elhiheted kedves olvasóm, mostanában sokszor. Csak úgy hobbyból is kimegyek és kapcsolok egyet! A fals csapadék jelzéssel kezdeni kell valamit. Miután feltételeztem, hogy a csapadékmérőtől bejövő vezetékben keletkezik a zavar, tettem rá egy optocsatolót. A hiba azonban nem szűnt meg. Balga módon azt hittem, hogy az optocsatoló ledjét a zavarjel képes működtetni, ezért beraktam egy relét. Gondoltam azt már csak nem húzza meg egy 5méteres kábelben keletkező zavarjel. De a fals jelzések továbbra is jöttek. Ekkor ráébredtem, hogy elvben sérülhet a csapadékjelzésem adata az I2C átvitel során úgy, hogy 170-et kapok a vételben. Egy kicsit túlgondolt megoldással felvettem az adatok közé még egy 2 byte-os változót. Az új megoldásban a szélsebességmérő végtelen ciklusban fut, és közben vizsgálja a csapadékmérő kontaktusát és számolja amikor a bemenet nulla. Tapasztalatom szerint 3600 és 4200 közötti értékek keletkeznek egy átbillenésben. Így hát ennek a számlálónak az adatait is átküldtem. Érdekes dolog derült ki. Időnként érkezett nagyon kicsi érték 1,2,3 vagy 4, de 10 feletti soha! Ezek az értékek akkor is jöttek, amikor a csapadékjelzés nem volt (170), de az is megtörtént, hogy érkezett egy nagyon kicsi szám és csapadékjelzés, amit a berendezés fals csapadék átbillenésnek értékelt. Tehát igazoltam, hogy sérülnek az adatok. Így viszont már a két adatból kiszűrhettem a fals csapadék jelzéseket. Csak akkor volt csapadék átbillenés, ha az átadott szám nagyobb mint kb 3200! A biztonság kedvéért 2000-et választottam! Jelenleg már nincs fals jelzésem, de új problémára is rávilágított ez a megoldás. Mivel itt már egy „relés” leválasztás is történt, az újonnan felfedezett probléma csakis a szélsebességmérő hall elemétől érkező vezetékben keletkezhet (az I2C vezetékeket már zenerrel védem).  A szélsebességmérő hall elemét elláttam védelemmel, mert egy hallelemet már tönkretettem fordított tápfesszel, és rájöttem, hogy a tetőre mászni csere miatt nem kellemes tevékenység. Ezt megelőzendő beépítettem a szükséges néhány alkatrészt. Arra azonban nem gondoltam itt sem, hogy a kb. 5 méteres vezetéken jöhetnek zavarok, és akár nagyobb feszültség is, mint az 5 V, amit egy Arduino bemenet elvisel. Még nem építettem be a zeneres megoldást erre a vezetékre, ezért jelenleg is vannak ebből problémák, de szoftveresen ezt is megoldottam. Bár jelenleg csak feltételezem, hogy ennek a vezetéknek a zavarjelei okozzák a gondokat! Az történik, hogy a szélsebességmérő jeleit feldolgozó ATMega8 nem megy tönkre. Sőt a program is működik fél másodpercenként vagy ritkábban, minimum 5 másodpercenként küldi a szélsebesség adatokat. A szélsebesség adatokkal együtt jönnek a csapadék kontaktus adatok is. Időnként azonban a szélsebességmérő programban létrehozott változókban bennragadnak értékek. Érdekes módon a szélsebesség adatot még nem láttam „beragadni” de  csapadékot igen. Amikor a szélsebességmérő adatot küld, a kontaktus számláló kicsike (1,2,3 vagy 4 értéke) beragad, azaz folyamatosan csak ezt ismételgeti. Hagytam futni, hogy megszűnik-e a probléma, de akár órákig jön át ez a szám. Közben a szélsebesség adat meg helyesen érkezik. Néha úgy ragad be, hogy a csapadékjelzést is küldi. Azonban a reset segít, a program újraindítás után elfelejti végre ezeket a hibás, beragadt értékeket! Tehát a megoldásom az lett, hogy amikor valamilyen hibát találok a kommunikációs során, akkor reset jelet küld a master (az időjárás állomás fő egysége) a kijelző és a szélsebességmérő chip-eknek. Azóta hogy ezt beépítettem külön számlálom a resetek számát, és néhány naponta előfordul egy-egy „master reset”. A reset minden problémát megold. Berendezésem immár hasonlít a Windows korai verzióira. Az öregebbek még talán emlékszenek a 90-es években a windows-os rendszerek hibajavítása az „indítsd újra” művelettel kezdődőtt! Nem említettem még, de olyan hiba is előfordul villanykapcsoláskor, hogy valamelyik eszköz, vagy szélsebességmérő, vagy a külső kijelző vezérlő nem válaszol a master-nak az I2C porton. Ha ezen hibák bármelyike tartósan 10 másodpercig fennáll, akkor megy a reset. Immár hetek óta lefagyás nélkül működik minden. Illetve mint említettem a master időnként resettel újraindítja a slave-eket, de a rendszer tovább fut, én ebből semmit nem veszek észre. Az fram-nak köszönhetően megmaradnak a statisztikák, nem vesznek el adatok. A master programjába beépítettem watcdog figyelést. Az Ardunino nano nagyon ritkán, több hetente produkál egy újraindulást, amit a watchdog indít el. Ezt csak valószínűsítem, mert nem tudom megkülönböztetni egy áramszünet utáni újra indulástól, de ritkán van áramszünet, és mostanában már a reset gombot sem nyomogatom.     

Azért itt még álljunk meg egy szóra! A reset jelet az Arduino nano egyik kimenete állítja elő. Eleinte direktben rákötöttem az ATMega chip-ek reset lábára. Azonban ekkor, ha programot akartam feltölteni, a nano-t ki kellett húznom a foglalatból (mert foglalatban van, ilyen ügyes vagyok), mivel nem engedte a kimenete a reset-et állítgatni az STK500 programozónak, amivel rátöltöttem a chip-ekre. Az lett a kényelmi megoldás, hogy beraktam a reset lábra egy felhúzó ellenállást, és egy soros diódát a nano kivezetése felé. Így ha chip-nek a programozó ad resetet, akkor a nano felé a dióda lezár, viszont a nano is tud reset jelet adni. Szuper megoldás, de jól megszívatott. Amikor programot töltöttem a chip-ek valamelyikéére, akkor az nem válaszolt a nano-nak, aki szolgai módon küldte is a reset jelet. Ez néha olyan ütemben érkezett, hogy épp belerondított a programfeltöltésbe. De nem mindig. Így napokig agyaltam, míg rájöttem, miért hal el a programfeltöltés időnként. Részben ezért is építettem a master programjába, hogy reset jel csak 10 másodperc után keletkezik az első hiba észlelése után.

Nagy sikerek

Nagy sikerből csak egy volt! Siker persze önmagába az is, hogy meg tudtam csinálni, és immár hetek óta működik minden része a szerkezetnek. Van azonban valami, ami „hű de jó” érzést okozott! Szó szerint évek óta küzdök a mátrix kijelzőkkel. Többször átírtam a programot, többször átépítettem a hardver! Szűrőkondik tömkelegét forrasztgattam szó szerint mindenhová, tápegységet cseréltem stb. Erről bővebben lehet olvasni itt is! Az is igaz, hogy itt több probléma is volt. Volt egy eredeti, ős elcseszés! A mátrix kijelzőkhöz, illetve az egész kültéri kijelzőhöz túl vékony földkábelt vezettem. Eltelt egy év, mire észrevettem, hogy a kábelen elveszik majdnem egy volt feszültség, amikor az áramfelvétel a legmagasabb. A teljes kijelzőnek kb. 800mA a csúcs áramfelvétele, de ez a kijelzett adatoktól is függ. Amikor erre rájöttem és kivezettem a földet és az 5V voltot egy villanyvezetékkel, már sokkal jobb lett a helyzet. Immár csak egy-két naponta romlott el a kijelzés. Sokáig figyelgettem a kialakuló hibás állapotot, míg rájöttem, hogy valójában itt is aza adat sérül a 2-3 m-es vezetéken. A sérülés olyan jellegű volt, hogy a kijelző olyan „parancsokat” kapott, ami bekapcsolta a shutdown vagy éppen a teszt üzemmódot (mindent kikapcsol vagy mindent bekapcsol), illetve a 7 szegmenses kijelzők meghajtására kitalált üzemmódokba kerül. Amikor ezt felismertem, már azonnal meg lett a megoldás. Írtam magamnak saját ledmatrix függvényeket, amiben csináltam egy „reset” eljárást, és időnként a kijelzőt alaphelyzetbe állítom. Sajna az Arduino IDE-ben letölthető ledmatrix könyvtárban nincs függvény egy ilyen teljes alaphelyzetbe állításra, bár inicializáláskor megcsinálja. Amióta átírtam a programot, a hiba teljesen eltűnt. Immár két hete nem láttam rossz kijelzést.

Nagy talányok

Itt sem pontos a többesszám, mert csak egy nagy talány keletkezett. A kültéri kijelzőm csak akkor működik, ha a programban bekapcsolom a soros port kezelést ( Serial.begin() ). A kültéri kijelző programja jelenleg egy ATMega328 chip-ben fut. Ahogy optimalizáltam a programot, egyre kisebb és kisebb lett. Kb. 12Kbyte-ról indultam, de végül belefértem 8 Kbyte-ba. Ehhez az kellett, hogy kikukázzam a MAX7912 mátrix meghajtásáról gondoskodó ladmatrix könyvtárat a fentebbleírt okok miatt. Sokat segített még, hogy alaposabban átnéztem a Dallas chip-ek kezelését. Itt maradt a letöltött könyvtár, de elég sok felesleges sort kiszedtem a programomból. A mérési eredményeket float változókban kezeltem, amit aztán int értékre alakítottam, hogy rövidebb legyen és gyorsabban átvihessem I2C-n a master programba, innen pedig RF4333Mhz-s adón a beltéri egységre. Biztosan segített a programnak mikor rájöttem, hogy ezek az adatok legyenek int változókba, és csak a kijelzésnél teszem arrébb a tizedespontot. Ezeknek az átalakításoknak köszönhetően egyszer csak kisebb lett a program mint 8 kbyte, és gondoltam itt az ideje a nagy spórolásnak és ki lehet cserélni a chip-et ATMega328-ról ATMega8-ra. Ám előtte még feltöltöttem a picike programot, amiben már nem volt bekapcsolva a soros port, mert minek. Hibátlan a program, nem kell semmit megfigyelni! Mindaddig minden rendben is volt, amíg nem kezdett fújni a szél. Ekkor ugyanis már szélsebesség adatot is kap a kijelző (0 szélsebességnél nem küldöm feleslegesen). Az első I2C kommunikációval resetelte (újraindította) magát a program. Resetelte volna a kijelzőt az I2C-n érkező hőmérséklet, légnyomás és páratartalom adat is, de az csak 10 percenként jön, így addig nem jutottam el, előbb kezdett fújni a szél. Értetlenül néztem a dolgot, ahogy a szemem előtt fél másodpercenként lezajlott egy-egy újra indulás. Mit változtattam utoljára? Kivettem a sorosport kezelést! Visszaraktam, és minden rendbejött! Sajnos azóta sem sikerült rájönnöm, hogy mi a baj. Most, hogy közeledik az ősz és a tél, egyre kevesebb kedvem lesz a kisházban szerelgetni, ezért az a gyanúm, hogy így is marad. Az okokat nem sikerült megfejteni, de tény, hogy a helyes működéshez kell az a fránya Serial.begin(), és ez pont annyit tesz hozzá  a programhoz, hogy már nem fér bele az ATMega8-ba!

Következhetnek a forráskódok! Szokásom szerint nagyon alaposan felkommenteztem, mert hónapok múlva már nem tudok kiigazodni a saját kódjaimon. Pedig várható továbbfejlesztés, mert a locsolórendszeremmel is össze akarom kötni sorosporton keresztül az időjárás állomást. Néhány méterre vannak egymástól, és a 0 és 1 kivezetés még szabad. Végeztem is kísérleteket, hogyan működik a sorosporti átvitel két olyan eszköz között, amik egymástól függetlenül újra indulhatnak, vagy egyik be sincs kapcsolva. Egy jó kis eljárást sikerült kidolgoznom, amiről itt olvashatsz! Szeretném átvinni a pontos időt, hogy a locsolórendszerre ne kelljen külön RF433Mhz-s vevőt szerelnem, valamint a beltéri egységre beküldeném a következő locsolás időpontját stb. Távvezérelhetném is a locsolórendszert a lakásból, de kánikulában, mint tudjuk az olcsó RF433 vevő nem működik. Pedig pont akkor kell locsolni.

Hamarosan közzéteszem a beltéri egység leírását is! Az is izgalmas feladat volt. Arduino Mega kellett hozzá, olyan nagy lett a forrás mérete!

Szélsebességmérő forráskódja:

#include <Wire.h>

float t_ido;       //segéd változó az átlagos körül fordulási idő számításához
long ido_5s=0;     //5 másodperc után állónak tekintem a szélkereket, ha nem volt impulzus, ehhez kell ez a segédváltozó
long ido1=0;       //a fél másodperc alatt történt impulzusok teljes ideje
int n_imp=0;       //ebben tárolom a felfutó élek darabszámát
float v=0;         //kiszámított szélsebesség =egy fordulat a szélsebességmérőn 2,5km/h (gyártói adat)
long ido_h,ido_l;  //a körül fordulás közbeni low, és high jelrészek hosszának méréséhez  
long ido_tmp;      //idomereshez segédváltozó
float arany=0;     //felmerült a gyanú, hogy két impulzust ad a hall szenzor, ezért a magas és alacsony 
                   //jelszintek időtartamának arányából próbálok következtetéseket levonni. Ez egy számított kitöltési tényező hányados!
                   //Most éppen a csapadékmérő kontaktus idejét (loop ciklusok számát) mérem benne, ezért nem utal a neve a funkciójára
volatile long led_kikapcs_ido=millis();   //minden I2C kommunikáció indításkor felvillan egy led, ennek időzítéséhez segédváltozó
volatile byte csapadek_kontaktus=0;       //ez jelzi 1-el, ha volt csapadékmérő átbillenés. Az I2C kommunikáció nullázza.
volatile unsigned int csap_kont_db=0;     //fals csapadék jelzések keletkeznek, ezért mérem, hogy a csapadékmérő kontaktujele milyen hosszu
                                                             //azaz hányszor van alacsony jel  loop()-ban. Kísérlet annak megállapítására, hogyan keletkeznek fals jelzések.
volatile byte korulfordulas_db=0;         //minden körül fordulásnál növelem eggyel, I2C kommunikáció nullázza

void setup()
{
  Wire.begin(8);                // I2C kommunikáció inicializálása 8-as eszköz címmel (mivel slave, címet kell megadni)
  Wire.onRequest(slave_kuld);   //Ezt a funkciót hívja meg az Arduino, amikor a master adatot kér a slave-től

  pinMode(2,INPUT);             //2-es kivezetéshez kötöttem az impulzus generátort
  digitalWrite(2,HIGH);         //felhúzó ellenállás bekapcsolása    
  pinMode(3,INPUT);             //csapadékmérő kontaktus jel fogadása            
  digitalWrite(3,HIGH);         //felhúzó ellenállás bekapcsolása  
  pinMode(5,OUTPUT);            //ezen a kimenten jelzi a masternak ha van adat, amit szeretne átküldeni
  digitalWrite(5,HIGH);         //Az alap állapot a HIGH és LOW-al jelzünk master-nak
  pinMode(7,OUTPUT);            //bekapcsolás jelzés
  digitalWrite(7,LOW);          //Az alap állapot a HIGH, rövid impulzus
  delay(100);
  digitalWrite(7,HIGH);         
  pinMode(8,OUTPUT);            //kommunikáció jelző
  digitalWrite(8,HIGH);         //Az alap állapot a HIGH, ekkor nem világit a led
  korulfordulas_db=0;
}

void loop()
{
  //ez egy teljes mérési folyamat kezdőpontja. Ha már lefutott a loop() egyszer, akkor
  //ide akkor kerülünk vissza, ha letelt legalább 0,5 másodperc forgás közben, vagy 
  //5 másodpercen belül volt legalább egy körbefordulás
  t_ido=0;
  ido_5s=millis();                 //innen számoljuk az 5 sec időt
  ido1=0;                          //két felfutó él között eltelt idő
  n_imp=0;                         //felfutó élek száma
  ido_tmp=millis();                //indul a jel low részének időmérése
  do {
    //elkezdjük keresni a felfutó élet
    //közben nézzük a csapadék kontaktus jelet is
    while (digitalRead(2)==0) {    //keressük a felfutó élet ezzel a ciklussal
      if (digitalRead(3)==0) {   
        csap_kont_db++;
      }          
      if (digitalRead(3)==0 and csapadek_kontaktus==0) {   //csapadék kontaktus érkezett, a következő adatátvitel fogja nullázni, ami legrosszabb esetben 5 sec múlva lesz
        csapadek_kontaktus=1;
      }          
      if (led_kikapcs_ido+100<millis()) {digitalWrite(8,HIGH);}  //az adattovábbítás bekacsolja a led-et és 100msec után kikapcsoljuk, így villan egy rövidet amikor van kommunikáció a masterrel
      if (ido_5s+5000<millis()) {  //ha 5 másodperc alatt sincs semmi, akkor áll
        //adas(0);
        n_imp=0;                   //mivel 5 sec-en beül nem jött lefutó imp, újra kell kezdeni az egész mérést, megvárni a felfutó élet és indtanai az időmérést
        v=0;
        arany=0;
        digitalWrite(5,LOW);       //jelezzük a masternak, hogy viheti az adatot
        ido_5s=millis();           //újra kezdjük az 5 sec mérését, és várjuk tovább a felfutó élet
                                   //5 másodperc múlva újra megkapjuk, hogy még mindig 0 a sebesség
      }
    }
    ido_l=millis()-ido_tmp;        //meg van a szélkerék alacsony kontaktusjel ideje

    //megjött a felfutó él, itt lehet kiértékelni az eddigi eseményeket
    //Ha ez már a második kontaktus (legalább) a folyamat elkezdése óta, akkor lehet sebességet számolni
    //Ha még csak az első kontaktus, akkor elindítjuk az időmérést
    ido_tmp=millis();
    //Az első felfutó él csak mérést indít a második után lehet sebességet számolni. 
   if (n_imp==0) { ido1=millis();n_imp++;}    //ha ez az első felfutó él, akkor elindítjuk az időmérést
   else {
     korulfordulas_db++;                      //ez a második felfutó él, vagyis az első körül fordulás
                                              //a körül fordulások számát akarjuk továbbítani, amit a továbbítás nulláz
     arany=(float)ido_l/ido_h;                //így mindig csak az utolsó fordulat arányát számoljuk ki, de egyenlőre ez is elég. Később majd mindet kell számolni a korrigáláshoz.

     //Max fél másodpercig mérjük az impulzusok számát és összesen idejét. Fél másodperc után megvárjuk ez első felfutó élet és ha
     //ez 5 másodpercen belül megérkezik kiszámoljuk az átlagos sebességet 0,5 másodperc körüli (max 5sec) időtartamra.
     //Ehhez vesszük a korul fordulások idejét, és a körül fordulások darabszámát és abból számolunk. Ha gyorsan forog a szélkerék
     //akkor a 0.5 másodperc letelte után szinte azonnal jön a felfutó és, de ha lassan akkor akár max. 5 másodpercet
     //is várhatunk. Így a legkisebb szélsebesség 2.5km/h / 5 = 0.5km/h. Lassú forgásnál akár 5 sec is eltelhet a következő
     //sebesség kiszámításáig. Ha a szélkerék áll, vagy 0.5km/h alatti sebességgel forog, akkor 0 sebességet mér a szélsebesség mérő
     //mérő, mert ennyi idő után nullázódik az impulzus számláló. 
     if (ido1+500<millis()) {                 //ha eltelt 500msec az első felfutó él óta, és jött legalább még egy impulzus,
                                              //akkor lehet sebességet számolni és továbbítani
        t_ido=(float)(millis()-ido1)/n_imp;   //átlagos körül fordulási idő számítása (min fél másodperces időszakokra átlagolok)
        v=(float)2.5*1000/t_ido;              // ez az utolsó fél másodperc átlagos szélsebessége
        digitalWrite(5,LOW);                  //jelezzük a masternak, hogy viheti az adatot
       break;                                 //kilépünk a belső ciklusból és újrakezdünk mindent
     }
     n_imp++;

   }
    ido_5s=millis();                          //innen figyeljük megint az 5sec időt, ha letelik a következő ciklusban, és incs lefutó él, akkor 0 a sebesség
                                             
    //itt elkezdjük keresni a lefutó élet (közben figyeljük a csapadékmérő kontaktusjelét is)t. Ha 5 másodpercig nem érkezik lefutó él, 
    //az azt jelenti, hogy nem forog a szélkerék. Ekkor leadjuk a master-nak a 0 szélsebességet, és újra indítjuk az 5 másodperces
    //időzítést. Ha 5 másodpercen belül megjön a lefutó él, akkor kiszámoljuk a magas jelszint idejét, és újra felfutó élet keresünk.
    //A sebesség kiszámítása felfutó éltől felfutó élig történik. Ha közben tovább ált, mint 5sec, akkor töröljük az n_imp változót,
    //ami azt jelenti, hogy újra kell kezdeni a kórul forgási idő mérését a következő felfutó élnél (min két felfutó él kell) lehet elvégezni.
    while (digitalRead(2)==1) {               //keresni kezdem a lefutó élet ezzel a ciklussal (addig fut, amíg a bemenet HIGH
      if (digitalRead(3)==0) {                //figyelem, hogy van-e csapadékmérő kontaktus jel, ha van, akkor minden ciklusban növelem a számlálót
        csap_kont_db++;
      }          
      if (digitalRead(3)==0 and csapadek_kontaktus==0) {          //csapadék kontaktus érkezett, a következő adatátvitel fogja nullázni, ami legrosszabb esetben 5 sec múlva lesz
        csapadek_kontaktus=1;                                     //az I2C kommunikációig már nem érzékel több csapadékmérő kontktusjelet. Vihetrban sem billen át 5 másodperc alatt kétszer.
      }          
      if (led_kikapcs_ido+100<millis()) {digitalWrite(8,HIGH);}   //az adattovábbítás bekacsolja a led-et és 100msec után kikapcsoljuk, így villan egy rövidet amikor van kommunikáció a masterrel
      if (ido_5s+5000<millis()) {                                 //ha 5 másodperc alatt sincs semmi, akkor áll a szélkerék, és v=0
        n_imp=0;                                                  //mivel 5 sec-en beül nem jött lefutó imp, újra kell kezdeni az egész mérést, megvárni a felfutó élet és indítani az időmérést
        v=0;                                                      //szélsebesség 0, mivel nem forog a szélkerék (illetve kevesebb mint 0.5km/h-val fúj a szél
        arany=0;                                                  //A kitöltéi tényező arány is 0, mivel nincs körül fordulás, így nem értelmezhető
        digitalWrite(5,LOW);                                      //jelezzük a masternak, hogy viheti az adatot, ami jelen esetben 0 km/h
        ido_5s=millis();                                          //újra kezdjük az 5 sec mérését, és várjuk tovább a felfutó élet,
                                                                  //5 másodperc múlva újra megkapjuk, hogy még mindig 0 a sebesség
        ido_tmp=millis();
      }
    }                                                             //megjött a lefutó él, elkezdjük majd  keresni a felfutó élet
    ido_h=millis()-ido_tmp;                                       //ennyi időt töltött magas szinten a szélkerék kontaktus
  } while (true);                                                 //Főciklus újra indul (várjuk a felfutó élet)
}


void slave_kuld() {                          
  //Ez a függvény akkor indul, amikor a master adatot kér a slave-től
  //7 byte-ot továbbítunk
  byte b[]={0,0};           //ebbe a tömbbe bontjuk szét a küldendő int értéket 
  unsigned int seb,ar;
  seb=v*100;                //A szélsebesség 100 szorosát viszem át, mert az float típusú
  memcpy(&b,&seb,2);        //egy unsigned int szétbontása 2 byte-ra a b tömbbe
  Wire.write(b[0]);  
  Wire.write(b[1]);
  ar=csap_kont_db;          //a loop() blső ciklusai mérték a kontaktusjel hosszát, és ezt is továbbítom. Ebből
                            //próbálok következtetni arra, ha fals jelzés érkezik. Nem tudom hogyan keletkezik ez a hamis jel.
                            //lehet, hogy a bemenet LOW szintet érzékel, de arra számítok, hogy ez sokkal rövidebb mint a
                            //csapadékmérő átbillenésének ideje.  csapadékmérő átbillenése alatt a csap_kont_db változó
                            //értéke az eddigi tapasztalatok szerint 3600-4200 közötti. 
  //ar=arany*100;           //ezt még a szélkerékmérő hall elem kapcsolási hiba kiderítéshez csináltam eredetileg, mostanáan nem jelentkezik a hiba
  memcpy(&b,&ar,2);         //egy unsigned int szétbontása 2 byte-ra a b tömbbe
  Wire.write(b[0]);  
  Wire.write(b[1]);
   
  //itt átvisszük kódolva azt, ha volt csapadék kontaktus. Nálam egy régi neoncső bekapcsolásakor 
  //zavarjel keletkezik, és úgy tűnik, ilyen esetben sérülnek az adatok az átvitelben,
  //ezért kisérletezek ilyen megoldással. Nem biztos, hogy ez működik, így is van nagyon
  //ritkán csapadék kontaktus jelzés
  Wire.write(korulfordulas_db);    //továbbítjuk a körül fordulások számát ami az utolsó adat átadás óta történt. Ez kell az átlagsebesség számoláshoz
  if (digitalRead(3)==1) {         //a csapadékmérő már átbillent, ezért kiértékeljük az utolsó átbillenést, ha volt
    if (csapadek_kontaktus==1) {   //volt átbillenés, tehát leadjuk, hogy átbillent, és nullázzuk a számlálót. Időnként fals átbillenés jelzések érkeztek,
                                   //ezért két bitekben jelentősen különböző érték átvitelével jelzem a csapadékmérő átbillenését és nem
                                   //1 és 0-val.
      Wire.write(170);             //volt átbillenés (binárisan: 10101010)
      csapadek_kontaktus=0;
      csap_kont_db=0;
    } 
    else {Wire.write(85);}         //nem volt átbillenés ((//binárisan: 01010101)
  }
  else {Wire.write(85);}           //épp átbillenés alatt van, ezért nem foglalkozunk vele, mintha nem billent volna át, olyan adotot viszünk át   
                                   //majd a következő adatátvitelnél átvisszük a csapadékjelzést és akkor nullázzuk a jelzőt     
  Wire.write(99);                  //lezáró byte. Ezt is a neoncső keltette zavarok kivédésére tettem bele, ha nincs a vett adat végén 99, akkor az fals átvitel volt és eldobom.
  korulfordulas_db=0;              //A körül fordulások számát összegzem a master programban, és ennek felhasználásával képzek egy egyperces sebesség átlagot.
                                   //ehhez kell a körül fordulások száma. Minden átvitelnél elkezdem újra gyűjteni.
  digitalWrite(5,HIGH);            //Így a következő LOW jelez a master-nek
  digitalWrite(8,LOW);             //bekapcsoljuk a led-et, a loop-ban fogjuk 100msec múlva kikapcsolni
  led_kikapcs_ido=millis();        //100msec időzítéshez
}

A kültéri LED kijelző és Dallas hőmérő kezelés forráskódja:

/**************************************************************
 * Kivezetések gyűjteménye::                                  *
 * A4 - SDA jelvezeték I2C kommunikációhoz                    *
 * A5 - SLC jelvezeték I2C kommunikációhoz                    *
 * A0 - fénymérő fotó tranzisztor feszjelének mérő bemente    *
 * D2 - latch pin (ST_CP)  74HC595                            *
 * D3 - clockPin (SH_CP) 74HC595                              *
 * D4 - dataPin (DS) 74HC595                                  *
 * D5 - ChipEnable (CE)74HC595                                *
 * D6 - LOAD (CS) MAX7219 ledmatrix                           *
 * D7 - CLK MAX7219 ledmatrix                                 *
 * D8 - DataIn MAX7219 ledmatrix                              *
 * D9 - LED                                                   *
 * D10 pince DS18b20 hőmérő panelen DS1 felirat               *
 * A1 akna felső DS18b20 hőmérő panelen DS2 felirat           *
 * A2 akna alsó DS18b20 hőmérő panelen DS3 felirat            *
 * A3 kisház DS18b20 hőmérő panelen DS3 felirat               *
***************************************************************/

//I2C busz kezelése 
#include <Wire.h>

//Fényérzékelő ellenállás bekötése
//======================================================================
//fényellenálláskivezetés Vezeték   Arduino UNO/NANO kivezetés
//  Fényellenállás 1              (fehér vezeték)    A0
//  Fényellenállás 2              (kék vezeték)      GND
//  felhúzó ellenállás            (1Kohm és A0-ra)   +5V
// felhúzó ellenállás 1kohm a +5v-ra, fényellenállás a föld felé
// Mért érték sötétben: 20 alatt, erős napsütésben kb 1000 fölött
// Komparálási szintek a fényerő beállításhoz:
const int fenyszint[]={20,245,  40,225,  70,210,  110,180, 150,170, 180,155,
                               230,130, 285,123, 320,105, 380,88,  440,70,  500,53,
                               560,35,  630,24,  700,12, 1024,0};
// A fényerő mérésekor ezek a komparálási szintek, illetve a hozzá tartozó PWM értékek.
// Az első számnál kisebb fényerő érték esetén az előző számpár második tagja
// lesz a PWM, míg a szint sorszáma a mátrix kijelző fényerőszintje.

//MAX7219-4DM 8x8 led matrix modul
//======================================================================
// Led matrix (matrix feliratú kábel):
//  MAX7219    Vezeték          Arduino nano/ATMega328
//  DataIn     piros vezeték    D8 (pin13)
//  CLK        fekete vezeték   D7 (pin12)
//  LOAD (CS)  zöld vezeték     D6 (pin11) 
//Matrix kijelző vezérléséhez a kimenetek
const byte MATRIX_MOSI=8;
const byte MATRIX_CLK=7;
const byte MATRIX_CS=6;
const byte MATRIX_NUM=3;   //kijelző modulok száma
//A mátrix kijelzőre írandó karakterképek. 
const byte adat[]={
      0,0,0,70,48,0,169,50,0,168,4,0,72,8,0,8,16,0,9,38,0,6,6,0,
      0,0,0,70,1,0,169,1,0,168,105,203,72,85,44,8,85,40,9,85,40,6,85,200,
      0,0,14,70,0,31,169,0,31,168,81,78,72,170,174,8,170,164,9,170,164,6,170,160,
      0,133,0,70,170,128,169,202,144,168,170,148,72,170,164,8,0,39,9,0,69,6,0,69
};
// Három kijelzőt írok, ehhez 3 x 8 byte kell, az egymás utáni 3-3 byte-ok az egymás alatti sorok adatai
// A négy sorm a négy különböző kijelzett adat karakterképei. A legelső kijelzőn mindig Celsius fok látható, ezért az nem változik
//(minden negyedik adat azonos minden sorban)

//hétszegmens kijelző vezérlés előkészítés
//-------------------------------------------------------------------------------------------
// 7 szegmens kijelző (7szegmens feliratú kábel) 74CH595 chip meghajtóval
// 10 modul sorba kötve, soros bitenkénti (modulonkénti 8 bit)beléptetéssel
//  74CH595             Vezeték           Arduino nano
//  latch pin (ST_CP)   kék vezeték       D2
//  clockPin (SH_CP)    fehér vezeték     D3
//  dataPin (DS)        sárga vezeték     D4
//  ChipEnable (CE)     zöld vezeték      D5
// ----------------------------------------------------------------------------------------
// 7 szegmens karakterkép előállításhoz segédlet
// --A--
// F   B
// --G--
// E   C 
// --D-- P (P=tizedespont)
// -----------------------------------------------------------------------------------------
// számjegyek kijelzéséhez szükséges aktív szegmensek:
// 0=ABCDEF,1=BC,2=ABGED,3=ABGCD,4=BCFG,5=ACDFG,6=ACDEFG,7=ABC,8=ABCDEFG,9=ABCDFG
// Mask: ABFGDPCE (a 8 bites bitfolyamban ebben a sorrendben lett vezetékezve
//       a 74CH595 IC kivezetése hozzákötve a kijelző modul egyes szegmenseihez 
//       tizedespont balról a 6. bit
// ---------------------------------------------------------------
//karakterképek definiálása: 0-9-ig számjegyek, 10-üres, 11-minuszjel
const byte karakterkep[12]={B11101011,  //0
                            B10000001,  //1
                            B01110011,  //2
                            B11010011,  //3
                            B10011001,  //4
                            B11011010,  //5
                            B11111010,  //6
                            B10000011,  //7
                            B11111011,  //8
                            B11011011,  //9
                            B00000000,  //ÜRES
                            B00010000}; //DUPLA ALÁHUZAS, jelenleg nem használt

// DALLAS hőmérő chip-ek bekötése, használatuk előkészítése
//===========================================================================
    // Dallas onwire hőmérő chip (felülről nézve balról jobbra haladva)
    //  18B20 kivezetés   Vezeték            Arduino UNO/NANO kivezetés
    //  GDD               kék vezeték        GND
    //  DATA              sárga vezeték      A3, A1, A6 kivezetéseken 
    //  VDD               piros vezeték      +5V
#include <OneWire.h>           //Dallas 18b20 hőmérő kezeléshez onewire könyvtár
byte ds_data[9];               //kiolvasott adtok tárolására
OneWire  ds1(10);              // a 10. kivezetéshez kell kapcsolni pince hőmérőjét 
OneWire  ds2(A1);              // a A1. kivezetéshez kell kapcsolni az akna felső hőmérőjét 
OneWire  ds3(A2);              // a A2. kivezetéshez kell kapcsolni az akna alsó hőmérőjét 
OneWire  ds4(A3);              // a A3. kivezetéshez kell kapcsolni az kisház hőmérőjét 

// Globális változól
// ========================================================================
int paratartalom=0;
int legnyomas=0;
//a külső hőmérsékletet történelmi okokból 100szoros értékben kapom, de nekem csak 10-szeres kell
//ezért átvételkor még osztom 10-el, ezért itt már ez egy 10-szeres int érték (csak tizedespontot kell kitenni
//megjelenítéskor
int kulso_homerseklet=999;
// a csapadék mennyiséget és szélsebességet  10-szeres értékkel int-ben 
// kapom meg I2C-n keresztül (így nem kell float-ot átvinni, és kijelzéskor csak tizedespontot kell kijelezni)
int csapadek=0;
int pill_szelseb=0;
//A hőmérséklet adatokat 100-al szorozva egy int-ban tárolom. //Így RF továbbításkor és I2C továbbításkor 
//csak int-et kell átvinni 2 byte-on, és nem float-ot 4 byte-on. itt ezeket nem jelzem ki, így értékük a programban közömbös.
int pince_homerseklet=9999;
int akna_homerseklet_also=9999;
int akna_homerseklet_felso=9999;
int kishaz_homerseklet=9999;

//Kijelzéskor csak akkor jelzem ki a szelet és a csapadékot, ha nagyobbak mint 0. Ha folyamatosan 
//fúj a szél, akkor 20 másodpercenként kijelzem a páratartalmat és a légnyomást is 2-2 másodpercig.
int paratartalom_tmp=0;   //kijelzésre kerülő adatok meghatározásához segédváltozó
int legnyomas_tmp=0;      //kijelzésre kerülő adatok meghatározásához segédváltozó
int csapadek_tmp=0;       //kijelzésre kerülő adatok meghatározásához segédváltozó

byte kijelzes_num=0;      //ez mondja meg, hogy éppen melyik adatot jelzem ki a kijelzőn 
byte ciklus=0;            //ha a szélsebesség nagyobb, mint 0, ennek segítségével időnként minden adatot megjelenítek (értéke 0-3-ig)

int matrix_res=0;         //A mátrix kijelzőre időnként kiküldök egy reset jelsorozatot, amivel alaphelyzetbe állítom
                          //Erre azért ven szükség, mert néha zavarossá válik a kijelző, amin csak egy reset segít.
                          //A reset során kikapcsolom a test módot, a scanlimit regisztert 7-re állítom, decode módot kikapcsolom
                          //és a shutdown módot is kikapcsolom a kijelzőn.
    
// a mért értéket karakterképének bitsorozta a 7 segment kijelzőbe léptetéshez (4 számjegy)
byte bitsor[20];  
long kijelzovaltas_ido=millis(); //az alsó sor értékeinek váltogatásának időzítéséhez
long mbar_ido=millis();          //16 másodpercenként akkor is kijelzem a légnyomást és páratartalmat, ha folyamatosan fúj a szél
long kiolvasas_ido=millis();     //10 percenként kiolvasom a dallas chip-ekből a hőmérsékletet (3 másodperccel előtte indítom a mérést)
long meres_ido=millis();         //10 percenként mérést indítok a dallas chip-ekben, ehhez segédváltozó 

void setup()
{
  Serial.begin(115200);
  //Serial.println("Indul...");
  Wire.begin(9);                 // I2C kommunikáció inicializálása 9-as eszköz címmel (mivel slave, címet kell megadni)
  Wire.onReceive(slave_fogad);   //Ezt a funkciót hívja az Arduino, amikor adatot kap a master-től
  Wire.onRequest(slave_kuld);    //Ezt a funkciót hívja meg az Arduino, amikor a master adatot kér a slave-től 
  analogReference(DEFAULT);      //A tápfeszt veszi referenciának (5V)
  //74HC595 előkészítése
  pinMode(2,OUTPUT);             //latch pin 74HC595
  pinMode(4,OUTPUT);             //data pin (DS) 74HC595
  pinMode(3,OUTPUT);             //Clock pin 74HC595
  pinMode(5,OUTPUT);             //chipenabla (CE) 74HC595
  // 8x8 matrix kimenetek előkészítése 
  pinMode(MATRIX_MOSI,OUTPUT);   //MOSI
  pinMode(MATRIX_CLK,OUTPUT);    //CLK
  pinMode(MATRIX_CS,OUTPUT);     //CS
  matrix_reset();
  //ide jönnek majd az első mérések, és egy 0. változó feltöltés, hogy rögtön indulhasson az értékek kijelzése
  szamjegybontas(kulso_homerseklet,0);   //külső hőmérséklet karakterképe bitsor tömbbe
  szamjegybontas(paratartalom,1);        //páratartalom karakterképe bitsor tömbbe
  szamjegybontas(legnyomas,2);           //legnyomás karakterképe bitsor tömbbe
  szamjegybontas(csapadek,3);            //kisház hőmérséklet karakterképe bitsor tömbbe 
  szamjegybontas(pill_szelseb,4);        //kisház hőmérséklet karakterképe bitsor tömbbe 
  //indítunk egy DS1 mérést, hogy amikor kiolvassuk, akkor már legyen amért hőmérsékelt
  delay(1000);
  kijelzes_num=1;
  kijelzes(kijelzes_num);         //első meghívás reseteli a kijelzőt (és azt követően minden 120-dik)
  kijelzovaltas_ido=millis();     //fél másodpercenként frissítem az adatokat a kijelzőn. Ennek akkor van szerepe, ha fúj a szél, mert követhető a szélsebesség változása
  meres_ido=millis()-598500;      //kényszerítünk egy azonnali mérést a dallas chip-ekkel a loop-ban ezzel az értékkel
  kiolvasas_ido=millis()-598500;  //kényszerítjük a loop-ban a mérés után a hőmérséklet kiolvasást a dallas chip-ekből
}

void loop() {
  /**********************************************************************************************************************
   * Ez a programrész 0,5 másodpercenként értéket küld az első hétszegmens kijelző sorra, é az alsó mátrix kijelzőre    *
   * Az algoritmus figyeli a szélsebességet, és a fúj a szél, akkor nagyrészt azt jelzi ki. Azonban 16 másodpercenként  *
   * Elindít egy kijelzési ciklust, amiben megjeleníti a légnyomást és a páratartalmat, illetve ha volt csapadék, akkor *
   * annak az értékét is. Aztán újra szélsebességet mutat. Ha nem fúj a szél, akkor páratartalom, légnyomás             *
   * és ha van akkor csapadék kijelzés megy folyamatosan 2-2 másodperces váltogatással.                                 *
   **********************************************************************************************************************/
  if (millis()>kijelzovaltas_ido+500) {  //2 másodpercenként váltjuk a kijelző alsó sorában kijelzett értékeket
    if (kijelzes_num==1 and paratartalom_tmp==0) {kijelzes_num=2;}   //ha a páratartalom nulla, akkor nem jelezzük ki, a következő adatot címezzük
    if (kijelzes_num==2 and legnyomas_tmp==0) {kijelzes_num=3;}      //ha a légnyomás nulla, akkor nem jelezzük ki, a következő adatot címezzük
    if (kijelzes_num==3 and csapadek_tmp==0) {kijelzes_num=4;}       //ha a csapadék nulla, akkor nem jelezzük ki, a következő adatot címezzük
    if (kijelzes_num==4 and pill_szelseb>0.1) {                      //ha szélsebesség nem nulla
      paratartalom_tmp=0;                                            //akkor minden más adatot nullázunk, így következő körben nem lesznek kijelezve
      legnyomas_tmp=0;
      csapadek_tmp=0;
      if (mbar_ido+20000<millis() and ciklus==3) {                   //16 másodperc múlva az adatoknak értéket adunk, hogy akvetkező körben ki legyenek jelezve
        //Serial.println("20sec");
        mbar_ido=millis();
        paratartalom_tmp=paratartalom;
        legnyomas_tmp=legnyomas;
        csapadek_tmp=csapadek;
      }
    }
    if (kijelzes_num==4 and pill_szelseb<0.1) {                      //ha szélsebesség nem nulla
      paratartalom_tmp=paratartalom;
      legnyomas_tmp=legnyomas;
      csapadek_tmp=csapadek;
      kijelzes_num=1;
      mbar_ido=millis();
    }
    kijelzes(kijelzes_num);
    ciklus++;
    if (ciklus>3) {
      ciklus=0;
      kijelzes_num++;
    }
    if (kijelzes_num>4) { kijelzes_num=1;}
    kijelzovaltas_ido=millis();
  }
 /**********************************************************************************************
   * Ez a programrész 10 percenként végez el egy mérést indítást az összes  dallas chip-en.    *
   * 3 másodperccel később kiolvasása a mérési eredményeket az összes dallas chip-ből          *
   *********************************************************************************************/
  if (millis()>meres_ido+600000) {     //mérés indítás kiküldése az összes dallas chipnek
    meres_ido=millis();                //innen indul a mérés kiadásának 600 másodperce időzítése
    meresinditas();
  }
  if (millis()>kiolvasas_ido+603000) { //adatok kiolvasása az összes dallas chipből
    kiolvasas();
    meres_ido=millis();                //innen indul a mérés kiadásának 600 másodperce időzítése
    kiolvasas_ido=millis();            //innen indul a kiolvasás 603 másodperces időzítése
  }
}

/*********************************************************************************************
 * Megméri az A6 bemenetre kötött fotótranzisztor és ellenállásosztóban az ellenállás        *
 * feszültségét. Ellenállás értéke 1Kohm. A fotótranzisztor kb 5000Lux, nál teljesen kinyit  *
 * ekkor az ellenálláson közel 5V feszültség mérhető. Sötétben a feszültség 0V               *
 *********************************************************************************************/
void fenyero_beallitas(){
  int fenyertek=analogRead(14);               //megmérjük a fotótranzisztor feszültségét
  int lf=0;  
  for (lf=0;lf<16;lf++) {                     //a fenyertek[] tömb elemeivel történő összehasonlítás
    if (fenyszint[lf*2]>fenyertek) {break;}
  }
//  hszegfeny=fenyszint[ledmatfeny*2+1];
  analogWrite(5,fenyszint[lf*2+1]);           // 7seg kijelzők fényereje (PWM értékek 0-255 között)
  matrix_comm(10,lf);                         // matrix kijelző fényerejének beállítása 0-15
} 

void szamjegybontas(int szamertek,byte index){
/**********************************************************************************************************************
 * Ez a függvény a paraméterben megkapott számot számjegyekre bontja, ha kell kioltja a vezető nullát, illetve        *
 * negatív előjelet rak elé, és ha kell tizedespont-ot is bekapcsolja.                                                *
 * A szamérték változóban veszi át az átalakítandó adatot, mig az index határozza meg az adat bitképének              *
 * helyét a bitsor[] tömbben. A 0,3 és 4 (hőmérséklet, csapadék, szélsebesség) index esetén tizedespont is kell.      *
 * A számjegyek bitenénti képét a bitsor[] tömbbe másolja, amiből majd a 7 seg kijelzőkbe léptetést                   *
 * közvetlenül el lehet végezni.                                                                                      *
 * ********************************************************************************************************************/
  int eszamertek=szamertek;          //eredeti szamertek még kelleni fog. Az átvett érték negatív is lehet
  int ezres=0;    
  int szazas=0;
  int tizes=0;
  int egyes=0;
  if (szamertek<0) {  szamertek=0-szamertek;}
  ezres=szamertek/1000;
  szazas=(szamertek-(ezres*1000))/100;
  tizes=(szamertek-(ezres*1000)-(szazas*100))/10;
  egyes=szamertek-(ezres*1000)-(szazas*100)-(tizes*10);
  if (eszamertek<1000) {  ezres=10;}                                //vezető nulla kioltása az ezres számjegyben
  if (eszamertek<100 && eszamertek>0) { szazas=10;}                 //vezető nulla kioltása az százas számjegyben
  if (eszamertek>-10 && eszamertek<0) { ezres=10;szazas=11;}        //vezető nulla kioltása az százas számjegyben
  if (eszamertek>-100 && eszamertek<=-10) { szazas=11;ezres=10;}    //vezető nulla kioltása az ezres számjegyben
  if (eszamertek<-100) {  ezres=11;}                                //vezető nulla kioltása az ezres számjegyben
  bitsor[index*4+0]=karakterkep[ezres];
  bitsor[index*4+1]=karakterkep[szazas];
  if (index==1 || index==2) { bitsor[index*4+2]=karakterkep[tizes];} //páratartalom és légnyomás esetén nem kell tizedespont, simán másoljuk az értéküket a helyükre
  else {  bitsor[index*4+2]=karakterkep[tizes]|B00000100;}           //tizedespont bekapcsolása és másolás 
  bitsor[index*4+3]=karakterkep[egyes];
}


void slave_fogad()  {
//Ez a függvény akkor indul, amikor a slave értesítést kap, hogy a master adatot fog küldeni
//Serial.println("fogad");
  int int_ertek;
  byte adat_tipus;
  byte b[]={0,0};
  adat_tipus=Wire.read();
  //Serial.print("adat tipus:");Serial.println(adat_tipus);
  if (adat_tipus==0) {
    b[0]=Wire.read();
    b[1]=Wire.read();
    memcpy(&int_ertek,&b,2);                //ez int értéket másol össze a két érkezett byte-ból
    kulso_homerseklet=int_ertek/10;         //történelmi okokból a külső hőmérséklet százzal lett szorozva küldéskor, ezért még 10-el osztani kell, de továbbra is int szám
    b[0]=Wire.read();
    b[1]=Wire.read();
    memcpy(& paratartalom,&b,2);            //ez int értéket másol össze a két érkezett byte-ból
//    paratartalom=int_ertek;
    b[0]=Wire.read();
    b[1]=Wire.read();
    memcpy(&legnyomas,&b,2);                //ez int értéket másol össze a két érkezett byte-ból
    //legnyomas=int_ertek;
    b[0]=Wire.read();
    b[1]=Wire.read();
    memcpy(&csapadek,&b,2);                 //ez int értéket másol össze a két érkezett byte-ból
    //csapadek=int_ertek;
    szamjegybontas(kulso_homerseklet,0);    //külső hőmérséklet karakterképe bitsor tömbbe
    szamjegybontas(legnyomas,2);            //legnyomás karakterképe bitsor tömbbe
    szamjegybontas(paratartalom,1);         //páratartalom karakterképe bitsor tömbbe
    szamjegybontas(csapadek,3);             //kisház hőmérséklet karakterképe bitsor tömbbe 
  }
  if (adat_tipus==1) {
    b[0]=Wire.read();
    b[1]=Wire.read();
    memcpy(&int_ertek,&b,4);                //ez int értéket másol össze a két érkezett byte-ból
    pill_szelseb=int_ertek/10;
    szamjegybontas(pill_szelseb,4);         //pillanatnyi szélsebesség karakterképe bitsor4 tömbbe 
    //Serial.println("pill_szelseb:");Serial.println(pill_szelseb);
  }
}

void slave_kuld()                            
//Ez a függvény akkor indul, amikor a master adatot kér a slave-től
{
  //Serial.println("küld");
  int ertek;
  byte b[]={0,0};
  ertek=pince_homerseklet+10000;
  memcpy(&b,&ertek,2);  //egy int szétbontása 2 byte-ra a b tömbbe
  Wire.write(b[0]);  
  Wire.write(b[1]);
  ertek=akna_homerseklet_also+10000;
  memcpy(&b,&ertek,2);  //egy int szétbontása 2 byte-ra a b tömbbe
  Wire.write(b[0]);  
  Wire.write(b[1]);
  ertek=akna_homerseklet_felso+10000;
  memcpy(&b,&ertek,2);  //egy int szétbontása 2 byte-ra a b tömbbe
  Wire.write(b[0]);  
  Wire.write(b[1]);
  ertek=kishaz_homerseklet+10000;
  memcpy(&b,&ertek,2);  //egy int szétbontása 2 byte-ra a b tömbbe
  Wire.write(b[0]);  
  Wire.write(b[1]);
}


void meresinditas() {
  if (ds1.reset()==1) {   //itt 1-el jelez, ha a reset jelre válaszol egy DS18b20, tehát van a vonalon chip. Ha nincs a vonalon DS18b20, akkor ez 0.
    ds1.skip();           //ezzel azt mondjuk a chip-nek, hogy egyedül van a vonalon, nem adunk meg eszköz címet, mindenképpen csinálja amira utasítást kap
    ds1.write(0x44,1);    //elindítunk egy mérést a chip-ben
  }
  if (ds2.reset()==1) {    //itt 1-el jelez, ha a reset jelre válaszol egy DS18b20, tehát van a vonalon chip. Ha nincs a vonalon DS18b20, akkor ez 0.
    ds2.skip();           //ezzel azt mondjuk a chip-nek, hogy egyedül van a vonalon, nem adunk meg eszköz címet, mindenképpen csinálja amira utasítást kap
    ds2.write(0x44,1);    //elindítunk egy mérést a chip-ben
  }
  if (ds3.reset()==1) {    //itt 1-el jelez, ha a reset jelre válaszol egy DS18b20, tehát van a vonalon chip. Ha nincs a vonalon DS18b20, akkor ez 0.
    ds3.skip();           //ezzel azt mondjuk a chip-nek, hogy egyedül van a vonalon, nem adunk meg eszköz címet, mindenképpen csinálja amira utasítást kap
    ds3.write(0x44,1);    //elindítunk egy mérést a chip-ben
  }
  if (ds4.reset()==1) {    //itt 1-el jelez, ha a reset jelre válaszol egy DS18b20, tehát van a vonalon chip. Ha nincs a vonalon DS18b20, akkor ez 0.
    ds4.skip();           //ezzel azt mondjuk a chip-nek, hogy egyedül van a vonalon, nem adunk meg eszköz címet, mindenképpen csinálja amira utasítást kap
    ds4.write(0x44,1);    //elindítunk egy mérést a chip-ben
  }
}

void kiolvasas() {
        //A mérés elindítás a pár másodperccel előbb megtörtént (a chip-nek kell min700msec a méréshez), mindig 
        //az utolsó mérés adatait lehet kiolvasni.  
        //Ha a dallas chip (nem válaszol a reset-re), akkor kihagyjuk a kiolvasást. 
        if (ds1.reset()==1) {                //A chip a reset-re válaszol, ki lehet olvasni az adatokat
          ds1.skip();ds1.write(0xBE);        //Megmondjuk a chip-nek, hogy egyedül van, és hogy kiolvasás következik
          for ( byte i=0;i<9;i++) {ds_data[i]=ds1.read();}   //adatok kiolvasása ds_data tömbbe   
          if ( OneWire::crc8(ds_data,8)!=ds_data[8]) {pince_homerseklet=8888;}   //crc ellenőrzés, crc hibát a 88,8 fok jelzi
          else {pince_homerseklet=(((ds_data[1]<<8)|ds_data[0])/16.0)*100;}      //ez egy jó mérési adat
        }
        else {pince_homerseklet=9999;}       //99,9 fok jelzi, hogy nincs chip a vonalon
        
        if (ds2.reset()==1) {                //A chip a reset-re válaszol, ki lehet olvasni az adatokat
          ds2.skip();ds2.write(0xBE);        //Megmondjuk a chip-nek, hogy egyedül van, és hogy kiolvasás következik
          for ( byte i=0;i<9;i++) {ds_data[i]=ds2.read();}   //adatok kiolvasása ds_data tömbbe   
          if ( OneWire::crc8(ds_data,8)!=ds_data[8]) {akna_homerseklet_felso=8888;}   //crc ellenőrzés, crc hibát a 88,8 fok jelzi
          else {akna_homerseklet_felso=(((ds_data[1]<<8)|ds_data[0])/16.0)*100;}      //ez egy jó mérési adat
        }
        else {akna_homerseklet_felso=9999;}  //99,9 fok jelzi, hogy nincs chip a vonalon
        
        if (ds3.reset()==1) {                //A chip a reset-re válaszol, ki lehet olvasni az adatokat
          ds3.skip();ds3.write(0xBE);        //Megmondjuk a chip-nek, hogy egyedül van, és hogy kiolvasás következik
          for ( byte i=0;i<9;i++) {ds_data[i]=ds3.read();}   //adatok kiolvasása ds_data tömbbe   
          if ( OneWire::crc8(ds_data,8)!=ds_data[8]) {akna_homerseklet_also=8888;}   //crc ellenőrzés, crc hibát a 88,8 fok jelzi
          else {akna_homerseklet_also=(((ds_data[1]<<8)|ds_data[0])/16.0)*100;}      //ez egy jó mérési adat
        }
        else {akna_homerseklet_also=9999;}   //99,9 fok jelzi, hogy nincs chip a vonalon
        
        if (ds4.reset()==1) {                //A chip a reset-re válaszol, ki lehet olvasni az adatokat
          ds4.skip();ds4.write(0xBE);        //Megmondjuk a chip-nek, hogy egyedül van, és hogy kiolvasás következik
          for ( byte i=0;i<9;i++) {ds_data[i]=ds4.read();}   //adatok kiolvasása ds_data tömbbe   
          if ( OneWire::crc8(ds_data,8)!=ds_data[8]) {kishaz_homerseklet=8888;}   //crc ellenőrzés, crc hibát a 88,8 fok jelzi
          else {kishaz_homerseklet=(((ds_data[1]<<8)|ds_data[0])/16.0)*100;}      //ez egy jó mérési adat
        }
        else {kishaz_homerseklet=9999;}      //99,9 fok jelzi, hogy nincs chip a vonalon
}


void kijelzes(byte kijelzes_num) {
    digitalWrite(2, LOW); //shift regiszter kimenetek lezárása (latchPin=0)
    //külső hőmérséklet
    shiftOut(4, 3, MSBFIRST, bitsor[0]);                     //külső hőmérséklet szazas
    shiftOut(4, 3, MSBFIRST, bitsor[1]);                     //külső hőmérséklet tizes
    shiftOut(4, 3, MSBFIRST, bitsor[2]);                     //külső hőmérséklet egyes
    shiftOut(4, 3, MSBFIRST, bitsor[3]);                     //külső hőmérséklet tizedes
    //1=páratartalom, 2=légnyomáa, 3=csapadék, 4=szél
    shiftOut(4, 3, MSBFIRST, bitsor[kijelzes_num*4+0]);      //ezres
    shiftOut(4, 3, MSBFIRST, bitsor[kijelzes_num*4+1]);      //szazas
    shiftOut(4, 3, MSBFIRST, bitsor[kijelzes_num*4+2]);      //tizes
    shiftOut(4, 3, MSBFIRST, bitsor[kijelzes_num*4+3]);      //egyes
    digitalWrite(2, HIGH);  //beléptetett érték kiengedése a shiftregiszter kimenetére (latchPin=1)
    
    if (matrix_res==0) {matrix_reset();}  //minden 120. meghíváskor (és a legelsőnél) a kijelzőt alaphelyzetbe állítjuk
                                          //test ki, scanlimit=7, shutdown ki, decode mód ki
    matrix_res++;
    if (matrix_res>120) {matrix_res=0;}  //ezt a funkciót másodpercenként 2x hívom meg, így percenként resetelem a kijelzőt
    //1=páratartalom, 2=légnyomás, 3=csapadék, 4=szél mértékegységek a mátrix kijelzőkre
    //karakterképek kiírása a mátrix kijelzőkre. Mindig egy sort léptetünk egyszerre sorban mindhárom
    //láncra fűzött kijelzőre, majd a kijelző cs bementére adott high szinttel ezeket beírja a kijelzőkbe.
    for (int i=0;i<8;i++) {              //végig megyünk a karakterképek sorain (8 sor)
      digitalWrite(MATRIX_CS,LOW);       //kezdődik az adatok (egy sor) beléptetése  
      for (int n=0;n<3;n++) {            //három chip van sorba kötve
        shiftOut(MATRIX_MOSI,MATRIX_CLK,MSBFIRST,i+1);    //megmondjuk melyik ledsort akarjuk írni (1-8)
        shiftOut(MATRIX_MOSI,MATRIX_CLK,MSBFIRST,adat[(kijelzes_num-1)*24+i*3+n]);  //ez az adat, amit beléptetünk a kijelölt ledsorba
      }
      digitalWrite(MATRIX_CS,HIGH);      //a beléptetett három kijelző adatot beírjuk a chip-ek memóriájába (kiléptetett adatok beírása a chip memóriájába
    }
   fenyero_beallitas();
}

void matrix_reset() {
  //az összes chip-re kiküldjük sorban az alapbeállításokat
  matrix_comm(15,0); //displaytest kikapcsolása
  matrix_comm(9,0);  //decode mód beállítása
  matrix_comm(11,7); //scanlimit beállítása
  matrix_comm(12,1); //shutdown kikapcsolása
} 

void matrix_comm(byte parancskod,byte adat) {
  //három chip lett sorba kötve, ezért háromszor küldünk ki minden parancskódot.
  //A chip-ek egymásnak adják tovább az adatokat
  digitalWrite(MATRIX_CS,LOW);  //most kezdődik az adatok kiléptetése a chip-ekre 
  for(int i=0;i<3;i++) { 
    //matrix_beleptet(parancskod,adat);
    shiftOut(MATRIX_MOSI,MATRIX_CLK,MSBFIRST,parancskod); //ez jelöli ki a parancsot
    shiftOut(MATRIX_MOSI,MATRIX_CLK,MSBFIRST,adat);       //ez a parancs értéke
  }
  digitalWrite(MATRIX_CS,HIGH);
}

Időjárás állomás központi egységének forráskódja! Ez végzi a BM280 lekérdezését, kezeli az FRAM-ot, lekérdezgeti a másik két egységet I2C buszon, vezérli a 2×16 kerekteres LCD szerviz kijelzőt, kezeli a RTC órát, és vezérli az RF433Mhz-s adót és vevőt:

/**************************************************************************************************
 * Kivezetések gyűjteménye:                                                                       *
 * A4 - SDA jelvezeték I2C kommunikációhoz                                                        *
 * A5 - SLC jelvezeték I2C kommunikációhoz                                                        *
 * D10 - kijelző ki/be nyomógomb bemenet (fejlesztéskor használtam, hogy éjszaka ne világítson    *
 * D2 - A slave jelzi ezen, ha adata van (zöld kábel a csatlakozón)                               *
 * D3 - RF433 adás kimenet                                                                        *
 * D4 - RF433 vétel bemenet                                                                       *
 * ---------------------------------------------------------------------------------------------- *
 * Szélsebesség és csapadékmérő csatlakozó vezetékszínek:                                         *
 * Zöld: adásjelző kimenet (Arduino 11)                                                           *
 * Narancs: SDA (Arduino A)                                                                       *
 * Sárga : SCL                                                                                    *
 * Piros Vcc                                                                                      *
 * Fekete GND                                                                                     *
 * ---------------------------------------------------------------------------------------------- *
 * Adás és vétel modul csatlakozó bekötése:                                                       *
 * zöld: GND                                                                                      *
 * Narancs: 12V illetve 5V (már csak 5V)                                                          *
 * Piros 5V                                                                                       *
 * Fehér RX                                                                                       *
 * Fekete TX                                                                                      *
 **************************************************************************************************/

#include <RH_ASK.h>           //RedioHead könyvtár
RH_ASK driver(2000,4,3);   //inicializálás 2000boud sebesség, 4 kimenet vétel, 3 kimenet adás
uint8_t adatb[5];             //ebbe a tömbbe kerülnek a vett adatok
unsigned int_ertek=0;

#include<avr/wdt.h>           //WatchDog header betöltése

//I2C busz kezelése 
#include <Wire.h>
#include <Adafruit_BME280.h>
#define SEALEVELPRESSURE_HPA (1013.25)
Adafruit_BME280 bme; // I2C

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

//óra modul programjai és változó deklarációi
//==========================================
//Az óra használatához szükséges dolgok
#define CLOCK_ADDRESS 0x68    //a modulon található óra IC címe az I2C buszon. 

struct dat_ido {              // a dátum és idő beállításához illetve kiolvasásához létrehozunk egy struktúrát dat_ido néven (változó típus)
  int ev;                     //év tárolása a struktúrában (évszázadokkal kell megadni, illetve így kapjuk meg az eredményt a dátum kiolvasásakor)
  byte ho,nap,het_napja;      //ho, nap és hét napjának tárolása a struktúrában
  byte ora,perc,masodperc;    //óra, perc és másodperc tárolása a struktúrában (24 órás üzemmódot fogunk használni)
  bool pontos;                //ez a változó fogja megadni az IDO kiolvasását követően, hogy pontos-e a kiolvasott időpont 
                              //(ha elemes működés közben leáll a DS3231 oszcillátora, akkor az "OSF" flag bebillen, és ezt olvassuk ki.
                              //Ha az óra nem pontos, akkor lehet vele jelezni, hogy be kell állítani merte esetleg a lemerült elem miatt elállírtódott
};

dat_ido ds3231;               //létrehozzuk az dat_ido típusú változónkat DS3231 változó néven

//Fram cím adatai és cella típus felsorolása
byte A0A1=0b00;
byte _i2cAddress= (0b101000 | A0A1)<<1;
enum cella_tipus { MIN, MAX, SUM, AVG, STO };
//napi átlaghoz tároló cellák címeinek felsorolása
const byte V_KULSO=0;
const byte V_KULSO_MIN=1;
const byte V_KULSO_MAX=2;
const byte V_PARA=3;
const byte V_PARA_MIN=4;
const byte V_PARA_MAX=5;
const byte V_LEGNY=6;
const byte V_LEGNY_MIN=7;
const byte V_LEGNY_MAX=8;
const byte V_PINCE=9;
const byte V_PAKNAA=10;
const byte V_PAKNAF=11;
const byte V_KISHAZ=12;
const byte V_KISHAZ_MIN=13;
const byte V_KISHAZ_MAX=14;
const byte V_IMP_SZAM=33;
const byte V_SZELES_IDO=34;
const byte V_SZELSEB_MAX=35;
const byte V_CSAPADEK_IMP=36;
const byte V_IDOPONTOSITAS=37;
const byte V_RESET=38;
const byte V_RESET_K=39;
//mérési eredményeket tároló globális vááltozók
int kulso_homerseklet=0;         //BME280-tól lekérdezett adat. A konkret hőméréklet érték 100-szorosát tárolom, kijelzéskor 100-al osztom
int paratartalom=0;              //BME280-tól lekérdezett adat. 
int legnyomas=1000;              //BME280-tól lekérdezett adat. 
int pince_homerseklet=0;         //A led kijelzőtől (slave 9) lekérdezett adat. A konkrét hőmérséklet érték 100-szorosát tárolom, kijelzéskor 100-al osztom
int akna_homerseklet_also=0;     //A led kijelzőtől (slave 9) lekérdezett adat. A konkrét hőmérséklet érték 100-szorosát tárolom, kijelzéskor 100-al osztom
int akna_homerseklet_felso=0;    //A led kijelzőtől (slave 9) lekérdezett adat. A konkrét hőmérséklet érték 100-szorosát tárolom, kijelzéskor 100-al osztom
int kishaz_homerseklet=0;        //A led kijelzőtől (slave 9) lekérdezett adat. A konkrét hőmérséklet érték 100-szorosát tárolom, kijelzéskor 100-al osztom
//szélsebességmérőtől érkezett adatok
int csapadek=0;                  //a csapadék mennyisége éjfél óta (Fram-ban gyűjtött adat, induláskor betölteni fram-ból
int pill_szelseb=0;              //az éppen kiolvasott szélsebesség adat
long imp_szam=0;
//kiszámolt illetve gyüjtött szélsebesség adatok
long szeles_ido=0;               //az az idő, amikor fújt a szél. Másodperceben!  (24 órás, éjfélkor kerül nullázásra)
int atlag_szelseb=0;             //számított átlagsebesség (24 órás, éjfélkor kerül nullázásra). Az utolsó nullázás óta megszámlált székerék körülfordulásainak száma osztva a szeles  idővel
int perc_atlag_szelseb=0;        //Az utolsó egy perc (percenként kerül továbbításra RF433 adóval) átlagsebessége. Az egyperc székerék körülfordulásainak száma osztva az idővel
int perc_imp_szam=0;             //Az utoló egy perc szélkerék körül fordulásainak száma 
long perc_szeles_ido=0;          //Az utolsó egy percben annak az ideje, amikor forgott a szélkerék (fújt a szél)
int max_szelseb=0;               //A szélsebesség maximuma (éjfélkor nullázódik)
//Segédváltozók
int elozo_szelseb=9999;          //Csak akkor küldünk a led kijelzőnek szélsebesség adatot, ha változott a szélsebesség az előző küldés óta. Pl. szélcsenben csökkenti a kommunikációt
int elozo_szelseb1=9999;         //Csak akkor küldünk az RF433 adóval szélsebesség  adatot, ha változott a szélsebesség az előző küldés óta. Pl. szélcsenben csökkenti a kommunikációt
long elteltido=millis();         //A szélkerék forgása esetén két adat között eltelt idő méréséhez segédváltozó. 
//a min, max és átlagértékek ezekben a változókban képződnek. FRAM-ból kerülnek feltöltésre. Azért kellenek, mert az FRAM írogatás sok iI2C kommunikációt eredményezne
long hkulso_a,hkulso_m,hkulso_x,para_a,para_m,para_x;         
long legny_a,legny_m,legny_x,hpince_a,haknaa_a,haknaf_a;
long hkishaz_a,hkishaz_m,hkishaz_x;
long reset_db=0;                //hányszor reseteltem a többi vezérlőt
long reset_k_db=0;              //hányszor érkezett hibás adat a szélsebességmérőtől
long indulo_resetszam=0;        //csak néhányszor akarom resetelni a külső vezérlőket a szélsebességmérő miatt. 
                                //ha nem javul meg egy-két resettől, akkor nem is fog, ilyenkor hibajelzés, és legfeljebb
                                //nem lesz szélsebesség adat a rendszerben

long meres_ido=millis();        //15 másodpercenkénti mérés időzítéséhez segédváltozó 
                                //a kijelzo nyomógombbal történő ki és bekapcsolásához kellenek
bool elengedve=LOW;             //a nyomógomb elengedett állapotát jelzi, akkor HIGH, ha egy nyomva tartás után (nyomva=HIGH) először megszakad a kontaktus
long elenged_ido=millis();      //az elengedés prellmentesítéshez az időmérésre
bool nyomva=LOW;                //ha HIGH, akkor nyomva van a nyomógomb, az első kontaktus HIGH-re állítja, elengedés után lesz LOW-ra visszaállítva
long nyomva_ido=millis();       //az megnyomás prellmentesítéshez időmérésre
bool lcd_vilagitas=LOW;         //az lcd kijelző háttérvilágításának időzített kikacsolásához használt segédváltozó             
bool ora_setup=LOW;             // az óra setup éppen folyamatan van, ha HIGH
long ora_setup_ido=millis();    //ha a nyomógombot 5 ec-nél hosszabban nyomjuk, akkor elkezdődik a setup. Ennek az időnem a méréséhez segédváltozó
byte setup_num=0;               //ez jelzi, hogy melyik időparamétert állítjuk be éppen. 
bool setup_num_novel=HIGH;      //ha növelni lehet egy elengedés után a setup_num-ot, akkor HIGH. Érték változtatás után lesz LOW, hogy ne növekedjen a setup_num értéke
bool mert_ertek_kijelzes=HIGH;  //mérési eredmények lcd-re írásának engedélyezés HIGH értékkel
long lcd_kijelzo_ido=millis();  //az lcd adatkiírás frissítéshez  (3 másodpercenként)
long lcd_frissit_ido=millis();  //az órát csak félpercenként kérdezem le, hogy csökkentsem az I2C kommunikációt, ehhez használom
byte lcd_eszkoz_num=0;          //az aktuálisan kijelzett eszköz indexe az lcd kijelzőn frissített mert adathoz
bool bme280_on=LOW;             //bme280 légnyomás, páratartalom és hőmérséklet mérő inicializálva lett-e, HIGH esetén igen
long idopontositas_perc=0;      //kiírom a kijelzőre, hogy mikor jött utoljára időpontosítás adat, az a változó másodperceben tartalmazza az eltelt időt. Az időpontosítás nullázza
long reset_ido=millis();        //ha 10 másodpercig nem jön adat a szélsebességmérőtől, akkor küldünk neki reset jelet, ehhez kell
long wdt_ido_tmp=millis();      //A watcdog számlálót a loop-ban 4 másodpercenként nullázom, ennek időzítéséhez használom
bool egyeb_mehet=0;             //Igyekszem elkerülni, hogy amikor a szélsebességmérő adatot akar küldeni, mással legyen elfoglalva a program és ne tudjon vele foglalkozni
                                //mivel a szélsebességmérő nagy szélben 20-50msec időnként kéri az adatátvitelt, ez a változó jelzi, ha megtörtént az adatátvitel
                                //és egy-egy feladatot végrehajt a program. Ha végzett visszabillenti 0-ra, és csak akkor foglalkozik időigényes feladattal, ha ez újra 1, vagyis
                                //jelzett a szélsebességmérő. Így a feladatok mindig a szélsebességmérő által ütemezetten zajlanak le.
long szelseb_adas_ido=millis(); //Az RF433 adón csak percenként egyszer adjuk le a szélsebesség adatot, ezt időzíti
long stat_adas_ido=millis();    //A statisztikai adatokat (átlag, min és max.) csak 15 percenként sugározzuk az RF433 adóval. Ezt időzíti
long fram_ir_ido=millis();      //A szélsebességmérő gyűjtött adatait memóriaváltozóba gyűjtöm, de félpercenként kiírom FRAM-ba is áramszünet esetére. Ezt időzíti
long szelseb_ido_max=millis();  //Ha nem fúj a szél, akkor nem küldöm a 0 sebességet az RF433 adón, csak 15 percenként. Ezt időzíti
bool stat_adas_mehet=0;         //A statisztikai adatokat is a szélsebességmérő kommunikációja végén indítom (5 részletben sugárzom), ezeknek a részeknek a kiküldését engedélyezi
byte feladat_fazis=0;           //A hosszabb ideig tartó funkciókat feladat csoportokra bontottam, és ez vezérli, hogy éppen melyik lesz végrehajtva a szélsebesség mérő kommunikáció után (8 feladat van)
bool stat_adas_kenysz=1;        //A statisztikai adatok RF433-on történő kisugárzását kényszeríti azonnal (ha még nincs itt az ideje akkor is)! Kikényszerítheti az elindulás, időpontosítás, csapadékjel.            
bool elso_szel_i2c_hiba=0;      //Ha a szélsebességmérővel nem lehet kommunikálni az I2C-n, akkor az első hibát regisztrálja, és ezt követő 20sec múlva történik csak reset. Egy hibátlan kommunikáció törli.
long hiba_ido;                  //Ez időzíti a 20 másodpercet a szélsebességmérő kommunikációs hibája esetén a reset kiadásáig
byte i2c_err=0;                 //ha 10 i2c hiba következik be, akkor resetelünk. Azt tapasztaltam, hogy a szélsebességmérő kommunikációban egy-egy zavarjel hatására
                                //sorozatban jönnek az i2c hibajelzések (nem 99 az utolsó átküldött byte), és ez csak reset hatására sznik meg, de néha magától.
                                //hagyok egy ki időt (10 kommunkációt) és ha nem javul meg magától akkor reset.
byte arany_db_fals=0;           //ugyanaz mint előző változó, csak ez a csapadék arany jelzésnél ragad be egy 10 alatti érték.

void setup() {
  // Szélsebességmérő és LED kijelző chip reset---
  pinMode(5,OUTPUT);     //Ez tud resetet adni a szélsebességmérőnek ha lefagy, és nem kap tőle adatot
  digitalWrite(5,HIGH);  //ekkor nincs resetelve a chip
  reset_ido=millis();    //csak 10 másodperc után adok reset jelet, ehhez az időzítéshez segédváltozó
  digitalWrite(5,LOW);   //ez itt a reset jel
  digitalWrite(5,HIGH);
  //---------------------------------------------
  Serial.begin(115200);
  Serial.println("Indul...");
  Wire.begin(); //I2C inicializálása
  //ds3231.ev=2022;ds3231.ho=8;ds3231.nap=11;ds3231.het_napja=5;         //dátum beállítása első bekapcsoláskor, ha még soha nem volt beáítva a DS3231 chip
  //ds3231.ora=6;ds3231.perc=45;ds3231.masodperc=0;                      //pontos idő beállítása
  //setTime24();*/
  getTime24();
  ido_iras_sorosportra();
  Serial.println();
  //---------------------------------------------
  lcd.begin(16,2);                            //LCD inicializálása
  lcd.clear();                                //kijelző törlése
  lcd.backlight();                            //háttérvilágítás bekapcsolása
  lcd.setCursor(0,0);lcd.print("indul");
  bme280_lekerdez();
  delay(100);
  bme280_lekerdez();                          //első lekérdezésre rosszul adja vissza a hőmérsékletet, nem tudom miért, ezért kell egy második
  csapadek=multimemo(V_CSAPADEK_IMP);         //mielőtt küldjük a kijelzőnek, le kell kérdezni a tárolt csapadék mennyiséget
  delay(2000);                                //kell 2 másodperc, hogy a kijelző számára küldött adat meg is érkezzen 
                                              //(a kijelző Arduino-ja indul, és nem azonnal kész az adatfogadásra I2C-n)
  adatok_kijelzonek();
  szelseb_kijelzonek();
  //---------------------------------------------
  if (!driver.init())  {}                     //RF inicializálása. Vételt és adást is megvalósit egyszerre, 
  //{Serial.println("RF inicializálás nem sikerült...");}    //úgy tapasztaltam, hogy ez a lekérdezés soha nem ad vissza hibát
  //else {Serial.println("RF inicializálás OK...");}
  //---------------------------------------------
  wdt_enable(WDTO_4S);                        //engedélyezük a watcdog-ot 4 szekundum várakozással 
                                              //4sec után a chip újraindítja magát, ha nincs közben
                                              // wdt_reset() függvényhívás, ami újraindítja a timert
  //---------------------------------------------
  pinMode(2,INPUT);                           //ezen a bemeneten jelzi a szélsebességmérő slave (nano), hogy adata van
  digitalWrite(2,HIGH);                       //felhúzó ellenállás be
  pinMode(10,INPUT);                          //nyomógomb erre a bemenetre van kötve
  digitalWrite(10,HIGH);                      //felhúzó ellenállás be
  if (digitalRead(10)==LOW) {                 //ha épp nyomva van a nyomógomb, akkor töröljük az FRAM tárolt adatait
    //statisztikai adatok
    multimemo(V_KULSO,AVG);multimemo(V_KULSO_MIN,MIN);multimemo(V_KULSO_MAX,MAX);
    multimemo(V_PARA,AVG);multimemo(V_PARA_MIN,MIN);multimemo(V_PARA_MAX,MAX);
    multimemo(V_LEGNY,AVG);multimemo(V_LEGNY_MIN,MIN);multimemo(V_LEGNY_MAX,MAX);
    multimemo(V_PINCE,AVG);multimemo(V_PAKNAA,AVG);multimemo(V_PAKNAF,AVG);
    multimemo(V_KISHAZ,AVG);multimemo(V_KISHAZ_MIN,MIN);multimemo(V_KISHAZ_MAX,MAX);
    multimemo(V_IMP_SZAM,STO);                //impulzusszám 
    multimemo(V_SZELES_IDO,STO);              //az az idő millisec-ben, amikor forgott a szélkerék
    multimemo(V_SZELSEB_MAX,MAX);             //max. széllökés
    multimemo(V_CSAPADEK_IMP,STO);            //csapadék impulzus gyűjtő
    multimemo(V_IDOPONTOSITAS,STO);           //max. széllökés
    multimemo(V_RESET,STO);                   //szélsebességmérő reset szám
    multimemo(V_RESET_K,STO);                 //szélsebességmérő reset szám
    csapadek=0;
    lcd.setCursor(0,0);lcd.print("FRAM torles!");
    delay(2000);
  }
  //kiolvassuk az FRAM-ból a tárolt statisztikai adatokat, és a szélkerék és csapadékmérő gyűjtött adatait, és betöltjük a memóriaváltozókba
  hkulso_a=multimemo(V_KULSO);hkulso_m=multimemo(V_KULSO_MIN);hkulso_x=multimemo(V_KULSO_MAX);
  para_a=multimemo(V_PARA);para_m=multimemo(V_PARA_MIN);para_x=multimemo(V_PARA_MAX);
  legny_a=multimemo(V_LEGNY);legny_m=multimemo(V_LEGNY_MIN);legny_x=multimemo(V_LEGNY_MAX);
  hpince_a=multimemo(V_PINCE);haknaa_a=multimemo(V_PAKNAA);haknaf_a=multimemo(V_PAKNAF);
  hkishaz_a=multimemo(V_KISHAZ);hkishaz_m=multimemo(V_KISHAZ_MIN);hkishaz_x=multimemo(V_KISHAZ_MAX);
  imp_szam=multimemo(V_IMP_SZAM);                 //tárolt össz. szélkerék impulzusszám
  szeles_ido=multimemo(V_SZELES_IDO);             //tárolt szeles idő betöltése
  max_szelseb=multimemo(V_SZELSEB_MAX);           //tárolt maximális szélsebesség
  idopontositas_perc=multimemo(V_IDOPONTOSITAS);  //idő pontosítás hány perce volt utoljára
  reset_db=multimemo(V_RESET);
  reset_k_db=multimemo(V_RESET_K);
  indulo_resetszam=reset_db;
  Serial.print("Reset szelmero:");Serial.print(reset_db);
  Serial.print(" Reset kijelzo:");Serial.println(reset_k_db);
  delay(1000);
  elteltido=millis();          //ezzel mérjük a szélsebeségmerőtől kapott adatok közti időt, ha nem volt nulla a szélsebesség
}

void loop() {
  TWBR = 128;                  //I2C buszsebesség 58Khz

  if (digitalRead(2)==0) {     //a 2-es bemeneten jelzi 0-val a szélsebességmérő ha adata van, és ezt rögtön ki is olvassuk
    reset_ido=millis();        //érkezett adat, beállítjuk a reset_ido változó, innen indul a 10sec-es időzítés
    adat_lekerdezes();
    egyeb_mehet=1;             //mivel maximum 500msec időnként jöhet egy-egy ilyen jelzés, fél másodpercünk van egy-egy feladat végrehajtásra, da maximum 5 sec
  }    
  if (egyeb_mehet==1) {
    LCD_kijelzo_kezeles();     //adatok kiírása LCD kijelzőre, két másodpercenként változó adattal
    switch (feladat_fazis) {
      case 0:
        napi_torles();         //30 másodpercenként frissti az időt (ds3231 változókat) és ha elmúlt éjfél, törli a napi gyűjtőket
        break;
      case 1:
        meres_inditas();       //BME280 lekérdezés és gyűjtök írása
        break;
      case 2:
        if (millis()>szelseb_adas_ido+60140) {    //egypercenként leadjuk az egyperces szélsebesség átlagot és a maximális szélsebességet
          if (elozo_szelseb1!=perc_atlag_szelseb or millis()>szelseb_ido_max+905670) {  //csak akkor sugározzuk rádión a sebességet, ha változott az utolsó adás óta, vagy letelt 15 perc
            szelseb_adas_ido=millis();    //egyperces időzítés innen indul
            szelseb_ido_max=millis();     //15 perces időzítés innen indul
            adas_433mhz(4,(unsigned int)perc_atlag_szelseb/10,(unsigned int)max_szelseb/10);
            elozo_szelseb1=perc_atlag_szelseb;
          } 
          perc_atlag_szelseb=0;
          perc_imp_szam=0;
          perc_szeles_ido=0;
        }
        break;
      case 3:
        if (millis()>stat_adas_ido+90545 or stat_adas_kenysz==1) {
          stat_adas_kenysz=0;
          stat_adas_ido=millis();
          stat_adas_mehet=1;        //ezt követően minden statisztikai adatot leadunk a következő ciklusokban
          adatkeres_kijelzotol();   //adatok lekérdezése a kijelzőben működő 4db dallas hőmérőtől
        }
        break;
      case 4: //Akna, pince és kisház hőmérséklet adatok beküldése a beltéri egységnek
        if (stat_adas_mehet==1) {
          adas_433mhz(3,(unsigned int)((akna_homerseklet_also-10000)/10+1000),(unsigned int)((akna_homerseklet_felso-10000)/10+1000));
          adas_433mhz(2,(unsigned int)((pince_homerseklet-10000)/10+1000),(unsigned int)((kishaz_homerseklet-10000)/10+1000));
        }
        break;
      case 5:  //szélsebesség és csapadék adatok beküldése a beltéri egységnek
        if (stat_adas_mehet==1) {
          adas_433mhz(5,(unsigned int)atlag_szelseb/10,(unsigned int)(szeles_ido/60000));
          int_ertek=csapadek*3;
          adas_433mhz(6,int_ertek,(unsigned int)0);
        }
        break;
      case 6: //hűmérséklet, páratartalom és légnyomás statisztikai adatok beküldése a beltéri egységnek
        if (stat_adas_mehet==1) {
          adas_433mhz(20,(unsigned int)(hkulso_a),(byte)(para_a),(byte)(legny_a-950));
          adas_433mhz(21,(unsigned int)(hkulso_m),(byte)(para_m),(byte)(legny_m-950));
          adas_433mhz(22,(unsigned int)(hkulso_x),(byte)(para_x),(byte)(legny_x-950));
        }
        break;
      case 7: //Akna, pince és kisház statisztikai adatok beküldése a beltéri egységnek
        if (stat_adas_mehet==1) {
          adas_433mhz(23,(unsigned int)(haknaa_a+10000),(unsigned int)(haknaf_a+10000));
          adas_433mhz(24,(unsigned int)(hpince_a+10000),(unsigned int)(hkishaz_a+10000));
          adas_433mhz(25,(unsigned int)(hkishaz_m+10000),(unsigned int)(hkishaz_x+10000));
          stat_adas_mehet=0;   //leadrtunk minden adatot, csak ha letelt a várakozási idő, akkor adunk újra
        }
        break;
      case 8: //szélsebességmérő beküldött adatainak biztonsági másolata az FRAM-ba áramszünet esetére
        if (millis()>fram_ir_ido+300000) {     //egy percenként írom bele az fram-ba a szélsebességmérő kontaktusjelét és a szeles időt
                                               //amit menet közben memóriaváltozóban tartok, hogy ne kelljen mindig I2C kommunikálni
                                               //Áramszünet esetén ennyi idő eseménye elveszhet, ami kicsit meghamisítja az átlag szélsebességet
                                               //de a pontossági igényem ezt megengedi. Nálunk évente néhány áramszünet van. Nagyobb problémát
                                               //az esetleges lefagyások jelentenek, míg észreveszem, hogy lefagyott a rendszer és újraindítom
          fram_ir_ido=millis();
          multimemo(V_IMP_SZAM,imp_szam);      //tároljuk az eddig gyűjtött összes impulzusszámot (induláskor be kell tölteni az imp_szam változóba)
          multimemo(V_SZELES_IDO,szeles_ido);  //tároljuk az eddig gyűjtött szeles időtartamot (induláskor be kell tölteni az szeles_ido változóba)
          multimemo(V_SZELSEB_MAX,max_szelseb);//szélsebesség maximum tárolása
        }
        break;
    }
    feladat_fazis++;
    if (feladat_fazis>8) {feladat_fazis=0;}
    egyeb_mehet=0;       //mindaddig nem megyünk be a fenti programrészbe, amíg nem jelez a szélsebesség mérő, 
                         //hogy adata van, s csak annak elhozása után csinálunk minden más időigényes tevékenységet (max. 500msec időben)
  }
  
  if (millis()>reset_ido+10000 and indulo_resetszam-reset_db<10) {  //csak 10x resetelek
    reset_ido=millis();
    digitalWrite(5,LOW);
    digitalWrite(5,HIGH);
    reset_db++;
    multimemo(V_RESET,reset_db);
    ido_iras_sorosportra();
    Serial.println("MS RST");
    delay(100);
    adatok_kijelzonek();
    szelseb_kijelzonek();
  }
   
  if (millis()>wdt_ido_tmp+1000) {   //Másodpercenként reseteljük a wotchdog számlálóját.
    // MHa a program lefagy, és nem hajtódik végre a wdt_reset, akkor
    // 4 másodperc után újraindul a program, mintha resetet nyomtunk volna
    wdt_reset(); //alaphelyzetbe állítjuk a a watcdog timer-t
    wdt_ido_tmp=millis();
  }
  //RF433 vevő ellenőrzése, hogy érkezett-e valahonnan adat

  if (driver.recv(adatb,4))  {         //akkor igaz, ha van 5 érvényes adat pufferben. Második paraméterben 5-nek kellene lenni, 
                                       //de az RHask könyvtárbank van valami hibája. A program változtatása során egyszer csak 
                                       //elkezdett csak 0-at venni. Mind az 5 byte 0. Akkor javul meg, ha a második paraméter 5-ről
                                       //4-re cserélem. Időnként azonban így is előfordult az eset, ekkor 3-ra cseréltem. Aztán megint
                                       //és akkor visszcsaréltem 4-re. Szóval kísérletezni kellett. Figyelni, hogy mit vesz, ha csupa 0 jön
                                       //akkor változtatni a paraméteren, újra fordítani és tölteni a programot. Mindenféle értéket próbáltam
                                       //míg végül találtam működőt. Sajna ez bármikor előfordulhat, ezért minden program változtatáskor
                                       //figyelni kell rá.
    Serial.print("vetel:");
    //Serial.print(adatb[0]);Serial.print(",");Serial.print(adatb[1]);Serial.print(",");Serial.print(adatb[2]);Serial.print(",");
    //Serial.print(adatb[3]);Serial.print(",");/Serial.print(adatb[4]);Serial.print(",");
    if (adatb[0]==0 and adatb[1]!=0 and adatb[2]!=0) {             //Tapasztaltam olyan esetet, amikor minden fogadott byte 0-volt, ezért figyelem, hogy
                                                                   //a dátum átvitelére szolgáló két byte nem lehet 0. Ez ugyanis biztosan nem fordulhatna 
                                                                   //elő csak 2000-ben, az meg már elmúlt. A két byte valamelyike biztosan nem 0 normál esetben.
      ora_pontositas(adatb[1]+adatb[2]*256,adatb[3]+adatb[4]*256);
      stat_adas_kenysz=1;                                          //ha a központi egységet bekapcsolom, akkor ez ezzel kikényszerít egy statisztikai adat sugárzást, 
                                                                   //mert a központ sugároz több órapontosítást bekapcsoláskor. Ez áramszünetnél hasznos
    }
    else {Serial.println();}
  }
  nyomogomb_kezeles();                                             //nyomógomb ellenőrzése. A nyomógomb bekapcsolja a háttérvilágítást rövid 
                                                                   //lenyomással, hosszúval be el lehet kezdeni az óra beállítást (ha nincs órapontostás adás) 
}

void adatok_kijelzonek() {
  byte b[]={0,0};
  //Serial.println("HLP To Led");
  Wire.beginTransmission(9);                        //elindít a master egy átvitelt a 9 című slave felé
  if (Wire.endTransmission()==0) {                  //vége az átvitelnek. Ezzel lehet ellenőrizni, hogy van-e 9-es slave a vonalon
                                                    //Ekkor a slave vesz egy byte-ot, aminek az értéke 255. Valószínűleg egy byte-ot
                                                    //mindenképpen át kell vinnie. Tehát a slave programjában figyelni kell arra, ha csak 
                                                    //egy 255 érkezik első byte-nak, akkor az csak ellenőrzés volt ebből következően 
                                                    //a slave.ben kezelni kell azt az esetet, ha az első byte 255.
    Wire.beginTransmission(9);                      //Újra elindítjuk az átvitelt, mert már tudjuk, hogy van ott működő slave
    Wire.write(0);                                  //adat index megadása, ez jelzi, hogy milyen adatot küldünk
    memcpy(&b,&kulso_homerseklet,2);                //egy int szétbontása2 byte-ra a b tömbbe
    Wire.write(b[0]);                               //szétbontott int első byte átvitele
    Wire.write(b[1]);                               //szétbontott int második byte átvitele
    memcpy(&b,&paratartalom,2);        
    Wire.write(b[0]);  
    Wire.write(b[1]);
    memcpy(&b,&legnyomas,2);      
    Wire.write(b[0]);  
    Wire.write(b[1]);
    unsigned int int_ertek=csapadek*3;              //csapadék adat átviteléhez 3-al szorozzuk, a kijelzőben a tizedespont átrakásával csinálunk belőle 0.3-at
    memcpy(&b,&int_ertek,2);     
    Wire.write(b[0]);  
    Wire.write(b[1]);
    Wire.endTransmission();                        // vége az átvitelnek
    elso_szel_i2c_hiba=0;                          //töröljük az esetleges hiba miatti reset lehetőségét (20 másodperc elteltével adjuk a reset jelet, 
                                                   //ha nem volt átvitel, addig ez a változó 1)
  }
  else {                                           //ha nem válaszol a LED kijelző, akkor indítunk egy időzítést, és 20 másodperc elteltével reset-eljuk 
    if (elso_szel_i2c_hiba==0) {                   //ez az első olyan eset, amikor nem válaszolt a 9-es slave (LED kijelző)
      hiba_ido=millis();
      elso_szel_i2c_hiba=1;
    }
    if (elso_szel_i2c_hiba==1 and millis()>hiba_ido+20000) {   //csak 20 másodperc után ad reset jelet, ha nem válaszol a LED kijelző
      digitalWrite(5,LOW);                                     //ez a kimenet a LED kijelző Arduino reset bemenetére van kötve, ez itt a reset jel
      digitalWrite(5,HIGH);
      reset_k_db++;
      multimemo(V_RESET_K,reset_k_db);             //az fram-ban gyűjtöm a resetek számát, hogy lássam hányszor fordul elő. Idővel ezt majd kiveszem a programból.
                                                   //azt már tudom, hogy a neoncsővilágításom bekapcsolásától néha lefagy a LED kijelző és nem válaszol
                                                   //ekkor kell resetelni. Teszek majd egy zener diódát a vonalra, így várhatóan csak adatsérülés lesz. Figyelem
                                                   //az eseményeket, és ha megszűnnek a resetek, akkor már nem kell ez az adatgyűjtés.
      delay(100);
      adatok_kijelzonek();                         //el kell küldeni az adatokat újra a LED kijelzőnek, hogy ne kelljen 2 percet várnia
      szelseb_kijelzonek();
      ido_iras_sorosportra();
      Serial.println("HLP LED RST");               //a sorosporton gyűjtöm az eseményeket, ezért ott is látszik, ha volt reset.
    }
  }
}


void szelseb_kijelzonek() {
  byte b[]={0,0};
  //Serial.print("Wnd To Led");
  //Serial.println(pill_szelseb);
  Wire.beginTransmission(9);           //elindít a master egy átvitelt a 9 című slave felé
  if (Wire.endTransmission()==0) {     //vége az átvitelnek, ellenőrizzük, hogy van-e slave a vonalon. Megjegyzéseket lásd az adatok_kijelzonek() függvénynél
    Wire.beginTransmission(9);         //Van slave a vonalon, ezért újra átvitelt kezdeményezünk
    Wire.write(1);                     //adat index megadása, ez jelzi, hogy milyen adatot küldünk
    memcpy(&b,&pill_szelseb,2);        //egy int szétbontása 2 byte-ra a b tömbbe
    Wire.write(b[0]);                  //szétbontott int első byte átvitele
    Wire.write(b[1]);                  //szétbontott int második byte átvitele
    Wire.endTransmission();            // vége az átvitelnek
    elso_szel_i2c_hiba=0;              //töröljük az esetleges hiba miatti reset lehetőségét (20 másodperc elteltével adjuk a reset jelet, addig ez a változó 1)
  }
  else {                               //ha nem válaszol a LED kijelző, akkor indítunk egy időzítést, és 20 másodperc elteltével reset-eljük 
    if (elso_szel_i2c_hiba==0) {
      hiba_ido=millis();
      elso_szel_i2c_hiba=1;
    }
    if (elso_szel_i2c_hiba==1 and millis()>hiba_ido+20000) {   //csak 20 másodperc után ad reset jelet, ha nem válaszol a szélsebességmérő
      elso_szel_i2c_hiba=0;
      digitalWrite(5,LOW);             //ez a kimenet a LED kijelző Arduino reset bemenetére van kötve, ez itt a reset jel
      digitalWrite(5,HIGH);
      reset_k_db++;
      multimemo(V_RESET_K,reset_k_db); //az fram-ban gyűjtöm a resetek számát. Megjegyzéseket lásd az adatok_kijelzonek() függvénynél
       delay(100);
      adatok_kijelzonek();             //el kell küldeni az adatokat újra a LED kijelzőnek, hogy ne kelljen 2 percet várnia
      szelseb_kijelzonek();
      ido_iras_sorosportra();
      Serial.println("WIND LED RST");
    }
  }
}


/**********************************************************************************************************
 * Ezekkel a függvényekkel küldöm el az adatokat. A három függvény együtt lekezeli azt, hogy              *
 * különböző típusú adatokat küldök (int és két byte vagy két int) és mindig azt hívja meg a program ami  *
 * Át tudja venni a megfelelő típusú változókat. Ha négy byte adatot adnék át, akkor a harmadik           *
 * függvény kerülne meghívásra közvetlenül                                                                *
 **********************************************************************************************************/
void adas_433mhz(byte kod,unsigned int i1,byte b1, byte b2) {
  adas_433mhz(kod,i1&255,(i1>>8)&255,b1,b2); 
}

void adas_433mhz(byte kod,unsigned int i1,unsigned int i2) {
  adas_433mhz(kod,i1&255,(i1>>8)&255,i2&255,(i2>>8)&255); 
}

void adas_433mhz(byte b0,byte b1, byte b2, byte b3, byte b4) {
  byte adat[]={b0,b1,b2,b3,b4};
  driver.send((uint8_t *)&adat, 5);      //első paraméter az átvinni kíván adatbájtok kezdőcíme a memóriában, második az átvinni kívánt byte-ok száma
}


/******************************************************************************************************
 * Ezt a függvényt hívom meg akkor, amikor a szélsebességmérő jelzi a az 5-os kivezetésen, hogy       *
 * adatot szeretne küldeni. A szélsebességmérő elküldi az utolsó küldés óta számított átlagsebességet *
 * 10-szeres értékben, hogy ne kelljen hozzá float, valamint elküldi a szélkerék körül fordulásainak  *
 * számát, amit az utolsó egy perc átlagsebességének kiszámításához használok.                        *
 * Megérkezik ezekkel az adatokkal együtt a csapadék kontaktus jelzés is. Mivel azt tapasztaltam      *
 * hogy a 0 és 1 jelzések (1 ha volt csapadék kontaktus) gyakran fals jelzéseket eredményeznek        *
 * inkább olyan byte-okat küldök, amikben a bitek mindegyike különbözik. Ezzel valóban csökkent       *
 * A fals csapadékjelzések száma. Tehát átvitel során gyakran sérülnek az adatok.                     *
 * Utolsó byte-nak 99-et ad le a szélsebességmérő. Azt tapasztaltam ugyanis, hogy ha sérülnek az      *
 * adatok, akkor minden sérül, tehát az utolsó byte is. Ezért ha az utolsó byte nem 99, akkor         *
 * mindent eldobog amit vettem.                                                                       *
 ******************************************************************************************************/
void adat_lekerdezes() {
  byte hiba=0;                  //ebbe fogom beolvasni az utolsó byte-ot. Ha nem 99, akkor sérültek az adatok
  byte ximp_szam=0;             //az utolsó adás óta történt korul fordulások száma
  unsigned int seb,ar;          //sebesség és arány ebbe lesz beolvasva
  byte csapadek_kod;            //ebbe olvasom be a csapadékjelző jelző byte-ját, ami 170-ha volt csapadékmérő átbillenés (+0.3mm eső)
  byte b[]={0,0};               //a küldött byte-okat ebbe olvasom be, és ebből a tömbből másolom össze, hogy int legyen belőlük
  unsigned int int_tmp;         //ebbe az int változóba másolom össze a kapott byte-okat
  //float arany;                //amikor a plusz int értéket a szélkerék kontaktusarányra használtam, még kelleni fog ha annak hibájával foglalkozok
  unsigned int arany;
  Wire.requestFrom(8,7);        // a master kér 7 bájtot a slave-től
  b[0] = Wire.read();            
  b[1] = Wire.read(); 
  memcpy(&int_tmp,&b,2);    
  pill_szelseb=int_tmp;         //megkaptuk a szélsebesség 100-szorosát
  b[0] = Wire.read();            
  b[1] = Wire.read(); 
  memcpy(&int_tmp,&b,2);
  //arany=(float)int_tmp/100;   //amikor a plusz int értéket a szélkerék kontaktusarányra használtam, még kelleni fog ha annak hibájával foglalkozok
  arany=int_tmp;                //megkaptuk a csapadékmérő kontaktus hosszát (egy darabszám, a szélsebességmérő programjában a 
                                //loop() hányszor érzékelt magas alacsony szintet a kontaktuson (3600-4200-ig tapasztaltam értékeket. Illetve néha 
                                //nagyon rövideket (1-10 között) ezek nyilván hibás adás eredményei, tehát nem szabad figyelembe venni.
  ximp_szam=Wire.read();        //az utolsó adás óta történt körül fordulások száma, az egyperces átlag számításánál és a napi átlag számításánál fogom felhasználni
  imp_szam=imp_szam+ximp_szam;  //itt gyűjtöm az adott napon történt körül fordulások számát, és ebből valamint a szeles időszak idejéből képzem az átlagsebességet
  perc_imp_szam=perc_imp_szam+ximp_szam;    //itt gyűjtöm az utolsó egy percben történt kórul fordulások számát, és ebből képzem az átlagsebességet. Percenként nullázom
                                            //Illetve percenként adom le az RF433 adóval az utolsó egy perc átlagsebességét, és akkor nullázom.
  csapadek_kod = Wire.read();   //az attól függően, hogy volt-e csapadékmérő átbillenés, 170 vagy ...
  hiba = Wire.read();           //ez az utolsó byte, ha nem 99 akkor mindent eldobok.
  if (hiba==99) {
    if (max_szelseb<pill_szelseb) {
      max_szelseb=pill_szelseb;
    }
    if (pill_szelseb>0) {
      szeles_ido=szeles_ido+(millis()-elteltido);            //Amikor a szélsebesség nagyobb mint nulla, merjük az eltelt időt az átlagsebességhez millisec-ben
      perc_szeles_ido=perc_szeles_ido+(millis()-elteltido);  //Amikor a szélsebesség nagyobb mint nulla, merjük az utolsó egy percben eltelt szele időt az utolsó egy perc átlagsebességhez millisec-ben
    }
    elteltido=millis();                                      //innen indítjuk a következő kommunikációig eltelt idő mérését, csak akkor használjuk fel, ha sebesség nem nulla
    
    //a napi átéagsebesség kiszámítása
    if (szeles_ido>0) {                                      //mindaddig amíg nem fújt a szél (szeles idő=0), nem lehet átlagsebességet számolni, mert  nem lehet 0-val osztani
      atlag_szelseb=(float)imp_szam*250/(szeles_ido/1000);}  //az átlagsebesség a körül fordulások száma osztva azzal az idővel, amíg fújt a szél
                                                             //a szeles_ido milisecundum-ban összegződik. A sebesség 100-szorosát számoljuk, 
                                                             //hogy ne legyen kerekítésből adatcsonkítás. Mivel egy másodperc alatti körül fordulás 2.5km/h-nak
                                                             //felel meg, az összes körül fordulást a másodpercek számával kell osztani és szorozni 2.5-al, hogy 
                                                             //átlagsebességet kapjunk. Pl. 10 másodperc alatt 20 körül fordulás esetén 20x2.5/10=5km/h
                                                             //(én a 100-szorosával számoltam, tehát nálam az átlagsebesség 500, amit majd kijelzésnél osztok 100-al)
    else {atlag_szelseb=0;}                                  //amíg aznap nem fújt a szél, addig az átlagsebesség 0
    
    //az utolsó egy perc átlagsebességének kiszámítása
    if (perc_szeles_ido>0) {                                 //a perc_szeles_ido változót az nullázza, amikor az RF433 adóval leadjuk az utolsó egy perc átlagsebességét. Ez egy percenként történik meg.
      perc_atlag_szelseb=(float)perc_imp_szam*250/(perc_szeles_ido/1000);}   //utolsó perc átlagsebességének kiszámítása (magyarázatot lásd feljebb pár sorral)
    else {perc_atlag_szelseb=0;}                             //amig nem fúj a szél az egy percen belül, addig az átlagsebesség 0
    
    if (arany>0) {                                           //Az arany változóban kapom meg a csapadékmérő kontaktusjelének hosszát (loop-ban hányszor érzékeltük alacsonynak)
      ido_iras_sorosportra();                                //időpont a sorosportra
      Serial.print("Csap db:");Serial.println(arany);        //adat kiírása sorosportra, mivel fals csapadék jelzések vannak, szeretném tudni, mikor milyen adat érkezik
    }
    if (arany<1000 and arany>0) {                            //ez egy fals csapadékjelzés lesz, mert ennél sokkal hosszabb egy valós jelzés
      arany_db_fals++;
      if (arany_db_fals>10) {                                //h több mint 10 hiba érkezik, KKOR RESETET KÜLDÜNK
        digitalWrite(5,LOW);                                 //ez a kimenet a LED kijelző Arduino reset bemenetére van kötve, ez itt a reset jel
        digitalWrite(5,HIGH);
        reset_k_db++;
        multimemo(V_RESET_K,reset_k_db);                     //az fram-ban gyűjtöm a resetek számát. Megjegyzéseket lásd az adatok_kijelzonek() függvénynél
        delay(100);
        adatok_kijelzonek();                                 //el kell küldeni az adatokat újra a LED kijelzőnek, hogy ne kelljen 2 percet várnia
        szelseb_kijelzonek();
        ido_iras_sorosportra();
        Serial.println("CSAPADEK FALS RST");
        arany_db_fals=0;
      }
    }

    if (pill_szelseb!=elozo_szelseb) {szelseb_kijelzonek();} //ha változott a szélsebesség, akkor kiküldjük a kijelzőnek. 
                                                             //A felesleges I2C kommunikáció elkerülése miatt (pl. nem fúj a szél, minek küldeni a 0-át)
    elozo_szelseb=pill_szelseb;                              //a következő szélsebesség adatnál tudjuk, hogy változott-e a szélsebesség

    //csapadékmérő jelzésének értékelése
    if (csapadek_kod==170) {                     //cspadékjelző átbillenés történt, ezt jelzi a 170
      if (arany>2000) {                          //csak ha az arany nagyobb mint 2000, akkor valós jelzés tapasztalatom szerint
        arany_db_fals=0;                         //Ez egy jó csapadékjel, ha volt fals, akkor a fals jelzés számlálót 
                                                 //töröljük, hogy a 10. után ne legyen reset, mert valószínleg rendbejött a működés
        csapadek=csapadek+1;                     //növeljük a csapadékmérő átbillenéseinek számát
        multimemo(V_CSAPADEK_IMP,csapadek);      //tároljuk a csapadék számlálót az FRAM-ban
        stat_adas_kenysz=1;                      //negyedóránként küldünk statisztikai adatokat, köztük a csapadék mennyiséget, de most kikényszerítünk egy azonnali küldést
        ido_iras_sorosportra();
        Serial.print("Csap:");Serial.println(csapadek*0.3);
      }
    }
    i2c_err=0;                      //ez egy jó I2C kommunikáció volt, mert 99-el lett lezárva. Ha volt is hibás kommunikáció
                                    //valószínleg rendbejött, ezért nem kell 10 után resetelni
  }
  else {                            //az utolsó byte nem 99 volt, így itt hibás adás történt
    ido_iras_sorosportra();               
    Serial.println("I2C ERR");
    i2c_err++;
    if (i2c_err>10) {
      digitalWrite(5,LOW);                 //ez a kimenet a LED kijelző Arduino reset bemenetére van kötve, ez itt a reset jel
      digitalWrite(5,HIGH);
      reset_k_db++;
      multimemo(V_RESET_K,reset_k_db);     //az fram-ban gyűjtöm a resetek számát. Megjegyzéseket lásd az adatok_kijelzonek() függvénynél
      delay(100);
      adatok_kijelzonek();                 //el kell küldeni az adatokat újra a LED kijelzőnek, hogy ne kelljen 2 percet várnia
      szelseb_kijelzonek();
      ido_iras_sorosportra();
      Serial.println("WIND LED RST");
      i2c_err=0;
    }
  }
}

void LCD_kijelzo_kezeles() {
  //három másodpercenként váltogatjuk a kijelzett adatokat. Nagyon tömör kijelzés, leginkább szervíz célokat szolgál
  if (millis()>lcd_kijelzo_ido+3000 and mert_ertek_kijelzes==HIGH) {
    lcd.setCursor(0,0);lcd.print("                ");
    lcd.setCursor(0,1);lcd.print("                ");
    lcd.setCursor(0,0);
    switch (lcd_eszkoz_num) {
      case 0:                              //külső hőmérséklet, légnyomás és páratartalom, valamint a fénymérés eredményének kijelzése
        lcd.print((float)kulso_homerseklet/100,1);lcd.print("Cfok");lcd.print(" ");lcd.print((int)legnyomas);lcd.print("mbar");
        lcd.setCursor(0,1);lcd.print((byte)paratartalom);lcd.print("%");lcd.print(" Feny:");lcd.print(analogRead(A6));
        lcd_eszkoz_num++;
        break;
      case 1:                             //akna alsó, felső, pince és kisház hőmérséklet kijelzése
        lcd.print("A.A:");lcd.print((float)(akna_homerseklet_also-10000)/100,1);lcd.print(" F:");lcd.print((float)(akna_homerseklet_felso-10000)/100,1);
        lcd.setCursor(0,1);lcd.print("P:");lcd.print((float)(pince_homerseklet-10000)/100,1);lcd.print(" H:");lcd.print((float)(kishaz_homerseklet-10000)/100,1);
        lcd_eszkoz_num++;
        break;
      case 2:                            //Csapadék, max. szélsebesség, Átlag szélsebesség (24 órás), és szeles időtartam kijelzése
        lcd.print((float)csapadek*0.3,1);lcd.print("mm Mx:");lcd.print((float)max_szelseb/100,1);lcd.print("km");
        lcd.setCursor(0,1);lcd.print("A:");lcd.print((float)atlag_szelseb/100,1);lcd.print("km I:");lcd.print((float)szeles_ido/60000,1);lcd.print("h");
        lcd_eszkoz_num++;
        break;
      case 3:                            //dátum és pontos idő kijelzése, kiadott resetek száma, valamint az utolsó időpontosítás óta eltelt percek száma
        lcd.setCursor(0,0);lcd.print(ds3231.ev-2000);lcd.print("/");
        if (ds3231.ho<10) {lcd.print("0");} 
        lcd.print(ds3231.ho);lcd.print("/");
        if (ds3231.nap<10) {lcd.print("0");} 
        lcd.print(ds3231.nap);lcd.print(" S");lcd.print(reset_db);lcd.print("K");lcd.print(reset_k_db);
        lcd.setCursor(0,1);if (ds3231.ora<10) {lcd.print("0");} 
        lcd.print(ds3231.ora);lcd.print(":");
        if (ds3231.perc<10) {lcd.print("0");} 
        lcd.print(ds3231.perc);lcd.print(" ip:");lcd.print(idopontositas_perc/60);
        lcd_eszkoz_num++;
        break;
    }
    if (lcd_eszkoz_num>3) { lcd_eszkoz_num=0;}
    lcd_kijelzo_ido=millis();
  }
}

void meres_inditas() {
  if (millis()>meres_ido+120345) { //kb. 2 percenként indítjuk a mérést a hőmérséklet és légnyomást
    //Serial.println("Meres, HPL adas.");
    meres_ido=millis();
    bme280_lekerdez();
    if (bme280_on) {
      //minden tárolt értéknek a 100 szorosát használjuk, mert így majd a végén 100-al osztva újra tizedes értékben
      //kapjuk meg az eredményt (long változót tárolunk, ami nem tud tizedes értéket tárolni, viszont a hőmérséklet
      //tizedes jegyet is tartalmaz. AZ osztást tároláskor fogjuk elvégezni, amikor a központi egység megkapta az adatot
      //páratartalom és a légnyomás esetén is szorzunk 100-al, ott nem kellene egyébként, mert egész értékek, de igy egységes.
      if (kulso_homerseklet>-5000 and kulso_homerseklet<5000) {  //időnként valami fals érték jöhetett be méréskor, mert a napi átalgokban 
                                                                 //vad értékeket tárolt az SD kártya. Ezért a vizsgálat, ha baromság jön be azt 
                                                                 //inkább kihagyom
         hkulso_a=multimemo(V_KULSO,kulso_homerseklet+10000);
         hkulso_m=multimemo(V_KULSO_MIN,kulso_homerseklet+10000);
         hkulso_x=multimemo(V_KULSO_MAX,kulso_homerseklet+10000);
      }
    } 
    para_a=multimemo(V_PARA,paratartalom);
    para_m=multimemo(V_PARA_MIN,paratartalom);
    para_x=multimemo(V_PARA_MAX,paratartalom);
    legny_a=multimemo(V_LEGNY,legnyomas);
    legny_m=multimemo(V_LEGNY_MIN,legnyomas);
    legny_x=multimemo(V_LEGNY_MAX,legnyomas);
    adatok_kijelzonek();
    adas_433mhz(1,(unsigned int)(kulso_homerseklet/10+1000),(byte)paratartalom,(byte)(legnyomas-950));
  }
}

void adatkeres_kijelzotol() {
  byte b[]={0,0};
  //Serial.println("LED READ");
  Wire.beginTransmission(9);                           // elindít a master egy átvitelt a 9 című slave felé
  if (Wire.endTransmission()==0) {                   //vége az átvitelnek. Ezzel lehet ellenőrizni, hogy van-e 9-es slave a vonalon
    Wire.requestFrom(9,8);                              // a master kér 8 bájtot a slave-től
    b[0]=Wire.read();
    b[1]=Wire.read();
    memcpy(&pince_homerseklet,&b,2);            //ez int értéket másol össze a két érkezett byte-ból
    b[0]=Wire.read();
    b[1]=Wire.read();
    memcpy(&akna_homerseklet_felso,&b,2);            
    b[0]=Wire.read();
    b[1]=Wire.read();
    memcpy(&akna_homerseklet_also,&b,2);          
    b[0]=Wire.read();
    b[1]=Wire.read();
    memcpy(&kishaz_homerseklet,&b,2);          
    //a kapott adaok beírása FRAM-ba és a statisztikai adatok memóriaváltozóinak frissítése  
    haknaa_a=multimemo(V_PAKNAA,(akna_homerseklet_also-10000));
    haknaf_a=multimemo(V_PAKNAF,(akna_homerseklet_felso-10000));
    hpince_a=multimemo(V_PINCE,(pince_homerseklet-10000));
    hkishaz_a=multimemo(V_KISHAZ,kishaz_homerseklet-10000);
    hkishaz_m=multimemo(V_KISHAZ_MIN,kishaz_homerseklet-10000);
    hkishaz_x=multimemo(V_KISHAZ_MAX,kishaz_homerseklet-10000);
    elso_szel_i2c_hiba=0;                                       //volt kommunikáció és 0-val jelezzük azt a tényt ebben a változóban
                                                                //gha nem működik a kommunikáció, akkor csak 20 másodperc elteltével fogunk resetelni.
                                                                //A 20 másodperc időzítés kezdetét ennek a változónak az 1-re állítása jelzi
  }
  else {                                                        //ha nem válaszol a szélsebességmérő, akkor indítunk egy időzítést, 
                                                                //és 20 másodperc elteltével reset-eljuk 
    if (elso_szel_i2c_hiba==0) {                                //az első kommunikációs hibánál a változót 1-re állítjuk
                                                                //ha 20 másodpercen beül történik sikeres kommunikáció, akkor elmarad a reset mert a változó ujra 0 lesz.
      hiba_ido=millis();                                        //innen indul a 20 másodperces időzítés
      elso_szel_i2c_hiba=1;                                     //változó egrye állítása jelzi, hogy volt egy sikeretelen kommunikáció
    }
    if (elso_szel_i2c_hiba==1 and millis()>hiba_ido+20000) {    //csak 20 másodperc után ad reset jelet, ha nem válaszol a szélsebességmérő
      elso_szel_i2c_hiba=0;
      digitalWrite(5,LOW);                                      //ez a kimenet a LED kijelző Arduino reset bemenetére van kötve, ez itt a reset jel
      digitalWrite(5,HIGH);
      reset_k_db++;
      multimemo(V_RESET_K,reset_k_db);                          //tároljuk a resetek számát az FRAM-ban
      delay(100);
      adatok_kijelzonek();                                      //reset után küldjük a  kijelzőnek az összes adatot, hogy minél előbb helyes értékeket jelezzen ki
      szelseb_kijelzonek();
      ido_iras_sorosportra();
      Serial.println("LED READ RST");                           //sorosportra is kiírjuk a reset eseményt
    }
  }
}


long multimemo(byte cim) {
/***************************************************************************************************************
 * Ez a multimemo függvény akkor hívódik meg, ha csak egyetlen paramétert adtunk meg a függvény                *
 * meghívásakor (csak olvasni akarunk a tároló cellából)                                                       *
 * A három paraméteres multimemo függvényt hívja meg, de default értékkel tölti fel a maradék két paramétert   *
 * A megadott tároló cella címről adtot fogunk olvasni.                                                        *
 ***************************************************************************************************************/
  long adat=0;
  bool iras=0;
  return(multimemo(cim,adat,iras));
}


long multimemo(byte cim, long adat) {
/***************************************************************************************************************
 * Ez a multimemo függvény akkor hívódik meg, ha két paramétert adunk meg, a cella címét és a beírandó         *
 * adatot, vagyis írni akarunk a cellába. A tároló cella formázását és alaphelyzetbe állítását végző           *
 * két paraméteres multimemo függvénytől az különbözteti meg, hogy ennek második paramétere long típusú.       *
 * A három paraméteres multimemo függvényt hívja meg, de a harmadik paramétert (iras vagy olvasás) default     *
 * értékkel tölti fel (iras=1, azaz írunk). A megadott tároló cella címébe fogjuk az adatot beírni.            *
 ***************************************************************************************************************/
  bool iras=1;
  return(multimemo(cim,adat,iras));
}

long multimemo(byte cim, long adat, bool iras) {
/*****************************************************************************************************************************************************************
 * Ez a multimemo függvény akkor hívódik meg, ha három paraméterrel hívjuk. Ez végzi a tárolandó adat feldolgozását, tárolását, és visszaadja az eredményt       *  
 * visszatérő értékként. A tároló cella típusát az első tárolt byte adja meg, ezt az a multimemo függvény írja, melynek két paramétere van, és a második         *
 * cella_tipus (enum-al definiált felsorolás) típusú adatot vár.                                                                                                 *
 * A függvény bemenő paraméterei:                                                                                                                                *
 *    cim: 0-73 memória cella, egy cella 7 byte,                                                                                                                 *
 *    adat: long típusú 4 byte-os adat, amit beírunk a kijelölt memória cellába                                                                                  *
 *    iras: ez mondja meg, hogy írunk vagy olvasunk a tároló cellából. 0-olvasunk, 1-írunk                                                                       *
 * A tároló cella szerkezete:                                                                                                                                    *
 *    0. byte típus: 0=minimum cella, 1=maximum cella, 2=summa cella, 3=átlag cella, 4=tároló cella                                                              *
 *    1-4. byte adat long adattípus, ez a tényleges tárolt long adat 4 byte-on                                                                                   *
 *    5-6. byte átlag esetén az összegzett adatok száma int adattípusú Csak az átlagoló tároló cella használja, ebben számolja az írások számát.                 *
 *****************************************************************************************************************************************************************/
  int xcim=cim*7;
  byte page=0;
  byte adat0;
  byte adat1;
  byte adat2;
  byte adat3;
  if (xcim<256) {page=0;} else {page=1;xcim=xcim-256;}
  Wire.beginTransmission(_i2cAddress | (page&1));
  Wire.write(xcim);
  Wire.endTransmission(); 
  Wire.requestFrom(_i2cAddress | (page&1),7);
  byte xtipus=Wire.read();                //elkérjük az első byte-ot, ami a tárolt dat típusát adja meg
  long xadat=long(Wire.read()) | long(Wire.read())<<8 | long(Wire.read())<<16 | long(Wire.read())<<24 ; //elkérjük és betöltjük az adat aktuális értékét
  int xdb=int(Wire.read()) | int(Wire.read())<<8;  //elkérjük és betöltjük az adatok számát
  
  if (iras==0 and xtipus!=3) {return(xadat);}  //ha olvasás és nem átlagot kértünk volt a művelet, akkor visszadjuk a kiolvasott long értéket
  if (iras==0 and xtipus==3) {if (xdb>0) {return((long)(xadat/xdb));} else {return(0);}}  //ha olvasás és átlagot kértünk, akkor osztani is kell, de csak ha xdb nem 0
  
  if (xtipus==0 and iras==1){             //ha írás és minimum cella
      if (xadat>adat) {                   //csak akkor írjuk be az új adatot, ha az eddig tárolt adatnál kisebb
        multimemo_adatiras(page,xcim,adat); //visszaírjuk az új adatot a tároló cellába
        return(adat);
      }
      else {return(xadat);}               //visszadjuk a cellában tárolt minimum értéket
    }  
      
  if (xtipus==1 and iras==1){             //ha írás és maximum cella
      if (xadat<adat) {                   //csak akkor írjuk be az új adatot, ha az eddig tárolt adatnál nagyobb
         multimemo_adatiras(page,xcim,adat); //visszaírjuk az új adatot a tároló cellába
        return(adat);
      }
      else {return(xadat);}               //visszadjuk a cellában tárolt maximum értéket
    }  
      
  if (xtipus==2 and iras==1){             //ha írás és summa cella
    adat=xadat+adat;                      //összeadjuk az új adatot ez eddig beírt adatok (tárolt) összegével
    multimemo_adatiras(page,xcim,adat);   //visszaírjuk az új adatot a tároló cellába
    return(adat);                         //visszadjuk az új cella értéket
  } 

  if (xtipus==3 and iras==1){             //ha írás és átlag cella
    adat=xadat+adat;                      //összeadjuk az új adatot ez eddig beírt adatok (tárolt) összegével
    xdb=xdb+1;                            //növeljük a beírások számát 1-el
    Wire.beginTransmission(_i2cAddress | (page&1));Wire.write(xcim+1); //a cella első byte-ját már nem kell írni, azt beállított a cella reset, azért cim+1-től írunk
    Wire.write(adat & 0xFF);Wire.write((adat>>8) & 0xFF);Wire.write((adat>>16) & 0xFF);Wire.write((adat>>24) & 0xFF); //beírjuk az új összegzett adatot
    Wire.write(xdb & 0xFF);Wire.write((xdb>>8) & 0xFF);  //beírjuk az írások számát
    Wire.endTransmission(); 
    return((long)(adat/xdb));             //visszadjuk az új átlag értéket
  } 

  if (xtipus==4 and iras==1){             //ha írás és sima tároló cella
    multimemo_adatiras(page,xcim,adat);
    return(adat);
  } 
}

void multimemo_adatiras(byte page, byte xcim, long adat) {
// az átlag cella kivételével mindet ugyanúgy kell beírni, ezért a konkrét írás ebben a közösen használt függvényben lett megvalósítva
  Wire.beginTransmission(_i2cAddress | (page&1));Wire.write(xcim+1); //a cella első byte-ját már nem kell írni, azt beállított a cella reset, azért cim+1-től írunk
  Wire.write(adat & 0xFF);Wire.write((adat>>8) & 0xFF);Wire.write((adat>>16) & 0xFF);Wire.write((adat>>24) & 0xFF);
  Wire.endTransmission(); 
}

long multimemo(byte cim, cella_tipus tipus) {
/*******************************************************************************************************************************************************************  
 * Ez a multimemó függvény akkor hívódik meg, ha két paraméterrel hívjuk, és a második cella_tipus típusú változó, amit enum-al hoztunk létre a program elején.    *
 * A függvény elvégzi egy cella típusának beállítását (a cella első byte-ja), és alaphelyzetbe állítja a tárolt adatokat.                                          *
 * Bemenő paraméterek:                                                                                                                                             *
 *    cim: 0-73 db memória cella, egy cella 7 byte,                                                                                                                *
 *    típus: felsorolás típusú (enum) paraméter, ami meghatározza a cella tárolási módját. Lehetséges értékei 0-4 között.                                          *
 * Típus által képzett cella típusok, és azok tárolási módja, az elvégzett műveletek leírása:                                                                      *
 *  MIN: Az íráskor megkapott adatot csak akkor tárolja, ha az kissebb mint az éppen tárolt adat. Mielőtt elkezdjük a minimumot gyüjteni, be kell írni             *
 *       a lehető legnagyobb long értéket, különben nem biztos, hogy megjegyzi a legelső értéket. A számláló cellarészt nem használja.                             *
 *  MAX: Az íráskor megkapott adatot csak akkor tárolja, ha az nagyobb mint az éppen tárolt adat. Mielőtt elkezdjük a maximumott gyüjteni, be kell írni            *
 *       a lehető legkissebb long értéket, különben nem biztos, hogy megjegyzi a legelső értéket.                                                                  *
 *  SUM: Képzi a beírt adatok összegét. Alapértelmezetten a tartalma 0.                                                                                            *
 *  AVG: képzi a beírt adatok átlagát. Minden beírt értéket szummáz az adat mezőben, számolja a beírások számát, és kiolvasáskor osztja az adatot a beírás számmal  *
 *       Alpértelmezetten az adat és a számláló is 0.                                                                                                              *
 *  STO: csak úgy simán tárolja az adatot, nem csinál vele semmit, ha volt bent előtte adat, azt felülírja. Alapértelmezett tartalma 0.                            *
 *******************************************************************************************************************************************************************/
  int xcim=cim*7;
  byte page;
  if (xcim<256) {page=0;} else {page=1;xcim=xcim-256;}
  Wire.beginTransmission(_i2cAddress | (page&1));
  Wire.write(xcim);
  if (tipus==MIN){
      // 2147483647 kezdő értéket írunk a tároló cellába, mert nincs nagyobb szám long esetén, így csak ennél kisebbek jöhetnek.
      Wire.write(0);Wire.write((long)2147483647 & 0xFF);Wire.write(((long)2147483647>>8) & 0xFF);Wire.write(((long)2147483647>>16) & 0xFF);Wire.write(((long)2147483647>>24) & 0xFF);
      Wire.write(0);Wire.write(0);
      Wire.endTransmission(); 
      return(0);
    }    
  if (tipus==MAX){
      // -2147483648 kezdő értéket írunk a tároló cellába, mert nincs kisebb szám long esetén, így csak ennél kisebbek jöhetnek.
      Wire.write(1);Wire.write((long)-2147483648 & 0xFF);Wire.write(((long)-2147483648>>8) & 0xFF);Wire.write(((long)-2147483648>>16) & 0xFF);Wire.write(((long)-2147483648>>24) & 0xFF);
      Wire.write(0);Wire.write(0);
      Wire.endTransmission(); 
      return(0);
    }    
  if (tipus==SUM or tipus==AVG or tipus==STO) {
      //össegző mező esetén csak simán törölni kell mindent
      Wire.write(tipus);Wire.write(0);Wire.write(0);Wire.write(0);Wire.write(0);
      Wire.write(0);Wire.write(0);
      Wire.endTransmission(); 
      return(0);
    }  
    Wire.endTransmission(); 
}




void setTime24() {
  //status regiszter beállítása
  Wire.beginTransmission(CLOCK_ADDRESS);
  Wire.write(0x0e);               //A control regiszterbe fogunk írni, azt címezzük meg
  Wire.write(0b01100011);         //BBSQW bit 1, vagyis elemes táplálásnál is működik az oszcillátor
                                  //ha 1-re állítanánk, elemes táplálásnál nem járna az óra, csak őrizni az utolsó időpontot
                                  //A RS2 és RS1 bit 0-val beállítja a négyszögjel kimenetet 1Hz-ra, ezen lehet változtatni:
                                  // 0b01101011 esetén 1024Khz, 0b01110011 esetén 4096Khz, 0b01111011 esetén 8192Khz.
                                  //A INTCN bit 0-val beállítja, hogy az INT/SQW kimenet beállított frekvenciájú jelet ad ki.
  //címbet a státus regiszter következik, nem kell megcímezni csak írni, és az adat oda kerül
  Wire.write(0b00000000);         //A status regiszterben töröljük az OSF fleg-et, ami 1-el jelzi majd, ha elemes táplálásnál 
                                  //nem működött az oszcillátor, vagyis az óra valószínűleg nem pontos.
                                  //Az EN32Khz bit-et is 0-ra írjuk, azaz letiltjuk a 32Khz-s kimenetet
  Wire.endTransmission();
  //dátum és időpont beállítása
  Wire.beginTransmission(CLOCK_ADDRESS);
  Wire.write(0x00);
  Wire.write(dec_bcd(ds3231.masodperc));
  Wire.write(dec_bcd(ds3231.perc));
  Wire.write(dec_bcd(ds3231.ora) & 0b10111111);
  Wire.write(dec_bcd(ds3231.het_napja)); 
  Wire.write(dec_bcd(ds3231.nap));
  Wire.write(dec_bcd(ds3231.ho));
  Wire.write(dec_bcd(ds3231.ev-2000)); 
  Wire.endTransmission();
}

void getTime24() {
  //kiolvassa a dátumot és az időpontot 
  Wire.beginTransmission(CLOCK_ADDRESS);
  Wire.write(0x00);
  Wire.endTransmission();
  Wire.requestFrom(CLOCK_ADDRESS, 7);
  ds3231.masodperc = bcd_Dec(Wire.read());
  ds3231.perc = bcd_Dec(Wire.read());
  ds3231.ora = bcd_Dec(Wire.read());
  ds3231.het_napja = bcd_Dec(Wire.read());
  ds3231.nap = bcd_Dec(Wire.read());
  ds3231.ho = bcd_Dec(Wire.read() & 0b01111111);
  ds3231.ev = bcd_Dec(Wire.read())+2000;
  //status regiszterből az OSF bit kiolvasása.
  Wire.beginTransmission(CLOCK_ADDRESS);
  Wire.write(0x0f);
  Wire.endTransmission();
  Wire.requestFrom(CLOCK_ADDRESS, 1);
  if (Wire.read()& 0b10000000) {ds3231.pontos=0;} else {ds3231.pontos=1;}  //1-el jelezzük, ha az óra valószínűleg pontos
}

byte bcd_Dec(byte val) {  // Convertál bcd számból decimálisba
  return ( (val/16*10) + (val%16) );
}
byte dec_bcd(byte val) {  // Convertál decimális számból bcd-be
  return ( (val/10*16) + (val%10) );
}

bool szokoev(int ev)  {
/**************************************************************************************
 * Ez a függvény megállapítja egy évről, hogy szökőév-e. Bemenő paraméter az év       *
 * évszázadokkal megadva pl. 2021. Az eredmény fals, ha nem szökőév, és true ha igen. *
 * Egy adott év akkor szökőév, ha 4-el osztható, de minden 100. év nem szökőév        *
 * minden 400. pedig mégis szökőév. Pl. szökőévek: 4, 8, ...2020, 2024 stb.           *
 * Nem szökőév 100, 200 ... 1900, 2000. Azonban mégis szökőév: 1200, 1600, 2000 stb.) *
 **************************************************************************************/
  if (ds3231.ev==0) {return false;}             //a 0.évet nem vesszük szökőévnek 
  bool szoko_ev=false;                
  if (ds3231.ev%4==0) {                         //ha néggyel osztva nem ad maradékot, akkor szökőév
    szoko_ev=true;
    if (ds3231.ev%100==0) {                     //ha 100-al osztva nem ad maradékot, akkor nem szökőév
      szoko_ev=false;
      if (ds3231.ev%400==0) {szoko_ev=true;}    //ha 400-al osztva nem ad maradékot, akkor mégis szökőév 
    }
  }
  return szoko_ev;
}


void ora_pontositas(unsigned int elmult_nap, unsigned int sec5) {
      ds3231.ev=2000;                             //2000-től indulunk a napokat évente csökkentő ciklussal
      int evnapja=365;                            //ez a változó az év aktuális napjainak számát adja meg
      while (elmult_nap>evnapja) {                //addig megy a ciklus, amíg kisebb a hátralévő napok száma, mint az aktuális év napjainak száma
        elmult_nap=elmult_nap-evnapja;            //kivonjuk az adott év napjainak számát
        ds3231.ev++;                              //növeljük az évet! Így a ciklus befejezésekor az éppen aktuális évnél fogunk járni
          if (elmult_nap==0) {elmult_nap=1;}      //ha pont megegyezik, akkor sem lehet 0 az eredmény, mert csak 1.-e lehet
          if (szokoev(ds3231.ev)) {evnapja=366;}  //a következő évben szökőév esetén 366-ot vonunk le 
          else {evnapja=365;}                     //ha a következő év nem szökőév, akkor 365-öt vonunk le
      }
      //megállapítottuk, hogy hány évet adtunk meg a bemenő számadatban, most jön a maradék alapján, hogy az éven belül hányadik hónapban vagyunk
      int ho_napjai[] = {31,28,31,30,31,30,31,31,30,31,30,31};  //tudnunk kell, hogy egy adott hónap hány napos
      if (szokoev(ds3231.ev)) {ho_napjai[1]=29;}                // szökőévben a február 29 napos 
      ds3231.ho=1;
      while (elmult_nap>ho_napjai[ds3231.ho-1]) {               //addig megy a ciklus, amíg a napok csökkentésével egy hónap alá nem kerül a maradék
        elmult_nap=elmult_nap-ho_napjai[ds3231.ho-1];           //csökkentjük a maradék napokat az adott hónap napjaival
        ds3231.ho++;                                            //növeljük a hónapot
      } 
      ds3231.nap=elmult_nap;                                    //megkaptuk az adott hónap napját is a maradékban
      ds3231.ora=sec5/720;                                      //ennyi óra van a megadott számban
      ds3231.perc=(sec5-ds3231.ora*720)/12;                     //ennyi perc van az órák által megadott öt másodpercek kivonása után
      ds3231.masodperc=(sec5-ds3231.ora*720-ds3231.perc*12)*5;  //ennyimásodperc marad az órák és percek öt másodperces számának kivonása után.
      // így már betöltöttük a ds3231 struktúrába a pontos dátumot és időt, lehet frissíteni az órát, ha ijen jel érkezik
      //Serial.print(" = ");Serial.print(ds3231.ev);Serial.print("-");Serial.print(ds3231.ho);Serial.print("-");Serial.print(ds3231.nap);
      //Serial.print("  ");Serial.print(ds3231.ora);Serial.print(":");Serial.print(ds3231.perc);Serial.print(":");Serial.println(ds3231.masodperc);

      setTime24();
      ido_iras_sorosportra();
      Serial.println();
      idopontositas_perc=0;  //az idopontositas_perc változót nullázom, mert jött időpontosító adat (aztán időnként növelem, és kijelzem az értékét)

}

void ido_iras_sorosportra() {
      Serial.print("Ido:");Serial.print(ds3231.ev);Serial.print(ds3231.ho);Serial.print(ds3231.nap);Serial.print("-");
      Serial.print(ds3231.ora);Serial.print(":");Serial.print(ds3231.perc);Serial.print(":");Serial.print(ds3231.masodperc);Serial.print(" ");
}

/****************************************************************************************************
 * inicializálja és lekérdezi a BME280 szenzort. Ha a szenzor inicializálása nem sikerül
 * akkor a függvény újbóli meghívásakor ismét megkísérli az inicializálást, Ha sikerül, akkor 
 * rögtön mér is egyet.
 * Ha már inicializálva van, akkor csak mér. Ha közben megszakad a BME280-al a kapcsolat
 * akkor a hőmérséklet lekérdezés -145.75 fokod ad vissza tapasztalatom szerint, ezért ekkor
 * a mérési eredmények hamisak, és legközelebb újra megpróbáljuk inicializálni
 ****************************************************************************************************/
void bme280_lekerdez()
{
  if (!bme280_on)
  {
    if (bme.begin(0x76, &Wire)) 
    {         
      bme280_on=HIGH;  //bme280 inicializálása sikerült
      //rögtön mérünk is egyet
      kulso_homerseklet=bme.readTemperature();
      paratartalom=bme.readHumidity();
      legnyomas=bme.readPressure()/100; 
    } 
    else
    {
      kulso_homerseklet=9999;paratartalom=99;legnyomas=0; 
    }
  }
  else
  {
    kulso_homerseklet=bme.readTemperature()*100;
    paratartalom=bme.readHumidity();
    legnyomas=bme.readPressure()/100; 
    if (kulso_homerseklet<-10000) 
    {
      bme280_on=LOW;  //ha nincs bme280 csatlakoztatva, akkor -145 fokot ad vissza, legközelebb megpróbáljuk ujra inincializálni
      kulso_homerseklet=9999;paratartalom=99;legnyomas=0;
    }
  }
}

void napi_torles() {
 /************************************************
  *félpercenként frissítjük a kijelzőn az időt   *
  ************************************************/
  if (millis()>lcd_frissit_ido+30000) {    //fél percenként frissítjük az időt a kijelzőn, és ha éjfél van, akkor töröljük a napi gyüjtőket is
    if (ora_setup==LOW) {
      getTime24();
      if ((ds3231.ora==23 and ds3231.perc==54) or (ds3231.ora==23 and ds3231.perc==56) or (ds3231.ora==23 and ds3231.perc==58)) {  //éjfél előtt biztos ami biztos ismételjük a statisztikai adatokat
        stat_adas_kenysz=1;   //ezzel kényszerítem, hogy azonnal sugározza az adatokat, hogy a beltéri egységre biztosan bekerüljön az utolsó
                              //állapota a statisztikai adatoknak. A beltéri egység pontosan éjfélkor fogja eeprom-ba írni az adatokat,
                              //ezért pár perccel előtte küldöm.
      }

      if (ds3231.ora==0 and ds3231.perc==5) {  //éjfélkor töröljük a napi gyűjtőket, és a szélsebességmérőnek is törlés parancsot küldünk
                                               //Ezzel a feltétellel lehet, hogy kétszer is törlünk, de ez nem baj
        //töröljük az FRAM gyűjtőket
        multimemo(V_KULSO,AVG);multimemo(V_KULSO_MIN,MIN);multimemo(V_KULSO_MAX,MAX);
        multimemo(V_PARA,AVG);multimemo(V_PARA_MIN,MIN);multimemo(V_PARA_MAX,MAX);
        multimemo(V_LEGNY,AVG);multimemo(V_LEGNY_MIN,MIN);multimemo(V_LEGNY_MAX,MAX);
        multimemo(V_PINCE,AVG);multimemo(V_PAKNAA,AVG);multimemo(V_PAKNAF,AVG);
        multimemo(V_KISHAZ,AVG);multimemo(V_KISHAZ_MIN,MIN);multimemo(V_KISHAZ_MAX,MAX);
        multimemo(V_IMP_SZAM,STO);        //impulzusszám 
        multimemo(V_SZELES_IDO,STO);      //az az idő millisec-ben, amikor forgott a szélkerék
        multimemo(V_SZELSEB_MAX,MAX);     //max. széllökés
        multimemo(V_CSAPADEK_IMP,STO);    //csapadék impulzus gyűjtő
        //kijelzes_valtoztatas=1;
        imp_szam=0;
        csapadek=0;
        max_szelseb=0;                    //szélsebesség maximum tárolása
        atlag_szelseb=0;                  //10 imp 10 másodperc alatt=1 másodperc impulzusidő=2,5km/h
        szeles_ido=0;                     //az az idő, amikor fújt a szél, millisecben, illetve ennek törlése
        ido_iras_sorosportra();
        Serial.println("Napi törlés!");


        if (kulso_homerseklet>-5000 and kulso_homerseklet<5000) { 
          hkulso_a=kulso_homerseklet+10000;    //negatív is lehet a hőmérséklet, de én csak long számot kezelek, ami nem lehet nagatív. Eltoljuk 100 fokkal (10000-el)
          hkulso_m=kulso_homerseklet+10000; 
          hkulso_x=kulso_homerseklet+10000;  
        }
        para_a=paratartalom;
        para_m=paratartalom;
        para_x=paratartalom;
        legny_a=legnyomas;
        legny_m=legnyomas;
        legny_x=legnyomas;
        hpince_a=pince_homerseklet+10000;       //negatív is lehet a hőmérséklet, de én csak long számot kezelek, ami nem lehet nagatív. Eltoljuk 100 fokkal (10000-el)
        haknaa_a=akna_homerseklet_also+10000;   
        haknaf_a=akna_homerseklet_felso+10000;  
        hkishaz_a=kishaz_homerseklet+10000;    
        hkishaz_m=kishaz_homerseklet+10000;   
        hkishaz_x=kishaz_homerseklet+10000;  
        adatok_kijelzonek();
        szelseb_kijelzonek();

      }
    }
    lcd_frissit_ido=millis();
    idopontositas_perc=idopontositas_perc+30;      //az idopontositas_perc változót 30 másodperccel növelem, hogy lássam, mikor jött utoljára időpontosítás. 
                                                   //Max 3600 lehet az értéke, vagy nem történt időpontostás, és a belső óra adja a pontos időt (percben jelzem ki)
    multimemo(V_IDOPONTOSITAS,idopontositas_perc); //időpontositas_perc értékét tárolom az fram-ban, hogy áramszünet után is tudjam az értékét
  }
}

/****************************************************************
 * A nyomógomb megnyomásával váltogatjuk a kijelző tartalmát   *
 ***************************************************************/
void nyomogomb_kezeles() {
    //megnyomta a nyomógombot, de még lehet, hogy prelles
  if (digitalRead(10)==LOW and nyomva==LOW) {nyomva_ido=millis();nyomva=HIGH;lcd.backlight();}    //megnyomta a setup gombot
  //ha 20msec múlva is nyomva tartja, akkor ez már nem prelles, biztosan nyomva van, lehet belépni a setup folyamatba
  if (digitalRead(10)==LOW and millis()>nyomva_ido+20 and nyomva==HIGH) {ora_setup_ido=millis();ora_setup=HIGH;}
  //elengedte a nyomógombot, de még lehet, hogy prelles
  if (digitalRead(10)==HIGH and elengedve==LOW and nyomva==HIGH) {elenged_ido=millis();elengedve=HIGH;}
  //már 20msec óta elenged, biztosan nem prelles, beállítási értéket váltunk, lehet várni az új megnyomásra
  if (digitalRead(10)==HIGH and elengedve==HIGH and millis()>elenged_ido+20) {
    nyomva=LOW;
    elengedve=LOW;
    //Serial.println(ora_setup);
    if (ora_setup==HIGH) {          //setup módban vagyunk, lehet beállítani a következő setup értéket és várakozni a váltásra vagy nyomva tartás esetén a beállításra
      if (setup_num_novel==HIGH) {setup_num=setup_num+1;if (setup_num==8){setup_num=2;}}
      setup_num_novel=HIGH;         //ha változtatás után nem kellett növelni, legözelebb már kell
     //Serial.println(setup_num);
     switch (setup_num) {
        case 1:                     //csak a világítást kapcsoljuk be a kijelzőn, és egy percig így is marad
          mert_ertek_kijelzes=HIGH; //mehet tovább a mérési eredmények kijelzése
          lcd_vilagitas=HIGH;       //ha éppen világit az LCD, akkor nem akarja majd újra bekapcsolni az első lenyomással, helyette
                                    //rögtön a setup_num értéke 2 lesz, tehát elindul az óra beállítása
          break;
        case 2:
          lcd.setCursor(0,0);lcd.print("Ora beallitas:  ");
          lcd.setCursor(0,1);lcd.print("Ev:    "+String(ds3231.ev)+"     ");
          mert_ertek_kijelzes=LOW;  //a merési eredmények kijelzését a setup idejére leállítjuk
          break;
        case 3:
         lcd.setCursor(0,1);lcd.print("Ho:      "+String(ds3231.ho)+"     ");
          mert_ertek_kijelzes=LOW;  //a merési eredmények kijelzését a setup idejére leállítjuk
          break;
        case 4:
          lcd.setCursor(0,1);lcd.print("Nap:     "+String(ds3231.nap)+"     ");
          mert_ertek_kijelzes=LOW;  //a merési eredmények kijelzését a setup idejére leállítjuk
          break;
        case 5:
          lcd.setCursor(0,1);lcd.print("Hetnapja:");
          lcd.setCursor(9,1);
          switch (ds3231.het_napja) {
            case 1:
              lcd.print("Hetfo");break;
            case 2:
              lcd.print("Kedd ");break;
            case 3:
              lcd.print("Szerd");break;
            case 4:
              lcd.print("Csut ");break;
            case 5:
              lcd.print("Pent ");break;
            case 6:
              lcd.print("Szomb");break;
            case 7:
              lcd.print("Vasar");break;
          }
          mert_ertek_kijelzes=LOW; //a merési eredmények kijelzését a setup idejére leállítjuk
          break;
        case 6:
          lcd.setCursor(0,1);lcd.print("Ora:     "+String(ds3231.ora)+"     ");
          mert_ertek_kijelzes=LOW; //a merési eredmények kijelzését a setup idejére leállítjuk
          break;
        case 7:
          lcd.setCursor(0,1);lcd.print("Perc:    "+String(ds3231.perc)+"     ");
          mert_ertek_kijelzes=LOW; //a merési eredmények kijelzését a setup idejére leállítjuk
          break;
      }
    }
  } 
  //ha 5 másodpercre elengedi a nyomógombot  setup folyamaton belül, akkor visszaállítjuk az eredeti állapotot
  if (digitalRead(10)==HIGH and millis()>ora_setup_ido+5000 and ora_setup) {
    ora_setup=LOW;nyomva=LOW;
    setup_num=0;
    mert_ertek_kijelzes=HIGH; //mehet tovább a mérési eredmények kijelzése
  }
  //ha 60 másodperce elengedte a nyomógombpt akkor a háttérvilágítást kikapcsoljuk
  if (digitalRead(10)==HIGH and millis()>ora_setup_ido+240000 and ora_setup==LOW) {
    lcd_vilagitas=LOW;
    lcd.noBacklight();
  }
  //egy másodpercig nyomvatartotta, változtatjuk az adott értéket, elengedéskor az állapotot beállítjuk az órába
  if (digitalRead(10)==LOW and ora_setup==HIGH and nyomva==HIGH and millis()>nyomva_ido+1000) {
    switch (setup_num) {
      case 2:
        ds3231.ev=ertekporgetes(ds3231.ev-2000,22,40)+2000;
        break;
      case 3:
        ds3231.ho=ertekporgetes(ds3231.ho,1,12);
        break;
      case 4:
        ds3231.nap=ertekporgetes(ds3231.nap,1,31);
        break;
      case 5:
        ds3231.het_napja=ertekporgetes(ds3231.het_napja,1,7);
        break;
      case 6:
        ds3231.ora=ertekporgetes(ds3231.ora,0,23);
        break;
      case 7:
        ds3231.perc=ertekporgetes(ds3231.perc,0,59);
        ds3231.masodperc=0;
        break;
    }
    setTime24();
    ora_setup_ido=millis();
  }
}


/********************************************************************************************
 * Ha óra beállításkor nyomva tartja a nyomógombot, akkor folyamatosan számoltatja felfelé  *
 * az éppen beállított adat értékét. Ha elérte a maximumot, akkor nullázza az értéket.      *
 ********************************************************************************************/
byte ertekporgetes(byte o_ert, byte o_min, byte o_max)
{
  do
  {
    wdt_reset(); //alaphelyzetbe állítjuk a a watcdog timer-t. Erre itt is szükség van, különben az óra beállítás
                 //alatt is resetet generál a watchdog, mert a fő ciklus nem fut miközben ebben a függvényben tartózkodik
                 //a program
    o_ert=o_ert+1;setup_num_novel=LOW;
    if (o_ert>o_max) {o_ert=o_min;}
    lcd.setCursor(9,1); 
    if (setup_num==5)
    {
      switch (o_ert) 
      {
        case 1:
          lcd.print("Hetfo");break;
        case 2:
          lcd.print("Kedd ");break;
        case 3:
          lcd.print("Szerd");break;
        case 4:
          lcd.print("Csut ");break;
        case 5:
          lcd.print("Pent ");break;
        case 6:
          lcd.print("Szomb");break;
        case 7:
          lcd.print("Vasar");break;
      }
    } 
    else
    {
      lcd.print(String(o_ert)+"  ");
    }
    delay(700);
  } while (digitalRead(10)==LOW);
  return o_ert;
}

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

Kattints egy csillagra az értékeléshez!

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