Belső EEPROM használata

Amikor először használtam az ATmega chip belső eeprom-ját, nem néztem meg alaposan az eeprom kezelésére rendelkezésre álló könyvtárat. Azt hittem, csak byte-onként lehet írni az eeprom-ba, illetve olvasni onnan. Nehezen boldogultam ez első long változó kiírásával, mert az ugye 4 byte-os, és nem triviális, hogyan kell byte-onként felbontani. Sikerült! Elkezdtem írni egy általános kezelő függvényt, amivel mindenféle típusú változókat lehet használni. Baromság! Egy ennyire gyakran felmerülő problémára már van a világban megoldás, csak szét kell nézni. Persze alaposabban int én tettem! Szerencsére időben felfedeztem, hogy az EEPROM.write() illetve EEPROM.read() függvényeken kívül van más is van ebben a függvény gyűjteményben. Az EEPROM.put() a leírások szerint bármilyen változót beleír az eeprom-ba egy megadott kezdőcímtől kezdve. Az EEPROM.get() pedig kiolvassa azt egy változóba. Persze a memória változóknak azonos típusúaknak kell lennie olvasáskor a beíráskor használt változó típussal.

Írtam hát gyorsan egy próba programot, hogy lássam tényleg működik a dolog. Nagyon kényelmes, mert struktúrákat is felismer és helyesen tárol. A stringekkel már vannak problémák. Lehetséges, hogy az én ismeret hiányom miatt szaladtam bele a tapasztalt problémákba, de az is lehet, hogy az EEPROM.put() tényleg rosszul van megírva. Bárhogy is van, a string-el vigyázni kell. Tapasztalataimat kommentek formájában a programba írtam.

Itt a demóprogram forrása! Nem szabad megijedni a forráskód hosszától, a program nagy része a kommentekből és a soros portra írásból áll. Az eeprom kezelése csak néhány sor.

#include <EEPROM.h>

bool xbool;         //bool típusú változó.
byte xbyte;         //byte típusú változó.
int xint;           //int típusú változó.
long xlong;         //long típusú változó.
float xfloat;       //float típusú változó.
String xszoveg;     //egy karakter stringet is definiálunk
String yszoveg;     //ezt egy működési hiba vizsgálata miatt csináltam
String zszoveg;     //ez is a működési hiba miatt keletkezett
int eeprom_cim;      //ebben a változóban vezetjük a mindíg soron következő címet, ahová írni fogunk az epromban (olvasásnál is ezt használjuk)

struct datum_ido{   //definiálunk datum_ido névvel egy struktúrát, ebben fogjuk tárolni a dátumot és idopontot
  int ev;
  byte ho;
  byte nap;
  byte ora;
  byte perc;
};
datum_ido xdatum_ido;  //készítünk egy datum_ido típusú változót xdatum_ido névvel


