/****************************************************************************************************************************************
   Projet Chronomètre et minuteur cyclique avec LCD 20X4 réalisé par:  Daniel Talbot Technicien
   Pour Ez-Weekend-Project au Club Technique des Laurentides
   Dans le cadre du programme C.I.P.A.D. (Cours d'initiation à la Programmation Arduino pour Débutant(e)s) gratuit et sans inscription.
   Copiez ce lien pour accéder à la vidéo concernant ce projet
   https://???
   ou celui-ci pour accéder à toutes les leçons déjà publiées...
   https://www.youtube.com/playlist?list=PLoffRtP427acSg1FbpFuH_g5ptz-aovFN
 *                                                                                                                                      *
 ****************************************************************************************************************************************/

#include <LiquidCrystal_I2C.h>
// Déclare l'adresse I2C du LCD à 0x27, 30 colones, 4 lignes.
LiquidCrystal_I2C lcd(0x27, 20, 4);

// Assignation des broches IN/OUT
int ledRouge = 9; // Assigne la broche 9 à la led rouge
int buzzer = 13;  // Assigne la broche 13 au piezo.
int SSR_relais = 10;     bool etat_SSRrelais = LOW;
int btn_StartStop = 2;   bool EtatBtn_StartStop;
int btn_Set_TimeON = 3;  bool EtatBtn_Set_TimeON;
int btn_Set_TimeOFF = 4; bool EtatBtn_Set_TimeOFF;
int btn_RecordSet = 5;   bool EtatBtn_RecordSet;
int Record, Start;

// Buffers: Variables de type Char.
char tempsON[20], tempsOFF[20];
char Chrono[20], MinuteurON[20], MinuteurOFF[20];

// Contrôle: de type Bool
bool  FlagON = false, etatBtnPresser, EtatChronoOnOFF;
bool etatRecord = false;

// Variable de temps:
//long Min = 60000, Sec = 1000;
long onTime, offTime;
long MyTimeON, H_ON = 0, M_ON = 0, S_ON = 0, MS_ON = 0;
long MyTimeOFF, H_OFF = 0, M_OFF = 0, S_OFF = 0, MS_OFF = 0;
//long minutes_OFF = 0, minutes_ON = 0;
long secondes_OFF = 0, secondes_ON = 0;
long Milliseconde, Seconde, Minute, Heure, TempsEcoule;
unsigned long currentTime = 0, previousTime = 0;
unsigned long startMillis, currentMillis;
//unsigned long period = 5000;

void setup() {
  // initialise lcd I2C 2004 & active le rétro éclairage
  lcd.init(); lcd.backlight();
  // Assigne le mode INPUT_PULLUP aux 5 boutons poussoirs.
  for (int pin = 2; pin <= 5; pin++)pinMode(pin, INPUT_PULLUP);
  /*
    pinMode(btn_StartStop, INPUT_PULLUP);
    pinMode(btn_Reset, INPUT_PULLUP);
    pinMode(btn_RecordSet, INPUT_PULLUP);
    pinMode(btn_Set_TimeON, INPUT_PULLUP);
    pinMode(btn_Set_TimeOFF, INPUT_PULLUP);
  */
  // Assigne le mode OUTPUT aux 3 broche de sortie.
  pinMode(ledRouge, OUTPUT);
  pinMode(SSR_relais, OUTPUT);
  pinMode(buzzer, OUTPUT);

  digitalWrite(SSR_relais, LOW); // État du relais au démarrage = Off.
  //Affichage au démarrage
  lcd.setCursor(0, 0);  lcd.print("    Chronometre:     "); // titre
}
void loop() {
  fn_boutons(); // Appel la fonctions boutons.
  fn_Chrono();
  fn_display(); // Appel la Fonction d'affichage sur lcd.
}

