DS3231 óra riasztás használata

Nem gondoltam, hogy valaha szükségem lesz a DS3231 óra alapos megismerésére, azonban mindennek eljön egyszer az ideje. Szeretnék egy beállítható idővel működő mérés adatgyűjtőt készíteni, ami adatokat rögzít SD kártyára. Ráadásul szeretném, ha akkumulátorról működne, amihez az Arduino-t alvás állapotában kell tartani két mérés között. Ehhez a DS3231 óra megszakítás funkciójára lesz szükség. Be kell programozni egy időpontot, és amikor ez elérkezik az óra felébreszti az Arduino-t, illetve ez biztosan egy ATmega328P chip lesz, mert az Arduino nano, vagy uno nagyon sokat fogyaszt. A DS3231 működésének megismerése bonyolultabb volt, mint gondoltam.

Lássuk először a részletes működést. Elővettem az adatlapját, és lefordítottam magamnak a szükséges részeket. Sajnos a fordítás során több mindent félreértettem, ezeket a hibákat javítottam, ahol kiderült. Bízom benne, hogy már jó a szöveganyag, és lehet vele dolgozni. Talán másnak is hasznos!

A DS3231 címezhető regiszterei:

Az alábbi ábra a DS3231 regisztereit mutatja. Több bájtos hozzáférés során, amikor a cím mutatója eléri a regiszter terület végét (12h), a következő beolvasott regiszter újra a 00h lesz. Az I2C START esetén, vagy ha több bájt olvasásakor a cím újra 00h címre kerül, az aktuális idő átkerül egy második regiszterkészletbe. Az időinformációt ezekből a másodlagos regiszterekből olvassa ki, miközben az óra továbbra is futhat. Ez kiküszöböli azt a problémát, hogy a regiszterek olvasása közben egyes bitek megváltozhatnak, ami miatt a kiolvasott érték nem lesz következetes (pl. 59 másodperc kiolvasása után az eltelt rövid időben a perc kiolvasásakor a perc már 1-el megnövekedhet, míg a másodperc 00-ra változik, ezért a kiolvasott érték 1 perccel későbbi időpontot mutatna).

Idő is dátum regiszterek (00h-06h):

Az idő és a naptár adatait a megfelelő regiszter bájtok kiolvasásával kapjuk meg. Az idő és a dátum adatokat a megfelelő regiszter bájtok írásával lehet beállítani. Az idő- és naptár regiszterek tartalma bináris kódolású decimális (BCD) formátumban kerül tárolásra.Magyarázatok a regiszterekben található jelző bitetekhez (12/24, AM/PM, Century), és a regiszter értékek kezeléséhez:

  • A DS3231 12 vagy 24 órás üzemmódban is működtethető. Az aktuális idő óráját mutató regiszter (02h) 6. bitje határozza meg, hogy 12 vagy 24 órás üzemmódban működik az óra. Ha ez 1, akkor a 12 órás üzemmód érvényes. 12 órás üzemmódban az 5. bit az AM/PM bit határozza meg, hogy a mutatott idő óra értéke délelőttöt vagy délutánt jelent. Amikor értéke 1, az délutánt (PM) jelent. 24 órás módban az 5. bit a második 10 órás bit, mert ekkor az óra tízes számjegye már nem csak 0 és 1 (0-12 óráig), hanem 2 is lehet (20–23 óráig), amihez két bitre van szükség.
  • Az évek kijelzésekor csak az év utolsó két számjegye kerül tárolásra. Az itt található értéket alapértelmezetten 2000-hez kell hozzáadni. Az évszázad bitjét (a 05h hónap regiszter 7. bitjét) akkor kell használni, amikor az éveket mutató regiszter (06h) értékét majd a jövőben 2100-hoz kell hozzáadni, illetve amikor majd 2099 év után az óra átfordul 2100-ra, akkor ez automatikusan bebillen 1-re.
  • A hét napjának változtatása éjfélkor történik. A hét napjának megfelelő értékeket a felhasználó határozza meg, de szekvenciának kell lennie (azaz ha 1 egyenlő vasárnap, akkor 2 egyenlő hétfővel és így tovább).
  • A nem logikus idő, dátumbevitel és hét napjának beírása meghatározatlan működést eredményez.
  • Az idő- és dátumregiszterek olvasásakor vagy írásakor a DS3231 másodlagos (felhasználói) puffereket használ a hibák elkerülésére a belső regiszterek frissítésekor. A visszaszámláló láncot a másodperc regiszter írása törli. Az átfutási problémák elkerülése érdekében az óra, perc és dátum regisztereket 1 másodpercen belül be kell állítani. Az 1 Hz-es négyszöghullámú kimenet, ha engedélyezve van, a másodperc érték beírását követő 500 ms után vált, feltéve, hogy az oszcillátor már fut.

Riasztási regiszterek (07h-0Dh):

Kétféle módon jelzi a DS3231 ha riasztás történt. Egyrészt lekérdezhető a status regiszter (0Fh) A1F és A2F bitjeiből, melyek 1-el jelzik, ha volt riasztás, másrészt ha az INT/SQW kimenet nem a négyszögjel kimenetnek van állítva, akkor alacsony szinttel jelzi a riasztási eseményt. Az INT/SQW névben az INT-et aláhúzassal kellene jelölni, mert ez jelöli, hogy az alacsony szint az aktív. Azonban az aláhúzás a weblapon nem jelent meg, nem küzdöttem vele. A DS3231 két dátum/idő riasztást tartalmaz. A riasztások bekapcsolhatók a riasztás engedélyezésével (AI1E és AI2E a kontroll regiszterben) és a kontrollregiszter INTCN bitjével. Az INT/SQW kimenet aktiválásához egyeznie kell a beállított biteknek az aktuális idő megfelelő bitjeivel (00h-02h, 03h vagy 04h). Az 1. riasztást úgy lehet beállítani, hogy a 07h-tól 0Ah-ig terjedő regiszterek bitjeit állítjuk be:

Az egyes dátum/idő riasztási regiszterek 7. bitjei maszkbitek. Ha az összes riasztáshoz tartozó maszk bit 0, akkor minden beállított értéket figyel az óra. Akkor lesz ilyen esetben riasztás, ha az aktuális idő megfelelő regisztereinek bitjei megegyeznek a dátum/idő riasztás regiszterekben tárolt bitekkel. Ha a maszk biteket 1-re állítjuk, akkor az adott időpont adatot nem nem vesz részt a riasztásban. például, ha az A1M1 maszk bit 1, akkor a hét vagy hónap napjának egyezőségét nem figyeli a rendszer, azaz bármilyen napon bekövetkezik a riasztás, ha az óra, perc és másodperc értékek megegyeznek. A lehetséges beálltásokat az alábbi táblázat tartalmazza.

A fenti táblázat a lehetséges beállításokat mutatja. A táblázatban nem felsorolt ​​konfigurációk logikátlan működést eredményeznek. A DY/DT bit (a hét napja vagy hónap napja regiszter 6. bitje) beállítja, hogy a regiszter 0–5 bitjeiben tárolt riasztási érték a hét napját vagy a hónap dátumát jelenti. Ha a DY/DT = 0, akkor a riasztás a hónap napjával való egyeztetéskor következik be. Ha a DY/DT = 1, akkor a riasztás a hét napjával való egyezéskor következik be.
Ha az aktuális idő regisztereinek bitjei megegyeznek a riasztási regiszter bitjeivel, akkor a megfelelő A1F vagy A2F riasztási jelzőbit 1-re billen. Ha a bit értéke 1, az aktiválja INT/SQW jelet. Az egyezést másodpercenként teszteli a DS3231. Ha azt szeretnénk, hogy a riasztást követően az INT/SQW kimenet újra magas legyen, akkor törölni kell a riasztást az A1F és az A2F bitekben is. Ha nem töröljük, akkor egy következő ciklikus riasztást nem tud jelezni a DS3231.

A 2. riasztást úgy lehet beállítani, hogy a 0Bh – 0Dh regiszterek bitjeit állítjuk be:

Ez a riasztási regiszter abban különbözik az 1. riasztási regisztertől, hogy a másodperc egyezését nem figyeli. A lehetséges riasztási eseteket a következő táblázat tartalmazza:

Control Register (0Eh)

7. bit: Oszcillátor (EOSC) engedélyezése. Ha 0-ra állítjuk, az oszcillátor működik. Ha 1-re állítjuk és a VBAT kivezetésről kap feszültséget a DS3231, akkor az oszcillátor leáll. Ez a bit 0, amikor az IC tápfeszültségét bekapcsoljuk. Ha a DS3231 IC-t a VCC kivezetés táplálja, az oszcillátor mindig be van kapcsolva, az EOSC bit állapotától függetlenül. Ha az EOSC le van tiltva (értéke 1), és a VBAT-ról működik a chip, az összes regiszter tartalma statikus az óra nem jár, de az utolsó érvényes időt őrzik a regiszterek, amíg az elem teljesen le nem merül.

6. bit: Négyszöghullám kimenet engedélyezése (BBSQW) akkumulátoros táplálás esetén. Ha az 1-re van állítva, és a DS3231 a VBAT kivezetésen keresztül kap feszültséget, az INT/SQW kimeneten megjelenik a négyszögjel, akkor is ha nincs feszültség a VCC kivezetésen. Az adatlap eredeti szövegének következő mondata ennek a bitnek a magyarázatához: “When BBSQW is logic 0, the INT/SQW pin goes high impedance when VCC falls below the power-fail trip point.”
Én ezt így fordítottam (értelmeztem): Amikor a BBSQW  0, az INT/SQW kivezetés nagy impedanciát mutat, amikor a VCC a minimális feszültségszint alá esik (kikapcsoljuk a tápfeszt és elemről tápláljuk az IC-t). Arra számítottam, hogy ez utóbbi mondat akkor is igaz, ha nem négyszögjel jön ki a vezetéken, hanem a megszakítás jelet szeretném felhasználni. A kísérleteim alapján azonban ez nem igaz. A mikor lekapcsolom a feszültséget a Vcc lábról, és az IC a Vbat bemeneten kap feszültséget elemről, az INT/SQW kimenet azonnal földre húzza magát. Nem hinném, hogy valamit elrontottam, mert ez egy nagyon egyszerű kapcsolás volt. Az INT/SQW kimenetet egy Arduino lábra kötöttem, ami bemenet. Ezt a bemenetet folyamatosan kérdezgettem, és néztem az állapotát. A DS3231 modulon van beépített felhúzó ellenállás, de gondoltam, hátha rossz, ezért kívülről raktam rá egyet. Ekkor is lemegy földre. Kipróbáltam úgy is, hogy ez a bit 1, de a ugyanaz az eredmény. Ha vissza adom a tápfeszt, azonnal magasra megy, és a risztás is bekövetkezik, tehát az IC működése rendben van attól, hogy egy időre nem volt tápfesz csak elemről. Ez elkeserítő számomra, mert ezek szerint elemről táplált állapotban nem képes megszakítás lábbal riasztani. Bár a leírás csak a négyszögjel kibocsátásról szól, de reménykedtem, hogy ettől még a megszakítás üzemmódban is működik a láb. Úgy tűnik nem. Egyébként a leírás szerint ez a bit 0, amikor a tápfeszültséget bekapcsoljuk.

5. bit: Hőmérséklet (CONV) konvertálása. Ennek a bitnek az 1-re állítása elindítja a hőmérséklet mérést, és a hőmérséklet regiszter (11h és 12h) értékének frissítését, és végrehajtja a TCXO algoritmust, hogy frissítse a kapacitási tömböt az oszcillátorban (ez nem tudom, hogy pontosan mit jelent, de feltehetőleg az óra pontosságának javításához van köze). Ez csak akkor történhet meg, ha az átalakítás még nem zajlik. A felhasználónak ellenőriznie kell a BSY állapotbitet, mielőtt a vezérlőt új TCXO végrehajtás elindítására kényszeríti. A felhasználó által kezdeményezett hőmérséklet-átalakítás nem befolyásolja a belső 64 másodperces frissítési ciklust. A felhasználó által kezdeményezett hőmérséklet-átalakítás kb. 2ms-ig nem befolyásolja a BSY-bitet. A CONV bit az íródástól az átalakítás befejezéséig 1-en marad, ez után CONV és  BSY 0-ra megy. A CONV bitet kell használni a felhasználó által kezdeményezett konverzió állapotának figyelésére.

Bit 4. és 3.: Rate Select (RS2 és RS1). Ezek a bitek szabályozzák a négyszöghullám frekvenciáját, amikor a négyszöghullám engedélyezve van. Az alábbi táblázat bemutatja az RS bittel választható négyszöghullámú frekvenciákat. Mindkét bit alapértelmezetten 1 (8,192 kHz), amikor a tápfeszültséget bekapcsoljuk.

Bit 2: Megszakító vezérlés (INTCN). Ez a bit vezérli az INT/SQW kivezetést. Ha az INTCN bit értéke 0, négyszög jelet ad ki az INT/SQW kivezetés. Ha az INTCN bit értéke 1, akkor az aktuális idő regiszterek és a riasztási regiszterek közötti egyeztetés 0-ra állítja az INT/SQW kimenetet (ha a riasztás szintén engedélyezve van). A megfelelő riasztási jelzőt mindig az INTCN bit állapotától függetlenül állítják be. Az INTCN bit értéke alapértelmezetten 1, amikor a tápfeszültséget bekapcsoljuk.

Bit 1: 2. riasztás megszakítás engedélyezése (A2IE). Ha az 1-re van állítva ez a bit, az lehetővé teszi a 2. riasztás jelző (A2F) számára, hogy INT/SQW kimenetet 0-ra állítsa megszakításkor (ha INTCN = 1). Ha az A2IE bit értéke 0 vagy az INTCN értéke 0, akkor az A2F bit nem kezdeményez megszakítási jelet az INT/SQW kivezetésen. Az A2IE bit alapértelmezetten le van tiltva (0), amikor a tápfeszültséget bekapcsoljuk.