void setup(){
  Serial.begin(9600);

/********************************************************************************************************** 
 * A következő ciklus törli a teljes eeprom-ot. Az EEPROM.lenght() megadja az eeprom-unk teljes hosszát.  *
 * Csak akkor futattam, amikor már alaposan teleforkáltam az eeprom-ot, és tiszta lappal akartam indulni  *
 **********************************************************************************************************/
 // for (unsigned int i = 0 ; i < EEPROM.length() ; i++ )
 //   EEPROM.write(i, 0);
 // Serial.print("EEPROM törölve, mérete:");Serial.println(EEPROM.length());


/********************************************************************************************************************************
 * Ebben a programrészben különféle változó típusokat írunk ki az epromba. "eecim" változó mindíg az aktuális címet tartalmazza *
 * ahonnan kezdve írjuk a változót. Azt, hogy hány byte-ot foglalunk el a változó kiírásával, az nekünk kell kiszámolni.        *
 * Ha visszaolvassuk a memória változó tartalmát az eeprom-ból, akkor ki kell számolni a váltzó címét, ahonnan kezdve kiírtuk.  *
 * Ha íráskor rosszul számoljuk ki a következő szabad eeprom terület kezdőcímét, akkor egymásra írhatjuk a változó tartalmakat, *
 * és visszaolvasáskor marhaságokat kaphatunk.                                                                                  *
 * Ha egyszer lefuttatuk ezt a programrészt, akkor ki lehet kommentezni a programból, mert a tárolt értékeket már lehet         *
 * visszaolvasni tetszőleges számban, és ekkor biztosak lehtünk a program működésében.                                         *
 ********************************************************************************************************************************/

  Serial.println("***************** Irunk az eeprom-ba: ");

  xbool = 1;            
  EEPROM.put( eeprom_cim, xbool);
  Serial.print("Bool: ");
  Serial.print( xbool, BIN);                        //xbool kiírása ellenőrzéshez
  Serial.print("  Merete:");
  Serial.println(sizeof(bool));

  xbyte = 66;          
  eeprom_cim=eeprom_cim+1;                          //a bool 1byte-ot foglal, tehát 1 byte-al kell növelni a következő eeprom címet
  EEPROM.put( eeprom_cim, xbyte);
  Serial.print("Byte: ");
  Serial.print( xbyte);                             //xbyte kiírása ellenőrzéshez
  Serial.print("  Merete:");
  Serial.println(sizeof(byte));

  xint = 29567;          
  eeprom_cim=eeprom_cim+1;                          //a byte 1 byte-ot foglal, tehát 1 byte-al kell növelni a következő eeprom címet
  EEPROM.put( eeprom_cim, xint);
  Serial.print("Int: ");
  Serial.print( xint);                              //xint kiírása ellenőrzéshez
  Serial.print("  Merete:");
  Serial.println(sizeof(int));

  xlong = 234567;    
  eeprom_cim=eeprom_cim+2;                          //az int 2 byte-ot foglal, tehát 2 byte-al kell növelni a következő eeprom címet
  EEPROM.put( eeprom_cim, xlong);
  Serial.print("Long:");
  Serial.print( xlong);                             //xlong kiírása ellenőrzéshez
  Serial.print("  Merete:");
  Serial.println(sizeof(long));

  xfloat = 1.2334567;  
  eeprom_cim=eeprom_cim+4;                          //az long 4 byte-ot foglal, tehát 4 byte-al kell növelni a következő eeprom címet
  EEPROM.put( eeprom_cim, xfloat);
  Serial.print("Float: ");
  Serial.print( xfloat,7);                          //xfloat kiírása ellenőrzéshez 7 tizedesjegy hoszan
  Serial.print("  Merete:");
  Serial.println(sizeof(float));

  xdatum_ido.ev=2010;             
  xdatum_ido.ho=8;
  xdatum_ido.nap=11;
  xdatum_ido.ora=6;
  xdatum_ido.perc=1;
  eeprom_cim=eeprom_cim+4;                          //az float 4 byte-ot foglal, tehát 4 byte-al kell növelni a következő eeprom címet
  Serial.print("Datum1:");
  EEPROM.put( eeprom_cim, xdatum_ido);
  Serial.print(xdatum_ido.ev);Serial.print(".");                        
  Serial.print(xdatum_ido.ho);Serial.print("."); 
  Serial.print(xdatum_ido.nap);Serial.print("  ");
  Serial.print(xdatum_ido.ora);Serial.print(":");
  Serial.print(xdatum_ido.perc);
  Serial.print("  Merete:");
  Serial.println(sizeof(datum_ido));

  // Azért csináltam egy második tartalom kiírást, mert a string változónál tapasztalt hibajelenség miatt, szerettem volna megtudni,
  // hogy struktúra esetén is keletkezhetnek hasonló hibák. Itt nincs hiba, tehát a string változó hossza zavarja meg a működést.
  xdatum_ido.ev=2020;             
  xdatum_ido.ho=9;
  xdatum_ido.nap=18;
  xdatum_ido.ora=7;
  xdatum_ido.perc=11;
  eeprom_cim=eeprom_cim+6;                              //az datum_ido 6 byte-ot foglal, tehát 6 byte-al kell növelni a következő eeprom címet
  Serial.print("Datum2:");
  EEPROM.put( eeprom_cim, xdatum_ido);
  Serial.print(xdatum_ido.ev);Serial.print(".");                        
  Serial.print(xdatum_ido.ho);Serial.print("."); 
  Serial.print(xdatum_ido.nap);Serial.print("  ");
  Serial.print(xdatum_ido.ora);Serial.print(":");
  Serial.print(xdatum_ido.perc);
  Serial.print("  Merete:");
  Serial.println(sizeof(datum_ido));
  
  xszoveg="Ez egy szoveg!";   //ha a kovetkező eeprom írásnál nem yszoveg változót használom, akkor nem ezt a tartalmat írja az eeprom-be
  eeprom_cim=eeprom_cim+6;                              
  Serial.print("String1:");
  Serial.print(xszoveg);                        
  EEPROM.put(eeprom_cim,xszoveg );                   
  Serial.print(" Merete:");
  Serial.println(xszoveg.length());   //itt már nem használhatom sizeof() függvényt, mert a string hossza változik
  
  // Ezen a ponton érdekes dolog történt. Ha xszoveg változónak az xszoveg változót használom de új tartalaommal
  // feltöltve, akkor az előző kiírásban megadott "Ez egy szoveg!" helyett a mostani "Ez egy masik szöveg!" tartalmat
  // írja bele az eepromba (mégaz előző kiírásról van szó), de csonkítva az ottani szöveg hosszával. Pedig azon a 
  // programsoron már túl is vagyunk a végrehajtásban. 
  // Sőt, ha csinálok egy zszoveg változót, és annak is adok értéket, akkor már jó a tartalom
  // de levágja az első két karaktert a szövegből. Nyilván az EEPROM.put() belső működéséből fakad a hibejelenség
  // Megoldás, hogy külön szöveges változót kell deklarálni a szövegek kiírásához. 
  //zszoveg="Ez egy szoveg!";       //ennek csa akkor van szerepel, ha xszoveg változóra írom  következő sorokban az yszoveg változót
  yszoveg="Ez egy masik szoveg!";   //nem használhatom az xszoveg változót, mert rossz tartalmat ír az előző szöveg tárolásban (String1).
  eeprom_cim=eeprom_cim+16;           //a string1 változó 16 byte-ot foglal, tehát 16 byte-al kell növelni a következő eeprom címet
  Serial.print("String2:");
  Serial.print(yszoveg);                         
  EEPROM.put(eeprom_cim,yszoveg );                    
  Serial.print(" Merete:");
  Serial.println(yszoveg.length()); //itt már nem használhatom sizeof() függvényt, mert a string hossza változik

/************************************************************************************************************************
 * E a programrész visszaolvassa az előzőkaben letárolt változók értékét. Itt is figyelni kell az egyes változók kezdő  *
 * címeinek helyes kiszámolására. Érdemes kipróbálni, mi történik, ha valahol rossz címet írunk be. pL a dárum típusnál *
 * az előző olvasási címhez ne 4-et, hanem 3-at adjunk hozzá                                                            *
 ************************************************************************************************************************/
  //töröljük a változó értékekekt, hogy lássuk, tényleg működik a visszaolvasás
  //az xszoveg yszoveg változókkal kapcsolatos hibajelenség itt már nem jelentkezik
  //helyesen fogja visszaolvasni a szövegeket az xszoveg változóba. Láthatóan csak az íráskor van gond
  xbool = 0;            
  xbyte = 0;          
  xint = 0;          
  xlong = 0;    
  xfloat = 0;  
  
  xdatum_ido.ev = 0;             //feltöltjük értékekkel az xdatum nevű változót, ami datum típusú
  xdatum_ido.ho=0;
  xdatum_ido.nap=0;
  //xszoveg="";   //Ha ezt az értékadást használom, akkor lecsípi a szöveg első karakterét az első szöveg kiolvasásáakor
                  //Ha az íráskor xszoveg változót használom, akkor a második szöveg kiolvasás első karakterét csípi le.
                  //Mindamelett a második esetben még az első szöveg is rossz tartalommal kerül az eeprom-ba.
                  //Ezek a hibajelenségek csak akkor keletkeznek, ha az eeprom írással foglalkozó programrész működik
                  //ha kikommentezem azt a részt, akkor már nem okoz problémát ez az értékedás. Az EEPROM.put() csinál
                  //valamit a memóriában, ami megzavarja a működést.
  
  eeprom_cim=0;    //eeprom kezdőcím ujra 0

  Serial.println("***************** Olvasunk az eeprom-bol:");

  Serial.print("Bool:");
  EEPROM.get( eeprom_cim, xbool );
  Serial.println( xbool, BIN );  

  eeprom_cim=eeprom_cim+1; 
  Serial.print("Byte:");
  EEPROM.get( eeprom_cim, xbyte );
  Serial.println( xbyte);  //

  eeprom_cim=eeprom_cim+1; 
  Serial.print("Int:");
  EEPROM.get( eeprom_cim, xint );
  Serial.println( xint);  //

  eeprom_cim=eeprom_cim+2; 
  Serial.print("Long:");
  EEPROM.get( eeprom_cim, xlong );
  Serial.println( xlong);  //

  eeprom_cim=eeprom_cim+4; 
  Serial.print("Float:");
  EEPROM.get( eeprom_cim, xfloat );
  Serial.println( xfloat,7);  //

  eeprom_cim=eeprom_cim+4; 
  Serial.print("Datum1:");
  EEPROM.get(eeprom_cim, xdatum_ido );
  Serial.print(xdatum_ido.ev);Serial.print(".");                         
  Serial.print(xdatum_ido.ho);Serial.print(".");                        
  Serial.print(xdatum_ido.nap);Serial.print("  ");                       
  Serial.print(xdatum_ido.ora);Serial.print(":");                       
  Serial.println(xdatum_ido.perc);  
                       
  eeprom_cim=eeprom_cim+6; 
  Serial.print("Datum2:");
  EEPROM.get(eeprom_cim, xdatum_ido );
  Serial.print(xdatum_ido.ev);Serial.print(".");                         
  Serial.print(xdatum_ido.ho);Serial.print(".");                        
  Serial.print(xdatum_ido.nap);Serial.print("  ");                       
  Serial.print(xdatum_ido.ora);Serial.print(":");                       
  Serial.println(xdatum_ido.perc);                       

  eeprom_cim=eeprom_cim+6; 
  Serial.print("String1:");
  EEPROM.get(eeprom_cim,yszoveg );
  Serial.println(yszoveg);  //

  eeprom_cim=eeprom_cim+16; 
  Serial.print("String2:");
  EEPROM.get(eeprom_cim,yszoveg );
  Serial.println(yszoveg);  //

}

