Akkumulátor vizsgáló

  • Lítium akkumulátor ciklikus töltéses és merítéses vizsgálata, feszültség, áram regisztrálása INA226 teljesítménymérővel
  • Három töltés/merítés ciklus regisztrálása eeprom-ba
  • Többféle beállítható vizsgálati paraméter (min. és max feszültség, regisztráció felbontása)
  • Eredmények kiolvasása soros porton keresztül és LCD kijelzőn
  • Többféle lítium akkumulátor vizsgálatának eredményei itt

Az amit megcsináltam nem teljesen indokolható a józan ész szabályai szerint. Úgy kezdődött, hogy  megláttam a kedvenc külföldi rendelési oldalamon egy 4 db-os lítium akku csomagot, amin egy akku esetében 12.400mAh kapacitást ígértek. Nem is volt drága. Rendeltem hozzá egy balancer-nek nevezett elektronikát is. Elképzeltem, hogy micsoda szuper powerbankot fogok magamnak csinálni! Amikor megérkezett a csomag, boldogan vettem kezembe az első 12.000mAh kapacitású akkumulátoromat, rádugtam egy USB-ről működő egy cellás töltőre, és gyanúsan gyorsan feltöltődött. Akárhogy is számolgattam, az 1A max. áramú töltővel 12 órát kellett volna tölteni, de ez kevesebb mint egy óra alatt megvolt. Jött egy teljes merítés ellenállásokkal, nomeg folyamatos feszültségmérésekkel, nehogy tönkre tegyem az aksit. Ez is gyanúsan gyorsan lezajlott! Ekkor szagot fogtam, hogy engem becsaptak! Persze megérdemeltem! Nem lehet az egyik lítium akkumulátor 12.000mAh kapacitású, ha az azonos méretű akkuk (a 18650-es sorozatról van szó) jellemzően 2-3000mAh kapacitás között mozognak. Négy akkumulátort kaptam a csomagban, gondoltam együtt 12.000mAh a kapacitásuk! Azért, hogy megnyugtassam a saját hiúságomat (hagytam magam átverni) próbáltam kideríteni a tényleges kapacitást, és reménykedtem a 3000mAh-ban! Hátha mégis jó üzletember vagyok és fillérekért 3000mAh/db kapacitású akkumulátorokat vettem. Ha így lenne, azonnal eladom mindet Tesla-nak, hogy végre megépíthesse az 1000km/töltés hatótávú autóját.

A fenti okok miatt terelődött a figyelmem az lítium akkumulátorokra. Sok-sok internetes keresgélés, valódi tudományos részletességgel megfogalmazott értekezés elolvasása után úgy éreztem, még mindig nem tudok semmit. Egy csomó kérdésre csak homályosan megfogalmazott válaszokat kaptam. Úgy tapasztaltam, minden oldalon ugyanazokat az általános információkat idézgetik! Elhatároztam hát, hogy önerőből ismerem meg az akkumulátorok működését. Nem a működési fizikájuk érdekelt, arról volt kielégítő infó bőven. Engem az érdekel, hogyan kell tölteni, mennyire függ a kivehető energia a töltéstől, a merítéstől, hőmérséklettől stb.

Kerítettem tehát egy akkumulátort (volt is rögtön négy darab a fiókban, veszteségként leírva a költségvetésből), és elkezdtem kísérletezgetni. Ám az akkumulátorok töltése és merítése igen hosszú, egy-egy vizsgálat igen sokáig tart. Nem állhatok ott felette és nézegethetem a műszerek aktuális értékeit napokon át! Ezért csináltam azt, aminek a leírása most következik. Megmutatom, hogy nézett ki az első verzió, a deszkamodell, amivel az első mérések készültek:

Beismerem, nem egy szépség! Sok oda nem tartozó dolgot is látni a képen, mert ez az én kis fejlesztő laborom. A lényeg az Arduino UNO, egy INA226 modul (alig látszik a tápegység bal alsó sarkában), két relé, a kijelző és nyomógombok. Állandó kontakhibákkal küszködtem, de fejlődött a program, és a végén egy egészen jól használható cél eszköz lett belőle.

Az akkumulátor vizsgáló felépítése

Még régebben vettem egy INA226 modult, ami képes 16bit felbontással mérni egy terhelés feszültségét és a rajta átfolyó áramot. Ez jeleni a műszer alapját. Az Arduino semmi mást nem csinál, mint másodpercenként egy alkalommal kiolvassa az INA226-ból az akkumulátor feszültségét és a rajta átfolyó áramot. Emellett integrálja az akkumulátor áramát, hogy egy merítési vagy töltési ciklus után megkapjuk az akkumulátor kapacitását. Meghatározott algoritmus szerint regisztrálja a folyamat lényeges pontjait az EEPROM-ba, hogy szép töltési és merítési karakterisztikákat tudjunk Excelben rajzolni. Nem kellett külön adattároló, az EEPROM is elegendő. Egy teljes töltés vagy merítés 10-20 mérési ponttal tökéletesen követhető. A kijelző kezdetben csak az aktuális adatok megjelenítésre szolgált, később hobbiból fejlesztettem, nem sok szerepe van a mérések alatt. A nyomógombok kezdetben csak az indítás és leállítás feladatának elvégzését kapták, de egyre több funkció épült be a szerkezetbe. Megjeleníthetők az EEPROM-ból a mérési eredmények (mérés közben is), és minden vizsgálati paraméter beállítható a nyomógombokkal, törölhetők ez előző mérési eredmények stb. Önálló működésű eszköz lett belőle, mert közben feleségem száműzött a lakásból a szerszámos sufniba, mivel nem akarta heteken át nézegetni a vacakjaimat. Oda viszont nem akartam mindig kicipelni a laptopot és minden alkalommal feltöltögetni az új beállításokat. Ahogy elnézem a fenti képet, teljesen jogos feleségem reakciója!
Beépítésre került két relé. Az egyik a töltés vagy merítés folyamatát tudja megszakítani, ha az akkumulátori feszültsége eléri a biztonsági határokat. Ez lítium akku esetén töltéskor 4.2V, merítéskor 2.5V a leírások szerint. Ha ezeket a határokat átlépjük, az akkumulátor károsodik, esetleg fel is robban. A másik relé akkor került beépítésre, amikor elhatároztam, hogy az akkumulátor vizsgáló berendezésem több töltési és merítési ciklust fog megvizsgálni. Ez a relé ekkor felváltva a töltőt, illetve a merítést végző ellenállást kapcsolja az akkumulátorra. Folyamatosan fejlesztettem a berendezést, egyre kényelmesebbé téve a méréseket. Gyakorlatilag elértem, hogy teljesen automatikusan tud működni, regisztrálja a töltési és merítési görbéket, és a soros porton illetve USB-n keresztül egy laptoppal gyorsan ki lehet olvasni az eredményeket.

Közben folyamatosan születtek az ötletek, hogy miket is akarok kipróbálni. Néhány példa! Egy csomó akkumulátorom van, különböző gyártmányúak, részben hamisítványok, a méretük azonos, feliratuk egymásra licitál (2500mAh-12.000mAh) és szerettem volna feltérképezni a különbségeket, megtalálni azt a jellemzőt, amiről még a megvásárlás előtt felismerhető a gyártó hazugsága. Találtam is ilyen paramétert, de erről később. Tudni szerettem volna, hogy függ-e a kapacitás a merítés illetve a töltés áramától, és ha igen, mennyire?! Ez mérésisorozatok tucatjait fogja jelenteni, és egy mérés esetleg napokat, vagy heteket vesz igénybe. Ezért fejlődött szinte teljesen automatikusra!

Amikor végre elkészültem, elkezdődtek a mérések, de a fenti képen látható deszka model folyton szétesett, mindig akadt egy vezeték, amit véletlenül kihúztam, esetleg „feleségem” kitakarított egy alkatrészt. Így aztán barkácsoltam egy műanyag dobozt. Nagyon durva, csúnya megoldásokkal készült, de merev, mindent beforrasztottam, és immár hajlandó hibamentesen hetekig működni.

A fenti képen a végleges berendezés látható teljes fegyverzetben az akkumulátor töltésre használt beállítható áramlimittel rendelkező tápegységgel és az éppen vizsgált gigantikus Li-ion akkumulátorral. Feliratai szerint ez fogja megoldani a világ energia problémát. Valós adatai alapján alkalmasabb papírnehezéknek!
Említést érdemel a műanyag doboz készítése! Erre különösen büszke vagyok. Ingyen, és egy óra alatt csináltam! Valahonnan kaptam egyszer egy dekorációs célokra gyártott 5mm vastag, egyik oldalán kemény fehér felülettel kiképzett lágy műanyag lapot. Ebből dekopír fűrésszel levágtam a megfelelő méretű oldalakat, pákával kiolvasztottam a kijelzőnek a nyilást (plusz egy kis csiszolgatás), és az egészet összeragasztottam. Nem lett elég stabil, ezért pákával kárpitosszegeket nyomtam felűről az oldal lemezekbe. Atomstabil dobozt kaptam. Mivel nem tudtam elég egyenesen vágni az oldalak éleit, még szellőzőnyílások is keletkeztek az illesztéseknél. Mondanám, hogy tudatos tervezés, de csak szimpla pontatlanság. A fenéklemezt alumínium lemezből vágtam ki, így ezt felhasználhattam az ellenállások hűtésére (lásd később).  Belülről is megmutatnám a műalkotást:

Az Arduino nano számára az oldal lemezen vágtam egy nyílást, ahová viszonylag szorosan beilleszkedett az USB csatlakozójával. A másik végébe pedig beolvasztottam a műanyag lemezbe két csavart. Ezzel a megoldással a tápegység csatlakoztatás és az adatletöltés problematikáját is frappánsan oldottam meg! A fenéklemezt egy csúcstechnológiás rögzítési megoldás tartja a dobozon, négy szikszalag darab. Nagy ötlet volt, hogy a fiókban talált hangszóró csatlakozót építettem be az akkumulátor töltő és az akkumulátor csatlakoztatására. Aki esetleg nem ismeri, ez egy zseniális csatlakozó. Megnyomsz egy ferde kis gombot (a nyílás alatti csíkos felölet), minek hatására egy 2x2mm-es négyszögletes nyílás válik szabaddá. Ebbe bedughatod a sima, sodrott vagy merev esetleg ónozott vezetéket, elengeded a gombot és rugók „satuba” fogják a vezetékvéget. Nem kell csavarozgatni, dugdosni, ónozni. Igen kényelmes, így néz ki közelebbről:

Az áramkör roppant egyszerű! Van az Arduino, aminek az I2C portjára (A4 és A5 kivezetés) rákötöttem párhuzamosan az LCD kijelzőt és az INA226-ot. Van három nyomógomb és a két relé. A relék egy régebbi felesleges beszerzés eredményeként maradtak a nyakamon, meghajtó elektronikával szerelt modulok, közvetlenül az Arduino kimenetről vezérelhetők.

  A legutolsó állapot kapcsolási rajza:

Az ábrát a SimulIDE programmal készítettem. Sajnos nem tudtam működőképes szimulációt készíteni, mert a program nem ismeri az akkumulátort mint elektronikus alkatrészt. A programot ezért nem tudtam benne kipróbálni. Remélem mindent jó helyre kötöttem az ábrán. A kijelző szimbolikus, mert a SimulIde nem tartalmaz I2C illesztővel ellátott LCD kijelzőt, bár alkatrészekből össze lehetne építeni a szimulátorban. Sok magyarázatot nem érdemel a kapcsolási rajz. Egyedül a relék funkcióját kell megérteni. REL2 végzi az akku töltő vagy merítő áramkörének megszakítását, ha a feszültség elérte töltéskor a beállított végfeszültséget (pl. 4.2V), vagy merítéskor lecsökkent a beállított minimum alá (Pl. 2.5V). A megszakított állapot a relé alaphelyzete. A REL1 eredetileg nem volt betervezve, amikor eldöntöttem, hogy több merítési és töltési ciklust akarok csináltatni automatikusan, akkor építettem be. Nyugalmi állapotban a relé egy terhelő ellenállást köt az akkumulátorra, és ha REL2 bekapcsol, akkor megindul a merítés. REL1 meghúzott állapotában a töltőt kapcsolja az akkumulátorra. A vizsgálataimhoz a végleges berendezésbe nem egy, hanem 3db terhelő ellenállást építettem be. Ezeket billenő kapcsolókkal párhuzamosan lehet kapcsolni, így a terhelő áramot több fokozatban lehet beállítani. Ezt a rajzon nem részleteztem. A három ellenállás érték 40ohm, 22ohm és 10ohm. Ilyen ellenállásokat találtam a fiókban. Természetesen nagy teljesítményű darabokra van szükség. Pl. a 10ohm ellenálláson 4.2V akku feszültségnél 420mA áram folyik át, ami a feszültséggel szorozva kb. 1.7W teljesítmény. Ide és a 22ohm-hoz  egy 5W-os kerámia ellenállást kerestem. A 40ohm-os ellenállás már csak alig langyos a rajta eső kb. 0,4W hatására. Bizony ezeket az ellenállásokat érdemes hűteni, különben égési sérüléseket okozhatnak merítés közben. Ezért lett a doboz fenéklemeze alumíniumból. Ez hűti az ellenállásokat merítéskor. A minimális merítő áram 100mA, a maximum (ha mind a hármat bekapcsolom) 600mA körüli. Pontos érték ugyebár nincs, hiszen az akkumulátor feszültsége folyamatosan változik, így a terhelőáram is.

Ennek a berendezésnek is a program a lelke. Sajnos a forráskód nem a legoptimálisabb, hiszen nem egy előre eltervezett állapotot valósítottam meg, állandóan változott az elképzelés, hogy mit is akarok.

A végleges verzió fontosabb tulajdonságai:

Beállítható az akkumulátor töltés végfeszültsége.

Ha töltés közben az akkumulátor feszültsége eléri illetve 1mV-al meghaladja ezt a feszültséget, akkor a töltést egy relé lekapcsolja, hogy megakadályozza az akkumulátor károsodását. Lítium akkumulátornál ez 4.2V egy 6 cellás autó ólomakkumulátornál 14.4V

Beállítható az akkumulátor merítés végfeszütsége.

