/****************************************************************************************************************************************
 * 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                                                                          *
 *  lien à venir                                                                                                                        *
 * 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>      // Bibliothèque pour l'écran LCD fonctionnant avec I2C.
LiquidCrystal_I2C lcd(0x27, 20, 4); // Définitions des paramètres de lcd 2004 à l'adresse 0x27.

// Déclaration des entrées/sorties de l'Arduino Uno R3.
const int btn_StartStop = 2;        // START/STOP, BP activé à l'état bas
const int btn_Reset = 3;            // RESET, BP activé à l'état bas
const int btn_Mute = 4;             // Mute , BP activé à l'état bas
const int buzzerPin = 13;           // Buzzer relié à la broche 13

// Déclaration des variables de type booléenne
bool etat_btn_StartStop, etat_btn_Reset, etat_BoutonAppuyer; // Variable qu'on passe à 1 après avoir presser un bouton afin d'éviter l'effet rebond
bool etat_ChronoOnOFF, etat_btn_Mute, etat_Mute;             // Variable qui détermine l'état de marche du chronomètre et la fonction Mute.

long TempsEcoule, Milliseconde, Seconde, Minute, Heure, Jour; // Variable de temps.
unsigned long currentTime = 0;    // Variable utilisée pour récupérer la valeur de millis()
unsigned long previousTime = 0;   // Variable utilisée pour stocker la valeur de millis() pour le prochain "loop"

// Variable de type char. Réserve en mémoire tampon 20 espaces de caractères pour chaque variable. Permet de créer des chaine de caratères.
char tempsON[20], tempsOFF[20];
char Chrono[20], MinuteurON[20], MinuteurOFF[20], Ref[20], Btn[20];

// Création de tableaux contenant plusieurs chaines de caractères prédéfinis et accessible par le nom du tableau et le No. d'index
// char* nom_du_tableau[index] = {"index 0" , index 1", "index 2", "ect...""};
char* Label[20] = {"Stop |Reset |",
                   "Start|Reset |"
                  };

char* selectMenu[] = {"Chronometre", "Minuteur", "Minuteur Cyclique",
                      "Chrono & Minuteur", "Test Alarm !", "press again"
                     };
//Liste des entêtes.
char* header[7] = {"Menu principal Fn", "ALARM TESTING!", "CHRONOMETRE:",
                   "MINUTEUR", "MINUTEUR CYCLIQUE", "CHRONO & MINUTEUR",
                   "jour:hh:mm:ss:millis"
                  };
// Liste des instructions.
char* instructions[6] = {"                   ", "For select function",
                         "Press Yellow button", "For this function",
                         "Press red button", "Or Yellow button"
                        };

// Variables pour la fonction "printCenter()"
int wide = 20;           // nombre de caractères: 20 pour un afficheur de 20 caractères par ligne et 16 pour un afficheur de 16 caractères par ligne
int ligne = 0 ;          // Numéro de la ligne de l'afficheur: 0 à 3 pour un afficheur à 4 ligne.
String text = "";        // Chaine de caractère nommée "text" à afficher.
int len = text.length(); // Longeur de la chaine de caratères "text".

int Mute = 0;            //Variable antirebond utilisée dans la fonction Mute

void setup()
{
  pinMode(btn_StartStop, INPUT_PULLUP); // Activation du mode ENTREE et résistance PULL UP
  pinMode(btn_Reset, INPUT_PULLUP);     // Activation du mode ENTREE et résistance PULL UP
  pinMode(btn_Mute, INPUT_PULLUP);      // Activation du mode ENTREE et résistance PULL UP
  pinMode(buzzerPin, OUTPUT);           // Activation du mode SORTIE.

  lcd.init(); lcd.backlight();          // Initialisation de l'écran LCD, et active le rétroéclairage de l'écran LCD
  printCenter(selectMenu[0], 0);        // écran de démarrage
  delay(1000);                         // pause de 1 seconde
  beep();                                // Beep
  Fn_Display();                         // fonction de l'Affichage principal
  //Alarm();
}
// fonction perso beep pour Émettre une tonalité de x kHz pendant x ms
void beep() {                           // Émet une tonalité de 2,5kHz pendant 100 ms
  tone(buzzerPin, 2500, 100);           // Plays 2,5kHz tone for 100ms
}
void tick() {                           // fonction perso beep pour Émettre une très brèvetonalité de 3kHz pendant 5 ms
  if (!etat_Mute) {                     // si la fonction Mute est false
    tone(buzzerPin, 5000, 5);            // Plays 5kHz tone for 5ms
  }
}
void Alarm() {  // Fonction d'Alarme ou réveil matin
  lcd.clear(); printCenter(header[1], 0); // Affiche l'état de l'alarme
  for (int repeat = 0; repeat <= 10; ++repeat) { // répète les 2 tonalitéd 10 fois chaque avant de se taire
    tone(buzzerPin, 800, 300); delay(200);        // Émet une tonalité de 800Hz pendant 300 ms, suivi d'une interruption de 200ms
    tone(buzzerPin, 500, 300); delay(200);        // Émet une tonalité de 500Hz pendant 300 ms, suivi d'une interruption de 200ms
    for (int flash = 0; flash <= 10; ++flash) {   // Fait clignoter le rétroéclairage de l'afficheur lcd par interval de 100ms.
      lcd.noBacklight();
    }
    delay(100);
    lcd.backlight();                              // rétabli le rétroéclairage de l'afficheur lcd.
  }
}
// fonction perso pour centrer le texte à afficher.
void printCenter(String text, int ligne) {
  len = text.length();                     // défini la longeur de la variable text
  lcd.setCursor((wide - len) / 2, ligne);  // positionne le curseur au centre de la ligne choisie et soustrait la longeur du text à afficher
  lcd.print(text);                         // affiche le text centré
}
void loop() {
  Fn_Scan_Buttons(); // Fonction de balayage de l'état de tous les boutons poussoirs
  Fn_Chronometre();  // Fonction de gestion du temps qui passe lorsque le chrono est à ON
}
void Fn_Scan_Buttons() { //balayage de l'état de tous les boutons poussoirs
  etat_btn_StartStop = digitalRead(btn_StartStop); // État Bouton Start/Stop
  etat_btn_Reset     = digitalRead(btn_Reset);     // État Bouton Start/Stop
  etat_btn_Mute      = digitalRead(btn_Mute);      // État Bouton Mute

  if (etat_btn_Mute == LOW && Mute == 0 ) {        // Contrôle le mute des tick()
    beep();  etat_Mute = !etat_Mute;              // Inverse l'état Mute (ON/OFF)
    delay(500); while (etat_btn_Mute)etat_btn_Mute = digitalRead(btn_Mute); // fonction anti rebond
  }

  if (etat_btn_StartStop == LOW && etat_BoutonAppuyer == 0) { // Test pression bouton START/STOP
    etat_BoutonAppuyer = 1;  beep();                          // Evite l'effet rebond
    etat_ChronoOnOFF = !etat_ChronoOnOFF;                     // Inverse l'état du chronomètre
  }

  if (etat_btn_Reset == LOW && etat_ChronoOnOFF == 0 && etat_BoutonAppuyer == 0) { // Test pression bouton RESET
    etat_BoutonAppuyer = 1; beep();                           // Evite l'effet rebond
    Milliseconde = 0; Seconde = 0; Minute = 0; Heure = 0;     // Remet toutes les variables de temps à 0.
    lcd.clear();                                              // Efface complétement le contenu de l'écran LCD
  }
}

void Fn_Chronometre() // Fonction pricipale de la gestion de l'horloge du chrono
{
  // Incrémentation du compteur de chrono en utilisant la fonction millis()
  currentTime = millis();                    // Le temps courant = l'horloge interne en ms.
  TempsEcoule = currentTime - previousTime;  // Le temps écoulée = au temps courant - le temps précédant
  previousTime = millis();                   // nouveau temps précédant = l'horloge interne en ms.

  // Fonction compteur d'incrémentation des variables Milliseconde, Seconde, Minute, Heure et jour
  if (etat_ChronoOnOFF == 1) {                 // Si Etat_ChronoOnOff = true exécute le code entre {}.
    Milliseconde = Milliseconde + TempsEcoule; // Incrémentation des millisecondes
    if (Milliseconde > 999) {                  // Si Milliseconde est supérieure à 999... (1000ms)
      Milliseconde = Milliseconde - 1000;      // ...Remise à 0 de la variable Milliseconde
      tick();                                  // Si Mute est OFF un tick par seconde se fait entendre
      Seconde++;                               // Incrémentation de 1 seconde toutes les 1000 Millisecondes
    }
    if (Seconde > 59) {                        // Si Seconde est supérieure à 59... (60sec)
      Seconde = 0;                             // ...Remise à 0 de la variable Seconde
      beep();                                  // un beep par minute se fait entendre
      Minute++;                                // Incrémentation de 1 Minute toutes les 60 Secondes
    }
    if (Minute > 59) {                         // Si Minute est supérieure à 59... (60 minutes)
      Minute = 0;                              // ...Remise à 0 de la variable Minute
      Heure++;                                 // Incrémentation de 1 Heure toutes les 60 Minutes
    }
    if (Heure > 23) {                          // Si Heure est supérieure à 23... (minuit ou 24 hr)
      Heure = 0;                               // ...Remise à 0 de la variable heure
      Jour++;                                  // Incrémentation de 1 Jour toutes les 24 Heures
    }

  }
  if (etat_btn_StartStop == HIGH && etat_btn_Reset == HIGH) {
    etat_BoutonAppuyer = 0;   // réinitialise la variable "etat_BoutonAppuyer" à "0" cequi évite l'effet rebond
  }
  Fn_Display();  // Fonction principale d'Affichage en temps réel.
}

void Fn_Display() { // rafraîchi l'Affichage en temps réel toutes le millisecondes.
  sprintf(Chrono, "%02ld:%02ld:%02ld:%02ld:%03ld", Jour, Heure, Minute, Seconde, Milliseconde); //Formate l'affichage.

  printCenter(header[2], 0);  // Affiche le titre de la fonction en cours
  printCenter(Chrono, 1);     // Affiche tous les chiffres composant le temps chrono
  printCenter(header[6], 2);  // Identifie chaque colonne de l'affichage sous le chrono

  switch (etat_ChronoOnOFF) { // Affiche le rôle des boutons "Start/Stop & Reset "
    case true: lcd.setCursor(0, 3); lcd.print(Label[0]); // si etat_ChronoOnOFF = vrai
      break;
    case false: lcd.setCursor(0, 3); lcd.print(Label[1]);// si etat_ChronoOnOFF = faux
      break;
  }
// Affiche l'état de la fonction Mute/Tick
  if (etat_Mute) {
    lcd.setCursor(14, 3);
    lcd.print("MUTE");
  }
  else if (!etat_Mute) {
    lcd.setCursor(14, 3);
    lcd.print("Tick");
  }

}
//********************************************************************************//
// Fonction Affichage des Chronos                                                 //
// "Ligne" = numéro de ligne ET "TypeTime" = texte fin ligne                      //
//********************************************************************************//
/*
  void AffichageChrono (int Ligne, char TypeTime[])
  {
  // Affichage du type de temps en bout de ligne
  lcd.setCursor(10, Ligne);
  lcd.print(TypeTime);
  // Affichage des ":"
  lcd.setCursor(2, Ligne);
  lcd.print(":");
  lcd.setCursor(5, Ligne);
  lcd.print(":");

  // Affichage des Millisecondes en fonction de sa valeur
  if (Milliseconde > 99) {
    lcd.setCursor(6, Ligne);
    lcd.print(Milliseconde);
  }
  else if (Milliseconde > 9 && Milliseconde < 100) {
    lcd.setCursor(6, Ligne);
    lcd.print("0");
    lcd.setCursor(7, Ligne);
    lcd.print(Milliseconde);
  }
  else if (Milliseconde > 0 && Milliseconde < 10) {
    lcd.setCursor(6, Ligne);
    lcd.print("00");
    lcd.setCursor(8, Ligne);
    lcd.print(Milliseconde);
  }
  else {
    lcd.setCursor(6, Ligne);
    lcd.print("000");
  }

  // Affichage des Secondes en fonction de sa valeur
  if (Seconde < 10) {
    lcd.setCursor(3, Ligne);
    lcd.print("0");
    lcd.setCursor(4, Ligne);
    lcd.print(Seconde);
  }
  else {
    lcd.setCursor(3, Ligne);
    lcd.print(Seconde);
  }

  // Affichage des Minutes en fonction de sa valeur
  if (Minute < 10) {
    lcd.setCursor(0, Ligne);
    lcd.print("0");
    lcd.setCursor(1, Ligne);
    lcd.print(Minute);
  }
  else {
    lcd.setCursor(0, Ligne);
    lcd.print(Minute);
  }
  }
*/