Bit 0: 2. riasztás megszakítás engedélyezése (A1IE). Ha az 1-re van állítva ez a bit, az lehetővé teszi a 1. riasztás jelző (A1F) számára, hogy INT/SQW kimenetet 0-ra állítsa megszakításkor (ha INTCN = 1). Ha az A1IE bit értéke 0 vagy az INTCN értéke 0, akkor az A2F bit nem kezdeményez megszakítási jelet az INT/SQW kivezetésen. Az A1IE bit alapértelmezetten le van tiltva (0), amikor a tápfeszültséget bekapcsoljuk.

Status Register (0Fh)

7. bit: Oszcillátor Stop Flag (OSF). Ebben a bitben az 1 azt jelzi, hogy az oszcillátor vagy leállt, vagy egy ideig leállt. Ez felhasználható az az óra pontosságának megítélésére. Az alábbiakban esetekben válthat az OSF 1-re:

  1. A tápellátás most kapcsoltuk be.
  2. A VCC-n és a VBAT-n egyike sem elegendő az oszcillátor működéséhez.
  3. Az EOSC bit kikapcsolt akkumulátorral támogatott módban volt miközben VBAT-ról történt a táplálás.
  4. A kristályra gyakorolt külső hatások (azaz zaj, szivárgás stb.) miatt leállt az osszcillátor.

Ez a bit az 1-en marad, amíg nem töröljük.

3. bit: Engedélyezi a 32 kHz-es kimenetet (EN32 kHz). Ez a bit vezérli a 32 kHz-es kivezetés állapotát. Ha az 1-re van állítva, a 32 kHz-es kivezetés engedélyezve van, és 32,768 kHz négyszöghullámú jelet ad ki. Ha 0-ra állítjuk, a 32 kHz-es kivezetés nagy impedanciájú állapotba kerül. Ennek a bitnek a kezdeti bekapcsolási állapota 1, és egy 32,768 kHz négyszöghullámú jel jelenik meg a 32 kHz-es kivezetésnél a DS3231 bekapcsolása után (ha az oszcillátor működik).

2. bit: Foglalt (BSY). Ez a bit azt jelzi, hogy az eszköz elfoglalt a TCXO funkciók végrehajtásában. 1-re állítódik, amikor a hőmérséklet-érzékelőre történő átalakítási folyamatban van, majd törlődik, amikor az eszköz 1 perces szünetet tart a következő mérésig.

1. bit: 2. riasztás jelző (A2F). Azt jelzi, hogy a 2. riasztáshoz tartozó idő megegyezett az óra aktuális idejével. Ha az A2IE bit 1, és az INTCN bit 1, akkor az INT/SQW kivezetés megszakítást jelez ennek a bitnek a hatására. Az A2F bitet törölni kell egy riasztást követően. Ezt a bitet csak a 0-ra lehet írni. Ha megpróbáljuk 1-re írni, az érték változatlan marad.

0. bit: 1. riasztás jelző (A1F). Azt jelzi, hogy a 2. riasztáshoz tartozó idő megegyezett az óra aktuális idejével. Ha az A1IE bit 1, és az INTCN bit 1, akkor az INT/SQW kivezetés megszakítást jelez ennek a bitnek a hatására. Az A1F bitet törölni kell egy riasztást követően. Ezt a bitet csak a 0-ra lehet írni. Ha megpróbáljuk 1-re írni, az érték változatlan marad.

Példa program az INT/SQW kimenet megszakításokkal történő használatára:

A programot bőven elláttam kommentekkel. Sok magyarázatot nem is fűznék hozzá. Ez a program még nem küldi alvásba az ATmega328P chip-et, és még a DS3231 elemes működése sincs benne megoldva. Annyit érdemes tudni, hogy elemes táplálás esetén alapértelmezetten az INT/SQW kimenet magas impedanciás állapotba kerül, azaz nincs rajta megszakítás jel. Ezt külön be kell kapcsolni, hogy működjön, és az általam használt DS3231 könyvtár ezt nem tartalmazza, tehát meg kell írni. Erre még nem volt időm, bár egyáltalán nem látszik bonyolultnak. Még érdemes megnézni a DS3231 könyvtár forráskódját is Github-on. Link a forrás első sorában. Ez volt az első amit letöltöttem magamnak. A leírások és példa programok elég szerények, de használható. Rengeteg DS3231 kezelő könyvtár létezik, de egyikben sem találtam állítási lehetőséget az elemes táplálásban történő INT/SQW kimenet beállítására, így maradtam ennél a könyvtárnál. Ha készen lesz a függvény, még annyit fog változni a program, hogy az ATmega chip egyik lábáról fogja a tápfeszt kapni a DS3231, hogy az I2C kommunikáció idejére be lehessen kapcsolni, amikor a következő riasztási időpontot beállítom majd.

#include <DS3231.h> // https://github.com/NorthernWidget/DS3231 beépített függvéynek forrása a linken megnézhető
#include <Wire.h>
DS3231 Clock;

byte Year,Month,Date,Hour,Minute,Second,DoW; //óra beállításkor illetve óra kiolvasás segéd változói, Dow a hét napja (1-7)
bool Century=false; //évszázad jelzése, ha 0 akkor 2000-2099, ha 1 akkor 2100-2199 
bool h12; //24 órás időpontot használunk
bool PM; //ha 12 órást használnánk, ez jelezné a délutánt (1-el)
byte ADay; //riasztásban a hét napja (1-7), vagy a hónap napja (1-31)
byte AHour, AMinute, ASecond, ABits;  //risztási időpont változói
bool ADy; //risztásban ez jelzi, hogy hét napja (1) vagy hónap napja (0) lesz figyelve
bool A12h, Apm; //12 órás üzemmód esetén A12h true, jelen programban 24 órás üzemmódot használunk


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

  pinMode(13, OUTPUT); //a 13-as kivezetésen van a LED
  digitalWrite(13, LOW); //led nem világít

 /******************************************************************************************* 
  * óra beállítás, csak egyszer kell beállítani aztán az elem őrzi az időt a DS3231-en   
  * miután egyszer lefordítottuk és le töltöttük a programot, az alábbi rész kikommentezhető
  *******************************************************************************************/
  /* Clock.setClockMode(false); //óra üzemmód 24 órás
    Clock.setSecond(0);
    Clock.setMinute(14);
    Clock.setHour(7);
    Clock.setDate(5);
    Clock.setMonth(7);
    Clock.setYear(20);
    Clock.setDoW(7); 
  */
    
  // kiolvassuk az időt és kiírjuk a sorosportra
  Serial.println("Ora időpontja");
  Serial.println("-------------");
  //kiolvassuk az óra idopontját
  Year=Clock.getYear();
  Month=Clock.getMonth(Century);
  Date=Clock.getDate();
  Hour =Clock.getHour(h12, PM);
  Minute =Clock.getMinute();
  Second =Clock.getSecond();
  DoW=Clock.getDoW();
  //kiírjuk a sorosportra, hogy lássuk is
  Serial.print("Het napja:");Serial.print(DoW, DEC);Serial.print(" datum:");
  Serial.print("2");if (Century) {Serial.print("1");} else {Serial.print("0");}
  Serial.print(Year, DEC);Serial.print("-");Serial.print(Month, DEC);Serial.print("-");Serial.print(Date, DEC);
  Serial.print(" ido:");
  Serial.print(Hour, DEC);Serial.print(':');Serial.print(Minute, DEC);Serial.print(':');Serial.print(Second, DEC);
  if (h12) {if (PM) {Serial.print(" PM ");} else {Serial.print(" AM ");}} else {Serial.println(" 24h ");}
  Serial.println();
  
  /******************************************************************************************************
  * risztás beállító függvény paraméterezéséhez segítség:
  * setA1Time(nap/hét napja, óra, merc, másodperc, alarm mód, map/hét napja flag, H12 flag, PM flag)
  * nap/hét napja = 0-30 vagy 0-7
  * óra = 0-12 vagy 0-24 (H12=1 essetén 12 órás)
  * perc=0-59
  * másodperc=0-59
  * alarm mód:
  *  0b1111 - minden másodpercben riaszt
  *  0b1110 - mikor a másodperc megeggyezik riaszt
  *  0b1100 - mikor a perc és a másodperc megeggyezik riaszt
  *  0b1000 - mikor az óra, perc és a másodperc megeggyezik riaszt
  *  0b0000 - mikor a hét napja vagy a hónap napja,az óra, perc és a másodperc megeggyezik riaszt 
  * nap/hét napja flag:
  *  0 - hónap napján riaszt (1-31)
  *  1 - hét napján riaszt (1-7)
  * H12 falg:
  *  0 - 12 órás idő megadás
  *  1 - 24 órás idő megadás
  * PM flag (csak akkor, ha H12=1):
  *  0 - délelőtt
  *  1 - délután
  **********************************************************************************************************/
  //Riasztási funkciók beállítása
  
  // beállítási példa: a hét ugyanazon napán (beállítás napja), a pillanatnyi idő + 1 perc időpontban lesz risztás, 24 órás üzemmód
  // Vigyázni, mert ha pont óra 59. percében adjuk ki, akkor a risztásben a perc 60 lesz, ami hüjeség
  //Clock.setA1Time(DoW, Hour, Minute+1, Second, 0b0000, true, false, false);
  
  //beállítási példa: a hét napja vagy a hónap napja nem számít, óra nem számít, perc nem számí, ha másodperc megeggyezik, akkor lesz risztás, 24 órás üzemmód
  // Vigyázni, mert ha pont óra 59. percében adjuk ki, akkor a risztásben a perc 60 lesz, ami hüjeség
  Clock.setA1Time(DoW, Hour, Minute+1, Second, 0b1110, true, false, false);
  
  Clock.turnOnAlarm(1);  //engedélyezzük a 1. risztást 
  Clock.turnOffAlarm(2); //a 2. riasztást most nem használjuk
    
  //lekérdezzük a risztási időpontot és kiírjuk a sorosportra
  Serial.println("Riasztási idopont");
  Serial.println("-----------------");
  Clock.getA1Time(ADay, AHour, AMinute, ASecond, ABits, ADy, A12h, Apm); //lekérdezzük a risztási adatokat
  if (ADy) {Serial.print("Het napja:");} else {Serial.print("Honap napja:");} Serial.print(ADay, DEC);
  Serial.print(" Idopont:");Serial.print(AHour, DEC);Serial.print(':');Serial.print(AMinute, DEC);Serial.print(':');Serial.print(ASecond, DEC);Serial.print(' ');
  if (A12h==LOW) {Serial.print("24h ");} else {if (Apm) {Serial.print("PM ");} else {Serial.print("AM ");}}
  if (Clock.checkAlarmEnabled(1)) {Serial.print("Risztas engedelyezve");} else {Serial.print("Riasztas tiltva");}
  Serial.print(" Alarm bits:");Serial.println(ABits,BIN);Serial.println();



 /*****************************************************************************************
  * Megszakítási bemenetek előkészítése
  * belső felhúzó ellenállás bekapcsolva, mert a DS3231 INT/SQW kimenete
  * magas impedanciás állapotban van ha nincs risztás, és a felhúzó ellenállás húzza fel 1-re,
  * risztáskor földre húzza az INT/SQW kimenetet 
  *****************************************************************************************/
  delay(500);
  pinMode(2,INPUT); //megszakítás 0 bemenet, erre kötjük a DS3231 INT/SQW kimenetét
  digitalWrite(2,HIGH);
  pinMode(3,INPUT); //megszakítás 1 bemenet, ezen nyomógomb lesz a risztásjelzés törléséhez
  digitalWrite(3,HIGH);

  /*****************************************************************************************************************
   megszakítások beállításának paraméterezéséhez segítség:
   attachInterrupt(kivezetés száma, meghívott függvény, esemény mód)
   esemény módok:
     LOW: az interrupt bemenet alacsony állapotban van
     CHANGE: ha a bemenet értéke változik, magasról alacsonyra vagy alacsonyról magasra
     RISING: ha a bemeneti jel alacsonyról magasra változik
     FALLING: ha a bemeneti jel magasról alacsonyra vált.
   A megszakítás közben a késleltetés [delay()] nem működik, és a millis() által visszaadott érték nem növekszik.
   A megszakítás végrehajtása közben a soros portra érkező adatok elveszhetnek.
   Valamennyi változót, amelyet módosítani szeretne a megszakításban, volatile-ként kell definiálnia.
   ****************************************************************************************************************/
  attachInterrupt(0, int_0, FALLING); //a kettes bemeneten lefutó élre (1->0) meghívódik a int_0 függvény (INT/SQW risztást ad)
  attachInterrupt(1, int_1, FALLING); //a hármas bemeneten lefutó élre (1->0) meghívódik a int_1 függvény (megnyomtuk a nyomógombot)

  /*
   * nem tudok rájönni, hogy miért, de a program elindulásakor egyszer meghívódik az int_0() függvény, és kigyúllad a led
   * Ezért kénytelen vagyok kikapcsolni a led-et azzal, hogy meghívom az int_1() függvényt. Lehetne úgy is, hogy simán 
   * kikapcsolom a led-et a kimenet LOW-ra állításával. Kinek hogyan teszik jobban. Ettől kezdve a megszakítás jól működik
   */
  //int_1();
  digitalWrite(13,LOW);    
}

void loop() {
  //lekérdezzük a D2 állapotát, vajon risztás után magas, vagy alacson (alacsony)
  if (digitalRead(2)) {Serial.println("2 high");} else {Serial.println("2 low");}
  
  // lekérdezi a risztás jelző állapotát, és egyben törli is a risztás jelző bitet IDS3231 status regizter (0Fh) A1F
  // ha nem töröljük a risztas jelző bitet, akkor az INT/SQW kivezetés magas marad, és nem történik következő risztás
  // ezt az int_0() függvénybe kelett volna rakni, de akkor nem tudtam volna lekérdezni a D2 bemenet állapotát
  // így látható, hogy riztás után mindaddig magas az INT/SQW, amíg nem töröljük
  Clock.checkIfAlarm(1); 
   
  delay(1000);
}