Ha merítés közben az akkumulátor feszültsége 1mV-al kisebb értékre csökken, akkor egy relé megszakítja a merítési folyamatot. Ez lítium akkumulátornál 2.5V egy ólomakkumulátornál nem határozható meg pontosan. Elvileg az ólom akkumulátorokat 0-ig lehet meríteni, de ez biztosan nem tesz jót. Kb. 6V-ra állítom majd, ha ólomakkumulátorokat is vizsgálok.

Beállítható egy feszültség korrekciós szám 4 tizedesjegy pontossággal.

Sajnos az INA226 nem tartalmaz feszültség referenciát (így sejtem), ezért a mérési eredmény nem teljesen pontos. Pontos, csak nem abszolút értékben. Ezért a mért eredményt fel lehet szorozni ezzel a számmal, hogy a kijelzőn megjelenített és regisztrált feszültség pontos legyen. Vagyis ez egy hitelesítő szám. Esetemben értékét úgy határoztam meg, hogy első lépésben beállítottam a számadatot 1-re. rákötöttem egy akkumulátort a berendezésre, megnéztem mekkora feszültséget jelez, valamint megmértem az akku pontos feszültségét egy hitelesnek tekinthető multiméterrel. A korrekciós szám, a két érték hányadosa. Egyébként kísérletezgetéssel is be lehet állítani. Addig kell növelni vagy csökkenteni, amíg a kijelzőn mutatott feszültség megegyezik a multiméteren látható feszültséggel.

Beállítható egy áram korrekciós szám 4 tizedesjegy pontossággal.

Nyilván az áramot sem méri abszolút értékben pontosan az INA226, ezért az áram pontos kijelzéséhez is kell egy korrekciós számadat. Működés ugyanaz, mint a feszültség korrekciónál, csak itt a multiméterrel áramot mértem, miközben merítettem vagy töltöttem az akkumulátort.

Beállítható egy nullázó áram érték.

Azt tapasztaltam, hogy amikor nem folyik áram az akkumulátoron keresztül, az INA226 akkor is kijelez egy áram értéket. Ez ugyan -0.15mA volt az én esetemben, de azért ezt is illik elnyomni, azaz hozzáadni +0.15mA értéket, hogy nulla legyen a kijelzett áram. Ha ezt nem teszem, akkor ezt a rendszeres hibát beleméri a végeredményként megszülető akku kapacitás értékbe. Ha pl. 10 órán át töltök egy akkumulátort, akkor ez pont -1.5mAh hibát okozna! Így utólag talán nem is annyira fontos ez a hiba. De már megcsináltam, a vele töltött időt nem kapom vissza!

Beállítható egy feszültség felosztás, illetve felbontási mennyiség.

Ez azt mondja meg, hogy egy-egy töltési vagy merítési folyamatot hány ponton akarok regisztrálni. Töltéskor és merítéskor is nagyon lassan változik az akku feszültsége (kivéve a kezdeti és a vég szakaszokban). Nem tudhatom, hogy mennyi ideig történik a mérés, így nem az eltelt idő alapján regisztrálok. Mivel az EEPROM-ban kevés a hely (1Kbyte), spórolni kell. Célszerűbbnek látszott, hogy a teljes várható akku feszültség tartományt osztom fel pontokra, és akkor regisztrálok, ha egy előző regisztrált feszültség értékhez képest megadott értékben változik meg az akkumulátor feszültsége. Kísérletekből tudom, hogy 10-20 regisztrációs pont bőven elegendő. Már 10 pontból is gyönyörű görbét rajzol az Excel.

Töltési vizsgálat.

Ekkor a rákötött akkumulátornak megméri a feszültségét. Ez lesz az indulási feszültség. Megnézi azt is a program, hogy éppen mekkora töltési végfeszültség lett beállítva, és a különbséget elosztja az előző pontban ismertetett felbontási mennyiséggel. Pl. ha egy akku feszültsége 3.5V és 4.2V-ig fogjuk tölteni és 10 regisztrációs pontot állítunk be, akkor 4.2-3.5=1.2V/10=0.12V feszültség változás esetén regisztrálunk. Így pont 10 mérési pontunk lesz az ábrázoláshoz. Mielőtt terhelést vagy töltést kapcsolok egy akkumulátorra. Erre azért van szükség, mert csak másodpercenként egyszer mérek, viszont az akkuk feszültsége egy másodperc alatt nagyot változik. Pl. egy lemerült akkumulátorra rákapcsolom a töltő áramot, a feszültsége több tized volttal is megugrik a szempillantás alatt. A REL2 kikapcsolásakor is regisztrálok egyet, hogy pontosan tudjam a kikapcsolás pillanatának idejét és a teljes végső kapacitást.
A töltési vizsgálat abból áll, hogy töltés fő üzemmódba kapcsoljuk a REL1-et (töltőt kapcsolunk az akkumulátorra), bekapcsoljuk a REL2 relét (zárjuk az áramkört), és addig várunk, amíg az akku feszültsége eléri a beállított végfeszültséget. Közben persze regisztrálunk. A töltési ciklus végén lekapcsolja a program a REL2 relét, így megszakítja az akku töltő áramát. A kijelzőn továbbra is látható az akkumulátor pillanatnyi feszültsége, ami kicsit csökkenni fog pár percig. Látható az is, hogy mennyi az akku kapacitása. Persze csak akkor, ha előtte teljesen lemerítettük. A töltést egyébként meg is lehet szakítani, a program megjegyzi az aktuális eltelt időt és az addig felvett kapacitást mAh-ban. Az idő mérése a leállított állapotban természetesen áll! Így egy vizsgálat tetszőleges számban megszakítható és folytatható. A kialakítás nem tökéletes, pl. áramszünet esetén nem tárolja az értékeket, csak ha a nyomógombbal szakítom meg a töltési folyamatot. A regisztrálások az EEPROM-ba folyamatosan tárolódnak mindaddig, amíg ki nem törlöm az EEPROM-ot. Akár több különböző akku töltését (esetleg merítését) is lehet regisztrálni. Viszont ekkor az akkumulátorok kapacitását előjelesen (töltés, merítés) összegzi, tehát nem igazán indokolt ilyet tenni.

Merítés vizsgálat.

Ez teljesen ugyanaz, mint amit a töltés vizsgálatnál leírtam. Mindössze itt a REL1 a merítést végző terhelő ellenállást kapcsolja az akkumulátorra. Ilyenkor természetesen a regisztráláshoz szükséges feszültség különbséget a pillanatnyi (indításkori) akku feszültségből és a minimális leállítási végfeszültségből számoljuk ki. A folyamat megszakítható, folytatható stb.

Ciklikus vizsgálat.

Ennek során első lépésben feltöltjük teljesen az akkumulátort. Aztán várunk 10 percet, hogy a feszültsége beálljon egy állandósult értékre (tapasztalatom szerint 4.2V-ról kb. 4.05V körüli értékre csökken). Ekkor indítunk egy merítést, aztán töltést háromszor megismételve a folyamatot. A töltések merítések között mindig 10 perc szünet található. A regisztrálás folyamatos. A ciklikus vizsgálat esetén a regisztrálási pontok számát tapasztalatom szerint kb. 10-15-re lehet beállítani, mert annyi pont fér el az EEPROM-ban. Egy regisztrálás során tárolom az időt másodpercekben unsigned int formában, a pillanatnyi feszültséget és áramot egy-egy integer értékben, valamint a pillanatnyi összegzett akkumulátor kapacitást szintén egy integer értékben, egy adatrekord 8 byte hosszú. Így összesen 120 regisztrálást lehet tárolni. A ciklikus vizsgálat a szünetekkel és a teljes legelső feltöltéssel együtt 12 ciklusból áll. Pl. egy 13 regisztrálási felbontással végzett teljes ciklikus vizsgákat 86 érték regisztrálását eredményezte. Az eltelt idő és az összegzett akkumulátor kapacitást minden ciklus elején nullázom.

A vizsgálat (töltés, merítés, ciklikus vizsgálat) alatt és után a kijelzőn megjeleníthetők a regisztrált adatok.

Ezek a regisztrációs pontok sorszámozva jeleníthetők meg. A „+” illetve ”–„ feliratú gombokkal (rövid megnyomáskor viselkednek + és – módon) léptetni lehet az adatokat előre hátra. Egyszerre egy regisztrációs pont adatai láthatók a képernyőn, azaz látszik a ciklus elindítása óta eltelt idő, az abban a pillanatban mért feszültség, áramerősség (előjelesen) és az akkumulátor kapacitás mAh-ban természetesen szintén előjelesen. Ha nem nyúlunk 20 másodpercig egyetlen nyomógombhoz sem, akkor visszavált a kijelző az aktuális adatok kijelzésére, amit másodpercenként frissít.

Mint látható a fenti kijelző fotókon, nagy hangsúlyt fektettem az ember-gép-ember kommunikáció minőségére. Ahol lehetett és kifért, rövid tájékoztató szövegeket helyeztem el, hogy a hozzám hasonló nehéz felfogóképességű felhasználó is könnyebben kitalálja, melyik nyomógomb mit is csinál az adott helyzetben.

A berendezés működtetése:

Fontos tudnivaló, hogy két nyomógombbal lehet működtetni minden funkciót. Megkülönböztetjük a nyomógomb rövid megnyomását, és a hosszú megnyomást. Hosszúnak számít 0.8 másodperc felett, alatta pedig rövidnek. A rövid megnyomás valamilyen adatot változtat, vagy információt léptet a kijelzőn, még a hosszú elindít valamilyen folyamatot.

A kijelzőnek van egy alapállapota. Az alapállapot az, amire 10 vagy 20 másodperc után automatikusan visszakapcsol a berendezés, ha nem nyomunk le nyomógombot. Ekkor a kijelzőn az aktuálisan mért adatok láthatók:

A nyomógombokkal lehet a fő üzemmódból átlépni egy regisztrált adat megjelenítési állapotba, lehet törölni az EEPROM-ban tárolt adatokat, és sorban előhívhatók a vizsgálat során beállítható paraméterek, amit meg is változtathatunk.

Nyomógombok funkciói:

Világítás – Ez a nyomógomb egy percre felkapcsolja a kijelző háttérvilágítását. Ezt egyebként bármelyik nyomógomb megteszi.

+, START – Ez a nyomógomb a működési paraméterek váltásakor növeli az éppen változtatott adat értékét. Ha hosszan nyomjuk meg, akkor elindítja az éppen beállított fő üzemód szerinti mérést és regisztrálást (töltés, merítés, ciklikus vizsgálat). Újbóli hosszú lenyomással a mérést leállítja. Ha a regisztrált adatok megjelenítésének állapotában vagyunk, akkor lépteti a kijelzett adatot időben az elejétől a vége felé.

-, MÓD – Ez a nyomógomb pillanatnyi mért adatok kijelzését végző állapotban rövid lenyomással beállítja a fő üzemmódot. A kijelző ciklikusan mutatja a „Töltés”, „Merítés”, Ciklikus vizsgálat” szöveget a felső sorban, és ezt lehet váltani minden megnyomással. Ott állunk meg, amelyikre éppen szükségünk van. Rövid lenyomással a működési paraméterek változtatásakor az éppen változtatott adatot csökkenti. Ha a regisztrált adatok megjelenítésének állapotában vagyunk, akkor rövid lenyomással lépteti a kijelzett adatot időben a végétől az eleje felé. Hosszú lenyomással átválthatunk a regisztrált adatok megjelenítése, törlés és a paraméterek beállítása állapotokba. Minden újabb hosszú lenyomással válthatjuk a funkciókat illetve paramétereket. A kijelzőn rövid leírás olvasható, hogy éppen mi történik. Kicsit nehézkes az egyes paraméterek beállítása, mert ennek a nyomógombnak a többszöri hosszú lenyomásával először meg kell keresni a szükséges paramétert, ott rövid + vagy – nyomógomb lenyomásokkal beállítani a kívánt értéket, és várni 10 másodpercet, hogy a készülék vissza kapcsoljon az alap állapotba.

EEPROM adatok törléséhez a MÓD gombot kell hosszan megnyomni kétszer (alap állapotban), és itt megnyomni röviden bármelyik nyomógombot.

A véletlen törlések megakadályozására 5 másodpercig visszaszámol a program, és ha ez alatt nem nyomjuk meg egyik gombot sem, csak akkor lesz végrehajtva a törlés. Ezt azért csináltam ilyen bonyolultra, mert egyik alkalommal nem nyomtam elég hosszú ideig a MÓD gombot, és a következő paraméter beállítása helyett eltöröltem egy teljes nap regisztrációját. Bosszantó hiba, nem akartam még egyszer elkövetni!

A paramétereket (pl. kikapcsolási végfeszültségek) nem lehet egy elindított vizsgálat alatt változtatni. Ilyenkor a MÓD gomb hosszú megnyomása csak a regisztrációs adatok megjelenítésébe visz át bennünket, a törlésbe vagy paraméter állításba már nem lehet eljutni!

 Még egy dolgot kell megemlíteni, az alkalmazott akkumulátor töltőt. Ez tulajdonképpen egy 24V 3A-es tápegység (itt olvashatsz róla részletsebben), amit kínai barátainktól szereztem be. Beállítható rajta a feszültség és a maximális áram. Első körben beállítottam a feszültségét 4,2V-ra (ellenőriztem multiméterrel, tényleg annyi). Rákötöttem az akkumulátor vizsgálóra, és a feszültség szorzó paramétert addig állítgattam, hogy az is 4.2V feszültséget mutasson. Ezzel megtörtént a hitelesítés. Aztán rövidre zártam a tápegységet, és beállítottam valamekkora áramot. Kezdetben 300mA-el kísérleteztem, de ezt többször változtattam. Ha egy akkumulátort töltök, és az le van merülve, maximum 300mA kezd folyni. Amikor az akku töltődik, egy idő után a feszültsége megközelíti a tápegységen beállított feszültséget, és a töltőáram folyamatosan csökkenni kezd. Amikor eléri az akku feszültsége a 4.2V-ot (vagy amit beállítottam végfeszültségnek), az áram értéke 0-ra csökken. A tapasztalatom az, hogy kb. 4.4V feszültséget kellett beállítani a tápegységen, hogy a töltés ne tartson végtelen ideig. Ekkor a 4.2V akku feszültség elérésekor még mindig folyik kb. 100mA áram (egy átlagos 18650-es lítium akku esetén).

Íme az alaposan kommentezett forrásprogram:

#include <EEPROM.h>     //eeprom kezelő könyvtár
#include<Wire.h>        //I2C kommunikáció kezelő könyvtár
#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
    