void loop(){ /* ez most üres */ }

Ritkán működő gépek monitorozása V2.0

Nemrégiben vásároltam egy 16×1 LCD kijelzőt. Kiderült, hogy nem működik a háttér világítása. Kis méregetés után kiderült, hogy a kijelzőbe beépített led tropa. Kapargattam egy kicsit az üveglap élét, és kiderült, hogy a kijelző javítható, csak be kell építeni egy új ledet. A javítás abból állt, hogy a kijelző üveglapjának pereméről lekapartam a feséket, és oda ragasztottam egy ledet, Ha ez világit, egész jól látható a kijelzett szöveg. Nem tökéletes, de használható. Így néz ki a preparált LCD:

Sajnos beépítés előtt elfelejtettem lefotózni a kijelző oldalt, így a led a képen nem látszik, de talán el lehet képzelni mi történt!

Mivel ezen a kijelzőn nem lehetett szoftveres úton ki/be kapcsolni a háttér világítást, keresni kezdtem valamilyen értelmes felhasználást. Pont előtte néhány nappal szükségem volt a szivattyú monitorozó áramkörbe épített áram érzékelő feszültség adatára, de sehogyan sem tudtam elérni, hogy a szivattyú működjön és közben mérjem az érzékelő feszültség kimenetét. Ahhoz, hogy járjon a szivattyú a vízszint érzékelőjét meg kell „emelni”, de eközben nem állhatok mérőműszerrel 5 méterrel távolabb. Ez a bosszantó probléma, ráébresztett arra, hogy a szivattyú monitorozó áramkörömben milyen jól felhasználhatnám a félig roncs LCD-t!

Minden maradt ugyanaz, csak kivezettem az ATmega328 lábairól az I2C buszt a kijelzőhöz, és átalakítottam a programot. Ime néhány kép az új kütyüről:

Ezen a kapcsoló dobozon számunkra a 7 szegmenses kijelző, a két nyomógomb és a legalsó sötét csík, az LCD kijelző a lényeg. Van még néhány világítós kapcsoló, azok itt most nem érdekesek.
A vezérlő elektronika egy kicsit átalakult, mert kapott egy külön kapcsoló üzemű tápegység modult a régebbi konnektorba dugható telefontöltő helyett. Hely problémákkal küszködtem, ezért döntöttem a nagyon drága 300Ft-os táp beépítése mellett:

Az áramérzékelő elhelyezése maradt olyan mint volt, de az áramérzékelő alaposabb megismerése alapján kétszer vezettem át a 230V-os vezetéket, így kétszer akkor feszültséget kaptam az egyenirányító kimentén:

Az áram érzékelőnek van egy alap kimenő feszültsége akkor is, amikor a szivattyú nem működik. Bekapcsoláskor ezt megmérem, és tárolom. Ezzel a mért értékkel a későbbiekben korrigálni fogom a mért adatokat. Ez egyben azt is jelenti, hogy az elektronikát akkor szabad bekapcsolni, amikor a szivattyú éppen nem működik. Ez egy áramszünetet követően nem biztos, hogy így is lesz.

Ennek az offset feszültségnek az oka az, hogy a D1 és D2 egyenirányító és a szinteltoló dióda feszültsége nem teljesen azonos. Az áramérzékelő kapcsolása emlékeztetőül:

Hőmérséklet változással a két dióda feszültsége egyre inkább eltér egymástól, ezért ez az offszet még változik is, de ez engem nem nagyon érdekel, nincs szükségem ilyen pontosságra. Tekintve, hogy immár le is tudok olvasni konkrét feszültség értéket, és tudom, hogy ez arányos a szivattyú áramfelvételével, van értelme valahogyan konvertálni a mért adatot. Ehhez feleségem villanyvasalóját használtam. Teljesítmény 1200W, tehát 1200W/230V=5,2A áramot vesz fel. A szivattyú helyett a vasalót kötöttem be, és a mutatott feszültséget feljegyeztem. Ezt követően már volt egy váltószámom, amivel a mért feszültségből közvetlenül áramfelvételt írhatok ki a kijelzőre. Tudom, hogy nem egy pontos megoldás, de nekem ennyi épp elegendő.

 A program jelentősen leegyszerűsödött kivettem belőle a tárolt adatokat „levillogtató” egy karakteres megjelenítő megoldást, hiszen már van LCD kijelző, nem kell a 7 szegmenses kijelzővel hülyéskedni. Az egyik nyomógomb direktben tápot ad egy soros ellenálláson keresztül a kijelző utólag beszerelt led háttér világításának, tehát a gombot nyomni kell folyamatosan, ha látni akarom az adatokat.
A másik nyomógomb váltogatja a regisztrált értékeket, és leolvasgathatom ami érdekel. A következő kijelzések közül lehet válogatni:

A a következőket lehet leolvasni:

  • Ossz.muk.ido: a szivattyú összes működési ideje. Ezt másodperc alapon mérem. Nem ez határozza meg az élettartamot, csak kíváncsi voltam rá!
  • Ossz.mot.indit: A szivattyú beindításának darabszáma a készülék üzembehelyzése óta. Epromban tárolt adat, nem törlődik áramszünetkor.
  • Arammero offset: Bekapcsoláskor a zárójelben látható adatot olvastam ki a konverterből. EZ a tapsztalati konverziós szám meghatározása után (villanyvasalós mérés) a kijelzett áramerősséget jelenti. Ez fals áramerősség, a motor 0A áramot vesz fel, tehát ezzel korrigálni kell a tényleges mért értéket.
  • Mot.erz.kuszob: Előre programban beállított adat, ennél nagyobb áram értéknél tekinti úgy a program, hogy működik a motor.
  • Tularam kuszob: A potenciométerrel beállítható áram érték, ami felett túláram riasztást ad a kijelző tizedes pontja. A túláram valamilyen komolyabb problémára is utalhat a szivattyúval, pl. beleakadt egy kő a darálóba stb.
  • Tularamok szama: Hány túláram jelzés volt a működési időben.
  • Uts.max.mot.aram: Ezért az adatért csináltam az egészet. Látható, hogy a szvattyú 3,72 A áramot vesz fel (legalább is utoljára ennyit), így teljesítménye 850W. És ez igaz, az adatlapja szerint 800W-os. Nem is tévedtem olyan nagyot a villanyvasalóval!
  • Motoraram: Az éppen aktuális motoráram. Most épp nem megy a szivattyú. Az a 0,02A abból fakad, hogy az offszet feszültség korrigálása nem teljesen pontos, az offszet kicsit mindig változik.
  • 24H inditasszam: Ezt az adatot jelzi ki a 7 szegmens kijelző is. Nálunk ez reggel általában 3 szokott lenni, mert az esti zuhanyzással elég sok vizet fogyasztunk. Délután ez az érték már csak 2.

És ha valakit érdekel, itt a forráskód is:

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

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


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

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