void int_0() {
  // a 2-es bemeneten érkezett egy 0 jelszint, beállítjuk a led_allpot wáltozót 1-re
  Serial.println("Megszakitas 0, led be");
  digitalWrite(13, HIGH);
}

void int_1() {
  // a 3-es bemeneten érkezett egy 0 jelszint, beállítjuk a led_allpot wáltozót 0-ra
  Serial.println("Megszakitas 1, led ki");
  digitalWrite(13, LOW);
}

Mint azt a kontrol regiszter 6. bitjénél (BBSQW) leírtam, nekem nem sikerült a DS3231-et elemmel táplált üzemmódban arra rábírnom, hogy az INT/SQW lábon megszakítás jelet küldjön. Vagyis a modult csak bekapcsolt Vcc állapotban lehet használni. Ez egyébként önmagában még nem baj, mert az áramfelvétele még így is igen kicsi a DS3231 chip-nek. A leírás szerint ha nem kommunikálok vele I2C buszon keresztül, 200 mikroA körüli áramot fogyaszt. Már ez is elegendően kicsi érték lenne. De nézzük, mit tud a DS3231 modul. Gyanítható, hogy a rajta lévő led, ami bekapcsolt Vcc esetén világít, már eleve használhatatlanná teszi a modult. És igen, 8 mA az áramfelvétel, de nem túl pontos árammérőm van, ezért ez csak nagyságrend. Ha kiforrasztom a led-et, akkor ez kisebb lesz pár milliA-el. De biztosan nem megy le 200 mikroA környékére. Ezért nézegetni kezdtem a modul kapcsolási rajzát, ami szerencsére fellelhető a neten:

Az R2 ellenállás értéke 1 Kohm, tehát itt 1mA alatti áramokra lehet számítani. Semmi más nincs ami fogyasszon. Illetve ott van még az AT24C32 eeprom. Azonban az adatlapja alapján ha nem írom és nem olvasom, akkor 35mikroA az áramfelvétele. Itt elakadtam. Vagy szétbarmolom a modult, és tönkre teszem a led-et, vagy keresek egy másik modult, amin csak óra van, nincs rajta led. Ezt tettem. Most várom, hogy megérkezzen aliexpresz-en. Addig pihi van!

Ha azonban nem lényeges az áramfelvétel, a fenti kapcsolás tökéletesen működik!

Elemes kapcsolások alapjai

Egy kis elmélettel kezdünk. Az ATmega328P chip és az összes többi társa működés közben jelentősnek mondható, 10mA nagyságrendű áramot vesz fel. Megmértem, ha semmit nem kötök rá, akkor 12,5mA. Tegyük fel, hogy akkumulátorról akarjuk táplálni. Vegyünk pl. egy klasszikus 18650-es lítium aksit. Az egyik onkrét típus (HTCNR18650) adatlapja szerint feszültsége 3,6V, kapacitása 2200mAh. A feszültsége az adatlap alapján teljesen feltöltött állapotban kb. 4V, és merítéskor folyamatosan csökken kb. 3V-ig, ahol egy teljes lemerítés elleni védelem le fogja kapcsolni. Ez a teljes lemerítés elleni védelem azért kell, mert a lítium aksik túlmerítés hatására szeretnek kigyulladni. Ki ne emlékezne a Samsung eszközökre, amik maguktól elégtek. A lítium aksi ráadásul nem csak az éghető anyagot tartalmazza, van benne oxigén is, így hiába dobod be egy vödör vízbe, tovább ég vidáman.
De tegyük fel, hogy még nem ég, és teljesen feltöltöttük. Ez azt jelenti, hogy 12,5mA terheléssel 2200/12,5=176 órát, vagyis kb. egy hetet üzemelne az áramkör. Ez elég kevés. Ha pl. egy konyhai időzítőt szeretnénk üzemeltetni, hetente tölteni kellene, akkor is ha egyszer sem időzítünk vele. A chip csak várakozik arra, hogy megnyomjuk az start gombot, és közben sokat fogyaszt. Tegyük fel, hogy a konyhai időzítőnk miközben működik (visszaszámol egy kijelzővel) 100mA áramot fogyaszt. Ebbe az áramfelvételbe akár világító led kijelző használata is beleférhet. Ha egy időzítés 10perc, akkor hányszor használhatnánk? 2200/100=22óra. Ha tojásfőzéskor időzítünk vele, akkor egy időzítés 10 perc, tehát 132-szer használhatjuk. Ha minden nap főzünk tojást, ez akkor is legalább négy hónap, és ez kielégítő eredmény. Négy havonta fel kell tölteni az aksit!
Nincs más dolgunk, mint az időzítések közötti szünetekben valahogyan el kell érni, hogy ne vegyen fel a chip 12,5mA áramot. Ezek a szünetek akár napokig is tarthatnak (én pl. csak nagyon ritkán eszek főt tojást), Lehetne egy főkapcsoló is, amivel teljesen lekapcsoljuk az áramkört az időzítőn, de ez nem elegáns. Küldjük inkább a chip-et aludni.

Ha kikapcsoljuk a program végrehajtásra szolgáló áramköröket, az AD konvertert, és minden más alvás időben szükségtelen eszközt, akkor az áramfelvétel jelentősen csökkenni fog. Az ATmega328 adatlapja szerint egészen 4,2mikroA-ig lehet lemenni. Ekkor már csak egy belső oszcillátor időzítővel, valamint két bemenetet figyelő áramkör működik. Ha a belső időzítő elszámolt egy beállított értékig (maximum 8 másodperc), vagy a két bemenet valamelyikére jelet kapcsolunk, akkor a chip felébred, és csinálhat valamit. Aztán újra el lehet küldeni aludni, amikor csak akarjuk. A 4,2mikroA áram igen kicsi. Meddig is működik az akkumulátorról? 2200mA/4,2mikroA=~524000 óra, vagyis kb. 21000 nap, azaz 60 év. Ez persze csak elméleti érték, sok mindennel nem számoltam. Sajnos az akkumulátorok és ez elemek merülnek maguktól terhelés nélkül is, így nem számíthatunk 60 évre, esetleg akkumulátornál egy-két évre, de egy lítium gombelemnél ez 7 év is lehet. A drágább lítium elemekre a gyártók éveket garantálnak. Egy konkrét típusnál 20% kapacitáscsökkenés eléréséhez 7 évet adtak meg emlékeim szerint.

Na akkor nézzük végre hogyan is kell elaltatni a chip-et. Nyilvánvaló, hogy erre van kész könyvtár. Egy elalvásról szóló internetes leírásban ezt találtam meg: https://github.com/rocketscream/Low-Power
Nagyon fontos!!! Ez a könyvtár nem kezeli az ATmega8 chip-et. Nekem ebből több darab is van raktáron, és ez elszomorított, de ez van. Vannak más könyvtárak, melyek lényegesen több vezérlő típusra fel lettek készítve.
Használathoz le kell tölteni a github-on megtalálható állományt, átnevezni „LowPower.zip”-re, és a könyvtár kezelőben mint zip állományt beimportálni. Sajnos ez a könyvtár az Arduino Ide könyvtár kezelőjében nincs benne tapasztalatom szerint. De így is jó, nem túl bonyolult az importálás.

A példaprogramokat a fájl/példák menüben tudjuk elérni:

Nem igényel sok magyarázatot a használata, két mintapéldát próbáltam ki, ezeket kissé át is alakítottam. Direkt a kis áramú kísérletekhez vettem egy 50mA-es 4 tizedesjegyet mutató panelműszert. Biztosan nem egy labor műszer, de legalább értékes számjegyeket lehetett leolvasni, és nem 0-át mint a multiméteremről. Ennek a műszernek a felbontása 0,1mikroA. LED-et kötöttem a chip-re, és figyeltem az áramfelvétel alvó és működő állapotban is.

Időzített bekapcsolás:

#include "LowPower.h" // LowPower könyvtár használata

void setup()
{
  pinMode(2, OUTPUT); // led a 13-as kivezetésre kerül
}

void loop() 
{
    // az ATmega328P chip alvó bódba megy 4 másodpercre
    // kikapcsolja az  ADC és a BOD modulokat a chipben
    // áramfelvétel 6,2 mikroA (a műszerem 0,1 mikroA érzékenységű)
    LowPower.powerDown(SLEEP_4S, ADC_OFF, BOD_OFF); 
    // 4 másodperc után itt folytatódik a program
    // bakapcsolt led esetén kb 17,6 mA áramfelvétel (nálam ebből a led 7,5mA)
    // a chip áramfelvétele kb 12-13 mA
    digitalWrite(2, HIGH);   
    delay(10000);   
    // ha nem kapcsolom ki a LED-et, akkor elalváskor a chip kimenet magas értéken marad
    // és az áramfelvétel kb. 6,7 mA vagyis a LED továbbra is fogyaszt, miközben a chip alszik
    // a chip kimenetei továbbra is működnek, csal a belső program végrehajtó logika alszik                    
    digitalWrite(2, LOW);   
}

A következő eredmények születtek:

  • Alvó módban az áramfelvétel 6,2 mikroA
  • Amikor a LED világit (10sec), az áramfelvétel 17,6 mA
  • Lehúztam a LED-et, ekkor az áramfelvétel 12 mA, tehát a LED 5mA körüli áramot vesz fel.

Néhány hasznos tudnivaló a programhoz:

  • A lehetséges időzített alvási időtartamok 15ms, 30ms, 60ms, 120ms, 250ms, 500ms, 1s, 2s, 4s, 8s
  • Ha egy kimenetet magas állapotban hagyunk elalváskor, akkor az magas is marad, és a rá kötött áramkörök áramot vesznek fel. Pl. egy led világít alvó chip esetén is. Feltehetőleg ennek az az oka, hogy a chip alváskor a 16Mhz órajelet kapcsolja ki, és statikus állapotban befagy a végrehajtás, ott ahol tartott. Ilyenkor a chipet felépítő MOSFET áramkörök 0 teljesítményt vesznek fel, ennek köszönhető a kicsi fogyasztás. Azonban a chip tápfeszültsége be van kapcsolva, a kivezetések áramkörei működnek.
  • Az ADC az analóg digitális konvertert jelenti, azt külön ki kell kapcsolni, mert feltehetőleg nem a 16Mhz órajelről kapja az órajelet, és tovább működne. Ezért az ADC-OFF a powerDown függvényben.
  • A BOD áramkör a chip tápfeszültségét figyeli. Alapértelmezetten 2,7 voltnál kapcsolná ki teljesen a chip-et, így most erre sincs szükségünk., hisz az aksi védőáramköre nem sokkal 3V alatt magától kikapcsol. Feltételezem, hogy virtuális akkumulátorunkon van védő áramkör, nem akarjuk, hogy fejlesztő laborunk leégjen. Hagyományos cink elemeknél a BOD hiánya gondot okozhatna, mert azok 0V-ig merülnek, így bizonytalan lenne a helyzet 2,7V környékén. Azonban az elemekből túlmerítéskor kifolyó sav a nagyobb károkat okozna, mint a bizonytalan működés.

Bekapcsolás az egyik chip kivezetésre kapcsolt feszültséggel:

#include "LowPower.h" //LowPower könyvtár használata

void setup() {
  pinMode(2, INPUT);  //2-es kivezetés bemenet ez a 0-as megszakítás bemenete
  pinMode(13, OUTPUT);//13-as kivezetésre kötjük a LED-et
}

void loop() {
  // a 0-ás megszakítást használjuk, ami a 2-es láb
  // az esemény a megszakitas() függvényt hívja meg (és felébreszti a chip-et)
  // LOW, akkor hajtódik végre, a a láb logikai alacsony szinten van,
  // CHANGE, akkor halytódik végre, ha a láb logikai szintje megváltozik
  // RISING, akkor hajtódik végre, ha a láb logikai szintje alacsonyról magasra vált
  // FALLING, akkor hajtódik végre, ha a láb logikai szintje magasról alacsonyra vált 
  attachInterrupt(0, megszakitas, LOW);
    
  // elküldjük aludni a chip-et.
  LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF); 
    
  // tiltjuk a megszakítást, nehogy a következő programrészeket újra 
  // megszakítsa a nyomógomb ismételt lenyomása.
  detachInterrupt(0); 
   
  delay(4000);   //Ez nem müködne a megszakitas() függvényen belül  
  digitalWrite(13, LOW);   

}

void megszakitas()
{
  digitalWrite(13, HIGH); 
}

Fontos tudnivaló, hogy a megszakítással meghívott függvényekben nem működik a delay() függvény. Mivel a programban azt akartam, hogy a led 4 másodpercig világítson, a delay() csak a loop()-ba kerülhetett. Egyébként a led bekapcsolása kerülhetett volna oda is, csak így látható a programban, hogy a megszakitas() függvény is csinál valami!

Programfeltöltés ATmega mikrovezérlőkre

Egy Arduino UNO R3-at használok programfejlesztésre, de az elkészült programot egy ATmega328 mikrovezérlőbe kellett beégetnem. Régebben nem volt még programozóm amivel egy chip-be betölthettem volna a programot, ezért az Arduino UNO R3-at szerettem volna erre felhasználni. Ennek a műveletsornak a lépéseit rögzítettem az alábbi dokumentációban. A módszer tanulásához a TAVIR weblapon talált leírást olvastam el!

A probléma megoldására a mai napig (2020) háromféle módszert használtam:

  1. Próbapanelon összerakott környezettel és az Arduino IDE ArduinoISP példaprogramjával.
  2. Egy 2019-ban Aliexpres-en vásárolt AVR ISP shield panel felhasználásával. Ez lényegileg ugyanaz mint az előző megoldás, csak kényelmesebbé vált a feltöltés során az „égetendő” chip mechanikai kezelése, dugdosása.
  3. STK500 programozó használata.

Az első két esetben két fő lépésből áll a műveletsor:

  1. Feltöltjük a programfeltöltésre szolgáló programot az Arduino UNO R3-ra. Ezzel elkészül a „programozó” készülékünk
  2. A mikrokontrollerbe töltendő programot „programozóval” töltjük be a chip-be. Ekkor az Arduino Uno R3 lesz a programozó, ami lényegileg 3 vezetékkel lesz összekötve az ATmega328P chip megfelelő lábaival. Nyilván chip-nek tápot kell adni, és kell minimum egy kvarcz kristály és két kondi az órajel előállításhoz.