//******************************INA226 paraméterek, kezeléshez szükséges változók
int i2c_cim=0x44;               //az INA226 
unsigned int fesz;              //a buszfeszültség, ahol egy bit változás 1,25 mV PL. ha ennek értéke 4000, akkor a feszültség 5V
int sont_fesz;                  //a sönt feszültsége, ahol egy bit változás 2,5 mikroV Pl. ha ennek értéke 4000, akkor a söntfesz 0,01V
byte msByte,lsByte;             //segédváltozók a két byte 16 bites számmá alakításához
//******************************az aktuális mérési eredmények változói
long akku_kapacitas=0;          //Az eltelt idő (1sec) és az akkori áram szorzata ebben összegződik a mérés teljes 
                                //időtartamában. A pillanatnyi áramot megszorzom 10-el, hogy egy tizedes pontossággal
                                //összegezzek. Nyilván kijelzéskor osztani fogom 10-el. Ráadásul másodpercenként összegzem 
                                //az áramot, azért ahhoz, hogy mAh-ban kapjam meg az értékét még 3600-al is osztani kell.
                                //Ha nem így tennék, akkor gondot okozhatna a nagyon kicsi mennyiségek összegzése (pl. 1mA/3600=0,0002777)
int pill_fesz;                  //ebben tárolom az aktuális akku feszültséget (INA226 Vbus feszültség). Mivel ez integer, a valós feszültség 1000 szerese
int pill_aram;                  //ebben tárolom az aktuális töltő vagy merítő áramot (előjeles érték),
                                //(INA226 sönt feszültség szorozva a sönt ellenállással, esetünkben 0,1 ohm). Mivel ez integer, a valós feszültség 1000 szerese
float nullazas;                 //ebben tárolom az áram korrekciót, nullára tolja az áramot, ha nincs töltés vagy merítés. 
                                //azt tapasztaltam, hogy ha nincs töltés, akkor egy kicsit eltér a nullától a kijelzett áram. 
                                //Jelenleg -0.15mA áramot akkor is mér, ha nem folyik áram, ezért +0.15mA nullázást kellett beállítani.
int fesz_felbontas;             //a teljes mérési tartományt (max és min kikapcsolási feszültség különbsége) ennyi részre osztom fel. 
                                //Pl. 2.5V-4.2V-ig töltök illetve merítek, és a feszültséget 20 részre osztom, akkor 
                                //(4.2-2.5)/20=0.085V Vagyis 85mV változás esetén regisztrálom az eltelt időt másodpercekben, 
                                //a pillanatnyi áramot, feszültséget és kapacitást
int fesz_lepeskoz;              //ekkora feszültség változáskor történik regisztrálás ((fesz_max-fesz_min)/fesz_felbontás) 
                                //integer értéket tárolok, ezért a kiszámolt érték 1000 szeresét tárolom (fesz_min és fesz_max is integer és 1000szeres érték)
int fesz_min;                   //ennél a feszültségnél kapcsol ki a merítés (lithium aksinál 2,5V), értéke ezerszeres (pl. 2500)
int fesz_max;                   //ennál a feszültségnél kapcsol ki a töltés (lithium aksinál 4,2V), értéke ezerszeres (pl. 4200)
//******************************segédváltozók a működéshez                           
float mAh=0;                    //Az akku kapacitás kijelzéshez segéd változó
unsigned int ido;               //idő múlása másodpercekben. Csak akkor számolok előre másodpercenként, ha rögzítés történik
unsigned int ido_tmp,ora,perc,masodperc; //segédváltozók az eltelt idő kijelzéséhez
long millis_tmp=millis();       //segédváltozó az egy másodperces időzítéshez
long sw1_time;                  //1-es nyomógomb lenyomás idejének figyeléséhez segédváltozó
long sw2_time;                  //2-es nyomógomb lenyomás idejének figyeléséhez segédváltozó
bool sw1_allapot=0;             //1-es nyomógomb lenyomott állapota esetén 1
bool sw2_allapot=0;             //1-es nyomógomb lenyomott állapota esetén 1
long vissza_time;               //visszakapcsolás kijelzés üzemmódba segédváltozója (10 sec időzítéséhez használom)
bool rogzites=0;                //ha adatrögzítés van folyamatban, akkor 1
bool modvalt=0;                 //a kijelző üzzemmódjának változtatását jelzi, ha hosszan nyomom meg a 2-es nyomógombot
bool leptet_fel=0;              //röviden nyomta a nyomógombot, léptetni kell felfele. (a változó értéke a gombnyomáskor 1 lesz, 
                                //amit a szükséges funkció végrehajtása után azonnal 0-ra állítok)
bool leptet_le=0;               //röviden nyomta a nyomógombot, léptetni kell lefele. (a változó értéke a gombnyomáskor 1 lesz, 
                                //amit a szükséges funkció végrehajtása után azonnal 0-ra állítok)
bool ism_tilt1=0;               //1-es gombon a hosszan nyomvatartás ismétlésének akadályozására. Akkor lesz 1, ha még nem engedte el a nyomógombot, de már egyszer letelt a 800msec
bool ism_tilt2=0;               //2-es gombon a hosszan nyomvatartás ismétlésének akadályozására. Akkor lesz 1, ha még nem engedte el a nyomógombot, de már egyszer letelt a 800msec
byte uzemmod=0;                 //kijelző üzemmódjai: 0 foyamtos kijelzés, 1 adatkiolvasás, 2 adatttörlés 3 kikapcsolási feszültség minimum beállítása
                                //4 kikapcsolási feszültség maximum beállítása, 5 feszültség korrekció beállítása, 6 áram korrekció beállítása
                                //7 nullázási érték beállítása, 8 feszültség felbontás darabszám beállítása 
bool frissit=0;                 //Ha értéke 1, akkor frissítjük a képernyőt. Frissítést indíthat a másodpercenkénti mérés, és a nyomógombok rövid,
                                //vagy hosszú lenyomása. A frissítés teendői után értékét 0-ra állítom vissza, így várakozik a program a következő eseményre
int xadat_int;                  //eeprom-ba íráshoz és kiolvasáshoz segéd változó
int xfesz,xaram,xmAh,xido;      //eeprom-ba íráshoz és kiolvasáshoz segéd változók
int yfesz,yaram,ymAh,yido;      //eeprom-ba íráshoz és kiolvasáshoz segéd változók
int kiolv_sorsz=0;              //adat kiolvasáskor annak az adatnak a sorszáma, amit szeretnék kiolvasni és képernyőn megjeleníteni
int akt_sorsz=0;                //mivel mindig végig olvasom az eeprom tartalmat, az aktuális, éppen olvasott rekord sorszámát mutatja
int akt_cim=16;                 //Ez a változó tárolja az éppen aktuálisan olvasott adatrekord címét az eeprom-ban
int elozo_sorsz=0;              //ahhoz kell, hogy ne írogasson folyton a képernyőre adat kiolvasáskor
byte torl_kesl=6;               //adat törléskor ezzel számlálunk a törlés előtt, hogy meggondolhassa magát (véletlen törlés megakadályozása véletlen rövid gombnyomás esetére
                                //6 a nyugalmi állapot, ha egy gombot megnyom, akkor értékét 0-ra állítjuk, ekkor indul a számlálás 5-ig, amikor bekövetkezik a törlés.
                                //Ha közben nyomógombot nyom, akkor értékét újra 6-ra állítjuk.
int utolso_fesz;                //ha van már tárolt adat, vagy épp tárolás folyik, ehhez képest nézi a feszültség változást, és ha az több mint fesz_lepeskoz, 
                                //akkor tárolja az akt adatot
byte fo_uzemmod=0;              //a töltés, merítés és ciklikus töltés merítés üzemmódja (0=töltés, 1=merítés, 2=ciklikus)
bool sorosport=0;               //ezzel jelezzük, hogy a sorosportra is ki kell küldeni a regisztrált adatokat
byte ciklus_fazis=0;            //ciklikus vizsgálat egyes lépéseinek elkülönítésére
int varakozas=0;                //segédváltozó ahhoz, hogy a ciklikus vizsgálat esetén 10 percet (600másodpercet) várakozzak a töltés illetve merítés után
long vilagitas_ido;             //a háttérvilágítás lekapcsolásához szükséges időt méri (1perc)

//eeprom térkép
//Cím     Adat funkciója
//-----   -------------------------
//14,15 - aktuális rögzítési idő sec-ben (int), max. 18 óra rögzítési idő
//16-19 - az aktuális rögzített akku kapacitás (hogy folytatni lehessen az összegzést
//20,21 - leállítási minimum (0.0-tól 36.0-ig azaz 0-360-ig)
//22,23 - leállítási maximum (0.0-tól 36.0-ig azaz 0-360-ig)
//24,25 - korrekciós tényező feszültség méréshez (int)
//26,27 - korrekciós tényező sönt feszültséghez (int)
//28,29 - nullázási eltolás (int)
//30,31 - regisztrációs felbontás (int)5-40
// adatrekordok felépítésé
//a 0. rekord kezdőcíme 32 (a megadott adat címek relatív címek pl. az első adat a 32 címen kezdődik és 39-en van vége)
//Cím   Adat funkciója
//---   ---------------------------
//0,1 - rögzítés kezdete óta eltelt idő másodpercben (unsigend int azaz 65535/3600-> kb 18 óra max rögzítési idő töltés merítés esetén külön külön ciklikus vizsgálatnál)
//2,3 - ektuális feszültség mV-ban (max 65V)
//4,5 - aktuális áram 0,1mA felbontásban (max. 3200.0mA)
//6,7 - aktuáis töltési kapacitás szint mAh-ban (max. 65000mAh)


