Timer ON-OFF cu 2 Canale si Arduino Nano V3

  Avand nevoie de un Timer care sa poata functiona cu timpi de ordinul orelor si al minutelor, un timer cu doua iesiri independente (si la care sa pot seta eu timpii necesari, separat pentru fiecare canal) ON si OFF,  la indrumarea prietenului Niq_ro am demarat realizarea unui Timer ON-OFF cu 2 Canale avand la baza un modul Arduino Nano V3 si afisarea informatiilor pe un LCD 16x2.

  Eu fiind un incepator (novice) in mediul Arduino, sub indrumarea/sfaturile/ajutorul lui Niq_ro am reusit sa duc la bun final un Timer ON-OFF cu 2 Canale bazat pe Arduino, care acum cand m-am familiarizat cat de cat cu Arduino,  nu mai imi pare asa de complicat creearea unui soft de la zero.

 Deci tot creierul acestui timer este un modul Arduino Nano, modul achizitionat de mine de pe Aliexpress la un pret destul de modic, a se vedea aici cel mai mic pret Arduino Nano V.3.

 Schema de principiu a Timerului este aceasta:



Dupa achizitionarea celor necesare (multe se aflau prin sertare deja) am trecut la desenarea unui cablaj pentru acest timer, cablaj care este desenat in programul Sprint Layout 6.
Asa arata cablajul desenat in Sprint Layout 6 pentru acest Timer:





Dupa verificarea cablajului, ca sa nu contina ceva greseli, am trecut la realizarea lui fizica:




Realizarea silkscreenului:


Montarea componentelor:







In primele teste Timerul functiona OK, pentru testare am folosit timpul minim care se poate seta, adica 1 minut la ON si 1 minut la OFF.
Atasez un mic clip cu Timerul din timpul primelor teste.



Daca Timerul functiona (deocamdata) conform dorintelor/cerintelor mele, in urma unei discutii cu Niq_ro am decis ca impreuna sa facem si un "fadding" pentru LED-ul de backlight al LCD-ului, tot aveam "la baza" modulul Arduino, spatiu in el pentru imbunatatiri exista, asa ca am decis sa alimentatm LED-ul de backlight de pe pinul D11 al lui Arduino, pin ce ne permite sa lucram cu PWM ca sa putem stinge lin LED-ul de backlight.
Daca dupa un timp de inactivitate a butoanelor (~ 1 minut, setabil din soft) backlightul afisajului se stinge lin (PWM) pana la un anumit nivel minim (pre-setat in scketch-ul Arduino), nivel ce ne permite citirea usoara a informatiei afisate pe LCD, nivel minim de iluminare care pe timp de noapte se dovedeste a fi destul de eficient/nederanjant datorita iluminarii reduse.
Despre acest "fadding" are si Niq_ro un articol postat aici

Dupa implementarea in cod si testarea acestui "fadding", a urmat partea cea mai dificila de realizat in regim de amator, confectionarea unei carcase adecvate pentru acest Timer.
Tot rascolind prin cutiile mele in cautarea unei cutii potrivite, in care sa pot monta timerul, am gasit in final una din material plastic care indeplinea cerintele.





Am probat asezarea LCD-ului, a placutei timerului, montare butoane si am demarat desenarea unui panou frontal in Sprint Layout 6, nu am realizat panoul in Front Designer 3, in Sprint parca merge mai repede si nu era un panou frontal asa de complex.

Panoul frontal desenat in Sprint:


Initial vroiam sa realizez panoul tot prin transfer de toner (metoda care se aplica si la realizarea cablajelor imprimate) pe o mica placuta de aluminiu, lucru realizat si in trecut la realizarea unor panouri frontale pentru Statia de lipit Wolf cat si pentru o Orga de Lumini.
Dupa cateva teste am renuntat si am apelat la prietenul Adicontakt (cei care acceseaza forumurile dedicate il cunosc).
Fiind un om cu suflet bun/mare, m-a ajutat cu realizarea panoului frontal si inscriptiile din zona mufei de intrare 220v si iesire canale, caruia vreau sa-i multumesc pe aceasta cale pentru ajutorul/sprijinul acordat.
Un mic clip filmat/trimis de Adicontakt, din timpul inscriptionarii panoului frontal prin tehnologia laser:




Carcasa cu panoul frontal montat



Alimentarea Timerului este realizata cu un alimentator functional ramas nefolosit de la un telefon Nokia, am scos placuta sursei in comutatie din alimentator si am inclus "mini-sursa" in carcasa timerului





Functionarea Timerului

 La alimentarea Timerului cu tensiune, prima data apar doua ecrane informative pe LCD, despre denumire program, precum si versiunea de soft folosita.

 Dupa afisarea celor doua ecrane informative, pe LCD apar timpii setati pentru ON si OFF iar timerul trece automat in starea ON si se aprinde LED-ul pentru ON aferent canalului activ (momentan doar 1 canal).  Pe LCD timpul setat la ON incepe sa scada de la timpul setat spre zero.
 Tot pe ecran mai este afisat unul din semnele "<" sau ">", semn care ne indica starea "activa" in care se afla timerul, ON sau OFF.

 Iluminarea afisajului a fost setata sa plece automat pe nivelul minim pre-setat din soft. La orice apasare scurta pe oricare buton din cele trei, iluminatul LCD-ului se aprinde la maxim si apoi dupa scurgerea timpului setat in soft (10-15 secunde) scade lin (PWM) la nivelul minim pre-setat. Pe timpul cat suntem in Meniul Set iluminatul ramane aprins pe nivel maxim.

 La o apasare scurta a oricarui buton, impreuna cu aprinderea iluminatului la maxim, apare pe LCD si mesajul "Apasa minim 2sec pe butonul SET" pentru a ne re-aminti cum se intra in Meniul Set al Timerului.
 Dupa o apasare de peste 2 sec a butonului SET, apare (la eliberarea butonului) mesajul "Ai apasat destul intram in MENIU" si dupa circa 1-2 secunde apare Meniul pentru Setari.

 Meniul pentru Setari este realizat din mai multe meniuri (sub-meniuri) care sunt accesate succesiv prin apasarea butonului SET.

 Primul meniu Set 1-1 (1-1, 1 inseamna ca setam Timer 1) se seteaza minute ON (din butoanele Plus si Minus se seteaza minutele dorite), se apasa SET se trece la meniul Set 1-2 unde setam ore ON, la fel ca la minute din Plus si Minus, se apasa Set se trece la meniul Set 1-3 unde setam minute OFF, se apasa Set se trece la meniul Set 1-4 unde setam ore OFF, apoi urmeaza meniurile set 2-1, 2-2, 2-3, 2-4 unde setam Timerul 2.
 Eu personal, fiind presat de timp ca sa definitivez Timerul (ca sa nu cumpar o priza temporizata :limb: ), am realizat deocamdata doar partea de Timer 1 in soft, urmeaza ca sa modific softul ca sa implementez si partea de Timer 2 in soft.

  Ultimul meniu se ocupa de salvarea datelor in soft (EEPROM), mesajul afisat pe LCD este "Iesire meniu SET - Salvare Setari-", dupa 1-2 secunde Timerul revine la ecranul principal si incepe decrementarea Timpului setat pentru ON (stare default este ON asa cum am scris si mai sus).

Daca NU se parcurg toate sub-meniurile pana se ajunge la ultimul meniu, cel in care se salveaza datele, sau NU se mai apasa nici un buton, dupa un anumit timp (setabil din soft ~ 1 minut) Timerul revine la meniul principal, dar FARA sa se salveze datele modificate la minute-ore.

Dupa ce fac si implementarea in soft a Timerului 2 , informatiile vor fi afisate pe LCD ciclic, la un interval de 1-2 secunde intre Timer 1 si Timer 2.

Codul folosit/realizat de mine, deocamdata doar partea pentru Timer 1.

Timer ON-OFF cu 2 Canale si Arduino Nano V.3 - versiune soft 1.3


/////////////////////////////////////////////////////////
// Timer ON-OFF cu 2 Canale si Arduino Nano V.3        //
// ver1.3 -  creat-optimizat GeoMar, co-autor Niq_ro   // 
//     **************  2o17 **************             //
/////////////////////////////////////////////////////////