Az STK500 programozó esetén az első lépés kimarad, hiszen Arduino UNO-ra nincs is szükség, azt a programozó helyettesíti. A leírás végén erről is olvashatsz, de legelőször nézzük meg azt az esetet, amikor nincs programozónk, de van kéznél Arduino Uno.

  1. A programozó szoftver feltöltése Arduino-ra

Első lépés az égető szoftver feltöltése az Arduino UNO R3-ra.

Elsőként csatlakoztattam az USB portra az Arduino UNO R3-at, és beállítottam az eszközök /alappanel menüben az Arduino Uno alappanelt, hiszen most magára az R3-ra szeretnék programot rátölteni, mégpedig az ArduinoISP programot, amitől programozó lesz belőle.

Ezt követi a programozó megadása az eszközök/programozó menüpontban. Én az AVR ISP-t állítottam be. Mindig is ezt használtam ha az Arduino UNO R3-ra akartam programot rátölteni:

Most jön a program kiválasztása. A file/minták menüből választottam ArduinoISP menüpontot. Ez a program kifejezetten erre a feladatra lett készítve:

Ezután jött a program égetése az R3-ra. Praktikusan rákattintottam a feltöltés ikonra:

Nálam minden rendben volt, villognak kicsit a ledek az Arduino UNO R3-on, és el is készült programozó.

  1. A program feltöltése „Arduino as ISP” programozóval

Ekkor összeraktam az ATmega328P-t egy próbapanelon, mert kellett hozzá néhány alkatrészt. Itt a kapcsolási rajz:

Ezen a kapcsolási rajzon szerepel egy led a soros áramkorlátozó ellenállással. Nyilván ezt nem raktam be, csak nem találtam pont olyan rajzot a neten, ami nekem kellett.

Sokat segített az összerakásnál a mikrovezérlő lábkiosztása, ezért segítségül itt van ez is:

Az én panelem így nézett ki:

Elismerem, hogy kicsit áttekinthetetlen, de működőképes. A kvarc-ot közvetlenül a mikrovezérlő lábaihoz dugtam be, a kondenzátorokat pedig a panel egyik végén kialakított „GND csomópontba”. Így a kondik teljes lábhosszúságára szükség volt. A reset láb felhúzó ellenállása látható még a képen a panel bal alsó sarkában.

Ekkor következett az R3 programozó és az ATmega328 összevezetékezése. Itt az Arduino weblapon található rajz, ez teljesen áttekinthető és egyértelmű:

Mindez nálam kicsit csúnyábban fest:

Amikor elkészült a „kábelezés” beállítottam a programozót az eszközök/programozó menüpontban. Itt az ArduinoISP-t választottam, tekintve, hogy az Arduino UNO R3 ettől a perctől kezdve egy külső programozó készülék:

Egyébként ha nem akarjuk folyton az Arduino R3-at használni programozásra, meglehetősen olcsón kapható az Aliexpres-en a már említett STK500 programozó. Az STK500 nevet kell a keresőbe beírni, és bőségesen válogathatunk az olcsóbbnál olcsóbb eszközök között. 1000Ft alatt már lehet kapni, ami kb. így néz ki:
Olvastam a neten fórumokban, hogy a kínai cuccok szarok. Ez biztosan így is van. Az én amatőr körülményeimben azonban tökéletesen megfeleltek. A képen látható cucc valójában ugyanaz, mint amit az Arduino UNO és a hozzá vezetékezett dugdosós panelen összeraktam. Egy kisebb ATmega vezérlő van rajta, és egy USB soros illesztő IC, Nomeg a kábelek, amik megkönnyítik a dugdosást.

Most jön az alaplap kiválasztása. Persze itt nem alaplapról van szó, hanem egy chip-ről. Bár a felsorolt alaplapok között nem fogunk ATmega328P nevűt találni alapból, ne essünk kétségbe. Mivel akkoriban (2015-ben) én sem találtam, elkezdtem próbálkozni. Láss csodát, az Arduino UNO alaplappal pont működött a dolog. Nyilván más menüpontokkal is ment volna, de azt, hogy ezek miben különböznek, azt nem tudom, talán nem is érdekes:

…és ezzel minden előkészület megtörtént! Megkezdődhetett a program beégetése. Tanulás képen a blink alapprogramot égettem be, ezt könnyű volt kipróbálni égetés után. Tehát a file/példák/basic/blink programot nyitjuk meg:

És következhet a pont az i-re. Be kell égetni a programot a chip-be. Ehhez a vázlat/feltöltés programozót használva menüpontot kell választani:

A képernyőn csak egy halovány felirat jelent meg a program alsó üzeneteket megjelenítő részén arról, hogy folyik az égetés (elfelejtettem kifotózni). Az R3 ledjei vadul villogtak pár másodpercig. Aztán „égetés kész” felirat jelent meg, és tényleg készen lehetett.

Valóságban nem ezzel kezdtem, hanem a bootloader égetéssel. Ráadásul a vásárolt chip-ben már volt bootloader az ismertető szerint, de ez úgy tűnik nem igazán probléma. Ehhez az eszközök menü/bootloader beégetése menüpontot kellett kiválasztani. Ezt a lépést a továbbiakban már soha nem csináltam, nyilván nem is kell. Azonban én mindig olyan chip-et vásároltam a későbbiekben, ami már gyárilag tartalmazta a bootloadert!

Természetesen az elkészült chip-et le is ellenőriztem. Lehúzogattam a programozó vezetékeket, csak a tápfeszt hagytam rajta. Rádugtam a 28-as lábra egy ledet (az Arduino 13-as digitális ki/bemenete egyben a beépített led, ami az ATmega328P chip-en a 28-as számú kivezetés), ami szépen villogott!!

Program feltöltés AVR ISP Schield használatával

Teltek múltak a hónapok, és chip égetésére nem is volt szükségem, de egy újabb munkához megint csak kellett. Közben az előző módszer leírásában említett próbapanelen vezetékezett környezet megsemmisült. Az Aliexpres-en megláttam egy Arduino UNO R3-ra rádugható panelt, amin már kész a környezet, az ATmega328P chip könnyedén bedugható a nyitható-zárható foglalatba. Így néz ki:

Ez szuper! Tulajdonképpen semmi nem változott, ez csak egy kész vezetékezés a kvarcz kristállyal, két kondenzátorral. Található meég rajta 3 led, ami a feltöltés állapotáról tájékoztat. Égetés közben a ledeken vagy egy kicsike tűzijáték, amit próbáltam értelmezni. Ha minden rendben, akkor a zöld led látványosan pulzál. Piros, ha valami nincs rendben. Egy kis hangszóró is van rajta, ami hangosan tülköl hiba esetén. Beállítani úgy kell mindent, amit a fentebb lévő leírásban olvasható.

Azonban sokat szívtam vele, mert előkerült egy ATmega8 chip. Nem vettem észre, hogy nem ATmega328P van a kezemben, és csodálkoztam, hogy nem működik. Több órás próbálkozás után végre rászántam magam és értelmeztem a hibaüzenetet. Előkerült egy nagyító, és megbizonyosodhattam róla, hogy az ATmega328 tényleg nem ugyanaz mint az ATmega8.
Olyan nyúlfarknyi programot kelett feltöltenem, ami az ATmega8-ban is elfért. Keresni kelett tehát egy olyan alaplapot, ami megfelel az ATmega8-nak!
Már előzőleg is említettem, hogy a feltöltéskor az alaplap választásakor bizonytalan voltam, mert nem találni ATmega328P alaplapot, csak Arduino UNO alaplapot, amivel szerencsére sikerült a feltöltés. Az ATmega8-nak viszont semmit nem találtam.Mindent kipróbáltam, semmivel nem működött. Így nem volt mit tenni el kellett kezdenem böngészni a netet, hátha van megoldás. Persze, hogy van! Találtam valahol egy cikket, ami pont erről szólt. Az alábbiakat kell tenni:

Meg kell keresni az Arduino IDE-ben a file/beállítások menüt, és a “további Alaplap-kezelő URL-ek:” sorba be kell írnia következőt: https://mcudude.github.io/MiniCore/package_MCUdude_MiniCore_index.json

Most el kell kezdeni az alaplap kiválasztást és rögtön a legfelső “Alaplap kezelő” menüpontot kiválasztani:

A megjelenő keresőbe írjuk be “minicore”, és válasszuk ki az egyetlen megjelenő alaplapot, és telepítsük:

…és igen, megtörtént a csoda. Van már az alaplapok között ATmega chip-ből mindenféle:

Túl hosszú volt az alaplap felsorolások listája, ezért inkább két képet csináltam. Viszont érdemes megfigyelni, hogy ez első képen kiválasztott ATmega8 alaplap alatt megjelent egy csomó új menüpont, amikre eddig nem kellett odafigyelni. A minicore alaplap kezelő leírásban erről rengeteg részletet találunk. A minicore kiválasztásakor a telepítés nyomógomb megnyomása előtt (keressük meg újra) kattintsunk a “More info” linkre, és szemezgessünk a rengeteg infóból.
Engem ezek közül az órajel frekvenciáról szóló rész hozott izgalomba. Egy alkalommal szerettem volna kvarcz kristály nélkül egyetlen led-et vezérelni. Pontosabban egy RGB led-ből akartam változtatható színű hangulatfényt építeni csupán virtusból. Remek lett volna, ha semmi nem kell a vezérlőn kívül. Olvasgattam is a vezérlő adatlapját, amiben megtaláltam a belső RC oszcillátorról szóló leírást. De azt már a szerény angol tudásom birtokában nem tudtam megfejteni, hogy ezt miképpen kellene beállítani. Feltettem hát a kérdést az egyik fórumon, hogy valaki segítsen. Nem az volt a válasz, hogy ezt meg ezt kell csinálni, hanem arról érdeklődtek, minek ez nekem, miért akarok filléreket spórolni a kvarcz kristályon és hasonlók. Elég lett volna nekem egy “Nem tudom!” vagy “Nem akarom megmondani!” is. Ez volt az a a pillanat, amikor rájöttem, sokan nem is akarnak segíteni.
Ezeknek a plusz menüpontoknak a működéséről az STK500 programozónál még némi információ. Ha tovább olvasod a leírást, haarosan odaérsz.

Program feltöltés STK500 programozóval

A programozó használatához minimális odafigyelésre van szükség. Ugyanis az Arduino nano áramkörére betervezték ugyan az ICISP csatlakozót, de a programozóval történő csatlakoztatás nem teljesen egyértelmű. Az Arduino nano “pinout” ábráján külön kirajzolták a csatlakozót:

Azonban itt nem szerepel semmi arról, hogy melyik az “1”-es csatlakozó pont, amit a nyákon feltüntettek. Persze könnyű kitalálni, hiszen ez az ábra is felülnézetben ábrázolja a csatlakozót. A programozó picike kis nyáklemezén, ami csatlakozót tartalmazza, szerepel az egyes kivezetések neve, így könnyű kitalálni, hogy a MISO lábat a csatlakozó jobb felső pontjához kell irányítani! Miután csatlakoztattuk a programozót az Arduino nano-hoz és bedugjuk a programozót az USB portba, a nano-n a megszokott tápfesz jelző piros led világit. Megnyugodhatunk, nem rontottuk el.
A programozó bedugásakor a Windows 10 USBasp nevű drivert próbált telepíteni, de a Windows eszköz kezelőjében látható volt, hogy a driver nem elérhető:

Nyilván az a baj, hogy az eszköznek driverre van szüksége. Kis kutakodás után a neten (Google usbasp szóra sok találatot ad fel). Két helyen is találtam drivert. Az egyik a következő elérési utvonalról tölthető le: https://protostack.com.au/download/USBasp-win-driver-x86-x64-v3.0.7.zip
Az itt található állományt ki kell bontani, és elindítani az installdriver.exe programot. Megnyomni a szokásos next-next gombokat, és kész is. A másik lehetőség a fórumokon a Zadig (https://zadig.akeo.ie/) weboldal. Innen a le kell tölteni a driver telepítő programot , és futtatni.

Az a lényeg, hogy a Windows eszközkezelőben megjelenjen egy USBasp nevű eszköz és rendben működjön. Ezzel az eszközzel azonban nem keletkezik COM port, így az Arduino IDE programban az eszközök menüben a “Port” menüpont szürke marad. Ez azonban nem baj. Alaplapnak válasszuk ki az éppen aktuális alaplapot (pl. Arduino nano, vagy Arduino UNO stb.). Programozónak válasszuk az USBasp nevű programozót. Ne felejtsük el, hogy a programot ekkor nem a szokásos módon a feltöltés ikonnal lehet elvégezni, hanem a vázlat menüpontban a “Feltöltés programozót használva Ctrl + Shift + U” menüpontot kell használni. Azon se lepődjünk meg, hogy a program feltöltés figyelmeztető üzenetet (Warning) eredményez.

Ezt nyugodtan figyelmen kívül hagyhatjuk, a feltöltés rendben megtörtént. Ha bootloadert égetünk, ugyanezt fogjuk tapasztalni. Elméletben a bootloader feltöltése a képen látható figyelmeztető üzeneteket eredményezte, így látszólag felkerült a chip-re, de valójában nem történt meg. Nem jöttem rá, hogy mi a hiba, de kísérletezgetéssel sikerült mégis a bootloader feltöltés. Abból indultam mi, hogy a bootloadert nem egy Arduino nano-ra akarom feltölteni, hanem egy ATmega328 chip-re. Azonban az Arduino IDE kiválasztható alaplapjai között nem szerepel ez a mikrovezérlő. Azonban kicsivel ez előtt leírtam, hogyan lehet letölteni a minicore alaplap kezelő könyvtárat. Ebben már van ATmega328. Így ezt választottam ki:

Ezt követően már lényegesen több beállítási lehetőség jelent meg, amiből semmit nem állítottam át, csak rákattintottam a bootloader égetése menüpontra.:

Ekkor már lényegesen több információ jelent meg a feltöltésről, és sikeres is volt. A feltöltés infóiból egy részlet:

Az Arduino piros led-je másodpercenként két gyors felvillanást produkált a feltöltését követően. Az eredeti program törlődött.
Azt figyeltem még meg, hogy az új bootloader-es nano bekapcsoláskor másként villogtatja a led-et. Amikor bedugom az usb portba és tápfeszt kap, az új bootloader hét alkalommal dupla felvillanással jelez. A régi bootloader csak szimpla felvillanásokat produkál:

Fuse bitek módosítása

Amikor megtaláltam a mincore alaplap kezelőt, kikísérletezgetni kezdtem. Elővettem az ATmega8 chip-et, összehuzaloztam egy led villogtató kacsolást a kvarcz kristállyal, két kondenzátorral. Ekkor már volt STK500-as programozóm, azt is belehuzaloztam a kapcsolásba. Be kellett kötni a GND, VCC, reset, Miso, Mosi és Sck vezetékeket. Amint a programozót bedugtam az USB portba, máris feszültséget kapott az ATmega8 chip. Ha lett volna rajta program, minden bizonnyal villog is a led azonnal. Töltöttem is rá egy blink példa programot, és a led villogott. Most jött a kísérletezgetés. Sorban kiválasztottam az esetemben lehetséges órajeleket. Nyilván az 1Mhz és 8Mhz belső órajel, és 16Mhz külső jöhet szóba. Rátöltöttem először az 1Mhz belső órajel beállítással egy bootloadert a chip-re:

A bootloader rátöltése törli a programot, ezért miután megláttam a bootloader betöltését nyugtázó OK üzenetet alul, a “Feltöltés programozót használva” menüponttal rátöltöttem újra a chip-re a blink programot. És a led villog. Gyorsan lekapkodtam a kvartz kristályt a kapcsolásról, adtam neki külön 5V tápfeszt, és a led tovább villog. Kipróbáltam a 8Mhz beállítást is (kvarcz-ot nem kell visszatenni), és azzal is működött. Aztán vissza akartam állni a kvarcz kristályos 16Mhz külső órajelre. Na ez már nem sikerült, csak ha a bootloader égetés előtt visszaraktam a kvarcz kristályt és a két kondit a helyére.
A bootloader égetése közben a képernyő alján rengeteg információ jelenik meg. Ezekből kiszemezgettem, hogy mit is csinál a folyamat, és milyen fuse biteket állít be:

 Bootloader-elBootloader nélkül
1Mhz belső órajelLfuse:10100001 Hfuse:11010100Lfuse:10100001 Hfuse:11010101
8Mhz belső órajelLfuse:10100100 Hfuse:11010100Lfuse:10100100 Hfuse:11010101
16Mhz külső órajelLfuse:10111111 Hfuse:11000100Lfuse:10111111 Hfuse:11000101

Még esetleg a BOD beállítás lehet hasznos a megjelenő menüpontok közül. ez a BOD paraméter azt a feszültséget határozza meg a vezérlőnek, ami alatt leáll a működés. Pl. ha elemről tápláljuk a vezérlőt és az elem feszültsége a beállított 2,7V alá csökken, akkor a vezérlő leállítja magát. Ez jobb, mintha bizonytalan működést idéz elő az elégtelen tápfeszültség, mivel egyes részei még működnek a vezérlőnek, míg mások nem.
Találtam táblázatot, hogy egyes vezérlőknél milyen feszültség értékeket lehet beállítani:

atmega328ATmega168atmega88ATmega48ATmega8
4.3V4.3V4.3V4.3V4.0V
2.7V2.7V2.7V2.7V2.7V
1.8V1.8V1.8V1.8V
TiltvaTiltvaTiltvaTiltvaTiltva

Viszont erre még nem volt szükségem, így kísérleteket nem végeztem a BOD feszültség tárgyában!

Lakás világítás vezérlő

Lakásunkban már szinte minden világitás ledes. Ezeket a led világításokat magam szerelgettem boltban kapható led szalagokból és led modulokból. Boltban kapható 12V kapcsoló üzemű tápegységeket használtam, esetenként 24V-os printer tápegységet. Dögös áramokról van szó, itt 10A-ek röpködnek. Kezdetben relével kapcsoltam be a világítást, egy primitív áramkörrel. Szerettem volna, ha egy kicsit komfortosabb a működés, pl. bárhonnan le tudom kapcsolni az összes szobában a világítást, de azt is nagyon szerettem volna, ha éjszaka felkapcsolva a villanyt, nem vakít el a hirtelen nagy fény, hanem lassan kúszik fel a fényerő. Ezért kezdtem bele ennek az áramkörnek a megépítésébe.

Az a fotó a megvalósított kapcsolást mutatja. Középen a vezérlő az ATmega328 chip-el, alul a hűtőbordán a MOSFET tranzisztorok, amikkel a 12 vagy 24V feszültséget kapcsolgatom a pwm jellel a fényerő szabályozáshoz. A MOSFET tranzisztorok működéséről az elektronikai alapokról szóló leírásomban olvashatsz! Alig van valami alkatrész benne!

Nagy előnye még ennek a kapcsolásnak, hogy egy szobában több kapcsolót is el lehet helyezni, párhuzamosan köthetők, és bármelyikről fel és le kapcsolható a villany. Ez a kényelem ma még nem teljesen egyértelmű. A szokásos villanyszerelők által bekötött alternatív kapcsolás nem pont ugyanez. Kaphatók készen villamos hálózatba szabvány módon beköthető elektronikus kapcsoló áramkörök, darabja 9-10.000Ft. Nekem az egész került ennyibe (led világítás nélkül).

Álltalános leírás a működésről

Az alapvető cél, hogy egy fényerőszabályozható (pwm) LED világításból és hagyományos 230V-os, relével vagy más módon kapcsolt világításból álló lakásvilágítást intelligens módon lehessen vezérelni. Összesen nyolc csatornás világításvezérlést valósít meg a program, ahol egy csatorna egy fali kapcsoló – lámpa párost jelent.
Így néz ki a megépített vezérlő:

Lakás világítás vezérlésem tulajdonságai, ha az alkalmazott mikrovezérlő ATmega168 vagy ATmega328:

  • Maximum 6 db fényerőszabályozott kimenet és még 2 db digitális (relével stb. kapcsolható) kimenet.
  • Minden fényerőszabályozott kimenet átállítható digitális vagy fényerőszabályozott működésre. Ha a programot beégetitek, alapértelmezetten fényerőszabályzás nélküliek a kimenetek.
  • A nem fényerőszabályozott (digitális) kimenetek a fali kapcsolóval ki és bekapcsolhatók egy lenyomással (klikkeléssel), vagyis minden fali kapcsoló “csengő kapcsoló” kell, hogy legyen. Sajnos a hagyományos kétállású kapcsolókat (fel/lekapcsolókat) le kell cserélni! A mi lakásunkban szinte minden helységnek két bejárata van (a lakás körbejárható), így minden bejárathoz került egy fali kapcsoló. A kapcsolók párhuzamosan vannak kötve, így bármelyikről kapcsolható a világítás. Egyik ajtón bejövök és felkapcsolom, a másikon kimegyek és lekapcsolom.
  • A fényerőszabályozott kimeneteket a hozzájuk tartozó kapcsolókkal a következő módon lehet vezérelni:
    • Egy klikkel ki és be lehet kapcsolni. Bekapcsoláskor a fényerő nem ugrik azonnal maximumra, beállítható ütemben úszik fel egy beállított fényerőre. Kikapcsoláskor a világítás villan egy rövidet, ezzel jelzi, hogy megkezdődött a fényerőcsökkentés, ami egy 20%-os minimum fényerő eléréséig tart. Ekkor beindul a segédfény funkció, ami beállítható ideig tartja ezt a fényerőt, hogy ne kelljen vaksötétben kimenni a helységből. A beállított idő után a segédfény kikapcsol.
    • A lámpa bekapcsolható a fali kapcsoló folyamatos nyomva tartásával is. A kapcsoló lenyomását követően az ismétlési idő elteltével (alapértelmezetten 500msec), minimum fényerővel kezd világítani a lámpa és folyamatosan emelkedik a fényerő a maximum felé. A maximum elérésekor a lámpa villan egyet. A kapcsolót bármely fényerőnél elengedjük, az aktuális fényerőt a rendszer megjegyezi, és következő felkapcsoláskor erre a fényerő értékre áll be a lámpa fényereje.
    • Ha a lámpa világit, a fali kapcsoló folyamatos nyomva tartásával a fényerő növelhető a maximumig. Ha a fényerő a maximumot elérte, a lámpa villan (elalszik egy pillanatra). Újabb ismételt nyomva tartással a fényerő minimumig csökken. Ha a fényerő éppen megfelelő, elengedjük a nyomógombot és az így beállított fényerőt a rendszer tárolja (ez a fényerő érték a tápfesz kimaradáskor elveszik, újra be kell állítani). Ha a lámpát lekapcsoljuk, és újra bekapcsoljuk a tárolt fényerővel fog világítani. Természetesen a fényerő felúszik a beállított fényerőre, és leúszik a minimum fényerőig, amelyen a segédfény funkcióra beállított ideig még világít.
    • Bármely fali kapcsoló dupla lenyomásával (dupla klikk), az összes lámpát lekacsolja a rendszer. Ez a funkció a fényerőszabályozott és nem fényerőszabályozott lámpákra is működik, a fényerőszabályozott részeken fényerő leúsztatás és segédfény funkció is működik. Mivel egy vezérlővel csak maximum 8 világítás vezérelhető, vannak bővítő ki és bemenetek ehhez a funkcióhoz. A bővítő kimeneten a tripla klikk egy rövid 10msec-es impulzust generál, ami egy másik hasonló vezérlő bővítő bemenetére köthető. A bemeneten megjelenő impulzus hatása azonos a tripla klikkel, vagyis az összes lámpa lekapcsol. Ez a bemenet esetleg egy központi lámpa lekapcsolásra is felhasználható, pl. a riasztó élesítése lekapcsolja a lámpákat. Segédfény funkció természetesen ekkor is működik!
    • Bármely fali kapcsoló négyszeri lenyomása (4 klikk) bekapcsolja a „nyaralás” üzemmódot. Ez jelenleg még fejlesztés alatt áll. Lényege, hogy a vezérlő figyeli a világítási szokásokat, és ezen megtanult értékek alapján véletlenszerűen, de élet szerűen kapcsolgatja a villanyokat az esetleges rablók megtévesztésére. Természetesen más funkció is kialakítható, de én ezt terveztem a rendszerbe. A funkció megvalósítása óra funkció és külső memória beépítést is megköveteli, ezért is várat magára a fejlesztés.
    • Bármely fali kapcsoló ötszöri lenyomása (5 klikk) az adott kapcsolóhoz tartozó lámpa működési paramétereinek a beállítására szolgáló setup folyamat elindulását eredményezi. Setup folyamat leírását lásd később.

Az egyes lámpák (csatornák) beállítható paraméterei

Összesen nyolc csatornát lehet vezérelni, ezek hivatkozása 0-7 közötti index értékkel történik. A programban egy be[] nevű tömb változóban lehet megadni a fizikai mikrovezérlő lábakat, melyeket bemenetnek definiálunk. A mellékelt programban mind a 6 analóg bemenet digitális bemenetnek lett definiálva. A ki[] nevű tömb változóban kell megadni a kimeneti lábakat. Ezzel szinkronban kell megadni a pwm_hdvr_mask[] tömb változóban, hogy melyek a pwm kimenetek, hiszen a program csak ezek fényerő szabályozását kezelheti, a többi kimenetet csak fel-le kapcsolgatja. Ezen változók jelenlegi beállításai:

be[]={14,15,16,17,18,19,0,1}; 
ki[]={3,5,6,9,10,11,2,4};         
pwm_hdvr_mask[]={1,1,1,1,1,1,0,0}; //a 2,4 láb nem lehet pwm, ezért 0

Az egyes csatornák többféle tulajdonságát a felhasználó működés közben is megváltoztathatja. Ezt azért építettem be, mert a lakásvilágításomat folyamatosan fejlesztem (cserélem a hagyományos lámpákat fényerőszabályozható led világításra, illetve ha egy LED lámpa elromlik, gyorsan tudom helyettesíteni hagyományos lámpával), és nem akartam minden egyes változtatásnál programot módosítani és a mikrovezérlő beégetésével bajlódni.

A paraméterek:

  • Csatorna típusa: pwm be és kikapcsolás (ki_pwm nevű tömb) 1-nincs pwm bekapcsolva, 2-pwm be van kapcsolva a csatornán. Ha van pwm, akkor működik a fényerő fel/leúsztatás, és beállítható az alapértelmezett fényerő, amire a lámpa beáll a felkapcsolást követően. Ekkor van segédfény funkció is.
  • Klikkek közötti maximális várakozási idő:  (klikk_wait_time nevű tömb)  A beállított érték * 100msec ideig várakozik a program a következő klikkelésre ahhoz, hogy többszörös klikkelésnek érzékelje azokat (alapértelmezett 5 vagyis 500msec). Vagyis ha 500msec-en belül klikkelünk még egyet, akkor kétszeres, háromszoros stb. klikknek érzékeli a program. Ha 500msec-en belül nem érkezik klikk, akkor befejezi a klikkek számolását és az addigi klikkelések számához tartozó parancsot hajtja végre. Felkapcsolja a lámpát, beállítja a maximális fényerőt stb. Túl kicsire nem érdemes állítani, mert egy fali kapcsolóval nem lehet túl gyorsan klikkelni és esetleg csak fel és le fogjuk tudni kapcsolni a lámpát. Azonban túl lassúra sem érdemes állítani, mert ha felkapcsoljuk a lámpát és gyorsan szeretnénk mégis lekapcsolni, akkor meg kell várnunk ezt az időt, különben többszörös klikknek érzékeli a program és valami mást fog csinálni. Fontos tudni, hogy a klikkeléseket akkor is számolja a program, ha nincs az adott csatornán beállítva pwm mód. A lámpák lekapcsolása, nyaralásmód bekapcsolása és a setup mód ezeken a csatornákon is kiadható. A dupla klikkeknek azonban nem lesz hatása, mert nem fognak fényerőt növelni, vagy csökkenteni.
  • Fel/le úsztatási idő:  (fade_time nevű tömb). A beállított érték * 0,5sec  (alapértelmezett 6, vagyis 3 sec) alatt úsztatja fel vagy le a fényerőt a maximumról minimumra. Csak ha van pwm beállítva, akkor értelmezett és felhasznált érték)
  • Segédfény ideje: (assistance_time nevű tömb). A (beállított érték-1) * 3sec  ideig minimális fényerőn fog világítani a lámpa a kikapcsolást követően. Ha értéke 1, akkor nincs segédfény bekapcsolva. Ha értéke 2 akkor 3sec stb. Csak  ha van pwm, akkor értelmezett és felhasznált érték)