void setup() {
  Wire.begin();                       // I2C kommunikáció inicializálása, nincs cím, ezért master
  Serial.begin(9600);                 //soros port inicializálása
  Serial.println(F("Indul..."));      
  lcd.begin(20,4);                    //LCD inicializálása
  lcd.backlight();                    //háttérvilágítás bekapcsolása
  lcd.setCursor(0,0);
  lcd_uniprint((String)"");           //ékezetek feltöltése a képernyőre
  
  //INA226 modul beállítása (nem használok kész könyvtárat, elemi parancsokkal állítom be)
  Wire.beginTransmission(i2c_cim);
  if(Wire.endTransmission()){
    Serial.println(F("INA226 nincs jelen"));
  }
  //configurációs regiszter bitek
  //D15-reset
  //D11-D9 - átlagolásszám, Értékek: 1=0b00,4=0b001,16=0b010,64=0b011,128=0b100,256=0b101,512=0b110,1024=0b111
  //D8-D6 - Vbus konv. idő, Értékek: 140us=0b000,204us=0b001,332us=0b010,588us=0b011,1100us=0b100,2116us=0b101,4156us=0b110,8244us=0b111
  //D5-D3 - Shönt konv. idő, Értékek: 140us=0b000,204us=0b001,332us=0b010,588us=0b011,1100us=0b100,2116us=0b101,4156us=0b110,8244us=0b111
  //D2-D0 - Működési mód: folyamatos mérés=0b111, indított mérés=0b011,
  //chip alaphelyzetbe állítása
  Wire.beginTransmission(i2c_cim);  //megszólítjuk az eszközt
  Wire.write(0x00);                 //a státus regisztert fogjuk írni
  Wire.write(0x80);                 //beállítjuk a D15 reset bitet 1-re, többi bit közömbös
  Wire.write(0x00);                 //a státus regiszter 16 bites, ezért kell egy következő byte-ot is írni
  Wire.endTransmission();           //vége a kommunikációnak
  //Beállítjuk a chip mérési paramétereit: (jelen esetben folyamatos mérés, átlagolás 4, mérési idő 1,1ms)
  Wire.beginTransmission(i2c_cim);  //megszólítjuk az eszközt
  Wire.write(0x00);                 //a státus regisztert fogjuk írni
  //segéd ábra a paraméter bitek megtalálásához
  //0b0000aaav     a-átlagolási idő bitek, v-Vbus konverziós idő
  //0bvvsssmmm     v-Vbus konverziós idő, s-sönt konverziós idő, m-működési mód
  Wire.write(0b00000011);           //két byte-on küldünk ki minden adatot, pont a Vbus konv. időnél
  Wire.write(0b00100111);           //törte ketté
  Wire.endTransmission();
  delay(20);                        //hagyunk időt eg mBgzltn58#érésre, különben a legelső eredmény kiolvasás eredménye 0-lesz

  pinMode(2,INPUT);                 //A 2-es kivezetést bemenetnek fogjuk használni, ez lesz az 1-es kapcsoló (+ illetve STAR/STOP)
                                    //1-es kapcsoló hosszan nyomva (0,8sec) start illetve stop, klikkre lépteti a regisztrált adatokat időben visszavefé
  digitalWrite(2,HIGH);             //felhúzó ellenállás be
  pinMode(3,INPUT);                 //A 3-es kivezetést bemenetnek fogjuk használni, ez lesz az 2-es kapcsoló (- illetve MÓD választó)
                                    //2-es kapcsoló hosszan nyomva (0,8 sec) megjelenítésbe léptet, klikkre lépteti a regisztrált adatokat időben előre 
  digitalWrite(3,HIGH);             //felhúzó ellenállás be
  pinMode(4,OUTPUT);                //Relé kimenet, ami a töltést, vagy a merítést ki és bekapcsolja 
  digitalWrite(4,HIGH);             //a relé így nincs meghúzva
  pinMode(5,OUTPUT);                //Relé kimenet, ami a töltő feszültséget, vagy a terhelést üzemmódot állítja be 
  digitalWrite(5,LOW);              //a relé így nincs meghúzva (töltés)
  pinMode(6,INPUT);                 //viágítás kapcsoló, felkapcsolja a kijelző háttérvilágítást (a többi is felkapcsolja, de ez nem változtat semmin) 
  digitalWrite(6,HIGH);             //felhúzó ellenállás be
  if (digitalRead(2)==LOW) {             //alapértékek beállítása
    //tárolt működési paraméterek alapértékre állítása
    EEPROM.put(14,(unsigned int)0);      //aktuális rögzítési idő 0 (ha a töltést vagy a merítés leállítjuk, akkor inen lehet folytatni (ciklikus vizsgálatot nem lehet folytatni)
    EEPROM.put(16,(long) 0);             //aktuális akku kapacitás 0 (ha a töltést vagy a merítés leállítjuk, akkor inen lehet folytatni (ciklikus vizsgálatot nem lehet folytatni)
    EEPROM.put(20,(unsigned int) 2500);  //feszültség minimum (ennél a feszültségnél leáll a merítés)
    EEPROM.put(22,(unsigned int) 4200);  //feszültség maximum (ennél a feszültségnél leáll a töltés)
    EEPROM.put(24,(unsigned int) 9526);  //feszültség korrekció (a mért feszültséget ezzel a számmal szorozzuk, ezzel hitelesíthetjük a mért feszültséget)
    EEPROM.put(26,(unsigned int) 11900); //áram korrekció (a mért sönt feszültséget ezzel a számmal szorozzuk, ezzel hitelesíthetjük a mért sönt feszültséget)
    EEPROM.put(28,(unsigned int) 0);     //nullázás (-1mA-től +1mA-i század pontossággal)
    EEPROM.put(30,(unsigned int) 20);    //feszültség felbontás (a min és max értékek között ennyi pontra bontja fel a regisztrálandó pontokat)
    //adatrekord kezdeti beállítása
    EEPROM.put(32,(unsigned int) 0);              //az adatterület 0. rekordja 0, azaz nincs adat, mert már az első rekord 0
    EEPROM.put(34,(int) 0);
    EEPROM.put(36,(int) 0);
    EEPROM.put(38,(int) 0);
    //ciklikus vizsgálatnál az egyes töltések és merítések során regisztrált akkumulátor kapacitás értékek törlése
    EEPROM.put(1010,(int) 0);   //töltés adatok ciklikus vizsgálat esetén
    EEPROM.put(1012,(int) 0);
    EEPROM.put(1014,(int) 0);
    EEPROM.put(1016,(int) 0);   //merítés adatok ciklikus vizsgálat esetén
    EEPROM.put(1018,(int) 0);
    EEPROM.put(1020,(int) 0);

    lcd.clear();
    lcd.setCursor(0,0);                        
    lcd_uniprint((String)F("Alapérték beállítva!"));
    delay(3000);
    lcd.clear();
  }

  //tárolt működési paraméterek kiírása a képernyőre
  EEPROM.get(20,xadat_int);           //feszültség minimum aktuális értékének kiolvasása
  fesz_min=xadat_int;
  EEPROM.get(22,xadat_int);           //feszültség maximum aktuális értékének kiolvasása
  fesz_max=xadat_int;
  lcd.setCursor(0,0);
  lcd.print(F("Fesz:"));
  lcd.print((float)fesz_min/1000,3);  //a feszültség minimum 1000 szeresét tároljuk, így osztani kell ezerrel
  lcd.print("-");
  lcd.print((float)fesz_max/1000,3);  //a feszültség maximum 1000 szeresét tároljuk, így osztani kell ezerrel
  EEPROM.get(30,xadat_int);           //feszültség felbontás aktuális értékének kiolvasása
  fesz_felbontas=xadat_int;
  lcd.print(" V");
  lcd.setCursor(0,1);                        
  lcd_uniprint((String)F("Lépés:"));
  lcd.print(xadat_int);
  fesz_lepeskoz=(fesz_max-fesz_min)/fesz_felbontas; //feszültség lépésköz kiszámítása (ezt minden töltés vagy merítés 
                                                    //indításakor újra számoljuk, mert a feltöltött vagy lemerített akkumulátor 
                                                    //pihentetés után kisebb illetve nagyobb feszültségre áll be, és azt tekintjük alapnak
  lcd.print("(");
  lcd.print((float)fesz_lepeskoz/1000,3);           //aktuális feszültség lépésköz 1000 szeresét tároltuk, ezért osztani kell ezerrel
  lcd.print("V)");
  EEPROM.get(24,xadat_int);                         //feszültség szorzó kiolvasása
  lcd.setCursor(0,2);                        
  lcd_uniprint((String)F("V szorzó:"));
  lcd.print((float)xadat_int/10000,4);              //a feszültség szorzó értéke egy 1 körüli szám, és ennek 10000 szeresét tároljuk, így kijelzéshez osztani kell 10000-el
  EEPROM.get(26,xadat_int);
  lcd.setCursor(0,3);                        
  lcd_uniprint((String)F("A szorzó:"));
  lcd.print((float)xadat_int/10000,4);              //a sönt feszültség (áram) szorzó értéke egy 1 körüli szám, és ennek 10000 szeresét tároljuk, így kijelzéshez osztani kell 10000-el
  delay(5000);
  
  lcd.clear();
  EEPROM.get(28,xadat_int);                         //nullázás tárolt értékének kiolvasása
  nullazas=(float)xadat_int/100;                    //ezt az értéket mindig hozzáadjuk a mért és számított áram értékhez. Mivel 100 szorost tároltunk, 
                                                    //ezért kell 100-al osztani és float-ra alakítani
  lcd.setCursor(0,0);                        
  lcd_uniprint((String)F("Nullázás:"));
  lcd.print(nullazas,2);
  lcd.print(" mA");
  akt_cim=32;   //ez a legelső adatrekord kezdete
  xadat_int=0;
  utolso_fesz=0;
  //megszámoljuk a tárolt adat rekordok számát
  do {
    EEPROM.get(akt_cim,xfesz);
    EEPROM.get(akt_cim+2,xaram);
    EEPROM.get(akt_cim+4,xmAh);
    EEPROM.get(akt_cim+6,xido);
    if (xfesz!=0) {utolso_fesz=xfesz;}  //a ciklus végén megkapjuk az utolsó tárolt feszültség adatot
    akt_cim=akt_cim+8;
    xadat_int++;
    if (akt_cim>1008) {break;}
  } while (xfesz!=0 or xaram !=0 or xmAh!=0 or xido!=0);  //akkor értünk az utolsó rekordhoz, ha mind a négy érték 0
  
  lcd.setCursor(0,1);                        
  lcd.print("Reg.adat:");lcd.print(xadat_int-1);lcd.print(" db");
  delay(5000);

  lcd.clear();
  vilagitas_ido=millis();
}