void fn_boutons() { // Regroupe les Fonctions des boutons poussoirs.
  // Lit la broche d'entrée du btn_StartStop et stock
  // sa valeur dans EtatBtn_xxx.
  EtatBtn_StartStop = digitalRead(btn_StartStop);
  EtatBtn_RecordSet = digitalRead(btn_RecordSet);
  EtatBtn_Set_TimeON = digitalRead(btn_Set_TimeON);
  EtatBtn_Set_TimeOFF = digitalRead(btn_Set_TimeOFF);
  digitalWrite(ledRouge, EtatChronoOnOFF); // Indique que le chrono est en marche

  if (!EtatBtn_Set_TimeON) {  // Réglage du temps ON
    secondes_ON = secondes_ON + 1; // Incrémente de 1 à chaque pression du bouton bleu.
    if (secondes_ON > 59) { // si dépassement de 59, remet à 0.
      secondes_ON = 0;
    } tick();
    S_ON = secondes_ON; fn_display();
    delay(60); // Délais de 60 ms entre chaque pression du bouton
    // Empêche les rebonds des boutons poussoirs
    while (EtatBtn_Set_TimeON) EtatBtn_Set_TimeON = digitalRead(btn_Set_TimeON);
  }
  if (!EtatBtn_Set_TimeOFF) { // Réglage du temps OFF
    secondes_OFF = secondes_OFF + 1; //Incrémente de 1 à chaque pression du bouton jaune.
    if (secondes_OFF > 59) { // si dépassement de 59, remet à 0.
      secondes_OFF = 0;
    } tick();
    S_OFF = secondes_OFF;
    fn_display(); // Rafraîchi l'affichage.
    delay(60); // Délais de 60 ms entre chaque pression du bouton
    while (EtatBtn_Set_TimeOFF) // anti rebond
      EtatBtn_Set_TimeOFF = digitalRead(btn_Set_TimeOFF);
  }
  // Enregitrement des temps ON/OFF du minuteur
  if (!EtatBtn_RecordSet) {
    beep(); delay(250);
    Record = Record + 1; // Nombre de fois que le bouton gris est pressé.
    if (Record == 1) { // S'il est égale à 1, exécute les instructions entre ses accolades.
      lcd.setCursor(17, 2); lcd.print("SET");// Set indique que la valeur du temps ON est stocké.
      onTime = secondes_ON;  MyTimeON = onTime;  S_ON = MyTimeON; // Enregistre les variables.
      fn_display(); // Rafraîchi l'affichage.
    }
    if (Record == 2) {  // S'il est égale à 2, exécute les instructions entre ses accolades.
      lcd.setCursor(17, 3); lcd.print("SET"); // Set indique que la valeur du temps OFF est stocké.
      offTime = secondes_OFF; MyTimeOFF = offTime; S_OFF = MyTimeOFF;  // Enregistre les variables.
      fn_display(); // Rafraîchi l'affichage.
      etatRecord = true; // Confirme que les valeurs sélectionnées on été engeristrées.
      delay(1000); // Délais de 1 secondes avant d'afficher les messages qui suivent...
      lcd.setCursor(14, 2); lcd.print("Ready!"); // Ready! indique que le minuteur ON est programmé et prêt à être lancé.
      lcd.setCursor(14, 3); lcd.print("Ready!"); // Ready! indique que le minuteur OFF est programmé et prêt à être lancé.
      tone(buzzer, 400, 250); delay(250); tone(buzzer, 600, 250);
      delay(2000);// Délais de 1 secondes avant d'afficher les messages qui suivent..
      lcd.setCursor(14, 2); lcd.print("Relais");
      lcd.setCursor(14, 3); lcd.print("  OFF "); // État actuel du relais. Prêt à être lancé avec le chronomètre (bouton rouge Start/Stop).
    }
    delay(60); // Délais de 60 ms entre chaque pression du bouton
    // Empêche les rebonds des boutons poussoirs
    while (EtatBtn_RecordSet)
      EtatBtn_RecordSet = digitalRead(btn_RecordSet);
  }
  // Si EtatBtnStartStop == LOW et etatBtnPresser == 0 on exécute les actions entre {}
  if (EtatBtn_StartStop == LOW && etatBtnPresser == 0) {
    // La variable etatBtnPresser prend la valeur de 1.
    etatBtnPresser = HIGH;
    // Inverse l'EtatEtatChronoOnOFF ON ou OFF
    EtatChronoOnOFF = !EtatChronoOnOFF;
    beep();
    if ((onTime > 0) && (offTime > 0)) {
      digitalWrite(SSR_relais, EtatChronoOnOFF);
    }
  }
  // Si EtatBtnStartStop == HIGH et etatBtnPresser == HIGH
  // on exécute les actions entre {}
  if (EtatBtn_StartStop == HIGH && etatBtnPresser == HIGH) {
    // La variable etatBtnPresser prend la valeur de 0.
    etatBtnPresser = !etatBtnPresser; // inversion etatBtnPresser
  }
}