A setup folyamata

  1. Klikkeljünk a fali kapcsolóval ötször (a fali kapcsolót nyomjuk le gyorsan egymás után ötször).  Bármely csatorna setup módja alatt a rendszerbe kötött többi kapcsoló nem működik, a többi lámpa megőrzi aktuális állapotát.
  2. A kapcsolóhoz tartozó lámpát a program két rövid és egy hosszabb (ti-ti-tá) időre felkapcsolja (villogtatja a lámpát).
  3. Ekkor klikkelések (fali kapcsoló gyors egymásutáni kapcsolgatásával) kell megmondanunk, hogy melyik paramétert akarjuk állítani. Két klikkelés között nem telhet el több mint 500msec. A következő paramétereket tudjuk beállítani:

1 klikk: pwm szabályozás ki vagy be kapcsolása

2 klikk: klikkelések közötti maximális ismétlési idő.

3 klikk: fel és leúsztatási idő beállítása

4 klikk: segédfény világítási ideje a lámpa leikapcsolás után

Az első klikkelésig maximum 5 másodperc áll rendelkezésre. Ha addig nem történik klikk, akkor a kapcsolóhoz tartozó lámpa rövid villantásával jelzi a program, hogy kilépett a setup módból.

  • Miután befejeztük a klikkeléseket (az utolsó klikk után eltelt 500msec) a lámpa felvillan annyiszor, ahány klikkelést végeztünk. Ezzel ellenőrizhető, hogy melyik paramétert fogjuk beállítani. Ha netán nem a megfelelő paramétert sikerült „leklikkelni” akkor ezen a ponton várjunk 5 másodpercet, és a program vissza fog térni a 2. pontban leírt állapotba (levillogtatja a ti-ti-tá setup mód nyitó üzenetet), és kezdhetjük elölről a paraméter megadást.
  • Most következik a paraméter értékének beállítása. A klikkelések megkezdésére 4 másodperc áll rendelkezésre. Itt is 500msec telhet el maximum két gombnyomás között. A következő értékek állíthatóak be az egyes paraméterek esetén:

1-es paraméter, pwm ki és bekapcsolás:
1 klikk pwm mód kikapcsolva
2 klikk pwm mód bekapcsolva

2-es paraméter, max. ismétlési idő: (1-10 klikk)
1 klikk 100msec,
2 klikk 200msec stb.

3-as paraméter, fel és leúsztatási idő: (1-10 klikk)
1 klikk 500msec,
2 klikk 1sec stb.

4-es paraméter, segédfény funkció:   (1-15 klikk)
1 klikk segédfény kikapcsolva
2 klikk 3 sec
3 klikk 6 sec stb.

5-ös paraméter, segédfény funkció:  (1-9 klikk)
1-1%,
2-3%,
3-5%,
4-10%,
5-15%,
6-20%,
7-30%,
8-40%,
9-50%

A paraméter érték beállítás megkezdéséhez, azaz az első gombnyomás megkezdéséhez 4 másodperc áll rendelkezésre. Ha nem teszünk semmit, akkor 4 másodperc után a program a paraméter értékét nem változtatja meg, és visszalép a 2. pontban leírt állapotba. (felvillantja a lámpát két rövid és egy hosszú villanással (ti-ti-tá), és várja a paraméter kiválasztását).

  • Amint leklikkeltük a paraméter értékét, az utolsó klikk után 1 másodperccel a program levillogtatja a fogadott klikkek számát. Ezzel ellenőrizhetjük a beállított értéket, ami tárolásra is került. Ezt követően visszatér a program a 2. pontban leírt állapotba. (felvillantja a lámpát két rövid és egy hosszú villanással (ti-ti-tá), és várja a paraméter kiválasztását).
  • Kiválaszthatjuk a következő paramétert, és annak értékét, vagy nem nyúlunk a kapcsolóhoz, és 5 másodperc múlva a lámpa rövid felvillanása jelzi, hogy kiléptünk a setup módból.

Gyári alapértelmezett értékek beállítása

Ehhez tegyünk a mikrovezérlő 7-es lábára egy kapcsolót, illetve valamilyen más módon kössük GND-re. Áramtalanítsuk a mikrovezérlőt és helyezzük áram alá úgy, hogy közben a 7-es láb földre van kötve. Pár másodperc után a láb GND-re kötését megszüntethetjük, mintavétel a láb állapotáról csak induláskor történik. Ha a program induláskor a 7-es lábon logikai 0-át talál, akkor az EEPROM-ba beírja az alapértelmezett értékeket. Következő induláskor már ezeket az értékeket fogja kiolvasni, illetve a setuppal ezeken az értékeken változtathatunk.

Forráskód:

#include <EEPROM.h>

//segéd változók
  byte sw_prell[]={0,0,0,0,0,0,0,0};            //kapcsoló prell figyeléshez
  long sw_time[]={0,0,0,0,0,0,0,0};             //kapcsoló lenyomás prell figyeléshez eltelt idő az első kontaktus óta
  long sw_on_time[]={0,0,0,0,0,0,0,0};          //a lenyomvatartás kezdő időpontja (millis())
  byte sw_valt[]={0,0,0,0,0,0,0,0};             //segéd változó, amig nincs ujabb klikk, addig blokkolja az állapot változást
  byte sw_prev_stat[]={0,0,0,0,0,0,0,0};        //kapcsoló előző ciklusbani állapotát tárolja. Kapcsoló lenyomás és elengedés 
                                                //érzékeléséhez kell
  byte lamp_prev_stat[]={0,0,0,0,0,0,0,0};      //lampa előző ciklusbani éllapotát tárolja lámpa felkapcsolás és lakapcsolás 
                                                //érzékeléshez kell
  long sw_nosens_time[]={0,0,0,0,0,0,0,0};      //a következő klikk megérkezéséig számolja az időt (ha több mint egy megadott érték, 
                                                //akkor nem jött további kapcsolás és ezt véglegesítjük)
  byte sw_nosens[]={0,0,0,0,0,0,0,0};           //ha bebillentjük 1-be akkor figyeli, hogy jön-e új klikk
  byte sw_nosens_prev_stat[]={0,0,0,0,0,0,0,0}; //sw_nosens előző ciklusbeni állapota
  byte fenyero_tmp[]={0,0,0,0,0,0,0,0};         //a fényerő felés leusztatááskor az éppen aktuális fényerő értéke
  long fenyero_nov_time2[]={0,0,0,0,0,0,0,0};   //fenyero novelés során segédváltozó időméréshez
  byte lamp_on_prev_stat[]={0,0,0,0,0,0,0,0};   //lámpa előző állapota bekapcsolt állapota esetén 1, kikapcsolva 0
  long fenyero_nov_time[]={0,0,0,0,0,0,0,0};    //a fenyero novelesnel az idozítés segédváltozója
  byte nyomvatart_prev_stat[]={0,0,0,0,0,0,0,0};//nyomvatartás végének észrevételéhez segédváltozó 
  long sw_tart_time[]={0,0,0,0,0,0,0,0};        //két kapcsoló lenyomás közötti időmérés segéd változója. Első lenyomáskor 
                                                //ez elengedéskor indul az időmérés, az ezt követő lenyomásokkor a felfutó éltől
                                                //indul a mérés
  byte villan[]={1,1,1,1,1,1,1,1};              //ha elérte a fényerő maximumot a fényerő növelés, akkor villan a lámpa, 
                                                //ezt engedélyezi, alapértelmezette 1 mert alapértelmezett változási irány a növelés
  byte jelzes=0;                                //négy klik esetén a háromszor felvillantjuk a lámpát, ennek a segédváltozója
  byte negyklik=0;                              //segédváltozó a négy klikk estén a lámpa villogtatáshoz, és összes 
                                                //lámpa lekapcsolásához.
  long negyklik_time=0;                         //segédválzozó négy klikk esetén időméréshez
  byte villanas_szaml=0;                        //segédváltozó négy klikk estén a lámpák villogtatásához
  byte zeroadress=16;                           //ettől a címtől kezdve tároljuk az adatokat az eeprom-ban
// chip lábkiosztásának beállításai
  byte exp_input=8;                       //bővítő bemenet lábszáma
  byte exp_output=12;                     //bővítő kimenet lábszáma
  byte factory_default=7;                 //gyári beállítás bemenet lábszáma
  byte be[]={14,15,16,17,18,19,0,1};      //bemneti lábak definiálása
  byte ki[]={3,5,6,9,10,11,2,4};          //kimeneti lábak definiálása
  byte pwm_hdvr_mask[]={1,1,1,1,1,1,0,0}; //a pwm vezérelhető kimeneteket maszkolja. 0-aláb nem pwm képes, 1-a láb pwm képes.
                                          //Hiába állítja be pwm-nek a felhasználó azt abemenetet ami csak digitális kimenet, 
                                          //mivel ez felülbírálja a beállított setup paramétert. A KI[] változóval szinkronban 
                                          //kell feltölteni. Azoknál a lábaknál, melyek nem pwm vezérelhetők 0-at kell megadni.
                                        
// Működési paraméterek változói EEPROM-ból töltődnek fel bekapcsoláskor
  byte ki_pwm[]={0,0,0,0,0,0,0,0};           //kimeneti lábak pwm kimenetek legyenek-e (1-igen,0-nem)
  byte klikk_wait_time[]={5,5,5,5,5,5,5,5};  //két lenyomás közötti maximális várakozási idő ahhoz, hogy számolja a lenyomások számát
  byte fade_time[]={6,6,6,6,6,6,6,6};        //fény fel és leúsztatásának tempóját határozza meg (érték * 0,5sec) a nulláról
                                             //maximumig (vagy fordítva) teljes ideje.
  byte assistance_time[]={2,2,2,2,2,2,2,2};  //segédfény időtartama (érték * 3sec) eddig még világit a lámpa lekapcsolás után
                                             //minimumfényerővel
 // byte minimumfenyero=20;                    //erre a fényerő értékre csökken kikapcsoláskor a lámpa
  byte min_fenyero[]={1,1,1,1,1,1,1,1};       //csatornánként az alapminimális fényerő értéke. Az eeprom-ban nem ezek az értékek kerülnek tárolásra
  
  byte fenyero[]={5,5,5,5,5,5,5,5};          //lámpa fényereje 5..255-ig. Felkapcsoláskor az ebben tárolt fényerőre 
                                           //kapcsolja be a lámpát. Nyomvatartáskor ennek az értékét változtatja a program
                                  
//Kapcsolók lámpál és egyéb futás közbeni változók
  byte lamp_on[]={0,0,0,0,0,0,0,0};         //lámpa bekapcsolt állapota esetén 1, kikapcsolva 0
  byte sw_on[]={0,0,0,0,0,0,0,0};           //kapcsoló nyomvatartás alatt értéke 1
  byte sw_klik[]={0,0,0,0,0,0,0,0};         //a kapcsoló klikkelések számát számolja folyamat közben
  byte sw_klik_mem[]={0,0,0,0,0,0,0,0};     //ha letelt a két klik közötti idő, akkor ebbe tárolóik le a klikkelések száma
  byte nyomvatart[]={0,0,0,0,0,0,0,0};      //jelzi, hogy nyomvatarjuk a nyomógombot, amig nyomjuk éstéke 1
  byte valtozas_fel_le[]={1,1,1,1,1,1,1,1}; //a fenyero változás iránya nyomvatartáskor 1-fel 0-le