void setup()
{
  Serial.begin(9600);
  analogReference(DEFAULT);  //A tápfeszt veszi referenciának (5V)
  Wire.begin();  // I2C busz használat indítása
  lcd.begin(20,4  ); //LCD inicializálása
//  lcd.noBacklight();      //háttérvilágítás bekapcsolása
  lcd.backlight();      //háttérvilágítás bekapcsolása

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

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

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

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

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

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

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

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



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


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

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

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

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

Ritkán működő gépek ellenőrzése

(Szennyvíz szivattyú működésének megfigyelése)

Az áramkör fejlesztését és megépítését azért kezdtem el, mert tönkrement a házunk átemelő szennyvíz szivattyúja. Nem vettük észre, hogy egy visszacsapó szelep eltömődése miatt a szennyvíz hálózatból visszafolyt a víz, és a szivattyú pár percenként újra és újra ürítette a tartályt. A folyamat kb. egy-két hónapig tartott. A villanyszámlán jelent meg az első árulkodó nyom (kb. duplájára nőtt a fogyasztás). Aztán a rengeteg indításól végképp megadta magát a motor, és elárasztott minket a visszafolyó szennyvíz. Az egész folyamatból semmit nem vettünk észre, mivel a szivattyú egy földalatti aknában üzemelt. Visszajelzés nem volt sehol arról, hogy éppen mikor jár a motor, egy nap hányszor indul el stb.. Pedig ha láttam volna, mi történik a föld alatt, a visszacsapó szelepbe beszorult ágdarab kipiszkálásával meg tudtam volna menteni a szivattyút. Az elfogyasztott felesleges energia is kb. 10.000-20.000Ft-ba került.

A ritkán (naponta néhány alkalommal) működő folyamatok megfigyelése egyébként is nehéz feladat. Ha egy gépnek normális esetben naponta egy-két alkalommal kell beindulnia, nagyon nehéz észre venni, ha ennél többször működik. Pedig a beindulások számának növekedése mindig valamilyen meghibásodásra, rendellenességre utal. Az én szennyvíz szivattyúm kb. 25.000Ft-os érték. Szerintem már ezt is érdemes „védeni” egy 2000Ftos áramkörrel!

Azt találtam ki, hogy az utolsó 24 óra indításszámát jelzem ki. Ennek a kijelzési módnak nagy előnye, hogy nem kell a rendszerbe óra. Dönthettem volna úgy is, hogy pl. az aktuális nap indításszámait jelzem ki, és éjfélkor nullázom az értéket, de ekkor órát kellet volna beépíteni. Lehetett volna az összes indításszámot kijelezni, akkor viszont több számjegy kijelzésére alkalmas eszközt (pl LCD, vagy több darab hétszegmenses kijelzőt) is kellett volna építeni az áramkörbe. Ha nem teszek be órát, csak a szumma indításszámot jelzem ki, az nem jó megoldás. Ki tudná megjegyezni, mennyi is volt tegnap a számláló állása. Így viszont az egész kütyü egy fiókból előkerült „hulladék” telefontöltővel kb. 2000Ft-ból elkészült! Egy kicsit azért lódítok, mert volt abban a fiókban némi nyáklemez, nyomógombok, és végül külön tápegységet is csináltam, mert nem telefontöltőm volt, csak egy 12V-os konnektorba dugható trafó. Mondjuk 3000Ft!

Az áramkör működése:

  1. Amikor a szivattyú beindul a program méri a működési időt és eggyel növeli az indítások számlálóját.
  2. A szivattyú működése közben az alsó négyszegmens „forgó” mozgást szimulálva jelzi a működést (ez nem kell, csak látványosabb, ha az ismerősöknek dicsekedni akarok).
  3. Egy hétszegmenses kijelzőn jelzi az utolsó 24 óra indításainak számát. Az én esetemben az a normális, ha maximum 2-3-at látok. (átlagos víz használattal maximum kétszer háromszor kell ürítenie egy nap). Ha a szivattyú indítás száma ennél nagyobb, pl. 4, akkor már valami baj van. Ha az ürítések száma több mint 8, akkor a kijelzett 8-as számjegyet a program feltűnően villogtatja. Azért a 8-at villogtatom és nem a 9-et, mert szerintem a 8-as számjegy elegánsabb hibajelzésre. (Ez volt a marketing, valójában így sikerült, és nem írtam át a programot utólag, mikor észrevettem).
  4. Többször előfordult az a szivattyúnkkal, hogy a visszacsapó szelep „ideiglenesen”  fennakadt. Ekkor a szivattyú többször is leürített. Aztán a szennyeződés átvergődött a rendszeren, és visszaállt a normális állapot, a visszacsapó szelep nem engedte vissza a szennyvizet, és nem kellett a továbbiakban a szivattyúnak sokszor ürítenie. Ha netán nem vennénk észre a villogó „8”-as jelzést és eltelik egy nap, megszűnik a villogó kijelzés, hiszen egy nap elteltével a régebbi indításokat „elfelejti” a rendszer. Engem megnyugtat, ha az ilyen abnormális esetekről utólag is tudok, ezért a kijelző tizedespontja folyamatosan villog egy kijelző “túlcsordulás” után, ezzel memorizálja a sok szivattyú beindulást. A villogás bármelyik lekérdező nyomógomb megnyomásával megszűnik. Természetesen egy áramszünet hatására is eltűnik a jelzés.
  5. Az áramkör üzembe helyezését követően az indításszámot és az összes működési időt eeprom-ban tárolja a program. Ezeknek az értékeknek a lekérdezésére két nyomógomb szolgál. Az egyiket lenyomva az egy db hétszegmenses kijelzőn négy egymás utáni számjegy felvillantással kijelzi az indítás számot, a másik nyomógomb megnyomásával pedig az összesített szivattyú működési időt percekben. A maximális kijelezhető indítás szám 9999, a működési idő pedig 9999 perc vagyis 166 óra. Feltételezve, hogy normális esetben napi maximum 2 indítás fordul elő, ezek az értékek 13 év működést tesznek lehetővé gyári alapbeállítás, vagyis tároló törlés nélkül (nálunk a szivattyú kb. fél perc alatt ürít le).
  6. Található egy bemenet, melyen egy reset gomb segítségével gyári alapbeállítást, azaz számláló törlést lehet végezni. Erre az én estemben kb. 10 évente lesz szükség. A reset gombot a bekapcsolás közben kell nyomva tartani mindaddig, amíg a hétszegmenses kijelző tizedespontja kb. egy másodpercig villogni kezd. később a program már nem vizsgálja a nyomógombot, véletlen megnyomás nem törli az adatokat.
  7. A szivattyú indításokat a program eeprom-ba összegzi. Az én esetemben az eeprom élettartama (100.000 írás) kb. 100 év élettartamot jósol, ezért írok minden szivattyú leállást követően. Azonban segítségül azoknak, akik gyakoribb folyamatot akarnak monitorozni, beépítettem egy algoritmust, ami csak minden 5. esetben írja ki az adatokat eeprom-ba. Ez az üzemmód a programban egy konstans átállításával kapcsolható be (eepromvedelem=1). Ha ezt használjuk, akkor azonban az áramkört elemmel kell védeni az áramkimaradástól, különben az utolsó (max) öt indítás adatai elvesznek, vagy el kell fogadni, hogy elvesznek.
  8. Van az áramkörnek még egy bemenete, amit a program folyamatosan figyel. Ha ezen LOW szintet talál, akkor a kijelzőn az indításszámot, és egy vészjelzést jelenit meg felváltva 2,4 másodperces ciklusokban. A vészjelzés a három vízszintes szegmens egymást követő felvillantása. Esetemben van a szennyvíz tartályban egy vízszint jelző, ami akkor kapcsol be, ha a vízszint elér egy magasságot. Ez csak akkor történhet meg, ha a szivattyú nem tudja szennyvizet leüríteni, és a vízszint a normális magasság fölé emelkedik. Nálunk a vészjelzés észrevételére kb. egy nap áll rendelkezésre, ezt követően, már folyik a trutyi az udvaron.

A program felépítése és működése:

A program nagyon egyszerű, de kicsit nehezen követhető a működése, mert megoldásomban nem használtam delay() függvényt az időzítésekre. A szivattyú működését jelző bemenetet a program 1 másodpercenként figyeli. Ha a bemeneten LOW szintet talál, az jelzi, hogy a szivattyú éppen működik. Természetesen delay() függvényt nem használhattam, mert összesen 4 bemenetet kell figyelni folyamatosan. Annyira lassúak a folyamatok, hogy nem okozott volna nagy hibát az időmérésben, indításszám számlálásban az sem, ha egyszerre csak egy folyamattal foglalkozik a program, de így szebb a megoldás. Ha jár a szivattyú, akkor is lehet indításszámot vagy működési időt lekérdezni, és a lekérdezések alatt is működhet a szivattyú figyelése, és a működési idő mérése.

A fő ciklus a nyomógomb lenyomásokat és állapotokat figyelgeti, és szükség esetén meghívja a megfelelő függvényeket.

A két legfontosabb függvény:

  • inditas_szamalalo()

Három 10 elemű tömbben tárolja az utolsó 9 indítás adatait:

  1. Működési idő: bekapcsolva[]
  2. A bekapcsolások között eltelt időt: ido[]
  3. Segéd változó az eeprom kímélés céljából: tarolasjelzes[]

A 0. tömbelem az „ido” változóban a két indítás közötti időt tárolja, értéke másodpercenként nő eggyel. Ha a motor épp elindul, akkor az ido[0] változóba találjuk az utolsó bekapcsolás óta eltelt időt és elindítjuk a szivattyú működési idő mérését (bekapcs_ido). Amikor a szivattyú leáll, akkor a bekapcs_ido változó tartalmát (ami szintén másodpercekben tartalmazza a működési időt) beleírjuk a bekapcsolva[0] tömbelembe. Ezt követően minden tömbelemet átírunk az eggyel nagyobb indexű tömbelembe. Gyakorlatilag az értékek minden indításkor egyel feljebb lépnek a tömbökben. Ebben a mini adatbázisban tehát az ido nevű tömbünkben az egyes indítások közötti időt találjuk másodperces felbontásban. Amikor a függvény másodpercenként lefut, sorban összeadja (0 tömbindextől kezdve) az eltelt időket. Ha ezek együtt meghaladják a 24 órát (86400 másodpercet), akkor befejezzük az összegzést és az a tömbindex ahol éppen járunk, megadja az utolsó 24 óra indításainak számát. Ezt az inditas_db változóba frissíti a függvény minden lefutáskor.

a tarolasjelzes[] tömbbe minden 5. indításkor 1-et írunk és ha ez az „1” érték eléri az utolsó tömbelemet (tarolasjelzes[9]=1), akkor eepromba írjuk az adatokat. Persze csak akkor, ha az eeprom kímélés be van kapcsolva (#define eepromkimels 1). Ha az eeprom kímélés nincs bekapcsolva, akkor minden motor leállításkor írjuk az új adatokat az eeprom-ba. Az eeprom írás és olvasás okozott fejtörést, míg valaki a TavIr fórumban segített az EEPROMWritelong és EEPROMReadlong függvényekkel. A probléma az volt, hogy long változókat kell tárolni és kiolvasni. A tárolható működési idő másodperc alapon így elegendően hosszú lehet.

  • lekerdezes()

Ez a függvény a két lekérdező nyomógomb megnyomásakor fut le. Kiolvassa az eeprom-ból az indításszámot vagy a működési időt, átalakítja a számértékeket 4 jegyű szöveges számértékké (pl.: „0123”), és a négy karakteres számjegyet sorban kijelzi a hétszegmenses kijelzőn. A setup() részben a szegmens[] tömbben tulajdon képen egy mini karaktergenerátort töltök fel, minden számjegynek és egyéb kijelzett jelzés állapotnak van egy 7 jegyből álló szegmens érték blokkja. A setup() részben ezt azt hiszem érthető módon kommenteztem. Mivel közös anódos a kijelzőm, a 0 érték jelzi a világító szegmenst.

Remélem a kommentek alapján megérthető a működés. Persze ez csak akkor kell, ha valamiért változtatni kell a programon. Pl. ha valakinek lényegesen gyorsabb folyamatot kell monitoroznia, akkor az időmérő tömb elemeit növelni kell. Lehet azonban úgy is gondolkodni, hogy pl. az utolsó egy óra indításszámát jelezzük ki. Ehhez csak a 86400 másodperc értékét kell 3600-ra csökkenteni. Teszteléskor én ezt az értéket 60-ra állítottam, és így „gyorsítottam” fel az időt. Vadul nyomkodtam a szivattyú működését jelző nyomógombot, és ezzel növeltem a kijelzett számértéket, miközben a program azokat az indításokat, melyek már több mint egy perce voltak sorban ejtette ki az összeszámolt indítás számból. Természetesen kipróbáltam 24 órás idővel is a tesztelési időszakban.
Külön kapcsolási rajzot nem készítettem, mert nincs ilyen programom. Annyira egyszerű a kapcsolás, hogy nincs is szükség rá. A programban a kommentekben leírtam, mit hová kell kapcsolni. Ha most kezdeném el az építést, akkor nem ATmega328P chip-et használnék, hanem Arduino nano-t. Ott meg a kivezetések számozása más mint a chip kivezetések számozása, így a kapcsolási rajz végképp értelmetlen. A fotókból azért némi segítség kapható.

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

//szivattyú működésérzékelő bemenet
//hétszegmenses kijlző kimenetek
// szegmens betüjele:  A,  F,   B,   G,   E,   C ,  D
//chip kivezetés:         13, 14, 15, 16, 17, 18, 19 
//Arduino kivezetés:    7,  8,   9,   10, 11, 12, 13
  pinMode(7,OUTPUT);  //13. chip kivezetés
  pinMode(8,OUTPUT);  //14. chip kivezetés
  pinMode(9,OUTPUT);  //15. chip kivezetés
  pinMode(10,OUTPUT);  //16. chip kivezetés
  pinMode(11,OUTPUT);  //17. chip kivezetés
  pinMode(12,OUTPUT);  //18. chip kivezetés
  pinMode(13,OUTPUT);  //19. chip kivezetés

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

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



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

}

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


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

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


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

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

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

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

//Serial.println();
}