void fn_Chrono() {
  // On stocke la valeur du timer interne du microcontrôleur récupéré grâce
  // à millis() dans la variable currentTime
  currentTime = millis();
  // On calcule la valeur de TempsEcoule en effectuant une
  // soustraction entre currentTime et previousTime.
  TempsEcoule = currentTime - previousTime;
  // On stocke la valeur du timer interne du microcontrôleur récupéré
  // grace à millis() dans la variable previousTime
  previousTime = millis();

  if (EtatChronoOnOFF == 1) { // Si EtatChronoOnOFF = HIGH...
    Milliseconde = Milliseconde + TempsEcoule;
    if (Milliseconde > 999) { // Compteur des secondes
      Milliseconde = Milliseconde - 1000;
      fn_compteur_A_Rebour();
      Seconde++; // Incrémente de 1 à chaque 1000ms.
      
    }
    if (Seconde > 59) { // Compteur des minutes
      Seconde = 0; Minute++; // incrémente de 1 minute toutes les 60 secondes
    }
    if (Minute > 59) { // Compteur des heures
      Minute = 0; Heure++; // incrémente de 1 heure toutes les 60 minutes
    }
  }
  //fn_display(); // Appel la Fonction d'affichage sur lcd.
}
void fn_display() { // Fonction d'affichage sur lcd.
  // sprintf: Formate l'affichage pour les buffer.
  sprintf(MinuteurON,  "T-ON:  %02ld sec", S_ON);
  sprintf(MinuteurOFF, "T-OFF: %02ld sec", S_OFF); //Formate l'affichage.
  sprintf(Chrono, "%02ld:%02ld:%02ld:%03ld", Heure, Minute, Seconde, Milliseconde);//Formate l'affichage.
  lcd.setCursor(4, 1); lcd.print(Chrono); //Affiche le chronomètre sur la 2e ligne du lcd.
  lcd.setCursor(0, 2); lcd.print(MinuteurON); // Afffiche le minuteur ON sur la 3e ligne du lcd.
  lcd.setCursor(0, 3); lcd.print(MinuteurOFF);// Afffiche le minuteur OFF sur la 4e ligne du lcd.
}
void fn_compteur_A_Rebour() { // Compte à rebour Temps On et OFF.
  if (MyTimeON > 0) { // Si MyTimeON = 1... ou programmé.
     MyTimeON--; // décrémentation de 1 seconde le temps ON
    S_ON = MyTimeON ; // Stock la valeur du temps ON pour l'afficheur.
  
    FlagON = true; // FlagON passe à Vrai
    etat_SSRrelais = FlagON; // active le relais
    digitalWrite(SSR_relais, etat_SSRrelais);  // Contrôle les relais externes

    // Affiche lorsque le relais est ON
    lcd.setCursor(14, 3); lcd.print("Relais");
    lcd.setCursor(14, 2); lcd.print("  ON  ");

 }
  if ((MyTimeOFF > 0) && (MyTimeON == 0)) {
    MyTimeOFF--;  // décrémentation de 1 seconde le temps OFF
    S_OFF = MyTimeOFF;// Stock la valeur du temps OFF pour l'afficheur.
   
    FlagON = false ; // FlagON passe à faux
    etat_SSRrelais = FlagON; //désactive les relais
    digitalWrite(SSR_relais, FlagON); // Mettre à jour le relais selon la valeur de FlagOn (On/OFF ou 1/0 ou true/false)

    // Affiche si le relais est OFF
    lcd.setCursor(14, 2); lcd.print("Relais");
    lcd.setCursor(14, 3); lcd.print("  OFF ");
 if ((MyTimeOFF == 0) && (MyTimeON == 0)) {
      MyTimeON = onTime;   // Stock la valeur du temps ON.
      MyTimeOFF = offTime; // Stock la valeur du temps OFF.
    }
  }


}
// Fonctions du buzzer ==========================================
// fonction beep pour Émettre une tonalité de x kHz pendant x ms
void beep() {// Émet une tonalité de 2,5kHz pendant 100 ms
  tone(buzzer, 2500, 100);
}
// Émet une très brève tonalité de 5kHz pendant 4 ms
void tick() {
  tone(buzzer, 5000, 4);   // Émet un son de 5 kHz pendant 1 ms
}
NOCOMNCVCCGNDINLED1PWRRelay Module