void setup() {
for (byte i=0;i<8;i++) {pinMode(be[i],INPUT);digitalWrite(be[i],HIGH);pinMode(ki[i],OUTPUT);}
  //be és kimenetek definiálása
pinMode(factory_default,INPUT);digitalWrite(factory_default,HIGH); 
  //gyári beállítás bemenet. Ha bekapcsoláskor 0, akkor betöltődi a gyári alapbeállítás
pinMode(exp_input,INPUT);digitalWrite(exp_input,HIGH);  
  //bővítő bemenet. Ha 0 jön, akkor minden lámpát kikapcsol (ugyanaz, mintha 3 klik-t nyomunk a kapcsolón)
pinMode(exp_output,OUTPUT);digitalWrite(exp_output,HIGH);
  //bővítő kimenet. Ha bármellyik nyomógombot háromszor lenyomjuk (tripla klikk), akkor 100msec 0-at ad a kimenetre
if (digitalRead(factory_default)==LOW)  //alapértrelmezett értékekkel töltjük fel az eeprom-ot (gyári alapbeállítás)
  {
    for (byte i=0;i<8;i++) 
    {
      EEPROM.write(zeroadress+(i*5),1);EEPROM.write(zeroadress+(i*5)+1,5);EEPROM.write(zeroadress+(i*5)+2,6);EEPROM.write(zeroadress+(i*5)+3,2);EEPROM.write(zeroadress+(i*5)+4,3);ki_pwm[i]=EEPROM.read(zeroadress+(i*5))-1;
      klikk_wait_time[i]=EEPROM.read(zeroadress+(i*5)+1);fade_time[i]=EEPROM.read(zeroadress+(i*5)+2);assistance_time[i]=EEPROM.read(zeroadress+(i*5)+3)-1;
      if (EEPROM.read(zeroadress+(i*5)+4)==1) {min_fenyero[i]=1;}
      if (EEPROM.read(zeroadress+(i*5)+4)==2) {min_fenyero[i]=3;}
      if (EEPROM.read(zeroadress+(i*5)+4)==3) {min_fenyero[i]=5;}
      if (EEPROM.read(zeroadress+(i*5)+4)==4) {min_fenyero[i]=10;}
      if (EEPROM.read(zeroadress+(i*5)+4)==5) {min_fenyero[i]=15;}
      if (EEPROM.read(zeroadress+(i*5)+4)==6) {min_fenyero[i]=20;}
      if (EEPROM.read(zeroadress+(i*5)+4)==7) {min_fenyero[i]=30;}
      if (EEPROM.read(zeroadress+(i*5)+4)==8) {min_fenyero[i]=40;}
      if (EEPROM.read(zeroadress+(i*5)+4)==9) {min_fenyero[i]=50;}
    }
    //alapértelmezett adatokkal történő feltöltés
    //pwm be és kikapcsolás (ki_pwm nevű tömb) 1-nem 2-igen (alapértelmezett 1, nincs pwm vagyis nincs fényerő fel-le 
    //                      üsztatás, csak ki-be kapcsolgatás) 
    //klikkek kozotti idő   (klikk_wait_time nevű tömb) /100msec    (alapértelmezett 5 vagyis 500msec-en belül kell 
    //                      lenyomni a gombot, hogy egy gombnyomás sorozatnak érzékelje és számolja a gombnyomások számát. 
    //                      Ennyi idő után érzékeli folyamatos nyomvatartásnak a gombnyomást és változtatja a fényerőt
    //fel-le usztatási idő  (fade_time nevű tömb) /kb. 0,5sec  (alapértelmezett 6, vagyis 3 sec alatt úsztatja fel vagy le
    //                      a fényerőt a maximumról minimumra. Csak ha van pwm beállítva, akkor értelmezett és felhasznált érték)
    //segédfény ideje       (assistance_time nevű tömb) /3sec  Ha értéke 1, akkor nincs segédfény bekapcsolva, 2-nél 3sec, 
    //                      3-nál 6-se stb. (alapértelmezett 2, vagyis 3sec-ig minimum fényerőn világit a lámpa, és csak ezt 
    //                      követően kapcsol ki. Csak  ha van pwm, akkor értelmezett és felhasznált érték) 
    //minimális fényerő     1-1%, 2-3%, 3-5%, 4-10%, 5-15%, 6-20%, 7-30%, 8-40%, 9-50%
  } //then ág vége
  else  //bekepcsoláskor a paraméter változók feltöltése az eeprom-ból
  {
    Serial.begin(9600); //csak fejlesztéshez a paraméterek kiíratása
    Serial.println("Lampa  pwm  klikk  usztat  seged");  
    Serial.println("-----  ---  -----  ------  -----");  
    for (byte i=0;i<8;i++) 
    {
      ki_pwm[i]=EEPROM.read(zeroadress+(i*5))-1;klikk_wait_time[i]=EEPROM.read(zeroadress+(i*5)+1);fade_time[i]=EEPROM.read(zeroadress+(i*5)+2);assistance_time[i]=EEPROM.read(zeroadress+(i*5)+3)-1;
      Serial.print("  ");   //csak fejlesztéshez a paraméterek kiíratása
      Serial.print(i);
      Serial.print("    ");
      Serial.print(ki_pwm[i]);
      Serial.print("    ");
      Serial.print(klikk_wait_time[i]);
      Serial.print("      ");
      Serial.print(fade_time[i]);
      Serial.print("       ");
      Serial.println(assistance_time[i]); 
    }
  } //else vége
} //void setup vége

void loop()
{
  klikk_detect();
  // ez a fuggvény nem vesz el időt, nincs benne delay utasítás. akár további funkciók is beilleszthetőek, de arra vigyázni kell,
  //hogy a további prgramrészekben se legyenek hosszú várakozások, mert a lámpa vezérlés esetleg nem fog működni
  //ha a program lemarad egy-egy kapcsoló lenyomásról.
} //void loop vége

void klikk_detect() { 
  //egy lenyomás hatására bekapcsol a lámpa. A bekapcsolás a nyomógomb elengedésekor következik be.
  //A nyomógomb elengedésekor elindul egy időzítás, ami minden lenyomáskor ujraindul 500msec (beállítható)időre, 
  //ez alatt további nyomógomb lenyomások történhetnek, de ezek a lámpát nem kapcsolják fel vagy le (nosens változó).
  //Miután letelt a (nosens) időszak, a sw_klik_mem változóban megkapjuk a klikkelések számát. Ha csak egy gombnyomás
  //volt és ezzel fel vagy lekapcsolódott a lámpa, akkor is megkapjuk a számot (1). Ha az első gombnyomás hosszabb ideig
  //tart mint 500msec, akkor az foilyamatos nyomvatartásnak minősül (nyomvatart=1) Ha felkapcsolt lámpánál nyomjuk 
  //folyamatosan a lámpát, akkor az elengedéskor a lámpa nem alszik el, és az sw_klik_mem tartalma 0 marad, a fényerő 
  //növekszik vagy csökken (valtozas_fel_le). Ha lekapcsolt lámpánál nyomjuk folyamatosan a nyomógombot, akkor 500msec
  //után a lámpa felkapcsol, a nyomvatartás ideéig emelkedik a fényerő és a nyomógom elengedésekor az sw_klik_mem 
  //változó 0 marad.
  for (byte i=0;i<8;i++) //végig nézzük az összes nyomógombot
  {
    if (sw_on[i]==0 and digitalRead(be[i])==LOW and sw_prell[i]==0)
       {sw_prell[i]=1;sw_time[i]=millis();} 
       //első lenyomás érzékelése
    if (sw_on[i]==0 and digitalRead(be[i])==LOW and sw_prell[i]==1 and millis()>sw_time[i]+50) 
       {sw_on[i]=1;sw_prell[i]=0;sw_on_time[i]=millis();} 
       // már 50msecv óta nyomva van, most már biztos, hogy lenyomták és nem prellezik
    if (sw_on[i]==1 and digitalRead(be[i])==HIGH and sw_prell[i]==0) 
       {sw_prell[i]=1;sw_time[i]=millis();} 
       //első elengedés érzékelése
    if (sw_on[i]==1 and digitalRead(be[i])==HIGH and sw_prell[i]==1 and millis()>sw_time[i]+50) 
       {sw_valt[i]=0;sw_on[i]=0;sw_prell[i]=0;} 
       // már 50msecv óta elengedte, most már biztos, hogy elengedte a kapcsolót és nincs prell
    
    if (sw_on[i]==1 and sw_prev_stat[i]==0) 
       {sw_tart_time[i]=millis();} 
       //kapcsoló felfutó élénél indítjuk az időmérést a nyomvatartás figyeléshez
    if (sw_on[i]==1 and sw_prev_stat[i]==1 and millis()>sw_tart_time[i]+(klikk_wait_time[i]*100)) 
       //kapcsoló nyomvatartás ha várakozási idő (500msec) letelt
    {
       nyomvatart[i]=1;sw_klik_mem[i]=0;
       if (lamp_on[i]==0)
         {lamp_on[i]=1;fenyero[i]=min_fenyero[i];valtozas_fel_le[i]=1;villan[i]=1;fenyero_nov_time2[i]=millis();}
         //a lámpa nincs felkapcsolva, ezért felkapcsoljuk
    }
    if (sw_on[i]==0 and sw_prev_stat[i]==1 and nyomvatart[i]==1) 
       {nyomvatart[i]=0;sw_klik[i]=0;sw_klik_mem[i]=0;sw_nosens[i]=1;sw_nosens_time[i]=millis();}
       //ha kapcsolót elengedtuk nyomvatartás után, akkor mindent lenullázunk, lámpa felkapcsolva marad, 
       //a következő kapcsoló érzékelésik még 400msec-et várni kell
    if (sw_on[i]==0 and sw_prev_stat[i]==1 and sw_klik[i]==0 and sw_nosens[i]!=1) //sw_on felfutó éle, lápát kel fel vagy le kapcsolni
    {
       sw_klik[i]++;sw_nosens_time[i]=millis();sw_nosens[i]=1; //lehet, hogy több klik lessz, azért növelni kell a klikk számlálót
                                                               //nosens időt elkezdjük mérni, és nosens is 1, ezzel jelezzük, hogy
                                                               //nosens ideig további lenyomások jöttek, nem kell fel le kapcsolni
       if (lamp_on[i]==0) 
         {lamp_on[i]=1;fenyero_nov_time2[i]=millis();}  //lampa felkapcsol
       else 
         {lamp_on[i]=0;}}  //lampa lekapcsol
       //sw_on lefutó éle első kattintáskor, lámpa fel vagy lekapcsol
    if (sw_on[i]==1 and sw_prev_stat[i]==0 and sw_klik[i]>0) 
       {sw_klik[i]++;sw_nosens_time[i]=millis();} 
       //sw_on felfutó második és további felfutó élei, lehet számlálni egyet, és ujraindítjuk a nyomógomb figyelési időzitőt 
    if (sw_nosens[i]==1 and millis()>sw_nosens_time[i]+(klikk_wait_time[i]*100)) 
       {sw_nosens[i]=0;}
       //500msec (illetve beállított érték) után ujra figyeli a nyomógombot
    if (sw_nosens[i]==0 and sw_nosens_prev_stat[i]==1) 
       {sw_klik_mem[i]=sw_klik[i];sw_klik[i]=0;jelzes=1;}
       //miután letelt a várakozási idő (500msec), a nosens lefutó élekor tároljuk a klikkek számát sw_klik_mem-ben
    sw_prev_stat[i]=sw_on[i]; //nyomógom elengedés felismeréséhez     
    lamp_prev_stat[i]=lamp_on[i]; //amikor a lampa felkapcsol, akkor szamolunk egyet
    sw_nosens_prev_stat[i]=sw_nosens[i]; //amig nosens 1 addig számoljuk a klikkeket, illetve a kapcsoló lenyomások nem kapcsolják a lámpát
    if (ki_pwm[i]==1 and pwm_hdvr_mask[i]==1) //csak pwm kimeneteknél és pwm-re beállított kimeneteknél kell fényerő szabályozással foglalkozni
    {
      if (fenyero[i]<min_fenyero[i]+1) 
         {fenyero[i]=min_fenyero[i]+2;}  
         //fényerő minimumra értünk. Azért állunk meg előtte egy értékkel, mert így kikapcsoláskor lesz segédfény
      if (fenyero[i]>254) 
         {fenyero[i]=254;}  
         //fenyero maximumhoz értünk
      if (lamp_on[i]==1 and millis()>fenyero_nov_time2[i]+(fade_time[i]*2))  
         //ha felkapcsoltuk a lámpát akkor a fenyero változóig növeljuk a fényerőt
      {
         if (fenyero_tmp[i]<fenyero[i]) 
            {fenyero_tmp[i]=fenyero_tmp[i]+1;fenyero_nov_time2[i]=millis();analogWrite(ki[i],fenyero_tmp[i]);}
            //még nem értük el a szükséges fényerőt
         if (fenyero_tmp[i]>fenyero[i]) 
            {fenyero_tmp[i]=fenyero_tmp[i]-1;fenyero_nov_time2[i]=millis();analogWrite(ki[i],fenyero_tmp[i]);}
            // csökkenteni is kell a fényerőt, ha folyamatosan nyomja a gombot (követéshez csökkentés is kell)
      }
      if (lamp_on[i]==0 and millis()>fenyero_nov_time2[i]+(fade_time[i]*2))  
         //lekapcsoltuk a lámpát, csökkenteni kell a fénmyerőt 0-ig
      {
         if (fenyero_tmp[i]==min_fenyero[i] and millis()>fenyero_nov_time2[i]+assistance_time[i]*3000+5) 
            {analogWrite(ki[i],0);fenyero_tmp[i]=0;}
            // minimumfényerőnél megállunk a csökkentéssel és várunk, amig segédfény idő le nem telik
         if (fenyero_tmp[i]>min_fenyero[i]) 
            {fenyero_tmp[i]=fenyero_tmp[i]-1;fenyero_nov_time2[i]=millis();analogWrite(ki[i],fenyero_tmp[i]);}
            //fenyerő csökkentés a minimum fényerőig
      }  
      if (lamp_on[i]==0 and lamp_on_prev_stat[i]==1) 
         {analogWrite(ki[i],0);fenyero_nov_time2[i]=millis()+90;}  
         //lampa lekapcsoláskor csökkemnteni fogjuk a fenyerőtz
      if (valtozas_fel_le[i]==1 and nyomvatart[i]==1 and millis()>fenyero_nov_time[i]+15) 
         {fenyero[i]=fenyero[i]+1;fenyero_nov_time[i]=millis();}
         //nyomvatartjuk a gombot és növeljuk a fényerőt maximumig
      if (fenyero[i]==255 and nyomvatart[i]==1 and villan[i]==1) 
         {analogWrite(ki[i],125);fenyero_nov_time2[i]=millis();villan[i]=0;} 
         //nyomvatartjuk a kapcsolót és elértük a maximumot megvillantjuk a lámpát rövid fényerő csökkentéssel
      if (fenyero[i]==255 and nyomvatart[i]==1 and millis()>fenyero_nov_time2[i]+200) 
         {analogWrite(ki[i],255);} 
         //200msec múlva visszakapcsolojuk a fényt maximumra (nyomvatartja a kapcsolót és elértük a maximumot, villanás van)
      if (valtozas_fel_le[i]==0 and nyomvatart[i]==1 and millis()>fenyero_nov_time[i]+15) 
         {fenyero[i]=fenyero[i]-1;fenyero_nov_time[i]=millis();}
         // nyomvatartjuk a gombot és csökkentjük a fényerőt
      if (nyomvatart[i]==0 and nyomvatart_prev_stat[i]==1) //nyomvatartás után elengedtük a nyomógombot
      {
         if (valtozas_fel_le[i]==1) 
           {valtozas_fel_le[i]=0;} //ha eddig "fel" irány volt, akkor most "le" jön
         else 
           {valtozas_fel_le[i]=1;villan[i]=1;} //ha eddig "le" irány volt, akkor most "fel" jön. Engedélyezzük a villanást is
                                               //ha elérte a mximumot a fényerő
      }
      nyomvatart_prev_stat[i]=nyomvatart[i]; //előző ciklus állapotának tárolása a változás megállapításához
//      if (sw_klik_mem[i]==2) 
//      {
//         lamp_on[i]=1;
//         if (valtozas_fel_le[i]==1) 
//            {fenyero[i]=254;valtozas_fel_le[i]=0;} //dupla klikkre növelnunk kell a fényerőt maximumra
//         else 
//            {fenyero[i]=min_fenyero[i]+1;valtozas_fel_le[i]=1;villan[i]=1;};sw_klik_mem[i]=0;sw_klik[i]=0;
//            //dupla klikkre csökkentenünk kell a fényerőt minimumra
//         } 
    }
    else
    {
      if (negyklik==0) {if (lamp_on[i]==1) {digitalWrite(ki[i],HIGH);} else {digitalWrite(ki[i],LOW);}}
    }
    if (digitalRead(exp_input)==LOW) {sw_klik_mem[i]=2;}
    if (sw_klik_mem[i]==2) //két klikk érkezett, minden lámpát lekapcsolunk
    {
       for (byte z=0;z<8;z++)
       {
         lamp_on[z]=0;sw_klik_mem[z]=0;
         //lámpák lekapcsolása
       }
       ;digitalWrite(exp_output,LOW);delay(10);digitalWrite(exp_output,HIGH); //bővítő kimenet írása
    }  //if vége
       // három klik hatására minden lámpát lekapcsolunk
    if (sw_klik_mem[i]==4) //négy klikk történt
    { 
       negyklik=1;negyklik_time=millis();sw_klik_mem[i]=0;villanas_szaml=0;
       for(byte y=0;y<8;y++)
       {
         digitalWrite(ki[y],LOW);lamp_on[y]=0;fenyero_tmp[y]=0;
        //négy klik hatására villogtatunk, és ezzel jelezzuk, hogy bekapcsoljuk az otthonlét szimulátort. Ez még nincs megírva
        //ide kellene írni az ekkor meghívott fügvényt.
       } //for vége
    } //if vége
    if (sw_klik_mem[i]==3)
       {sw_klik_mem[i]=0;digitalWrite(ki[i],LOW);lamp_on[i]=0;fenyero_tmp[i]=0;param_select(i);} 
       //3 klikkel belépünk a setup módba. Előtte  a lámpát lekapcsoljuk. lamp_on[i]=0 lekapcsolja a lámpát
       // fenyero_tmp[i]=0 ahhoz kell, nehogy a következő programciklus visszakapcsolja és fokozatosan 
       //csökkentse 0-ra, miutan kikapcsoluk a lámpát (csak pwm-nél van jelentősége)
    lamp_on_prev_stat[i]=lamp_on[i];
  }
  if (negyklik==1 and millis()>negyklik_time+500)
  {
    for(byte k=0;k<5;k++){
      if (villanas_szaml==0) {if (ki_pwm[k]==0) {digitalWrite(ki[k],HIGH);} else {analogWrite(ki[k],255);}}
      if (villanas_szaml==1) {if (ki_pwm[k]==0) {digitalWrite(ki[k],LOW);} else {analogWrite(ki[k],0);}}
      if (villanas_szaml==2) {if (ki_pwm[k]==0) {digitalWrite(ki[k],HIGH);} else {analogWrite(ki[k],255);}}
      if (villanas_szaml==3) {if (ki_pwm[k]==0) {digitalWrite(ki[k],LOW);} else {analogWrite(ki[k],0);}}
    } 
    villanas_szaml++;
    if (villanas_szaml==4) {negyklik=0;villanas_szaml=0;}
    negyklik_time=millis(); 
  } 
}