void loop() {
  if (vilagitas_ido+60000<millis()) {lcd.noBacklight();}             //Kikapcsoljuk a kijelző háttérvilágítását, ha egy perce nem 
                                                                     //lett nyomógomb megnyomva (nyomógomb lenyomása vilagitas_ido=millis())
  if (digitalRead(6)==LOW) {lcd.backlight();vilagitas_ido=millis();} //megnyomtuk a világítás felkapcsoló nyomógombot
  if (digitalRead(2)==LOW and sw1_allapot==0) {                      //megnyomtuk az 1-es gombot, indul az időzítés, hogy megtudjuk rövid vagy hosszú lenyomás lesz
    ism_tilt1=0;sw1_allapot=1;sw1_time=millis();
    vissza_time=millis();lcd.backlight();vilagitas_ido=millis();
  }   
  if (digitalRead(2)==LOW and sw1_time+800<millis() and sw1_allapot==1 and ism_tilt1==0) {      //ha hosszan nyomvatartotta, 
                                                                                                //indítjuk vagy leállítjuk a fő üzemmeód által beállított
                                                                                                //töltést, merítést vagy ciklikus vizsgálatot
    lcd.backlight();vilagitas_ido=millis();
    ism_tilt1=1;
    //**************************** ez egyszeri töltés üzemmód *******************************  
    if (fo_uzemmod==0) {             
      if (rogzites==0) {                 //indítjuk a rögzítést, mert jelenleg áll     
        EEPROM.get(14,ido);              //kiolvassuk az eddigi rögzítési időt (ha 0 akkor új rögzítés lesz, ha nem nulla, folytatjuk a töltést)
        EEPROM.get(16,akku_kapacitas);   //kiolvassuk az eddigi töltéskor tárolt akku kapacitást
        rogzites=1;                      //ez fogja indítani a töltést
        if (ido==0) {                    //most kezdjük a töltést, újra kell számolni a pillanatnyi feszültség alapján a felosztást
          fesz_lepeskoz=(fesz_max-pill_fesz)/fesz_felbontas;
          adat_iras(pill_fesz,pill_aram,(int)(akku_kapacitas/3600),ido);   //tároljuk a kezdeti adatokat (mielőtt rákapcsoljuk az akkura a töltőt)
          utolso_fesz=pill_fesz;
        }
      } 
      else {                             //leállítjuk a töltést
        EEPROM.put(14,ido);              //tároljuk az aktuális töltési időt
        EEPROM.put(16,akku_kapacitas);   //tároljuk az aktuális kapacitást
        rogzites=0;                      //ez állítja le a rögzítést
      }
    }
    //**************************** ez egyszeri merítés üzemmód *******************************  
    if (fo_uzemmod==1) {           
      if (rogzites==0) {                 //indítjuk a rögzítést, mert jelenleg áll
        EEPROM.get(14,ido);              //kiolvassuk az eddigi rögzítési időt (ha 0 akkor új rögzítés lesz, ha nem nulla, folytatjuk a merítést)
        EEPROM.get(16,akku_kapacitas);   //kiolvassuk az eddigi merítéskor tárolt akku kapacitást
        rogzites=1;                      //ez fogja indítani a merítést
        if (ido==0) {                    //most kezdjük a merítést, újra kell számolni a pillanatnyi feszültség alapján a felosztást
          fesz_lepeskoz=(pill_fesz-fesz_min)/fesz_felbontas;
          adat_iras(pill_fesz,pill_aram,(int)(akku_kapacitas/36000),ido);   //tároljuk a kezdeti adatokat (mielőtt rákapcsoljuk az akkura a terhelést)
          utolso_fesz=pill_fesz;
        }
      } 
      else {                            //leállítjuk a merítést
        EEPROM.put(14,ido);             //tároljuk az aktuális merítési időt
        EEPROM.put(16,akku_kapacitas);  //tároljuk az aktuális kapacitást
        rogzites=0;                     //ez állítja le a rögzítést
      }
    }
    
    //**************************** ez egy ciklikus vizsgálat üzemmód *******************************  
    if (fo_uzemmod==2) {           
      if (rogzites==0) {                   //indítjuk a rögzítést, mert jelenleg áll (törlünk minden adatot)
        EEPROM.put(14,(unsigned int)0);    //ido törlése
        EEPROM.put(16,(long)0);            //akku kapacitás törlése
        EEPROM.put(32,(unsigned int) 0);   //az adatterület 0. rekordja 0, azaz nincs adat
        EEPROM.put(34,(unsigned int) 0);
        EEPROM.put(36,(unsigned int) 0);
        EEPROM.put(38,(unsigned int) 0);
        EEPROM.put(1010,(int) 0);         //töltés adatok ciklikus vizsgálat esetén
        EEPROM.put(1012,(int) 0);
        EEPROM.put(1014,(int) 0);
        EEPROM.put(1016,(int) 0);         //merítés adatok ciklikus vizsgálat esetén
        EEPROM.put(1018,(int) 0);
        EEPROM.put(1020,(int) 0);
        fesz_lepeskoz=(fesz_max-fesz_min)/fesz_felbontas;  //itt a min és max. határok között fogunk mozogni
        ciklus_fazis=0;                                    //a ciklikus vizsgálatnak 12 fázisa van: (0) kezdeti teljes feltöltés
                                                           //(1) teljes merítés1, (2) szünet, (3) teljes töltés1, (4) szünet
                                                           //(5) teljes merítés2, (6) szünet, (7) teljes töltés2, (8) szünet
                                                           //(9) teljes merítés3, (10) szünet, (11) teljes töltés3 (12)
        akku_kapacitas=0;                                  //akku kapacitást töröljük (minden ciklus végén)
        rogzites=1;                                        //ezzel indul a rögzítés
        varakozas=0;                                       //a következő szünetnél ez fog 600-ig számolni
      }
      else {                              //leállítottuk a ciklikus vizsgálatot. Nem lehet újra indítani, csak újra kezdeni
        rogzites=0;                       //ezzel áll le a rögzítés
        digitalWrite(4,HIGH);             //kikapcsoljuk a relét, amin keresztül eddig töltöttük vagy merítettük az akkut (meghúzva földre kötötte a nagatív pontját)
      }
    }

  }
  if (digitalRead(2)==HIGH and sw1_allapot==1) {                     //már nem nyomja az 1-es nyomógombot
    sw1_allapot=0;
    if (millis()-sw1_time<800 and millis()-sw1_time>20) {            //ha 20ms-nél tovább nyomta, de kevesebb mint 800msec-ig, akkor léptetni kell
      leptet_fel=1;frissit=1;lcd.backlight();vilagitas_ido=millis(); //frissíteni kell, leptetni kell, bekapcsoljuk a háttérvilágítást, elindítjuk a világítás kikapcsolás időzítést
    }
  }  

  if (digitalRead(3)==LOW and sw2_allapot==0) {                      //megnyomtuk az 2-es gombot, indul az időzítés, hogy megtudjuk rövid vagy hosszú lenyomás lesz
    ism_tilt2=0;sw2_allapot=1;
    sw2_time=millis();vissza_time=millis();
  }                                        
  if (digitalRead(3)==LOW and sw2_time+800<millis() and sw2_allapot==1 and ism_tilt2==0) {      //ha hosszan nyomvatartotta, akkor üzemmódot váltunk, vagy üzemmódból visszalépünk
    lcd.backlight();vilagitas_ido=millis();                                                     //bekapcsoljuk a háttérvilágítást
    ism_tilt2=1;uzemmod=uzemmod+1;frissit=1;                                                    //üzemmód változót növeljük
    if (rogzites==1 and uzemmod>1) {uzemmod=0;}                                                 //ha rögzítésben vagyunk, akkor nem lehet törölni, paraméter állítani stb.
                                                                                                //csak az adatlekérdezést engedjük meg (1-es üzemmód)
    if (uzemmod==9) {uzemmod=0;}                                                                //8. üzemmód után újra 0 jön
    if (uzemmod==1) {leptet_le=1;kiolv_sorsz=1;sorosport=1;}                                    //ha adatlekérdezésbe kapcsoltunk, akkor kell egyetléptetni is, 
                                                                                                //ki kell küldeni a sorosportra az eddig rögzített adatokat (sorosport=1)
  }   
  if (digitalRead(3)==HIGH and sw2_allapot==1) {             //már nem nyomja a 2-es nyomógombot
    sw2_allapot=0;
    if (millis()-sw2_time<800 and millis()-sw2_time>20) {    //ha 20ms-nél tovább nyomta, de kevesebb mint 800msec-ig,
      lcd.backlight();vilagitas_ido=millis();                //bekapcsoljuk a háttérvilágítást
      leptet_le=1;frissit=1;                                 //leptetni kell és frissíteni a képernyőt
    }
    if (rogzites==0 and uzemmod==0) {                        //épp nem rögzítünk és 0. (aktuális mérési eredmények kijelzése)üzemmódban vagyunk
      fo_uzemmod++; if (fo_uzemmod>2) {fo_uzemmod=0;}        //váltani foguk minden gombnyomással a fő üzemmódot
      switch (fo_uzemmod) {
        case 0:                   //töltés
          digitalWrite(5,LOW);    //a relé így meg van húzva (töltés)
          break;
        case 1:                   //merítés
          digitalWrite(5,HIGH);   //a relé így nincs meghúzva (merítés)
          break;
        case 2:                   //ciklikus vizsgálat
          digitalWrite(5,LOW);    //a relé így meg van húzva (töltéssel kezdődik a ciklus)
          break;
      }
      leptet_le=0;frissit=1;      //végrehajtottuk amit kell töröljük a frissites és a leptet_le változót, hogy érzékelhessük a következő gombnyoást
    }
  }  

  if (millis()>vissza_time+10000 and uzemmod>1) {uzemmod=0;frissit=1;}   //legalább 10 másodperce nem nyúltunk nyomógombhoz, és nem 0 vagy 1 üzemmódban vagyunk
                                                                         //paraméter beállításból így 10 másodperc tétlenség után visszajutunk a folyamatos mérésbe
  if (millis()>vissza_time+20000 and uzemmod==1) {uzemmod=0;frissit=1;}   //legalább 20 másodperce nem nyúltunk nyomógombhoz, és nem 1-es üzemmódban vagyunk
                                                                         //adat megjelenítésből így 20 másodperc tétlenség után visszajutunk a folyamatos mérésbe

  if (rogzites==1 and fo_uzemmod!=2) {digitalWrite(4,LOW);}                     //elindítottuk a rögzítést, bekapcsoljuk a relét, mert nem ciklikus vizsgálat van
  if (rogzites==0 and fo_uzemmod!=2) {digitalWrite(4,HIGH);}                    //leállítottuk a rögzítést, bekapcsoljuk a relét, mert nem ciklikus vizsgálat van
  if (rogzites==1 and fo_uzemmod==2 and ciklus_fazis==0) {digitalWrite(4,LOW);} //ciklikus vizsgálat 0. fázisában a rögzítés indításakor bekapcsoljuk a relét 
                                                                                //(további fázisok majd kapcsolgatják amikor kell)

  //egy másodpercenként mérünk egyet, és növeljük a mérési időt
  if (millis()>millis_tmp+1000) {   //csak 1000ms után csinálunk mérést és kijelző frissítést
    millis_tmp=millis();
    //***************** Kiolvassuk az INA226-ból a sönt feszültség értéket *****************************
    Wire.beginTransmission(i2c_cim);  //megszólítjuk az eszközt
    Wire.write(0x01);                 //a sönt feszültség regisztert fogjuk olvasni
    Wire.endTransmission();           //kommunikáció lezárása
    Wire.requestFrom(i2c_cim,2);      //2 byte-ot várunk az INA226-tól (16 bites a regiszter)
    if(Wire.available()){             
      msByte=Wire.read();             //kiolvassuk az első byte-ot
      lsByte=Wire.read();             //kiolvassuk a második byte-ot
      sont_fesz=(msByte<<8)+lsByte;   //a két byte-ból csinálunk egy 16 bites számot, és ez az eredmény
      if (sont_fesz>=65530) {sont_fesz=0;}  //időnként hatalmas negatív értékeket adott vissza, ezt kiszűrtem, mert nyilván valamilyen rossz mérés eredménye
    }
    //***************** Kiolvassuk az INA226-ból a feszültség értéket *****************************
    Wire.beginTransmission(i2c_cim);  //megszólítjuk az eszközt
    Wire.write(0x02);                 //a Vbus feszültség regisztert fogjuk olvasni
    Wire.endTransmission();           //kommunikáció lezárása
    Wire.requestFrom(i2c_cim,2);      //2 byte-ot várunk az INA226-tól (16 bites a regiszter)
    if(Wire.available()){
      msByte=Wire.read();             //kiolvassuk az első byte-ot
      lsByte=Wire.read();             //kiolvassuk a második byte-ot
      fesz=(msByte<<8)+lsByte;        //a két byte-ból csinálunk egy 16 bites számot, és ez az eredmény
    }
    
    if (uzemmod==0 or uzemmod==2) {frissit=1;}                        //frissíteni kell a képernyőt, mert új adatok vannak csak ha az uzemmód 0 (kijelezzük a mért eredményt)
                                                                      //vagy ha az uzemmód 2 (törlés), mert utóbbi esetben másodperces visszaszámláláshoz kell a frissítés
    EEPROM.get(24,xadat_int);                             //feszültségszorzó (korrekció) tárolt értékének kiolvasása az eeprom-ból

    pill_fesz=round(((float)fesz*1.25*xadat_int)/10000);  //három tizedesjegy pontosságra kerekítem, és szorzom 1000-el, 
                                                          //így egy integer-ben kapom meg a feszültséget. Megjelenítés előtt 
                                                          //1000-el osztani kell majd, tároláshoz pont megfelelő

    EEPROM.get(26,xadat_int);                                                //áramszorzó (korrekció) tárolt értékének kiolvasása az eepromból
    pill_aram=round((float)sont_fesz*0.00025*(xadat_int/10)+(nullazas*10));  //pillanatnyiáram mA tízszeresében, hogy egy tizedesjegy pontossággal
                                                                             //tároljam. Megjelenítéskor 10-el osztani kell

    //**************************** teendők rögzítés alatt *********************************************
    if (rogzites==1) {                                    
      ido=ido+1;                                          //csak rögzítéskor növeljük az eltelt időt
      akku_kapacitas=akku_kapacitas+(long)(pill_aram);    //itt osztani kellene az áram mA értéket 3600-al (így lenne mAh a mértékegység) 
                                                          //de a float pontossága miatt nem itt célszerű, hanem a kijelzésnél illetve a tárolásnál, hogy 
                                                          //nagyobb számokkal dolgozzak és ne okozzon hibát. Az akku kapacitást double típusban gyűjtöm, 
                                                          //mert így igen nagy értékeket is kaphatok
                                          
      if (abs(utolso_fesz-pill_fesz)>fesz_lepeskoz) {
        adat_iras(pill_fesz,pill_aram,(int)(akku_kapacitas/3600),ido);   //tároljuk az adatokat mert a lépésköznél nagyobb mértékben változott a feszültség
        utolso_fesz=pill_fesz;
      }
      if (pill_fesz>fesz_max and fo_uzemmod==0) {                        //kilépünk a töltésből, ha a feszültség nagyobb mint a megengedett maximum
        adat_iras(pill_fesz,pill_aram,(int)(akku_kapacitas/3600),ido);   //tároljuk az adatokat, mert vége a töltésnek
        utolso_fesz=pill_fesz;
        rogzites=0;                                                      //leállítjuk a töltést
      }   
      if (pill_fesz<fesz_min and fo_uzemmod==1) {                        //kilépünk a merítésből, ha a feszültség kisebb mint a megengedett minimum
        adat_iras(pill_fesz,pill_aram,(int)(akku_kapacitas/3600),ido);   //tároljuk az adatokat, mert vége a merítésnek
        utolso_fesz=pill_fesz;
        rogzites=0;                                                      //leállítjuk a merítést
      }   
      //************** ciklikus töltés teendői rögzítés alatt (fázisok váltása, relék vezérlése, várakozások ********************************
      if (fo_uzemmod==2 and (ciklus_fazis==1 or ciklus_fazis==3 or ciklus_fazis==5 or ciklus_fazis==7 or ciklus_fazis==9 or ciklus_fazis==11)) {   //varakozási cilusban vagyunk, várjuk a végét
        varakozas++;
        if (varakozas>600) {                                               //vége a várakozásnak, letelt a 10 perc akku pihentetés
          adat_iras(pill_fesz,pill_aram,(int)(akku_kapacitas/3600),ido);   //tároljuk az adatokat, mert vége a töltésnek
          utolso_fesz=pill_fesz;
          ciklus_fazis++;
          digitalWrite(4,LOW);
          varakozas=0;
          ido=0;
        }
      }

      if (pill_fesz>fesz_max and fo_uzemmod==2 and (ciklus_fazis==0 or ciklus_fazis==4 or ciklus_fazis==8)) {    //kilépünk a töltési ciklusból, ha a feszültség nagyobb mint a megengedett maximum
        adat_iras(pill_fesz,pill_aram,(int)(akku_kapacitas/3600),ido);   //tároljuk az adatokat, mert vége a töltésnek
        utolso_fesz=pill_fesz;
        switch (ciklus_fazis) {
          case 4:
            EEPROM.put(1010,(int)(akku_kapacitas/3600));   //töltési szumma adat regisztrálása
            break;
          case 8:
            EEPROM.put(1012,(int)(akku_kapacitas/3600));   //töltési szumma adat regisztrálása
            break;
        }
        digitalWrite(5,HIGH);   //a relé így meghúzva (merítés)
        ciklus_fazis++;
        akku_kapacitas=0;
        ido=0;
        digitalWrite(4,HIGH);
      }   
      if (pill_fesz<fesz_min and fo_uzemmod==2 and (ciklus_fazis==2 or ciklus_fazis==6 or ciklus_fazis==10)) {    //kilépünk a merítési ciklusból, ha a feszültség kisebb mint a megengedett minimum
        adat_iras(pill_fesz,pill_aram,(int)(akku_kapacitas/3600),ido);   //tároljuk az adatokat, mert vége a merítésnek
        utolso_fesz=pill_fesz;
        switch (ciklus_fazis) {
          case 2:
            EEPROM.put(1016,(int)(akku_kapacitas/3600));   //töltési szumma adat regisztrálása
            break;
          case 6:
            EEPROM.put(1018,(int)(akku_kapacitas/3600));   //töltési szumma adat regisztrálása
            break;
          case 10:
            EEPROM.put(1020,(int)(akku_kapacitas/3600));   //töltési szumma adat regisztrálása
            break;
        }
        digitalWrite(5,LOW);                               //a relé így nincs meghúzva (töltés)
        ciklus_fazis++;
        akku_kapacitas=0;
        ido=0;
        digitalWrite(4,HIGH);
      }   
      if (pill_fesz>fesz_max and fo_uzemmod==2 and ciklus_fazis==12) {   //kilépünk a töltésből, ha a feszültség nagyobb mint a megengedett maximum
        adat_iras(pill_fesz,pill_aram,(int)(akku_kapacitas/3600),ido);   //tároljuk az adatokat, mert vége a töltésnek
        utolso_fesz=pill_fesz;
        EEPROM.put(1014,(int)(akku_kapacitas/3600));                     //töltési szumma adat regisztrálása
        rogzites=0;
        digitalWrite(4,HIGH);
      }   
    } 
  }
  /*****************************************************************************************************************************
   *  Ebben a programrészben a frissit változó egy értéke esetén újra írjuk a képernyőre az aktuális adatokat.                 *
   *  Az aktulális adat minden üzemmódban más. 0. üzemmódban a mérési eredményeket frissítjük                                  *
   *                                           1. üzemmódban fel le léptethetjük a rögzített adatokat (eeprom-ból)             *
   *                                           2. üzemmódban törölhetjük az adatokat (5 másodperc biztonsági várakozással      *
   *                                           3. üzemmódban beállíthatjuk a merítés minimális akku feszültségét (minden       *
   *                                                                  gombnyomással változik a feszültség értéke)              *
   *                                           4. üzemmódban beállíthatjuk a töltés maximális akku feszültségét (minden        *
   *                                                                  gombnyomással változik a feszültség értéke)              *
   *                                           5. üzemmódban beállíthatunk egy feszültség szorzót, amivel hitelesíthetjük      *
   *                                                                  a mért akkumulátor feszültséget. Megmérjük a             *
   *                                                                  tényleges feszültséget, és az INA226 által mért          *
   *                                                                  feszültséget szorozhatjuk az itt beállított számmal      *
   *                                           6. üzemmódban beállíthatunk egy áram szorzót, amivel hitelesíthetjük            *
   *                                                                  a mért áramot. Megmérjük a tényleges áramot, és az       *
   *                                                                  INA226 által mért söntfeszültség és sötellenállás        *
   *                                                                  szorzatából számolt feszültséget ezzel a számmal         *
   *                                                                  szorozhatjuk, hogy a mért és számított áram megegyezzen  *
   *                                           7. üzemmódban beállíthatunk egy nulla áramot. Azt tapasztaltam, hogy amikor     *
   *                                                                  nem folyik át áram a söntellenálláson, akkor is mér az   *
   *                                                                  INA226 egy kicsi söntfeszültséget, ami esetemben kb.     *
   *                                                                  -0.15mA-nek adódott. Ezt lehet egy itt beállítható       *
   *                                                                  értékkel nullázni működés közben                         *
   *                                           8. üzemmódban beállíthatjuk, hogy hány részre bontsuk fel az akku induló        *
   *                                                                  feszültsége és a végfeszültsége közötti tartományt. Ha   *
   *                                                                  mérünk, és az előző regisztrált adat óta nagyobb volt    *
   *                                                                  a feszültség változás, akkor újra regisztrálunk.         *
   *  Az üzemmódok a 2-es nyomógomb hosszú megnyomásával lehet váltani. Az üzemmódokban csak akkor 0kerül a program ezekre     *
   *  a programrészekre, ha a frissit változót 1-re állítja egy gombnyomás, vagy egy mérés. A 0. üzemmódban a mérés            *
   *  generálja a frissítést, a többi üzemmódban a gombnyomások. Az egyszerűség érdekében a gombnyomások az adott értéket      *
   *  Azonnal meg is növelik vagy csökkentik, és az új érték kijelzésre kerül. Tehát az adott EEPROM cellákat minden           *
   *  paraméter állítás írja, az érték azonnal tárolódik is. Egy paramétert csak 100.000-szer változtathatunk meg!             *
   *  Az adat törlés üzemmód kicsit más, mert itt a rövid gombnyomásból eredő véletlen törlést egy visszaszámlálással          *
   *  akadályoztam meg. A visszaszámlálás alatt megszakítható a törlés. A visszaszámláláshoz az egy másodperces mérés          *
   *  időpontjai indítanak a frissit változó 1-re állításával, tehát itt nem csak a gombnyomás akciózik.                       *
   *  A mérések és az esetleges adat regisztrálás az 1-es üzemmódban történő regisztrált adat megtekintés közben is            *
   *  zavartalanul folyik tovább, mert a loop folyamatosan fut, nincs sehol delay().                                           *
   *****************************************************************************************************************************/
  
  if (frissit==1) {
   frissit=0;  //akkor frissítünk, ha mérés történik, vagy lenyomtuk valamelyik nyomógombot (a frissit változót méréskor vagy gombnyomáskor 1-re állítjuk)
   switch (uzemmod) {
      case 0:
        frissit=0;                                      //miután megtörtént a kijelzés frissítése vagy a paraméter változtatása, ezzel akadályozzuk meg, 
                                                        //hogy a következő loop ciklusban újra elinduljon 
//        if (leptet_le==1) {uzemmod=1;break;}           //Ha röviden megnyomta 
        if (leptet_fel==1) {uzemmod=1;break;}            //Ha röviden megnyomta a 1-es nyomógombot (Start), akkor átváltunk regisztrált adat megtekintés üzemmódba
                                                        //(a 2-es a nyomógombnak (Mód) a hosszú lenyomása is ugyanebbe az üzemmódba visz)
        lcd.setCursor(0,0);
        switch (fo_uzemmod) {                           //kijelezzük, hogy milyen fő üzemmódban vagyunk (töltés, merítés, ciklikus vizsgálat)
          case 0:
            lcd_uniprint((String)F("Töltés              "));break;
          case 1:
            lcd_uniprint((String)F("Merítés             "));break;
          case 2:
            lcd_uniprint((String)F("Ciklikus vizsgálat  "));break;
        }
        //kezelési segtség egy sorban, illetve státus kijelzés
        lcd.setCursor(0,1);
        if (fo_uzemmod==2) {
          if (rogzites==0) {lcd_uniprint((String)F("Indít:+ Mód:- (1sec)"));}
          else {
            lcd_uniprint((String)F("Ciklus:             "));
            lcd.setCursor(8,1);
            lcd.print(ciklus_fazis);
          }
        }
        else {
          if (rogzites==0) {lcd_uniprint((String)F("Indít:+ Mód:- (1sec)"));}
          else {lcd_uniprint((String)F("Elindítva:          "));}
        }
        //idő átszámítása másodpercről óra, percre másodperc-re
        ido_tmp=ido;
        ora=ido_tmp/3600;
        ido_tmp=ido_tmp-ora*3600;
        perc=ido_tmp/60;
        masodperc=ido_tmp-perc*60;
        lcd.setCursor(0,2);
        if (ora<10) {lcd.print("0");lcd.print(ora);} else {lcd.print(ora);};lcd.print(":");
        if (perc<10) {lcd.print("0");lcd.print(perc);} else {lcd.print(perc);};lcd.print(":");
        if (masodperc<10) {lcd.print("0");lcd.print(masodperc);} else {lcd.print(masodperc);};lcd.print(" ");
        mAh=(float)akku_kapacitas/36000;  //mivel mA-másodperc mértékegységben van, osztani kell 3600-al, hogy mAh-ban legyen
                                          //és még 10-el, mert egy tizedes pontossággal összegeztem az áramot, így ez is 10-szeres érték
        lcd_uniprint(mAh,7,0);                       //akku kapacitás kijelzése
        lcd.print("mAh");  
        lcd.setCursor(0,3);                                       
        lcd_uniprint((float)pill_fesz/1000,6,3);     //Vbusz feszültség kiírása
        lcd.print("V");            
        lcd_uniprint((float)pill_aram/10,10,1);      //söntáram kijelzése 
        lcd.print("mA");  
        leptet_fel=0;leptet_le=0;
        break;
    case 1:
      if (sorosport==1) {           //ha hosszú gombnyomással (Mód gomb) léptünk a regisztrált adatok megjelenítésébe
                                    //akkor a soros portra is kiírjuk a regisztrált adatokat. Innen tovább lehet másolni pl. excel-be.
                                    //Az adatok ";"-vel vannak elválasztva, ez CSV formátum, amit az Exvel meg tud nyitni.
        sorosport=0;
        akt_sorsz=0;
        //az adot kiírásakor nem használok ékezetes betűket, mert az Excel nem tudja helyesen beolvasni alapból
        Serial.println(F("Beallitasok:"));
        Serial.print(F("Feszultseg hatar:"));
        Serial.print((float)fesz_min/1000,1);
        Serial.print(F(" - "));
        Serial.print((float)fesz_max/1000,1);
        Serial.println(" V");
        Serial.print(F("Feszultseg felbontas (reg. szint):"));
        Serial.print((float)fesz_lepeskoz/1000,3);
        Serial.println(" V");
        Serial.println(F("Vizsgalati osszefoglalo adatok (csak ciklikus vizsgalatnal):"));
        //ezekt az adatokat csak ciklikus vizsgálatnál töltöm fel, így csak egy teljesen végigfuttatott
        //ciklikus vizsgálatnál van bennük helyes adat, egyébként nem szabad vele foglalkozni.
        EEPROM.get(1016,xadat_int);
        Serial.print("Merites (1):");Serial.print((float)xadat_int/10,1);Serial.println("mAh");
        EEPROM.get(1010,xadat_int);
        Serial.print("Toltes (1):");Serial.print((float)xadat_int/10,1);Serial.println("mAh");
        EEPROM.get(1018,xadat_int);
        Serial.print("Merites (2):");Serial.print((float)xadat_int/10,1);Serial.println("mAh");
        EEPROM.get(1012,xadat_int);
        Serial.print("Toltes (2):");Serial.print((float)xadat_int/10,1);Serial.println("mAh");
        EEPROM.get(1020,xadat_int);
        Serial.print("Merites (3):");Serial.print((float)xadat_int/10,1);Serial.println("mAh");
        EEPROM.get(1010,xadat_int);
        Serial.print("Toltes (3):");Serial.print((float)xadat_int/10,1);Serial.println("mAh");
        Serial.println(F("Regisztralt adatok:"));
        Serial.println(F("Ido (perc);Feszultseg (V);Aram (mA);Kapacitas (mAh);"));
        do {
          EEPROM.get(akt_sorsz*8+32,xfesz);
          EEPROM.get(akt_sorsz*8+34,xaram);
          EEPROM.get(akt_sorsz*8+36,xmAh);
          EEPROM.get(akt_sorsz*8+38,xido);
          if (xfesz!=0 or xaram!=0 or xmAh!=0 or xido!=0) {   //az utolsó 0-as rekordot nem kell megjeleníteni
            Serial.print(xido/60);Serial.print(";");
            Serial.print((float)xfesz/1000,3);Serial.print(";");
            Serial.print((float)xaram/10,1);Serial.print(";");
            Serial.print((float)xmAh/10,1);Serial.println(";");
          }
          akt_sorsz++;
        } while (xfesz!=0 or xaram!=0 or xmAh!=0 or xido!=0);
      }
      
      /******************** Ezek a részek már nem a sorosportra íráshoz tartoznak, ezek az adatmegjelenítést, léptetést végzik *************/
      if (leptet_fel==1 and kiolv_sorsz<121) {      //Az 1-es gombot (Start) nyomta meg röviden, kijelezzük a következő kiolvasott adatot.
                                                    //Maximum 121 adatrekord lehet, ennél nem lehet több a kiolvasandó rekord sorszáma
        kiolv_sorsz++;                              //növeljük az aktuális adatrekord sorszámát (amit ki szeretnénk jelezni)
      }
      if (leptet_le==1 and kiolv_sorsz>=1) {        //Az 2-es gombot (Mód) nyomta meg röviden, kijelezzük a következő kiolvasott adatot
        if (kiolv_sorsz>1) {kiolv_sorsz--;}         //csökkentjük az aktuális adatrekord sorszámát (amit ki szeretnénk jelezni)
      }
      if (leptet_fel==1 or leptet_le==1) {          //mivel gombnyomás történt, változott a kiolv_sorsz változó (fentebb), frissítjük a képernyőt
        
        //megkeressük a szükséges rekordot és kiolvassuk az adatait
        //A kiolv_sorsz változót növelik vagy csökkentik a gombnyomások
        //és a ciklus megáll annál a rekordnál, aminek a sorszáma egyezik ezzel a változóval.
        akt_sorsz=0;
        do {
          EEPROM.get(akt_sorsz*8+32,xfesz);
          EEPROM.get(akt_sorsz*8+34,xaram);
          EEPROM.get(akt_sorsz*8+36,xmAh);
          EEPROM.get(akt_sorsz*8+38,xido);
          if (xfesz==0 and xaram==0 and xmAh==0 and xido==0) {kiolv_sorsz=akt_sorsz;break;}
          akt_sorsz++;
          if (akt_sorsz==kiolv_sorsz) {break;}
          yfesz=xfesz;yaram=xaram;ymAh=xmAh;yido=xido;
        } while (xfesz!=0 or xaram!=0 or xmAh!=0 or xido!=0);
        
        //kiírjuk a képernyőre a megtalált rekord adatait
        lcd.clear();
        lcd.setCursor(0,0);                        
        lcd_uniprint((String)F("Adatok lekérdezése: "));

        if (akt_sorsz==0) {             //ha rögtön az első rekord négy 0-át tartalmaz, akkor nincsenek adatok
          lcd.setCursor(0,2);                        
          lcd_uniprint((String)F("Nincs rögzített adat!"));
        }
        else {
          //itt következik egy adatrekord megjelenítése a képernyőn
          ido_tmp=yido;
          ora=ido_tmp/3600;
          ido_tmp=ido_tmp-ora*3600;
          perc=ido_tmp/60;
          ido_tmp=ido_tmp-perc*60;
          masodperc=ido_tmp;
          lcd.setCursor(0,1);                        
          lcd_uniprint((String)F("Sorszám:"));lcd.print(kiolv_sorsz);lcd.print(" ");
          if (ora<10) {lcd.print("0");lcd.print(ora);} else {lcd.print(ora);};lcd.print(":");
          if (perc<10) {lcd.print("0");lcd.print(perc);} else {lcd.print(perc);};lcd.print(":");
          if (masodperc<10) {lcd.print("0");lcd.print(masodperc);} else {lcd.print(masodperc);};lcd.print("  ");
          lcd.setCursor(0,2); 
          lcd.print((float)yfesz/1000,3);lcd.print("V ");lcd.print((float)yaram/10,1);lcd.print("mA");
          lcd.setCursor(0,3); 
          lcd.print((float)ymAh/10,1);lcd.print("mAh");
        }
      }
      leptet_fel=0;leptet_le=0;              //így gombnyomásonként csak egyszer megyünk bele ebbe a programrészbe
      break;
    case 2:
      frissit=0;                             //akkor frissítünk, ha újra lezajlik egy mérés! Ha az üzemmód 0 vagy 2, 
                                             //akkor a méréskor (másodpercenként) 1-re állítjuk a frissit változót
      lcd.setCursor(0,0);                        
      lcd_uniprint((String)F("Tárolt adat törlés! "));
      if (torl_kesl==6) {
        lcd.setCursor(0,1);                        
        lcd_uniprint((String)F("   Gombnyomással    "));
        lcd.setCursor(0,2);                        
        lcd_uniprint((String)F("     Indítható!     "));
        lcd.setCursor(0,3);                        
        lcd_uniprint((String)F("                    "));
      }
      if (leptet_fel==1 or leptet_le==1) {    //megnyomtuk valamelyik nyomógombot
        //leptet_fel=0;leptet_le=0;
        if (torl_kesl<5) {                    //meg visszaszámlálás közben vagyunk, tehát a gombnyomás megszakítja a törlést
          lcd.setCursor(0,1);                        
          lcd_uniprint((String)F("                    "));
          lcd.setCursor(0,2);                        
          lcd_uniprint((String)F("Nem történt törlés! "));
          lcd.setCursor(0,3);                        
          lcd_uniprint((String)F("                    "));
          delay(3000);
          lcd.clear();
          torl_kesl=6;uzemmod=0;leptet_fel=0;leptet_le=0;break;
        }
        torl_kesl=0;                          //meg nem indult el a visszaszámlálás, tehát elindítjuk (törlést akarunk 5 sec meggondolási idővel)
      }
      if (torl_kesl<6) {                      //elindult a törlési visszaszámlálás (mert a változó kisebb mint 6)
        lcd.setCursor(0,1);                        
        lcd_uniprint((String)F("Törlés "));lcd.print(5-torl_kesl);lcd_uniprint((String)F(" sec múlva! "));
        lcd.setCursor(0,2);                        
        lcd_uniprint((String)F("Dombnyomással       "));
        lcd.setCursor(0,3);                        
        lcd_uniprint((String)F("      megszakítható!"));
        torl_kesl++;
        if (torl_kesl==5) {                   //5-ig elszámolt, közben nem volt gombnyomás, tehát törlünk
          EEPROM.put(14,(unsigned int) 0);    //töltés vagy merítés eddig regisztrált ideje (sec)
          EEPROM.put(16,(long) 0);            //törlés vagy merítés eddig regisztrált kapacitása (mAh)
          ido=0;akku_kapacitas=0;
          EEPROM.put(32,(unsigned int) 0);    //az adatterület 0. rekordja 0, azaz nincs adat
          EEPROM.put(34,(unsigned int) 0);
          EEPROM.put(36,(unsigned int) 0);
          EEPROM.put(38,(unsigned int) 0);
          lcd.setCursor(0,1);                        
          lcd_uniprint((String)F("                    "));
          lcd.setCursor(0,2);                        
          lcd_uniprint((String)F("   Adatok törölve!  "));
          lcd.setCursor(0,3);                        
          lcd_uniprint((String)F("                    "));
          delay(3000);
          utolso_fesz=0;                      //így a regisztrálás indításakor biztosan rögzít egyet.
          lcd.clear();
          torl_kesl=6;uzemmod=0;leptet_fel=0;leptet_le=0;break;
        }
      }
      leptet_fel=0;leptet_le=0;               //így gombnyomásonként csak egyszer megyünk bele ebbe a programrészbe
      break;
    case 3:
      //ennnél a feszültségnél fog kikapcsolni a berendezés merítés közben, ha az akku feszültsége ennél kissebbé válik.
      //Merítés üzemmódban ez a vizsgálat végét jelenti (megszakítjuk az akku áramkörét egy relével)
      //Ciklikus vizsgálat esetén ekkor várunk 10 percet, és a következő töltési ciklussal folytatjuk.
      //Egy lithium akkunál ez kb. 2.5V feszültséget jelent. Pl. egy ólomakkumulátornál 6V (6 cellás autó akkumulátor)
      lcd.setCursor(0,0); 
      lcd_uniprint((String)F("A merítés kikapcso- "));
      lcd.setCursor(0,1);                        
      lcd_uniprint((String)F("lási végfeszültsége."));
      lcd.setCursor(0,2);                        
      lcd_uniprint((String)F("                    "));
      lcd.setCursor(0,3);                        
      lcd_uniprint((String)F("+,-  tovább:1sec (-)"));
      EEPROM.get(20,xadat_int);
      if (leptet_fel==1 and xadat_int<30000) {EEPROM.put(20,xadat_int+100);}
      if (leptet_le==1 and xadat_int>500) {EEPROM.put(20,xadat_int-100);}
      EEPROM.get(20,xadat_int);
      fesz_min=xadat_int;
      lcd.setCursor(15,2);                        
      lcd.print((float)fesz_min/1000,2);
      lcd.print("V");
      leptet_fel=0;leptet_le=0;               //így gombnyomásonként csak egyszer megyünk bele ebbe a programrészbe
      break;
    case 4:
      //ennnél a feszültségnél fog kikapcsolni a berendezés töltés közben, ha az akku feszültsége ennél nagyobbá válik.
      //Töltés üzemmódban ez a vizsgálat végét jelenti (megszakítjuk az akkui áramkörét egy relével)
      //Ciklikus vizsgálat esetén ekkor várunk 10 percet, és a következő merítési ciklussal folytatjuk.
      //Egy lithium akkunál ez kb. 4.2V feszültséget jelent. Pl. egy ólomakkumulátornál 14.4V (6 cellás autó akkumulátor)
      lcd.setCursor(0,0);                        
      lcd_uniprint((String)F("A töltés kikapcso-  "));
      lcd.setCursor(0,1);                        
      lcd_uniprint((String)F("lási végfeszültsége."));
      lcd.setCursor(0,2);                        
      lcd_uniprint((String)F("                    "));
      lcd.setCursor(0,3);                        
      lcd_uniprint((String)F("+,-  tovább:1sec (-)"));
      EEPROM.get(22,xadat_int);
      if (leptet_fel==1 and xadat_int<30000) {EEPROM.put(22,xadat_int+100);}
      if (leptet_le==1 and xadat_int>1000) {EEPROM.put(22,xadat_int-100);}
      EEPROM.get(22,xadat_int);
      fesz_max=xadat_int;
      lcd.setCursor(15,2);                        
      lcd.print((float)fesz_max/1000,2);
      lcd.print("V");
      leptet_fel=0;leptet_le=0;              //így gombnyomásonként csak egyszer megyünk bele ebbe a programrészbe
      break;
    case 5:
      //Feszültség korrekció.
      //a mért buszfeszültségből (akku feszültsége) kiszámított feszültséget lehet ezzel a számmal korrigálni.
      //Értéke 8000-13000-ig állítható, ami 0,8-1,3-al történő szorzást jelent. 
      //Az én INA226 példányom esetén ~5V USB tápfesznél 0,9526-al kellett szorozni.
      //Úgy számoltam ki, hogy megmértem a legpontosabb műszeremmel az akku feszültségét (nem töltöttem és nem is merítettem közben az akkut)
      //és a kijelző által mutatott feszültség értéket elosztottam a valós mért feszültséggel. Persze úgy is lehet, hogy addig állítgatjuk ennek 
      //a paraméternek az értékét amíg a mért és mutatott feszültség megegyezik!
      lcd.setCursor(0,0);                        
      lcd_uniprint((String)F(" A feszültségmérés  "));
      lcd.setCursor(0,1);                       
      lcd_uniprint((String)F(" hiteles értékéhez  "));
      lcd.setCursor(0,2);                        
      lcd_uniprint((String)F(" szüks. korrekció!  "));
      lcd.setCursor(0,3);                        
      lcd_uniprint((String)F("                    "));
      EEPROM.get(24,xadat_int);
      if (leptet_fel==1 and xadat_int<13000) {EEPROM.put(24,xadat_int+1);}
      if (leptet_le==1 and xadat_int>8000) {EEPROM.put(24,xadat_int-1);}
      lcd.setCursor(0,3);                        
      EEPROM.get(24,xadat_int);
      lcd_uniprint((float)xadat_int/10000,6,4);lcd.print("*V   +,-,1sec-");
      leptet_fel=0;leptet_le=0;              //így gombnyomásonként csak egyszer megyünk bele ebbe a programrészbe
      break;
    case 6:
      //Áram korrekció.
      //a mért söntfeszültségből kiszámított áramot lehet ezzel a számmal korrigálni.
      //Értéke 8000-13000-ig állítható, ami 0,8-1,3-al történő szorzást jelent. 
      //Az én INA226 példányom esetén ~5V USB tápfesznél 1,19-el kellett szorozni.
      //Úgy számoltam ki, hogy megmértem a legpontosabb műszeremmel az áramot (sorba kötve az akkuval töltés közben, áramgenerátoros töltési állapotban)
      //és a kijelző által mutatott áram értéket elosztottam a valós mért árammal. Persze úgy is lehet, hogy addig állítgatjuk ennek a paraméternek az értékét
      //amíg a mért és mutatott áram megegyezik!
      lcd.setCursor(0,0);                        
      lcd_uniprint((String)F("   Az árammérés     "));
      lcd.setCursor(0,1);                       
      lcd_uniprint((String)F(" hiteles értékéhez  "));
      lcd.setCursor(0,2);                        
      lcd_uniprint((String)F(" szüks. korrekció!  "));
      lcd.setCursor(0,3);                        
      lcd_uniprint((String)F("                    "));
      EEPROM.get(26,xadat_int);
      if (leptet_fel==1 and xadat_int<13000) {EEPROM.put(26,xadat_int+1);}
      if (leptet_le==1 and xadat_int>8000) {EEPROM.put(26,xadat_int-1);}
      EEPROM.get(26,xadat_int);
      lcd.setCursor(0,3);                        
      lcd_uniprint((float)xadat_int/10000,6,4);lcd.print("*mA  +,-,1sec-");
      leptet_fel=0;leptet_le=0;               //így gombnyomásonként csak egyszer megyünk bele ebbe a programrészbe
      break;
    case 7:
      //áram nullázás beállítása (-200 és 200 között állítható, ami +/- 2mA korrekciót eredményez)
      //Az én INA226 példányomnak +0,15mA korrekcióra volt szüksége (~5V USB-ről kapott tápfeszültség esetén)
      lcd.setCursor(0,0);                        
      lcd_uniprint((String)F("Árammentes állapot- "));
      lcd.setCursor(0,1);                       
      lcd_uniprint((String)F("ban 0 áramhoz szüks."));
      lcd.setCursor(0,2);                        
      lcd_uniprint((String)F("előjeles áram korr.!"));
      lcd.setCursor(0,3);                        
      lcd_uniprint((String)F("                    "));
      EEPROM.get(28,xadat_int);
      if (leptet_fel==1 and xadat_int<200) {EEPROM.put(28,xadat_int+1);}
      if (leptet_le==1 and xadat_int>-200) {EEPROM.put(28,xadat_int-1);}
      EEPROM.get(28,xadat_int);
      lcd.setCursor(0,3);                        
      lcd.print((float)xadat_int/100,2);lcd.print("mA     +,-,1sec-");
      nullazas=(float)xadat_int/100;
      leptet_fel=0;leptet_le=0;                //így gombnyomásonként csak egyszer megyünk bele ebbe a programrészbe
      break;
    case 8:
      //töltés közbeni regisztrálások száma (5 és 40 között beállítható)
      lcd.setCursor(0,0);    
      lcd_uniprint((String)F("Töltés vagy merítés "));
      lcd.setCursor(0,1);   
      lcd_uniprint((String)F(" közben regisztrált "));
      lcd.setCursor(0,2); 
      lcd_uniprint((String)F(" adatok darabszáma. "));
      lcd.setCursor(0,3);                        
      EEPROM.get(30,xadat_int);
      if (leptet_fel==1 and xadat_int<40) {EEPROM.put(30,xadat_int+1);}
      if (leptet_le==1 and xadat_int>5) {EEPROM.put(30,xadat_int-1);}
      EEPROM.get(30,xadat_int);
      lcd.setCursor(0,3);                        
      lcd.print(xadat_int);lcd_uniprint((String)"db       +,-,1sec-");
      fesz_felbontas=xadat_int;
      fesz_lepeskoz=(fesz_max-fesz_min)/fesz_felbontas;
      leptet_fel=0;leptet_le=0;                //így gombnyomásonként csak egyszer megyünk bele ebbe a programrészbe
      break;
    }
  }
}