#include <LiquidCrystal.h>
#include <EEPROM.h>
#define bec A2
#define BUT1 A3     // Buton Minus
#define BUT2 A4     // Buton Plus
#define BUT3 A5     // Buton Meniu
#define fundal 11   // led de fundal

int inceput = 0;
int aprinscat = 0;   // in secunde
int stinscat = 0;    // in secunde
byte aprins = 0;     //  becul e considerat oprit
byte minuteaprins, oreaprins, minutestins, orestins ;

int timpramas = 0;
int actual = 0;
int oreramase = 0;
int minuteramase = 0;
int secunderamase = 0;
int timpramas1 = 0;

byte meniu = 10; //  MENIU = 0 initial
//  MENIU = 1
//  MENIU = 2
//  MENIU = 3
//  MENIU = 4
//  MENIU = 5
//  MENIU = 6
//  MENIU = 7
//  MENIU = 8
//  MENIU = 9 - meniu de initializare
byte minon;
byte oreon;
byte minoff;
byte oreoff;

// -- Variabile pentru butoane --------
int buttonstate = 0;    //flag to see if the button has been pressed, used internal on the subroutine only
int pushlengthset = 2000;   // value for a long push in mS
int pushlength = pushlengthset;   // set default pushlength
int pushstart = 0;  // sets default push value for the button going low
int pushstop = 0;   // sets the default value for when the button goes back high

boolean buttonflag = false; // default value for the button flag

unsigned long intraremeniu;
unsigned long iesiremeniu = 60000; // timp revenire meniu initial daca nu se apasa buton meniu

int ultimaapasare;     // se memoreaza timpul cand se apasa ultima data butonul
int timplumina = 10;   // timp cat ramane LED-ul de fundal aprins
byte fundalminim = 35;
byte fundalmaxim = 255;
byte nivelfundal = 35;
byte treaptanivel = 7;  // treapta scadere iluminare d ela maxim la minim


//---- Definire pini pentru conectare LCD ----//
LiquidCrystal lcd (2,  //RS no digital 7
                   3,  //EN no digital 6
                   4,  //D4 no digital 5
                   5,  //D5 no digital 4
                   6,  //D6 no digital 3
                   7); //D7 no digital 2

void setup() {
  lcd.begin(16, 2);                                     //Initializare LCD 20 x 4
  pinMode(bec, OUTPUT);  // pinul alocat becului e stins
  digitalWrite(bec, LOW);  // bec stins

  //pinMode(fundal, OUTPUT);  // pinul alocat backlight 
  analogWrite(fundal, fundalmaxim);  // se aprinde lumina de fundal la minim

  // setare butoane pentru meniu
  pinMode(BUT1, INPUT);
  pinMode(BUT2, INPUT);
  pinMode(BUT3, INPUT);

  digitalWrite(BUT1, HIGH); // pull-up activat
  digitalWrite(BUT2, HIGH);
  digitalWrite(BUT3, HIGH);

  lcd.setCursor(0, 0);
  lcd.print(" Temporizator Cu");

  lcd.setCursor(0, 1);
  lcd.print(" 2 Canale ON-OFF");
  delay(3000);
  lcd.clear();
  
  lcd.setCursor(0, 0);
  lcd.print(" Versiunea V 1.3");

  lcd.setCursor(0, 1);
  lcd.print(" GeoMar & Niq_ro");
  delay(3000);
  analogWrite(fundal, fundalminim);  // se aprinde lumina de fundal la minim
  lcd.clear();
  
} // sfarsit program ce ruleaza la inceput si dupa resetare