void param_select(byte select_lamp) {
  //ha lenyomta ötször a nyomógombot, akkor minden más nyomógob figyelése felfüggesztve
  //megvillogtatjuk a lámpát (a nyomógombhoz tatozót) .-.. (morze L)jelzéssel és várjuk, hogy nyomkkodja a nyomógombot
  //minden egyes alkalommal, maikor paraméter kiválasztásra várunk, ez a morse kód lesz levillogtatva a lámpán.
  // ha nem nyomja le a nyomógombot, akkor 10 sec mulva kilépünk a setupból, ezt egy lámpa villanás jelzi
  //két nyomógomb lenyomás között maximum 500msec telhet el. Ha ennél sűrűbba nyomkodja, akkor számoljuk. 
  //ha 500msec-nél többet várt, és legalább egyszer lenyomta, akkor belépünk a paraméter megadásba
  //Hogy melyik paramétert fogjuk megadni, azt a nyomógomb lenyomások száma adja meg. Négy paraméter van, tehát max. négy
  //nyomógomb lenyomást várunk. Ha többet nyomott le, akkor nem lesz beállítva paraméter.
  //ha visszatértünk paraméter beállításból, akkor ujra indul a 10 sec, és ezen belül ujra lehet paramétert kiválasztani megadni.
  byte setup_indulas_jelzes=1;
  byte param_number=0;  // a kiválasztott paraméter száma
  long select_time; //kiválasztásra szolgáló időt méri
  while ( millis() < select_time+4000) {
    if (setup_indulas_jelzes==1) 
    {
      digitalWrite(ki[select_lamp],LOW);delay(200);digitalWrite(ki[select_lamp],HIGH);delay(200);digitalWrite(ki[select_lamp],LOW);
      delay(200);digitalWrite(ki[select_lamp],HIGH);delay(200);digitalWrite(ki[select_lamp],LOW);
      delay(200);digitalWrite(ki[select_lamp],HIGH);delay(800);digitalWrite(ki[select_lamp],LOW);
      // ..- jelzés arra, hogy várja a paraméter sorszámának megadását
      setup_indulas_jelzes=0;
      select_time=millis();
    } //If vége
    if (sw_on[select_lamp]==0 and digitalRead(be[select_lamp])==LOW and sw_prell[select_lamp]==0)
       {sw_prell[select_lamp]=1;sw_time[select_lamp]=millis();} 
       //első lenyomás érzékelése
    if (sw_on[select_lamp]==0 and digitalRead(be[select_lamp])==LOW and sw_prell[select_lamp]==1 and millis()>sw_time[select_lamp]+50) 
       {sw_on[select_lamp]=1;sw_prell[select_lamp]=0;} 
       //már 50msecv óta nyomva van, most már biztos, hogy lenyomták és nem prellezik
    if (sw_on[select_lamp]==1 and digitalRead(be[select_lamp])==HIGH and sw_prell[select_lamp]==0) 
       {sw_prell[select_lamp]=1;sw_time[select_lamp]=millis();} 
       //első elengedés érzékelése
    if (sw_on[select_lamp]==1 and digitalRead(be[select_lamp])==HIGH and sw_prell[select_lamp]==1 and millis()>sw_time[select_lamp]+50) 
       {sw_on[select_lamp]=0;sw_prell[select_lamp]=0;} 
       // már 50msecv óta elengedte, most már biztos, hogy elengedte a kapcsolót és nincs prell
    if (sw_on[select_lamp]==1 and sw_prev_stat[select_lamp]==0) 
       {param_number++;sw_tart_time[select_lamp]=millis();} 
       //kapcsoló felfutó élénél indítjuk az időmérést a következő lenyomás figyeléshez
    if (param_number>0 and millis()>sw_tart_time[select_lamp]+500)  
       {param_setup(param_number,select_lamp);param_number=0;setup_indulas_jelzes=1;select_time=millis();}
       // ha 500msec-ig nem nyom újabb nyomóggombot, akkor belépünk a paraméter setupba a lenyomások számával
       // paraméter beállítás után ujraindítjuk a setup időt, lehet ujabb paramétert beállítani
    digitalWrite(ki[select_lamp],LOW); //biztosan ne égjen a lámpa
    sw_prev_stat[select_lamp]=sw_on[select_lamp]; //nyomógom elengedés felismeréséhez     
  }
  digitalWrite(ki[select_lamp],LOW);delay(200);digitalWrite(ki[select_lamp],HIGH);delay(200);digitalWrite(ki[select_lamp],LOW);
  //egy rövid (200msec) villanás jelzi, hogy kilépünk a setup-ból
}

void param_setup(byte param_number, byte select_lamp) {
  //Amikor belépünk ebbe a függvénybe, a nyomógomb lenyomások száma jelzi, hogy melyik paramétert fogjuk beállítani.
  //Elősször levillogtatjuk a paraméter számát, majd várjuk, hogy nmyomógomb lenyomások számával megadja  a 
  //beállítandó paraméter értékét. 4sec van arra, hogy elkezdje nyomkodni a gombot, ha nem teszi, kilépünk 
  //a paraméter kiválasztásból, amit egy villanás jelez. Ha legalább egyszer lenyomta a gombot, de ha 500msec-ig 
  //nem nyomja le ujra, akkor befelyeződött a paraméter érték megadása, meghívódik a param_setup függvény
  byte param_value=0;
  long param_ido;
  for (byte u=0;u<param_number;u++) 
  {
    digitalWrite(ki[select_lamp],LOW);delay(400);digitalWrite(ki[select_lamp],HIGH);delay(400);
    //annyit villantunk hosszan (400msec), ahányszor lenyomta a nyomógombot
  }
  digitalWrite(ki[select_lamp],LOW);delay(500); // lekapcsoljuk a lámpát
  //kezdődik a nyomógomb figyelés
  param_ido=millis();
  while ( millis() < param_ido+4000) 
  {
    if (sw_on[select_lamp]==0 and digitalRead(be[select_lamp])==LOW and sw_prell[select_lamp]==0)
       {sw_prell[select_lamp]=1;sw_time[select_lamp]=millis();} 
       //első lenyomás érzékelése
    if (sw_on[select_lamp]==0 and digitalRead(be[select_lamp])==LOW and sw_prell[select_lamp]==1 and millis()>sw_time[select_lamp]+50) 
       {sw_on[select_lamp]=1;sw_prell[select_lamp]=0;} 
       // már 50msecv óta nyomva van, most már biztos, hogy lenyomták és nem prellezik
    if (sw_on[select_lamp]==1 and digitalRead(be[select_lamp])==HIGH and sw_prell[select_lamp]==0) 
       {sw_prell[select_lamp]=1;sw_time[select_lamp]=millis();} 
       //első elengedés érzékelése
    if (sw_on[select_lamp]==1 and digitalRead(be[select_lamp])==HIGH and sw_prell[select_lamp]==1 and millis()>sw_time[select_lamp]+50) 
       {sw_on[select_lamp]=0;sw_prell[select_lamp]=0;} 
       // már 50msecv óta elengedte, most már biztos, hogy elengedte a kapcsolót és nincs prell

    if (sw_on[select_lamp]==1 and sw_prev_stat[select_lamp]==0) {param_value++;sw_tart_time[select_lamp]=millis();} 
       //kapcsoló felfutó élénél indítjuk az időmérést a következő lenyomás figyeléshez
    if (param_value>0 and millis()>sw_tart_time[select_lamp]+500)  {param_store(select_lamp,param_number,param_value);
                                                                   param_value=0;param_ido=millis()-4000;}
       // ha 500msec-ig nem nyom újabb nyomóggombot, akkor belépünk a paraméter setupba a lenyomások számával
    digitalWrite(ki[select_lamp],LOW); //biztosan ne égjen a lámpa
    sw_prev_stat[select_lamp]=sw_on[select_lamp]; //nyomógom elengedés felismeréséhez     
  } //while ciklus vége
}

void param_store(byte select_lamp,byte param_number,byte param_value) {
  //Ennek a függvénynek a feladata a beállított paraméter érték tárolása az EEPROM-ban, és a vezérlést végző tömb
  //változókban. Elősször levillogtatja a program a paraméter értéket, aztán tárol.
  for (byte u=0;u<param_value;u++) {digitalWrite(ki[select_lamp],LOW);delay(400);digitalWrite(ki[select_lamp],HIGH);delay(400);}
    // annyit villantunk hosszan (400msec), ahányszor lenyomta a nyomógombot
  digitalWrite(ki[select_lamp],LOW);delay(500); // lekapcsoljuk a lámpát
  // kezdődik a paraméter érték tárolás és memória változó felülírás az új értékkel
  EEPROM.write(zeroadress+(select_lamp*5)+param_number-1,param_value);
  for (byte i=0;i<8;i++) 
  {
    ki_pwm[i]=EEPROM.read(zeroadress+(i*5))-1;klikk_wait_time[i]=EEPROM.read(zeroadress+(i*5)+1);fade_time[i]=EEPROM.read(zeroadress+(i*5)+2);
    assistance_time[i]=EEPROM.read(zeroadress+(i*5)+3)-1;
      if (EEPROM.read(zeroadress+(i*5)+4)==1) {min_fenyero[i]=1;}
      if (EEPROM.read(zeroadress+(i*5)+4)==2) {min_fenyero[i]=3;}
      if (EEPROM.read(zeroadress+(i*5)+4)==3) {min_fenyero[i]=5;}
      if (EEPROM.read(zeroadress+(i*5)+4)==4) {min_fenyero[i]=10;}
      if (EEPROM.read(zeroadress+(i*5)+4)==5) {min_fenyero[i]=15;}
      if (EEPROM.read(zeroadress+(i*5)+4)==6) {min_fenyero[i]=20;}
      if (EEPROM.read(zeroadress+(i*5)+4)==7) {min_fenyero[i]=30;}
      if (EEPROM.read(zeroadress+(i*5)+4)==8) {min_fenyero[i]=40;}
      if (EEPROM.read(zeroadress+(i*5)+4)==9) {min_fenyero[i]=50;}
  }
  delay(1000);
}

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!