int adat_iras(int xfesz, int xaram, int xmAh, unsigned int xido)
{
  //első üres hely keresése (mind a négy cella 0
  byte rekordszam=0;
  int akt_cim=24;                         //ez a legelső adatrekord kezdetéhez
  int akt_fesz,akt_aram,akt_mAh;  //csak azért kellenek, hogy megkeressük a legutolsó adatrekordot
                                          //ha mind a négy változóba 0 érték kerül, akkor ez előző volt az utolsó adatrekord
  unsigned int akt_ido;
  do {
    akt_cim=akt_cim+8;
    EEPROM.get(akt_cim,akt_fesz);
    EEPROM.get(akt_cim+2,akt_aram);
    EEPROM.get(akt_cim+4,akt_mAh);
    EEPROM.get(akt_cim+6,akt_ido);
    if (akt_cim>992) {return 0;}          //ha megtelt az EEPROM, akkor 0-val térünk vissza
                                          //Jelenleg a programban nem kezelem azt az esetet, ha megtelt az EEPROM
                                          //lehetne ezt is figyelni, de nem volt lényeges, mert a ciklikus vizsgálatnál
                                          //is elférek, ha 13-ra állítom a felbontást. Tapasztalatom szerint a 13 bővenelegendő
                                          //egy látványos Excel grafikon rajzolásához. 
    rekordszam++;
  } while (akt_fesz!=0 or akt_aram !=0 or akt_mAh!=0 or akt_ido!=0);
  EEPROM.put(akt_cim,xfesz);
  EEPROM.put(akt_cim+2,xaram);
  EEPROM.put(akt_cim+4,xmAh);
  EEPROM.put(akt_cim+6,xido);
  akt_cim=akt_cim+8;
  EEPROM.put(akt_cim,0);
  EEPROM.put(akt_cim+2,0);
  EEPROM.put(akt_cim+4,0);
  EEPROM.put(akt_cim+6,0);
  Serial.print("Tárolás (");Serial.print(rekordszam);Serial.print("):");Serial.print(ido);Serial.print("sec ");
  Serial.print((float)xfesz/100,3);Serial.print("V ");
  Serial.print((float)xaram/100,1);Serial.print("mA ");
  Serial.print((float)akku_kapacitas/36000,1);Serial.println("mAh");   //mivel integerként kapom meg ezt az adatot és mAh/másodperc mértékegységben,
                                                                       //kijelzés előtt osztani kell 3600-al, hogy mAh legyen, és még 10-el, hogy
                                                                       //egy tizedes pontossággal lehessen kijelezni
  return rekordszam;                      //ha sikerült az írás, akkor a rekord sorszámmal térünk vissza
}