Fontos lehet még, hogyan is figyeltem a szivattyú működését. A szivattyúnak egy beépített vízszint kapcsolója van. Ha magas a vízszint, akkor bekapcsol, ha lecsökkent ürítéskor a vízszint, akkor meg kikapcsol. Az egész egy levegővel töltött műanyag buborék, ami úszik a vízen (szaron), és ki be kapcsolgatja a szivattyút! Valamilyen módon meg kellett állapítani, hogy a szivattyú éppen energiát vesz fel a rendszerből Fontos szempont még az érintésvédelem, hiszen egy 230V-os szivattyút nem szerencsés semmilyen módon összekötni az Arduino-val. Akkor, amikor készítettem ezt a kütyüt, még nem tudtam, hogy kapható ez:

Forrás: https://www.aliexpress.com/item/32950949653.html?spm=a2g0s.9042311.0.0.27424c4d9KXUGZ

Ez egy olyan szerkezet, aminek a fekete színű tekercsének a belső nyílásán (nem látszik a képen), át kell vezetni a szivattyú vagy más villamos gép egyik vezetékét. Csak az egyiket, és nem a védőföldet. Az a kis fekete tekercs transzformátorként működik, kicsi feszültség indukálódik benne, amikor a szivattyú működik, amit az elektronika felerősít, és ha egy beállított értéknél nagyobb a feszültség meghúz a relé. Ezt ugyan túláram jelzésre találták ki, de ha elegendően feltekerjük a potméterrel az érzékenységet, akkor folyamatosan meghúzva tartja a relét a szivattyú működés közben. Már csak a relét kell bekötni az ATmega megfelelő bemenetére mintha egy nyomógomb lenne, és megtörtént a leválasztásos működés jelzés.