void loop() {
  // -------------------------------------

  if (meniu > 10)
  {
    lcd.clear();
    meniu = 10;
  }

  if (meniu == 0)
  {
      actual = millis() / 1000;
    
     if ((digitalRead(BUT3) == LOW) or (digitalRead(BUT2) == LOW) or (digitalRead(BUT1) == LOW))
    {
      analogWrite(fundal, fundalmaxim);  // se aprinde la maxim fundalul
      delay(50);
       nivelfundal = fundalmaxim;
      ultimaapasare = millis()/1000;  // se memoreaza timpul cand s-a apasat ultima data butonul
    }
    if ((actual - ultimaapasare > timplumina) &&  (nivelfundal > fundalminim))  // daca a trecut timpul de aprindere lumina fundal la maxim si nu s-a stins la minim
      {
        nivelfundal = nivelfundal - treaptanivel;   // nivel aprindere in scadere
      if (nivelfundal < fundalminim) nivelfundal = fundalminim; // nu trebuie sa scada valaorea sub minim
        analogWrite(fundal, nivelfundal);
     }

    // ------------------------------------------
      
    if ((actual - inceput < aprinscat)  and (aprins == 0))  // daca trebuie sa fie aprins becul si nu e inca aprins
    {
      aprins = 1;
      digitalWrite(bec, HIGH);   // becul sa fie aprins
    }

    if ((actual - aprinscat - inceput > 0) and (actual - aprinscat - inceput < stinscat) and (aprins == 1)) // daca becul e intervalul cand e stins
    {
      aprins = 0;
      digitalWrite(bec, LOW);   // becul sa fie stins
    }

    if (((actual - inceput - aprinscat) >= stinscat) and (aprins == 0)) // daca trebuie sa reluam treaba
    {
      inceput = millis() / 1000;
    }

    pushlength = pushlengthset;
    pushlength = getpushlength ();
    delay (10);

    if (pushlength > pushlengthset)
    {
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("Ai apasat destul");
      lcd.setCursor(0, 1);
      lcd.print("Intram in  Meniu");
      delay(2000);
      pushlength = pushlengthset;
      delay(2000);
      intraremeniu = millis();  // memoreaza timpul cand s-a intrat in meniul set
      delay(100);
      meniu = 1;
    }
    if ((pushlength < pushlengthset) || (digitalRead(BUT1) == LOW) || (digitalRead(BUT2) == LOW))
    {
      meniu = 9;
      pushlength = pushlengthset;
    }

    // -------------------------------------
    

    if (aprins == 0)
    {
      timpramas = stinscat - actual + inceput + aprinscat;

      oreramase = timpramas / 3600;
      timpramas1 = timpramas - 3600 * oreramase;
      minuteramase = timpramas1 / 60;
      secunderamase = timpramas1 - 60 * minuteramase;

      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print(" ON  Timer > OFF");
      lcd.setCursor(8, 1);
      if (oreramase < 10)
      {
        lcd.print("0");
      }
      lcd.print(oreramase);
      lcd.print(":");
      if (minuteramase < 10)
      {
        lcd.print("0");
      }
      lcd.print(minuteramase);
      lcd.print(":");
      if (secunderamase < 10)
      {
        lcd.print("0");
      }
      lcd.print(secunderamase);

      lcd.setCursor(0, 1);
      if (oreon < 10)
      {
        lcd.print("0");
      }
      lcd.print(oreon);
      lcd.print(":");
      if (minon < 10)
      {
        lcd.print("0");
      }
      lcd.print(minon);
      lcd.print(" ");
  }

    // ----------------------------

    if (aprins == 1)
    {
      timpramas = stinscat - actual + inceput + aprinscat;
      
      timpramas = aprinscat - actual + inceput;
      oreramase = timpramas / 3600;
      timpramas1 = timpramas - 3600 * oreramase;
      minuteramase = timpramas1 / 60;
      secunderamase = timpramas1 - 60 * minuteramase;

      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print(" ON < Timer  OFF");
      lcd.setCursor(0, 1);
      if (oreramase < 10)
      {
        lcd.print("0");
      }
      lcd.print(oreramase);
      lcd.print(":");
      if (minuteramase < 10)
      {
        lcd.print("0");
      }
      lcd.print(minuteramase);

      lcd.print(":");
      if (secunderamase < 10)
      {
        lcd.print("0");
      }
      lcd.print(secunderamase);

      lcd.setCursor(11, 1);
      if (oreoff < 10)
      {
        lcd.print("0");
      }
      lcd.print(oreoff);
      lcd.print(":");
      if (minoff < 10)
      {
        lcd.print("0");
      }
      lcd.print(minoff);
      lcd.print(" ");
    }
  }
  

  // --------------------------------

  if (meniu == 1) {
    while (meniu == 1) {
      if (millis() - intraremeniu > iesiremeniu) 
      {lcd.setCursor(0, 0); lcd.print ("Iesire Meniu SET"); delay(1500); meniu =10; lcd.clear();};
      lcd.setCursor(0, 0);
      lcd.print("Setare Canal 1-1");
      lcd.setCursor(0, 1);
      lcd.print("Set min ON = ");
      if (minon < 10)
      {
        lcd.print("0");
      }
      lcd.print(minon);
      lcd.print(" ");

      if ((digitalRead(BUT1) == LOW) and (minon > 0))
      { minon = minon - 1;
        delay(250);
      }
      if ((digitalRead(BUT2) == LOW) and (minon < 59))
      { minon = minon + 1;
        delay(250);
      }

      if (digitalRead(BUT3) == LOW)
      {
        EEPROM.write(201, minon);
        intraremeniu = millis();
        meniu = 2;
        delay(250);
        lcd.clear();
      }
    }
  }

  // --------------------------------

  if (meniu == 2) {
    while (meniu == 2) {
      if (millis() - intraremeniu > iesiremeniu)
      {lcd.setCursor(0, 0); lcd.print ("Iesire Meniu SET"); delay(1500); meniu =10; lcd.clear();};
      lcd.setCursor(0, 0);
      lcd.print("Setare Canal 1-2");
      lcd.setCursor(0, 1);
      lcd.print("Set Ore ON = ");
      if (oreon < 10)
      {
        lcd.print("0");
      }
      lcd.print(oreon);
      lcd.print(" ");

      if ((digitalRead(BUT1) == LOW) and (oreon > 0))
      { oreon = oreon - 1;
        delay(250);
      }
      if ((digitalRead(BUT2) == LOW) and (oreon < 23))
      { oreon = oreon + 1;
        delay(250);
      }

      if (digitalRead(BUT3) == LOW)
      {
        EEPROM.write(202, oreon);
        intraremeniu = millis();
        meniu = 3;
        delay(250);
        lcd.clear();
      }
    }
  }

  // --------------------------------

  if (meniu == 3) {
    while (meniu == 3) {
      if (millis() - intraremeniu > iesiremeniu)
      {lcd.setCursor(0, 0); lcd.print ("Iesire Meniu SET"); delay(1500); meniu =10; lcd.clear();};
      lcd.setCursor(0, 0);
      lcd.print("Setare Canal 1-3");
      lcd.setCursor(0, 1);
      lcd.print("Set min OFF = ");
      if (minoff < 10)
      {
        lcd.print("0");
      }
      lcd.print(minoff);
      lcd.print(" ");

      if ((digitalRead(BUT1) == LOW) and (minoff > 0))
      { minoff = minoff - 1;
        delay(250);
      }
      if ((digitalRead(BUT2) == LOW) and (minoff < 59))
      { minoff = minoff + 1;
        delay(250);
      }
      if (digitalRead(BUT3) == LOW)
      {
        EEPROM.write(203, minoff);
        intraremeniu = millis();
        meniu = 4;
        delay(250);
        lcd.clear();
      }
    }
  }

  // --------------------------------

  if (meniu == 4) {
    while (meniu == 4) {
      if (millis() - intraremeniu > iesiremeniu)
      {lcd.setCursor(0, 0); lcd.print ("Iesire Meniu SET"); delay(1500); meniu =10; lcd.clear();};
      lcd.setCursor(0, 0);
      lcd.print("Setare Canal 1-4");
      lcd.setCursor(0, 1);
      lcd.print("Set Ore OFF = ");
      if (oreoff < 10)
      {
        lcd.print("0");
      }
      lcd.print(oreoff);
      lcd.print(" ");

      if ((digitalRead(BUT1) == LOW) and (oreoff > 0))
      { oreoff = oreoff - 1;
        delay(250);
      }
      if ((digitalRead(BUT2) == LOW) and (oreoff < 23))
      { oreoff = oreoff + 1;
        delay(250);
      }
      if (digitalRead(BUT3) == LOW)
      {
        EEPROM.write(204, oreoff);
        intraremeniu = millis();
        meniu = 5;
        delay(250);
        lcd.clear();
      }
    }
  }

  // --------------------------------

  if (meniu == 5) {
    while (meniu == 5) {
      if (millis() - intraremeniu > iesiremeniu)
      {lcd.setCursor(0, 0); lcd.print ("Iesire Meniu SET"); delay(1500); meniu =10; lcd.clear();};
      lcd.setCursor(0, 0);
      lcd.print("Setare Canal 2-1");
      lcd.setCursor(0, 1);
      if (digitalRead(BUT3) == LOW)
      {
        intraremeniu = millis();
        meniu = 6;
        delay(250);
        lcd.clear();
      }
    }
  }

  // --------------------------------

  if (meniu == 6) {
    while (meniu == 6) {
      if (millis() - intraremeniu > iesiremeniu)
      {lcd.setCursor(0, 0); lcd.print ("Iesire Meniu SET"); delay(1500); meniu =10; lcd.clear();};
      lcd.setCursor(0, 0);
      lcd.print("Setare Canal 2-2");
      lcd.setCursor(0, 1);
      if (digitalRead(BUT3) == LOW)
      {
        intraremeniu = millis();
        meniu = 7;
        delay(250);
        lcd.clear();
      }
    }
  }

  // --------------------------------

  if (meniu == 7) {
    while (meniu == 7) {
      if (millis() - intraremeniu > iesiremeniu)
      {lcd.setCursor(0, 0); lcd.print ("Iesire Meniu SET"); delay(1500); meniu =10; lcd.clear();};
      lcd.setCursor(0, 0);
      lcd.print("Setare Canal 2-3");
      lcd.setCursor(0, 1);
      if (digitalRead(BUT3) == LOW)
      {
        intraremeniu = millis();
        meniu = 8;
        delay(250);
        lcd.clear();
      }
    }
  }

  // --------------------------------

  if (meniu == 8) {
    while (meniu == 8) {
      if (millis() - intraremeniu > iesiremeniu)
      {lcd.setCursor(0, 0); lcd.print ("Iesire Meniu SET"); delay(1500); meniu =10; lcd.clear();};
      lcd.setCursor(0, 0);
      lcd.print("Setare Canal 2-4");
      lcd.setCursor(0, 1);
      if (digitalRead(BUT3) == LOW)
      {
        lcd.setCursor(0, 0);
        lcd.print("Iesire Meniu Set");
        lcd.setCursor(0, 1);
        lcd.print("-Salvare Setari-");
        delay(2000);
        meniu = 10;
        delay(250);
        lcd.clear();
      }
    }
  }

  if (meniu == 10)
  {
    inceput = millis() / 1000; // memoram timpul cand incepe sa ruleze programul (in secunde)
    // ----------------------------

    minon = EEPROM.read(201);
    oreon = EEPROM.read(202);
    minoff = EEPROM.read(203);
    oreoff = EEPROM.read(204);

    minuteaprins = minon;
    oreaprins = oreon;
    minutestins = minoff;
    orestins = oreoff;

    // calculam timpii de aprins si timpii de stins
    aprinscat = (3600 * oreaprins + 60 * minuteaprins); // timpii sunt in s
    stinscat =  (3600 * orestins + 60 * minutestins);

    aprins = 0;
    meniu = 0;
    delay(250);
    lcd.clear();
  }

  if (meniu == 9)
  {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Apasa minim 2sec");
    lcd.setCursor(0, 1);
    lcd.print(" pe butonul SET ");
    delay(2000);
    lcd.clear();
    meniu = 0;
  }

  delay(100);
} // sfarsitul programului ce ruleaza in continuu

// --------------------------------

int getpushlength () {
  buttonstate = digitalRead(BUT3);
  if (buttonstate == LOW && buttonflag == false) {
    pushstart = millis();
    buttonflag = true;
  };

  if (buttonstate == HIGH && buttonflag == true) {
    pushstop = millis ();
    pushlength = pushstop - pushstart;
    buttonflag = false;
  };
  return pushlength;
}

// ------  S F A R S I T U L     P R O G R A M U L U I    -------------- //




 Daca cineva doreste sa implementeze (inaintea mea) si partea de Timer 2 in soft, am rugamintea (daca doreste) ca sa-mi trimita sketch-ul,sa il pot posta aici.


 Urmeaza sa revin si cu un videoclip in care sa se vada funtionarea - setarea Timerului.

  ... va urma ...