/**************************************************************************************************************************************************************************
 * lcd_uniprint() függvény:                                                                                                                                               *
 *   Az alább részletezett függvény formázottan ír ki adatok lcd kijelzőre. Az lcd kijelzőt a programban a megszokott módon kell inicializálni, a kijelző hivatkozási     *
 *   neve kötelezően "lcd". Ha más névre van szükség, akkor a legutolsó függvény változatban át kell írni az lcd.print() függvényben az előtagot.                         *
 *   Két LCD kijelzőt nem kezel, ahhoz vagy a függvényeket kell megduplázni más néven, vagy új paramétert kell bevezetni, és belül kiválasztani a kijelzőt.               *
 *                                                                                                                                                                        *
 * Ékezetes szöveg írása LCD kijelzőre: lcd_uniprint( (String) szöveg);                                                                                                   *
 *    - Paraméter egy tetszőlegesen ékezetes betűket is tartalmazó string típusú változó, vagy szöveg "" között                                                           *
 *      Sajnos ha idézőjelek között direktben adjuk meg a stringet, akkor azt nem String-nek tekinti, és rossz függvény változatot hív meg, ezért konvertálni kell        *
 *      a (String) konverzióval, amit az időzőjelekkel megadott string elé kell beírni. Ha string típusú változót adunk meg paraméterként, akkor rendesen működik         *
 *      pl.:lcd_uniprint((String)"Nagy:ÁÉÍÓÖŐÚÜŰ");                                                                                                                       *
 * Float típusú számadat, beállítható tizedesjegy számmal és minimális hosszal: lcd_uniprint( (float) szám, minimális hossz, tizedesek száma);                            *
 *    - Az első paraméter egy float típusú, tizedes jegyeket tartalmazó szám. Amennyiben nem float típusú, akkor elé kell írni (float), hogy mindenképpen float legyen    *
 *      különben nem tudja meghívni a hozzá tartozó lcd_uniprint() függvényt meghívni, ami float típusú paramétert vár.                                                   *
 *    - Második paraméter a megjelenített szám minimális hossza a tizedesjegyekkel és a tizedesponttal együtt. Ha nem kell tizedespont, akkor (long)-ot kell elé írni     *
 *      és az előző függvényváltozat fogja kiírni az adatot. Ha a számadat hosszabb mint ez a megadott érték, akkor a kiírt adat is több helyet igényel.                  *
 *      Ha a minimális hossz értéke 0, akkor pont annyi helyre fogja kiírni a számot, amennyit igényel.                                                                   *
 *    - harmadik paraméter a tizedesjegyek száma.                                                                                                                         *
 *      pl.:lcd_uniprint(float_ertek_minusz,8,4);                                                                                                                         *
 *                                                                                                                                                                        *
 *Szükséges könyvtárak és beállítások a programban:                                                                                                                       *
 * #include <Wire.h>    //I2C library                                                                                                                                     *
 * #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, ez most 2x16 karakteres kijelző                                            *
 **************************************************************************************************************************************************************************/