Én azonban akkor még nem tudtam, hogy ez készen kapható, és a Hobbielektronika weboldal fórumán valakitől megkaptam ezt a kapcsolást:

Ez lényegében ugyanaz, mint a kész áramkör. A tekercsben keletkező feszültséget egyenirányítani kell, és az Arduino analóg bemenetén már lehet is mérni a feszültséget. A D1 diódára azért van szükség, mert 0,7V-al megemeli a tekercsben indukálódott áramból R1 ellenálláson keletkező feszültséget, pont annyival amekkora a D2 nyitófeszültsége. Így az egyenirányító már 0V-tól kezdve “egyenirányít”. Hogy biztosan ne legyen 5V-nál magasabb a feszültség a tekercs után, én még betettem egy 4,7V-os zener diódát védelemnek, ez nincs a rajzon. Az egész áramkör a vezérlővel együtt így néz ki:

A tekercs áramérzékelő néven bármelyik trafikban kapható (trafik alatt elektronikai boltot értek pl. Lomex). Kb 1000 menetes kell legyen a tekercs, ekkor ha átvezetjük a szivattyú egyik vezetékét a lukon, a szivattyú 5A-es árama néhány volt feszültséget fog előállítani a R1 100 ohm-os terhelő ellenálláson. Egyszerű kiszámolni. A tekercsben (mert ez egy áramváltó valójában) 5A/1000=5mA áram fog folyni. Ez 100 ohm ellenálláson 0,5V feszültséget csinál. Már ez is bőven elég, de ha mégis kevés lenne (mondjuk nem szivattyú és kisebb az áramfelvétele) akkor fűzzük át a lyukon a vezetéket kétszer, vagy háromszor is. A feszültség 2-3-szor nagyobb lesz!

Az áramkörre beépítettem a teszteléshez nyomógombokat. Ezek simán párhuzamosan kapcsolódtak azokkal a nyomógombokkal, amiket a készülék dobozára szereltem. A nyomógombok vezetékeit csatlakozókkal dugtam a panelre, hogy az egész szétszedhető legyen. Csináltam RESET gombot is, ezzel lehet törölni az eeprom-ban tárolt adatokat, ha bekapcsoláskor nyomva tartom.A kijelzőt beforrasztottam, ahhoz nem volt csatlakozóm. A potméterrel a túláram küszöböt lehet beállítani. Ha kiugróan magas áramot vesz fel a motor, akkor kigyullad a kijelzőn a tizedespont. A tápegység 5V feszültséget állít elő egy 7805 táp IC-vel. A kimenetére egy led-et is beforrasztottam, lássam ha áram alatt van a szerkezet. A bal oldalon alul látható piros.fekete vezeték az áramérzékelő vezetéke. A bekötésnél látható a két dióda és a szükséges kondik ellenállások. Az ATmega328 chip közelében van a 4,7V-os zener dióda, ami az esetleges tól nagy jelfeszültségektől védi a chip-et!

Memória típusok

Mielőtt külső memória használatába kezdünk, érdemes kicsit feleleveníteni a memóriákkal kapcsolatos tudásunkat. Lássuk sorban a jellegzetes memóriák működését és tulajdonságaikat. A DRAM memóriával nem foglalkozok, mert az Arduino környezetben nem feltétlenül praktikus, inkább a számítógépek világában elterjedt típus.

SRAM: (pl. PCF8570). A memóriában az egyes bitek tárolását minimum 6 tranzisztorral megvalósított logikai (flip-flop) áramkör tárolja. A memória élettartama gyakorlatilag végtelen, azonban tápfeszültség kikapcsoláskor elveszti a tartalmát. Az adatlap akkumulátoros táplálást javasol a következő áramköri megoldással:

Az SRAM sebességével sem lehet kifogás. Az áramkörök átbillenéséhez néhány nanosec szükséges, így ez a ram típus nagy sebességű felhasználásokra is alkalmas. Pl. az ATmega chip-be épített ram is ilyen elven működik. Ebben a ram-ban tárolja a program változókat futás közben. Mivel egyetlen bit tárolásához elég sok tranzisztor kell, nagy a chip-ben elfoglalt alapterület, így ez a ram nem gyártható gazdaságosan nagy kapacitásban. Persze felmerül a kérdés, hogy mi a „nagy” kapacitás. A mai világban azt hiszem a gigabyte már nagynak számít!

EEPROM: (pl. 24LC04B): Egyetlen bit tárolásához két tranzisztor szükséges. A tároló cella egy CMOS tranzisztor, aminek a vezérlő gate elektródája felett egy szigetelő rétegben kialakított térrészbe kerül programozáskor elektron többlet. Ha van elektron a térrészben az 1, ha nincs az 0. Törléskor az elektronokat fordított feszültséggel eltávolítják ebből a szigetelőanyaggal körülvett „eltemetett” rétegből. Sajnos a töltések beinnyektálása és kiszivattyúzása közben egyre több elektron esik csapdába a szigetelő anyagban is, és ezek egy idő után olyan mértékben halmozódnak fel, hogy már nem lehet a 0 és 1 állapotot megkülönböztetni. Ekkor az EEPROM cella már nem használható tovább. Ez általában 100 ezer és 1 millió írás környékén következik be attól függően, milyen vékony a szigetelő anyag. Nyilván a vékonyabb szigetelő rétegű EEPROM jobb minőségű és többször írható. Mondanom sem kell, hogy az EEPROM tápfeszültség kikapcsolásakor is megtartja a tárolt információt, hiszen az eltemetett rétegben tárolt elektronok maguktól nem tudnak kijönni, Illetve idővel mégis megszöknek, de ez kb. 10 év után eredményezi a tárolt információ megváltozását. Tehát egy EEPROM-ban nem célszerű fiatalkori fényképeinket archiválni, mert unokáink már nem tudják majd kiolvasni és megnézni, milyen szépek is voltunk valaha. AZ USB pendrive-ok is EEPROM cellákban tárolnak, így javasolt néhány évente újra írni bennük a tárolt információt. Nagy előnye az EEPROM típusú ramnak, hogy 3,3V-os és 5V-os rendszerekhez is közvetlenül illeszthető. Az ATmega chipekben is találunk EEPROM memóriát. Ez gyengébb minőségű, kb. 100ezer újraírást visel el. Meg kell még említeni a sebességét is. Sajnos az EEPROM nem egy gyors memória néhány milisec egy cella megírása. Nyilván egy byte-ot párhuzamosan írnak, de már néhány kbyte adat tárolása is tetemes időt vesz igénybe. Minél több cellát írunk egyszerre, annál gyorsabb lesz a EEPROM működése, így érdemes kompromisszumot kialakítani. Egy ilyen kompromisszum eredménye a flash memória.  