void lcd_uniprint(long szam, byte min_fix_hossz) {
/*************************************************************************************************  
Ez a függvény akkor hívódik meg, ha az első paraméter Long típusú és még egy Byte típusú         *
paraméter lett megadva. A megkapott Long típusú számot a Byte típusú változóval megadott         *
minimális hosszban megadott karakterszámon írja ki. Ha a második paraméter nulla, akkor a        *
számjegyek igényelt hosszának megfelelő helyet fog foglalni. Ha a számjegyek száma nagyobb, mint *
a megadott minimum, akkor hosszabb helyet fog elfoglalni a kiíráskor a megadott szám.            *  
**************************************************************************************************/
  if (min_fix_hossz==0) {lcd_uniprint(String(szam));}                    //a megadott minimális hossz 0, annyi helyet foglal amennyi kell
  else {
    String str_tmp=String(szam);                                         //betöltjük egy string típusú változóba
    byte hossz=str_tmp.length();                                         //megállapítjuk a hosszát
    if (min_fix_hossz>hossz) {                                           //rövidebb mint a minimum
      for (byte i=0;i<(min_fix_hossz-hossz);i++) {str_tmp=" "+str_tmp;}  //amennyivel rövidebb, annyi szóközt teszünk elé
    }
    lcd.print(str_tmp);                                                  //nincsenek ékezetek lehet használni az lcd.print()-et
  }
}

void lcd_uniprint(float szam,byte min_fix_hossz,byte tizedes) {
/**************************************************************************************************  
Ez a függvény akkor hívódik meg, ha az első paraméter Float típusú és még két Byte típusú         *
paraméter lett megadva. A megkapott Float típusú számot az első paraméterben megadott             *
karakterszám hosszban írja ki, ha ez a paraméter nulla, akkor a számjegyek igényelt hosszának     *
megfelelő helyet fog foglalni. Ha a számjegyek száma nagyobb, mint a megadott minimum, akkor      *
hosszabb helyet fog elfoglalni a kiíráskor a megadott szám. A tizedes jegyek száma a harmadik     *
paraméterrel állítható be                                                                         *
***************************************************************************************************/
  if (min_fix_hossz==0) {lcd_uniprint(String(szam,tizedes));}           //a megadott minimális hossz 0, annyi helyet foglal amennyi kell
  else {
    String str_tmp=String(szam,tizedes);                                //betöltjük egy string típusú változóba, megadjuk a String()-nek a tizedesjegyek számát
    byte hossz=str_tmp.length();                                        //megállapítjuk a hosszát
    if (min_fix_hossz>hossz) {                                          //rövidebb mint a minimum
      for (byte i=0;i<(min_fix_hossz-hossz);i++) {str_tmp=" "+str_tmp;} //amennyivel rövidebb, annyi szóközt teszünk elé
    }
    lcd.print(str_tmp);                                                 //nincsenek ékezetek lehet használni az lcd.print()-et
  }
}


void lcd_uniprint(String szoveg) {
/*******************************************************************************************
Ez a függvény képes feltölteni a magyar ékezetes betűk (csak kis betűk, mert többre nincs  *
memória hely) karakterképét az LCD kijelzőre, ha üres String paraméterrel hívjuk meg. Ha a *
paraméter nem egy üres string, akkor megkeresi az ékezetes betűket (2 byte-on kerülnek a   *
stringbe), eltávolítja az egyik felesleges byte-ot, a másikat pedig arra a kódra cseréli,  *
amit a kijelzőnek kell küldeni az adott user karakter megjelenítéséhez (0-7 kódok).        *
********************************************************************************************/
String kiir="";
byte karakter;
byte j=0;
byte hossz=szoveg.length();                //az átvett string hossza
if (hossz==0) {                            //üres karakterrel hívtuk meg a függvényt, ez azt jelenti, hogy feltöltjük a kijelzőt a magyar kerekterek képével
  byte tt[8] = {B10,B100,B1110,B1,B1111,B10001,B1111};                                        //á betű karakterképe
  lcd.createChar(0,tt);
  tt[0]=B10;tt[1]=B100;tt[2]=B1110;tt[3]=B10001;tt[4]=B11111;tt[5]=B10000;tt[6]=B1110;        //é betű karakterképe
  lcd.createChar(1,tt);
  tt[0]=B10;tt[1]=B100;tt[2]=B0;tt[3]=B1110;tt[4]=B100;tt[5]=B100;tt[6]=B1110;                //í betű karakterképe
  lcd.createChar(2,tt);
  tt[0]=B10;tt[1]=B100;tt[2]=B0;tt[3]=B1110;tt[4]=B10001;tt[5]=B10001;tt[6]=B1110;            //ó betű karakterképe
  lcd.createChar(3,tt);
  tt[0]=B00101;tt[1]=B01010;tt[2]=B0;tt[3]=B1110;tt[4]=B10001;tt[5]=B10001;tt[6]=B1110;       //ő betű karakterképe
  lcd.createChar(4,tt);
  tt[0]=B10;tt[1]=B100;tt[2]=B10001;tt[3]=B10001;tt[4]=B10001;tt[5]=B10011;tt[6]=B1101;       //ú betű karakterképe
  lcd.createChar(5,tt);
  tt[0]=B00101;tt[1]=B01010;tt[2]=B0;tt[3]=B10001;tt[4]=B10001;tt[5]=B10011;tt[6]=B1101;      //ű betű karakterképe
  lcd.createChar(6,tt);
  return;
}
for (byte i=0;i<hossz;i++) {                             //minden karakteren sorban végig megyünk
  if ((byte) szoveg[i]==195 or (byte) szoveg[i]==197)    //ha a kerekter 195 vagy 197, akkor azt kihagyjuk az eredmény stringből és vesszük a következő karaktert
  {
    i++;
    karakter=(byte) szoveg[i];
    kiir.concat(" ");                                    //ez a hiányos C++ tudásom miatt van, mivel nem tudtam hozzáfűzni a 0-7 ASCII kódot, ezért hozzáfüzük egy szóközt, 
                                                         //amit a switch-ben lecserélek a megfelelő feltöltött karakter kódjára, illetve a karakterkészletben meglévő 
                                                         //ö és ü betű kódjára
    switch (karakter) {
      case 129: kiir.setCharAt(j,0);break;    //Á        //a j. pozicióban (utolsó hozzáfűzött karaktert cserélem a feltöltött karakter kódjára
      case 161: kiir.setCharAt(j,0);break;    //á
      case 137: kiir.setCharAt(j,1);break;    //É
      case 169: kiir.setCharAt(j,1);break;    //é
      case 141: kiir.setCharAt(j,2);break;    //Í
      case 173: kiir.setCharAt(j,2);break;    //í
      case 147: kiir.setCharAt(j,3);break;    //Ó
      case 179: kiir.setCharAt(j,3);break;    //ó
      case 150: kiir.setCharAt(j,239);break;  //Ö
      case 182: kiir.setCharAt(j,239);break;  //ö
      case 144: kiir.setCharAt(j,4);break;    //Ő
      case 145: kiir.setCharAt(j,4);break;    //ő
      case 154: kiir.setCharAt(j,5);break;    //Ú
      case 186: kiir.setCharAt(j,5);break;    //ú
      case 156: kiir.setCharAt(j,245);break;  //Ü
      case 188: kiir.setCharAt(j,245);break;  //ü
      case 176: kiir.setCharAt(j,6);break;    //Ű
      case 177: kiir.setCharAt(j,6);break;    //ű
    }
  }
  else {kiir.concat(szoveg[i]);}                       //ez egy kijelzőn is megtalálható karakter, simán csak másoljuk
  j++;                                                 //egy karakterrel több van a kijelzőre irandó stringben
}
//SimulIde áramkör szimulátorban csak karakterenként lehet kiírni, mert ott
//az lcd.print() függvény valamilyen okból stringet nem képes kiírni csak az első
//karakterét. Ezt a három sort csak akkor használom, ha SimulIde-ben akarom futtatni 
//for (byte i=0;i<j;i++) {
//  lcd.print(kiir[i]);    
//}
lcd.print(kiir);                                       //..és akkor kiírjuk a kijelzőre az ékezetes kódokra cserélt szöveget
}

A profi programozók valószínűleg a hajukat tépik a sok globális változótól és a program felépítésétől, de figyelembe kell venni, hogy kísérletezgetés közben írogattam. Kb. két hónap alatt készült el erre az állapotra (lényegében csak hétvégente van időn néhány óra hobbira). A dolog csak annyival indult, hogy regisztrálok időnként az EEPROM-ba. Sokkal több lett belőle, és ez meglátszik a program zagyvaságán is. Azonban optimalizálásnak, szépítgetésnek nincs értelme. Ez egy célberendezés, amit ha nem sajnálok, talán szét is szedek idővel, és alkatrészeit másra fogom felhasználni. De ez még hónapokra van, hiszen a vizsgálatok nagyon sok időt vesznek majd igénybe. Ki kell használnom a téli hidegeket és a nyári kánikulát is, mert termosztatikus mérésekre alkalmas laborom nincs jelenleg!

Most, hogy van már egy komplett bevizsgáló készülékem, elég sok ötletem van, hogy miket is szeretnék vele megvizsgálni. Szeretném megnézni a töltési és merítési végfeszültségek hatását az akkuból kinyerhető energiára. Azt is szeretném megtudni, hogy a töltő és merítő áram hogyan hat az akku kapacitására? Kíváncsi vagyok a hőmérséklet hatásaira is! Szerencsére éppen jön a tél, és immár mozgatható készülékemet valóban száműzöm a szerszámos suffniba, és ha szerencsém lesz, 0 fok vagy akár nagyobb mínuszokban is végig csinálhatom a ciklikus vizsgálatot. Szerencsére időjárás állomásom óránként szolgáltatni fogja az aktuális hőmérsékletet, mindössze „mérési jegyzőkönyvet” kell vezetnem, hogy az időpontokból vissza lehessen keresni az akkori hőmérsékletet. Tisztára olyan ez, mint az egyetemen a laborgyakorlatok. Sajnos akkori tanáraim, laborvezetőim jelentős része már nem él, így hiába dolgozok vele, jeles osztályzatot már nem kaphatok tőlük!
Szeretném a boltban aranyárba vett „valódi” 18650-es akkut összehasonlítani a hamisítványokkal, és találni egy jellemzőt, amiből látható, ha egy akku hamis, vagy gyanús, hogy nem fog jól működni. Egyébként találtam ilyen paramétert. Megfigyeltem, hogy a hamisítványok nagyon könnyűek. Egyébként lítium akkumulátorként viselkednek, csak a súlyuk nagyon kicsi. Úgy képzelem, hogy vesznek egy kisebb méretű valódi akkumulátort, és becsomagolják egy nagyobb akkumulátor testbe. A teret valamilyen könnyű anyaggal töltik ki.
Az akkumulátorok vizsgálat egyébként már folyamatban van. Néhány hét múlva a megszerzett tapasztalatokat a „gyakorlat” menüpontban szeretném megosztani.

Sajnos rossz híreim is vannak. Az első mérések alapján (már vagy tízet megcsináltam mire észre vettem) úgy tűnik, hogy valahol hibát követtem el a berendezésben. Ugyanis az akkumulátorok töltése után kapott kapacitás értékek jó esetben azonosak a merítéskor összegzett kapacitás értékekkel. Rossz esetben a merítéskor picivel még nagyobb számot is kaptam néha. Pl. egy HESTORE-ban vásárolt jó minőségű 2600mAh akkunál töltéskor 2625mAh ment bele, de merítéskor 2639mAh jött ki belőle. Első körben azt hittem megalkottam a perpetuum mobile alapjait, de aztán gondolkodtam egy kicsit. Az a gyanúm, hogy az árammérés nullpontját valahogyan mégis rosszul toltam el. Kicsivel több áramot mérhet negatív irányba merítéskor, mint pozitív irányba töltéskor. Bár a Li-ion akkumulátorok hatásfoka 90-97% közötti, elég valószerűtlen, hogy több energia jöjjön ki, mint amennyi bemegy. Legyen 95% a hatásfok. 2625*0.95=2494, tehát kb. 130mAh-val többet mért merítéskor. Ekkor a 6,6 órás töltési és 4,4 órás merítési idő alapján kb. 10-15mA-re becsülöm a hibát.  Ez nem nagy érték, simán benézhettem valamit. Azonban a vizsgálatom tárgya egyelőre nem ez, ezért sok időt nem szántam a kérdésre. Menet közben esetleg rábukkanok a megoldásra.
Viszont elképzeltem, hogy mit szólt volna ehhez az eredményhez Dudás József Tanár Úr, aki a BME villamos karon mérés laboron oktatott a precíz és pontos munkavégzésre! Első körben elégtelen (pedig jelest szeretnék)! Aztán kicsit gondolkodik, és felajánlja, hogy csináljam meg „önként” újra a laborgyakorlatot. Ezt illik elfogadni, tehát ugrott a holnapi mozi a csajokkal. Jobb lett volna az egyes! Elérte célját, a végére egyre jobb jegyeket szereztem!

Szeretnék egy kis kedvet csinálni a leendő „tanulmány” elolvasásához, nomeg bemutatnám a regisztrált adatok felhasználását. Alább látható három diagramm a töltés paramétereinek változásáról, amit excelben készítettem egy már említett 2600mAh kapacitású akkumulátorral. A töltési végfeszültség 4.2V-ra lett beállítva. A regisztrált pontok számát 13-ra állítottam. A 10 perces pihentetési szakaszt nem ábrázoltam ezért töltéskor csak 8 pontot regisztrált a műszer. Láthatóan ennyi pont is tökéletesen elég egy használható görbe felrajzolásához! A töltés maximális árama 500mA. A vízszintes tengely az idő múlása percekben. A teljes töltési idő 393 perc, azaz 6,5 óra. Hát igen!! Maximálisan 500mA-el ennyi idő kell!

Hogy teljes legyen a kép, megmutatnám még ugyanennek az akkunak a merítését is (kb. 600mA-el)! Itt már 13 pontból készült a görbe, hiszen a merítés előtti pihentetéssel alig változik az akku feszültsége, szemben a merítés utáni pihentetéssel, ahol 10 perc alatt 2.5V-ról felkúszik 3.5V-ra!

Nemrégiben elkészültem az akkumulátorok tulajdonságainak leírásával. A hőmérséklet függés vizsgálata miatt meg kellett várnom a kánikulát, ezért tartott ilyen sokáig. Összefoglalóm itt olvasható.

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!