FLASH MEMÓRIA: Ez tulajdonképpen az EEPROM speciális változata. A tároló cellákat blokkokba szervezik, és egy-egy blokkot egyszerre lehet írni és törlni. Ettől az írás sebessége nagyon nagy lehet. Azonban a blokkban található tároló elemek esetlegesen akkor is törlésre és írásra kerülnek, ha az adott cellát éppen nem akarjuk írni. Így aztán a flash memóriát inkább nagyobb állományok tárolására célszerű használni. Nem is véletlen, hogy pl. az USB pendrive-ok és SD kártyák flash memóriát tartalmaznak.

SD kártya: Ez valójában egy flash memória, így nem meglepő, hogy átlagosan 100.000 újraírást garantálnak a gyártók. Nem is kell több, hiszen fényképezőgépekkel, mp3 lejátszókkal ennek töredékét sem tudjuk elhasználni. Óriási tároló kapacitásúak, ezért adatgyűjtésre, adatrögzítésre ideálisak. Arduino amatőrök számára nagy hátrány (vagy épp előny), hogy a rendelkezésre álló SD kártya kezelő program könyvtárak fileok kezelésére lettek kialakítva. Egy SD kártyára nem lehet csak egyszerűen adatokat írni, állományokat (pl. txt vagy csv) kell létrehozni, és azok tartalmába lehet adatokat beleírni. Visszaolvasáskor meg kell nyitni az állományt és meg kell keresni az adatot. Sok esetben ez a cél, de a program meglehetősen bonyolult tud lenni. Ráadásul az SD kártya kezelő függvények rengeteg program memóriát esznek meg (kb. 10Kbyte), tehát egy-egy mérési adat vagy program paraméter tárolására ez nem igazán jó. Ha sok-sok mérési eredményt akarunk valahol rögzíteni, akkor megtaláltuk az igazi eszközt.

F-RAM: (pl. FM24C04A). Az írások száma (100.000.000.000.000)  10 a tizennegyediken. Működési elve szerint a tároló elem egy feroelektromos hatással megspékelt kondenzátor (nem mágneses elvű, mágneses térre nem érzékeny!!). Ennek a kondenzátornak a fegyverzetei között egy ólom-cirkonát-titanát réteget alakítanak ki (nyugi, én is olvastam, nem magamtól tudok ilyen okos dolgokat). Ebben a rétegben a molekulák képesek az elektromos tér irányába befordulni, tehát az anyag emlékezik. Amikor feszültséget kapcsolunk egy emlékező kondenzátorra, és a molekulák „befordult” állapotban vannak (1-et tárol a tároló elem), a molekulák visszafordulásához plusz energia kell. Ilyenkor a kondenzátorba befolyó áram egy extra áram impulzust tartalmaz. Ha a molekula éppen fordítva állt (0-át tárol), akkor nincs áram impulzus. Természetesen a kiolvasással azonnal el is veszítettük a tárolt információt, tehát minden egyes kiolvasás után vissza kell írni a z információt a kondenzátorba, ami egy megfelelő feszültség rákapcsolásával történik meg. Ha 0 volt a kondenzátorban tárolt érték, akkor természetesen nem kell csinálni semmit, csak akkor kell vissza írni, ha a 1 volt a tartalom. Ennek a RAM típusnak hátránya lehet, hogy 4V és 5,5V közötti feszültség tartományban működik, így 3,3V-os rendszerekben nem lehet használni, vagy szintillesztőre lesz szükség, és külön 5V-os tápegységre. Olvastam valahol, hogy már kifejlesztették a 3V-on is működő FRAM típust, de még nem találkoztam vele. Az FRAM nagyon gyors. Közel az SRAM sebességével képes működni, így nagyon ígéretes típus, de még nehézségekbe ütközik a nagy méret gyártása. Ennek oka, hogy a kondenzátornak a térfogatát nem lehet tetszőlegesen kicsire készíteni, mert ha csökkentjük a befordulásra képes molekulák számát, csökken az áramimpulzus nagysága is, amit viszont érzékelni kell.
Érdekes történelmi analógia, hogy a távoli múltban a számítógépek hőskorában egy hasonló elven működő memória típussal kezdődött a ram-ok története. Ferritgyűrűs memóriának hívták. 1-2mm átmérőjű ferrit gyűrűkön vezettek át vezetékeket, és áram impulzusokkal felmágnesezték az a gyűrűt. Kiolvasáskor érzékelték, hogy mágneses volt, vagy sem. 1Kbyte memória kapacitás egy utazó bőrönd méret. Minden bizonnyal azért lett a Pentagon a világ legnagyobb épülete, mert kellett az atomrakéták irányításához 10Mbyte memória!
F-RAM-ot már használtam. Készítettem is hozzá egy univerzális függvényt, amit különféle tároló cella típusok létrehozására lehet használni. Lehetséges “beledobálni” az F-RAM-ba értékeket, és kiolvasáskor a függvény visszaadja az értékek maximumát, minimumát átlagát. Talán másoknak is felmerülnek hasonló feladatai mint nekem.