// import des librairies
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>           // Pour faire un serveur web
#include <ESP8266mDNS.h>                // Pour le DNS du serveur web  
#include <ESP8266HTTPUpdateServer.h>    // Pour la mettre à jour le programme par le réseau
#include <TimeLib.h> 
#include <Wire.h>                       // Pour communiquer avec l'écran par l I2C
#include <NtpClientLib.h>               // https://github.com/gmag11/NtpClient    Pour avoir la date et l'heure
#include <Ephemeris.h>                  // https://github.com/MarScaper/ephemeris Pour calculer l'heure du lever et coucher du soleil en fonction de sa position géographique
#include <LiquidCrystal_I2C.h>          // https://github.com/johnrickman/LiquidCrystal_I2C Pour utiliser l'écran LCD I2C 
#include "DHT.h"                        // https://github.com/adafruit/DHT-sensor-library Pour lire les sondes DHT

//*****************************************************************************************************\\
//******************  Partie à modifier avec vos paramètres  ******************************************\\
//*****************************************************************************************************\\

const char* ssid = "XXXXX";                       // Le noms de votre réseau wifi
const char* password = "YYYYY";                   // La clé de votre réseau wifi
const char* serveur = "OOOOO";                    // L'adresse IP de votre serveur web (NAS, Raspberry pi ou hébergeur)
const char* host = "terranodemcu";                // le noms qui remplace l'IP pour la fonction update 
const char* update_path = "/update";              // le chemin pour le fichier d' update
const char* update_username = "xxxxx";            // On défini ici le login pour la fonction update
const char* update_password = "yyyyy";            // On défini ici le mot de passe pour la fonction update
char* ville = "Varage";                           // On donne le noms de sa position géographique
float lat = 43.605201;                            // On donne sa latitude (https://www.coordonnees-gps.fr/)
float lon = 5.955421;                             // On donne sa longitude
char* serveurNTP = "fr.pool.ntp.org";             // Le serveur NTP que l'on va interrogé
const byte timeZone = 1;                          // GMT +1 (paris) 
const long interval = 10000;                      // interval des relevés (pour la fonction millis)
unsigned int targetJour = 28;                     // la température désiré le jour                  
unsigned int targetNuit = 23;                     // la température désiré la nuit

// pour la fontion événement  les prenoms, dates anniversaires et heure d'avertissement
const char* noms[] = {"manu", "sosso", "yohan", "clara"};  
int dateanniv[] = {2612, 1008, 2806, 2206};
int heureanniv[] = {730, 800, 1600, 1630, 1930, 2000};
// jour et heure d'avertissement du repas des serpents
int daterepas[] = {1, 8, 16, 24};
int heurerepas[] = {830, 2030};

      // les GPIO utilisé :
// pour la com I2C
const byte Sda = 2;           // D4     
const byte Scl = 4;           // D2 
// défini le capteur DHT22
const char DHTTYPE = DHT22;   // DHT11, DHT21 ou DHT22
// Pins des sondes
const byte DHTPINPC = 14;     // D5
const byte DHTPINPF = 0;      // D3
// Pins des relais
const byte lum = 16;          // D0    
const byte chauff = 5;        // D1
// On défini le bouton
const byte  buttonPin = 12;   // D6
// Détecteur de sécheresse
const byte PinHumi = 13;      // D7
byte secheresse;             
// Le buzzer
const byte buzzerPin = 15;    // D8
// Les LEDs
const byte ledPin1 = 1;       // D9
const byte ledPin2 = 3;       // D10

//*****************************************************************************************************\\
//*****************************************************************************************************\\

// les notes de musique
#define NOTE_B0  31
#define NOTE_C1  33
#define NOTE_CS1 35
#define NOTE_D1  37
#define NOTE_DS1 39
#define NOTE_E1  41
#define NOTE_F1  44
#define NOTE_FS1 46
#define NOTE_G1  49
#define NOTE_GS1 52
#define NOTE_A1  55
#define NOTE_AS1 58
#define NOTE_B1  62
#define NOTE_C2  65
#define NOTE_CS2 69
#define NOTE_D2  73
#define NOTE_DS2 78
#define NOTE_E2  82
#define NOTE_F2  87
#define NOTE_FS2 93
#define NOTE_G2  98
#define NOTE_GS2 104
#define NOTE_A2  110
#define NOTE_AS2 117
#define NOTE_B2  123
#define NOTE_C3  131
#define NOTE_CS3 139
#define NOTE_D3  147
#define NOTE_DS3 156
#define NOTE_E3  165
#define NOTE_F3  175
#define NOTE_FS3 185
#define NOTE_G3  196
#define NOTE_GS3 208
#define NOTE_A3  220
#define NOTE_AS3 233
#define NOTE_B3  247
#define NOTE_C4  262
#define NOTE_CS4 277
#define NOTE_D4  294
#define NOTE_DS4 311
#define NOTE_E4  330
#define NOTE_F4  349
#define NOTE_FS4 370
#define NOTE_G4  392
#define NOTE_GS4 415
#define NOTE_A4  440
#define NOTE_AS  455
#define NOTE_AS4 466
#define NOTE_B4  494
#define NOTE_C5  523
#define NOTE_CS5 554
#define NOTE_D5  587
#define NOTE_DS5 622
#define NOTE_E5  659
#define NOTE_F5  698
#define NOTE_FS5 740
#define NOTE_G5  784
#define NOTE_GSH  830
#define NOTE_GS5 831
#define NOTE_A5  880
#define NOTE_AS5 932
#define NOTE_B5  988
#define NOTE_C6  1047
#define NOTE_CS6 1109
#define NOTE_D6  1175
#define NOTE_DS6 1245
#define NOTE_E6  1319
#define NOTE_F6  1397
#define NOTE_FS6 1480
#define NOTE_G6  1568
#define NOTE_GS6 1661
#define NOTE_A6  1760
#define NOTE_AS6 1865
#define NOTE_B6  1976
#define NOTE_C7  2093
#define NOTE_CS7 2217
#define NOTE_D7  2349
#define NOTE_DS7 2489
#define NOTE_E7  2637
#define NOTE_F7  2794
#define NOTE_FS7 2960
#define NOTE_G7  3136
#define NOTE_GS7 3322
#define NOTE_A7  3520
#define NOTE_AS7 3729
#define NOTE_B7  3951
#define NOTE_C8  4186
#define NOTE_CS8 4435
#define NOTE_D8  4699
#define NOTE_DS8 4978

// TIMEOUT de la connexion NTP
#define NTP_TIMEOUT 1500  

// On défini le serveur web pour l' update
ESP8266WebServer httpServer(80);
ESP8266HTTPUpdateServer httpUpdater;

// On définie l'écran avec son adresse 0x27 pour le mien
LiquidCrystal_I2C lcd(0x27, 16, 2);   

byte UTCOffset;                                                               // Offset pour prendre en ompte l'heure d'été pour le calcule du levé et couché du soleil                      
bool wifiFirstConnected, syncEventTriggered, ledState, flag_killer = false;   // les flags dont on a besoin a l'état false
NTPSyncEvent_t ntpEvent;                                                      // pour stocker le retour des événements déclenché
unsigned long previousMillis = 0;                                             // variable de temps pour la fonction millis  
unsigned int Hnow, Hmatin, Hsoir, Hjour, Mjour, Hnuit, Mnuit;                 // les variables de temps pour nos calculs

// On défini les sondes
DHT dhtPC(DHTPINPC, DHTTYPE);
DHT dhtPF(DHTPINPF, DHTTYPE);

// On défini les variables pours les datas des sondes
float tC,hC,tF,hF;

// On définie les variables où seront stockées les caractères perso             
const byte HEAD = 1; 
const byte TAIL = 2;             
const byte CLEAR = 3;
const byte SKULL = 4;
const byte BONES = 5;     
const byte ACCENT = 6;  
const byte COEUR = 7;
const byte REPAS = 8;

// Le dessin des caractères HEAD TAIL CLEAR (https://maxpromer.github.io/LCD-Character-Creator/)
uint8_t snakeHead[8] = {0x11, 0xa, 0x1f, 0x15, 0xa, 0x11, 0xe};
uint8_t snakeTail[8] = {0x4, 0xe, 0x1b, 0x1b, 0xe, 0xe, 0x4};
uint8_t noSnake[8] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
uint8_t tete[8] = {0x0, 0x0, 0x0, 0x1F, 0x15, 0x1F, 0x11, 0x1F};
uint8_t os[8] = {0x11, 0x0E, 0x0E, 0x11, 0x0, 0x0, 0x0, 0x0};
uint8_t eAigu[8] = {0x01, 0x02, 0x0, 0x0E, 0x11, 0x1F, 0x10, 0x0F};
uint8_t heart[8] = {0x0, 0xa, 0x1f, 0x1f, 0xe, 0x4, 0x0};
uint8_t bell[8]  = {0x4, 0xe, 0xe, 0xe, 0x1f, 0x0, 0x4};

// Compteur de la fonction "sec" et "beep"
unsigned int alarm, compteur = 0; 

// On déclare les variables temps pour gérer la boucle des événements
unsigned long tempsanniv, tempsrepas;

// fonction de connexion
void onSTAGotIP (WiFiEventStationModeGotIP ipInfo) {                 // lorsque l'on a une IP      
    wifiFirstConnected = true;                                       // on active le flag de première connexion
}

// fonction de deconnexion
void onSTADisconnected (WiFiEventStationModeDisconnected event_info) {        // si on est déconnecté 
    NTP.stop();                                                               // on stop la synchro au serveur NTP
    WiFi.reconnect ();                                                        // on lance la reconnexion au wifi
}

// fonction de traitement des event avec le serveur NTP
void processSyncEvent (NTPSyncEvent_t ntpEvent) {                            
    if (ntpEvent == timeSyncd) {                             // si le serveur NTP nous repond correctement                  
        NTP.getTimeDateString (NTP.getLastNTPSync ());       // on synchronise l'heure
        if (NTP.isSummerTime ()) {                           // si c'est l'heure d'été
          UTCOffset = 2;                                     // l'ofset vaut 2 
        } else { 
          UTCOffset = 1;                                     // l'ofset vaut 1
        }  
        // on calcule le vevé et couché       
        printRiseAndSet(ville, lat, lon, UTCOffset, day(),month(),year());
        // on met le flag des chansons et événements à false au cas ou il a été activé
        flag_killer = false;
    }
}

// fonction pour mettre 0 devant les nombres de 0 a 9, pour être sur 2 chiffres.
void printDigits(int digits){ 
  if(digits < 10) {
    lcd.print('0');
  }
  lcd.print(digits);
}

// fonction qui affiche l'heure
void digitalClockDisplay(){  
  printDigits(hour());
  lcd.print(":");
  printDigits(minute());
  lcd.print(":");
  printDigits(second());  
}

// fonction qui affiche la date 
void digitalDateDisplay(){  
  printDigits(day());
  lcd.print("/");
  printDigits(month());
  lcd.print("/");
  printDigits(year());
}


// la fonction qui calcul le lever et coucher du soleil en fonction de sa position et de la date
void printRiseAndSet(char *city, FLOAT latitude, FLOAT longitude, int UTCOffset, int day, int month, int year) {
  Ephemeris::setLocationOnEarth(latitude,longitude);                                                          // definie notre location       
  SolarSystemObject sun = Ephemeris::solarSystemObjectAtDateAndTime(Sun,day,month,year,0,0,0);                // pour avoir le levé et couché du soleil
  if( sun.riseAndSetState == RiseAndSetOk ) {                                                                 // si notre location est correcte
    int hours,minutes;    
    FLOAT seconds;    
    Ephemeris::floatingHoursToHoursMinutesSeconds(Ephemeris::floatingHoursWithUTCOffset(sun.rise,UTCOffset), &hours, &minutes, &seconds); // on réupère l'heure du levé
    Hjour = hours;                                                                                                                        
    Mjour = minutes;
    Hmatin = hours * 100 + minutes;                                                                                                      // mise en forme pour les calculs (8:00 devient 800)  
    Ephemeris::floatingHoursToHoursMinutesSeconds(Ephemeris::floatingHoursWithUTCOffset(sun.set,UTCOffset), &hours, &minutes, &seconds); // on réupère l'heure du couché
    Hnuit = hours;
    Mnuit = minutes;
    Hsoir = hours * 100 + minutes;                                                                                                       // mise en forme pour les calculs
  }  
}

// La fonction qui s'occupe du chauffage et de la lumière
void terrarium() {  
  byte target;             
  // Pour faciliter les calcul (21h03 devient 2103) 
  Hnow = hour() * 100 + minute();                 // Heure actuelle   
  if ( Hnow > Hmatin && Hnow < Hsoir ) {          // Si c'est le jour      
    target = targetJour;                          // La consigne du chauffage est targetJour           
    digitalWrite(lum, LOW);                       // on coupe le relais, ce qui alume la lumière
  }  
  else {                                          // Sinon , donc c'est la nuit                                   
    target = targetNuit;                          // La consigne du chauffage est targetNuit    
    digitalWrite(lum, HIGH);                      // on active le relais, ce qui coupe la lumière
  }
  // On lis la sondes point chaud
  hC = dhtPC.readHumidity();  
  tC = dhtPC.readTemperature();    
  // Lire la sonde point froid
  hF = dhtPF.readHumidity();  
  tF = dhtPF.readTemperature();     
  // si la temperature au point chaud est supérieur à la target OU si il y a un échèc de leture de la sonde     
  if (tC > target + 0.25 || isnan(tC)) {
    digitalWrite(chauff, HIGH);            // on active le relais, ce qui coupe le haffage
  } 
  // si la temperature au point chaud est inférieur à la target 
  if (tC < target - 0.25) {                
    digitalWrite(chauff, LOW);             // on coupe le relais, ce qui allume le chauffage
  }    
  if ( isnan(tC) || isnan(tF) ) {         // si il y a un échèc de leture de la sonde
    return;                               // on quitte la fonction terrarium
  }
  // si la lecture de la sonde est ok on continue 
  WiFiClient client;                              // Création de la connexion TCP 
  const int httpPort = 80;                        // constante pour le port 80
  if (!client.connect(serveur, httpPort)) {       // si on n'arrive pas a se onnecter au serveur distant    
    return;                                       // on quitte la fonction terrarium  
  }         
  // si on la connexion au serveur distant est ok                                  
  // on envoi toutes ces datas par l'URL et par GET au fichier sonde.php sur le serveur (dans le dossier terranodemcu)
  client.print(String("GET /terranodemcu/sondes.php?tempC=") + String(float (tC)) + "&humiC=" + String(float(hC)) + 
               "&tempF=" + String(float(tF)) + "&humiF=" + String(float(hF)) + "&Hjour=" + String(int(Hjour)) + 
               "&Mjour=" + String(int(Mjour)) + "&Hnuit=" + String(int(Hnuit)) + "&Mnuit=" + String(int(Mnuit)) +
               " HTTP/1.1\r\n" +  "Host: " + serveur + "\r\n" + "Connection: close\r\n\r\n");   
  client.flush();   // on vide le buffer
}

// La fonction bouton qui lance l'affichage LCD et coupe les chansons ou événement si il est appuyé pendant une chanson.
void bouton() {                 
  int reading = digitalRead(buttonPin);  // on lit l'état du bouton
  if (reading == 0) {                    // si on appuye
      flag_killer = true;                // on passe ce flag à true, il sert à tuer une chanson ou un événement
      affichage();                       // on lance l'affichage      
  }    
}

// la fontion qui vérifie qu'il y a de l'eau
void sec() {   
  secheresse = digitalRead(PinHumi);    // on lit la valeur du détecteur
  if (secheresse) {                     // si la valeur du détecteur vaut 1 c'est qu'il n'y a plus d'eau    
    // pour éviter de lancer l'alarme sur une fausse détection   
    alarm++;                            // on incrémente le compteur  
    if (alarm > 2) {                    // au 3 eme tour de boucle
      eau();                            // on lance la fonction eau
      alarm = 0;                        // on remet le compteur alarm à 0
    }          
  } else {                           
    alarm = 0;                          // si la valeur du détecteur vaut 0 alarm vaut 0;
  }  
}

// la fontion qui affiche qu'il n'y a pas d'eau et lance la chanson Starwars
void eau() {
  lcd.backlight();
  lcd.setCursor(1,0);
  lcd.write(SKULL);
  lcd.setCursor(4,0);
  lcd.print("Il n'y a");
  lcd.setCursor(14,0); 
  lcd.write(SKULL);   
  lcd.setCursor(1, 1);
  lcd.write(BONES);  
  lcd.setCursor(3,1);
  lcd.print("plus d'eau");
  lcd.setCursor(14,1); 
  lcd.write(BONES);
  Hnow = hour() * 100 + minute();        // Heure actuelle      
  if ( Hnow > 700 && Hnow < 2200 ) {     // Si c'est le jour      
    Starwars();                          // On lance l'alarme
  } else {                               // Sinon on me laisse dormir 
    delay(1500);        
  }
  lcd.clear();
  lcd.noBacklight();
}

// le setup 
void setup () {
  // On déclare les GPIO pour l' I2C (D1 et D2 par défaut)
   Wire.begin(Sda,Scl);
  // On initialise le LCD
  lcd.begin();  
  lcd.clear();
  lcd.setCursor(3,0);
  lcd.print("Debut de l '");
  lcd.setCursor(1,1);
  lcd.print("initialisation");
  delay(2000);
  static WiFiEventHandler e1, e2;                        // on déclare 3  event sur le wifi: e1 e2 et e3
  e1 = WiFi.onStationModeGotIP (onSTAGotIP);                 // la fonction de connexion bis  est associé a l'evenement Wifi e1
  e2 = WiFi.onStationModeDisconnected (onSTADisconnected);   // la fonction de déconnexion est associé a l'evenement Wifi e2  
  WiFi.mode (WIFI_STA);                               // on definie le mode de Wifi 
  WiFi.begin (ssid, password);                        // on demarre la connexion au réseau Wifi   
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);  
  }
  // on definie notre synchro
  NTP.onNTPSyncEvent ([](NTPSyncEvent_t event) {      // quand un evenement se produit
        ntpEvent = event;                             // on definie nos event                              
        syncEventTriggered = true;                    // on passe le flag syncEventTriggered a vrai 
    });  
  MDNS.begin(host);
  MDNS.addService("http", "tcp", 80);  
  // On déclare le buzzer en sortie   
  pinMode(buzzerPin, OUTPUT);   
  // On déclare les pins des relais en sortie
  pinMode(lum, OUTPUT);
  pinMode(chauff, OUTPUT);
  // On initialise les DHT 
  dhtPC.begin();
  dhtPF.begin();
  // On déclare le GPIO du bouton et du détecteur de sécheresse en entrée avec la résistance de PULLUP: 
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(PinHumi, INPUT_PULLUP);  
  // les LED , après la déclaration des LED en sortie, on aura plus rien sur le port série
  // car elles sont branché sur le TX RX du NodeMCU il faut donc commenté ces 2 lignes pour le débug
  pinMode(ledPin1, OUTPUT);
  pinMode(ledPin2, OUTPUT);
  digitalWrite(ledPin1, LOW);
  digitalWrite(ledPin2, LOW);  
  // Création des caractères perso
  lcd.createChar(HEAD, snakeHead);
  lcd.createChar(TAIL, snakeTail);
  lcd.createChar(CLEAR, noSnake);
  lcd.createChar(SKULL, tete);
  lcd.createChar(BONES, os);  
  lcd.createChar(ACCENT, eAigu);
  lcd.createChar(COEUR, heart);
  lcd.createChar(REPAS, bell);
  // on defini la fonction de mise à jour avec nos paramètre (le serveur, le chemin, le login et mdp)          
  httpUpdater.setup(&httpServer, update_path, update_username, update_password);
  // on associe la page handle_Root a la racine du serveur web
  httpServer.on("/", handle_root);
  // on associe les page chansons a des alias 
  httpServer.on("/Star", handle_Star);
  httpServer.on("/Happy", handle_Happy);
  httpServer.on("/Game", handle_Game);  
  // on démarre le serveur web
  httpServer.begin();  
  lcd.clear();
  lcd.setCursor(3,0);
  lcd.print("Fin de l' ");
  lcd.setCursor(1,1);
  lcd.print("initialisation");
  delay(2000); 
  lcd.clear(); 
  lcd.noBacklight();    
}


// la boucle
void loop () {   
  MDNS.update();                                        // On maintient notre nom d'hote                                                      
  httpServer.handleClient();                            // On l'ance l'écoute sur le serveur
  bouton();                                             // On check le bouton
  if (wifiFirstConnected) {                             // Si le flag de la première connexion est activé
      wifiFirstConnected = false;                       // On le bascule a false
      NTP.setInterval (63);                             // On régle l'interval de synchonisation en seconde  
      NTP.setNTPTimeout (NTP_TIMEOUT);                  // On régle le timout de synchonisation
      NTP.begin (serveurNTP, timeZone, true);           // On démarre la syncho avec le serveur NTP toute les 63s
  }
  
  if (syncEventTriggered) {                             // Si une synchro est efféctuée
      processSyncEvent (ntpEvent);                      // On lance la fonction qui traite le retour de la synchro (erreur ou réussite)
      syncEventTriggered = false;                       // On repasse le flag syncEventTriggered a false 
  }

  unsigned long currentMillis = millis();               // Variable currentMillis vaut millis() ( le temps depuis le debut du programme)                     
  if (currentMillis - previousMillis >= interval) {     // Sion dépasse ou atteint l'interval (donc ici toute les 10 secondes) 
    previousMillis = currentMillis;                     // On reset notre compteur temps
    // on lance nos fonctions
    terrarium();                                        
    sec();
    evenement(); 
  }
}

// intro animation LCD 
void intro() {         
  lcd.setCursor(0,1);
  lcd.write(HEAD);   
  delay(200);  
  for (int i=0; i<6; i++) {
   int j = i+1;
   lcd.setCursor(i,1);
   lcd.write(TAIL);
   lcd.setCursor(j,1);
   lcd.write(HEAD);
   delay(200);
  }          
  lcd.setCursor(0,1);
  lcd.write(CLEAR);
  lcd.setCursor(6,1);
  lcd.write(TAIL);
  lcd.setCursor(7,0);
  lcd.write(HEAD);
  delay(200);      
  lcd.setCursor(0,0);
  lcd.print("Terra");
  for (int i=1; i<7; i++) {
   int j = i+6;
   int k = j+1;
   lcd.setCursor(i,1);
   lcd.write(CLEAR);
   lcd.setCursor(j,0);
   lcd.write(TAIL);
   lcd.setCursor(k,0);
   lcd.write(HEAD);
   delay(200);
  }   
  lcd.setCursor(7,1);
  lcd.print("NodeMCU");
  for (int i=7; i<9; i++) {
   int j = i+6;
   int k = j+1;
   lcd.setCursor(i,0);
   lcd.write(CLEAR);
   lcd.setCursor(j,0);
   lcd.write(TAIL);
   lcd.setCursor(k,0);
   lcd.write(HEAD);
   delay(200);
  }      
  lcd.setCursor(8,0);
  lcd.write(CLEAR);
  lcd.setCursor(15,0);
  lcd.write(TAIL);
  delay(200);
  for (int i=9; i<16; i++) {
   lcd.setCursor(i,0);
   lcd.write(CLEAR);
   delay(200);
  }   
  lcd.clear();
}

//sortie animation LCD  
void outro () {          
  lcd.setCursor(2,0);
  lcd.print("Bye bye !!!");
  lcd.setCursor(1,1);
  lcd.print("Terra-NodeMCU");
  delay(1500); 
  lcd.clear(); 
  lcd.noBacklight();
 }

// La fonction de l'affichage des datas des sondes en temps réel sur l'écran LCD ********
void affichage() {
  // on allume le retro-éclairage
  lcd.backlight();
  delay(100);    
  // on lance l'animation d'introduction
  intro();
  // on lance l'animation 
  lcd.setCursor(0,0);
  lcd.print("Nous sommes le :");
  lcd.setCursor(0,1);
  digitalDateDisplay();
  delay(1500); 
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Il est :");
  lcd.setCursor(0,1);
  digitalClockDisplay();
  delay(1500); 
  lcd.clear();    
  lcd.setCursor(3,0);
  lcd.print("Point chaud :");
  lcd.setCursor(0,1);
  lcd.print("Temp = ");
  lcd.print(float (tC));
  lcd.print(" ");
  lcd.write(223);
  lcd.print("C");
  delay(1500);    
  lcd.setCursor(0,1);
  lcd.print("Humi =  ");
  lcd.print(float (hC));
  lcd.print(" %");
  delay(1500);
  lcd.clear();    
  lcd.setCursor(3,0);
  lcd.print("Point froid :");
  lcd.setCursor(0,1);
  lcd.print("Temp = ");
  lcd.print(float (tF));
  lcd.print(" ");
  lcd.write(223);    // caractère spécial du °
  lcd.print("C");
  delay(1500);        
  lcd.setCursor(0,1);
  lcd.print("Humi =  ");
  lcd.print(float (hF));
  lcd.print(" %");
  delay(1500);
  lcd.clear();   
  // on lance l'animation de sortie
  outro();         
}

// la fonction qui gére les événements
void evenement(){
  int Hnow = hour() * 100 + minute();
  int DMnow = day() * 100 + month();  
  int Dnow = day();    
  if (flag_killer == false) {                                           // si le flag est false
    for (int i = 0; i < (sizeof(dateanniv)/sizeof(int)); i++) {         // pour chaque date du tableau dateanniv
       if (DMnow == dateanniv[i]) {                                     // si elle est égale à la date d'aujourd'hui 
         for (int j = 0; j < (sizeof(heureanniv)/sizeof(int)); j++) {   // a chaque heure du tableau heureanniv on lancera la musique        
             if (Hnow == heureanniv[j]) {                 
                if((millis() - tempsanniv) > 10000) {   
                  lcd.backlight();
                  lcd.setCursor(1,0);
                  lcd.print("happy birthday");
                  lcd.setCursor(0,1);
                  lcd.write(COEUR);
                  lcd.write(COEUR);
                  lcd.write(COEUR);
                  lcd.setCursor(5,1);
                  lcd.print(noms[i]); 
                  lcd.setCursor(13,1); 
                  lcd.write(COEUR);
                  lcd.write(COEUR);
                  lcd.write(COEUR);                          
                  HappyBirthday();                
                  lcd.clear();
                  lcd.noBacklight();                                              
                  tempsanniv = millis();         
                }              
             }                     
          }              
       } 
    }  
    for (int i = 0; i < (sizeof(daterepas)/sizeof(int)); i++) {          // pour chaque date du tableau daterepas   
       if (daterepas[i] == Dnow) {                                       // si elle est égale à la date d'aujourd'hui
         for (int j = 0; j < (sizeof(heurerepas)/sizeof(int)); j++) {    // a chaque heure du tableau heurerepas on lancera la musique                    
             if (Hnow == heurerepas[j]) {                   
                if((millis() - tempsrepas) > 20000) {                 
                  lcd.backlight();
                  lcd.setCursor(1,0);
                  lcd.print("c'est l'heure");
                  lcd.setCursor(0,1);
                  lcd.write(REPAS);
                  lcd.write(REPAS);                
                  lcd.setCursor(4,1);
                  lcd.print("du repas"); 
                  lcd.setCursor(14,1); 
                  lcd.write(REPAS);
                  lcd.write(REPAS);                                          
                  GameOfThrones();                
                  lcd.clear();
                  lcd.noBacklight();               
                  tempsrepas = millis();         
                }              
             }                     
          }  
       }    
    }
  }
}

// la pages web d'accueil
void handle_root() {
  char page[] = "<body style='background:#7F7F7F;'>"               
  "<center><h2 style='color:#EC7878;'>La fonction Update</h2></center><br/>"
  "<center><p><a href=\"update\"><button style='background:#EC7878;border:2px solid black;font-size: 20px;padding: 20px 20px;border-radius: 25px;'>update du programme</button></a></p></center>"  
  "<center><h2>Commande des chansons</h2></center><br/>" 
  "<center><div>"
  "<p><a href=\"Star\"><button>Starwars</button></a> &nbsp; &nbsp; <a href=\"Happy\"><button>Happy Birthday</button></a> &nbsp; &nbsp;"  
  "<a href=\"Game\"><button>Game Of Thrones</button></a></p>"    
  "</center></div>"  
  "</body>";                
  httpServer.send(200, "text/html", page); // repond avec la page web codee en HTML
}

// la pages web pour Starwars
void handle_Star() {     
  char page[] = "<body style='background:#7F7F7F;'>"
                "<div style='margin-top:10%;'>"
                "<center><h1>Starwars</h1></center><br/>"
                "<center><p><a href=\"/\"><button>Home</button></a></p></center>"
                "</div></body>";
  httpServer.send(200, "text/html", page); // repond avec la page web codee en HTML
  Starwars();  
}

// la pages web pour Game Of Thrones
void handle_Game() {    
  char page[] = "<body style='background:#7F7F7F;'>"
                "<div style='margin-top:10%;'>"
                "<center><h1>Game Of Thrones</h1></center><br/>"
                "<center><p><a href=\"/\"><button>Home</button></a></p></center>"
                "</div></body>";
  httpServer.send(200, "text/html", page); // repond avec la page web codee en HTML
  GameOfThrones();
}

// la pages web pour Happy Birthday
void handle_Happy() {    
  char page[] = "<body style='background:#7F7F7F;'>"
                "<div style='margin-top:10%;'>"
                "<center><h1>Happy Birthday</h1></center><br/>"
                "<center><p><a href=\"/\"><button>Home</button></a></p></center>"
                "</div></body>";
  httpServer.send(200, "text/html", page); // repond avec la page web codee en HTML
  HappyBirthday();
}


// La fonction beep qui joue les notes et allume les leds
void beep(int note, int duree) {   
    int lecture = digitalRead(buttonPin);   
    if (lecture == 0) {
      flag_killer = true;
    }
    if (flag_killer == true) {
      noTone(buzzerPin);       
    } else {         
      tone(buzzerPin, note, duree);     
      // choix des leds à allumer en fonction de la valeur de 'compteur' et de la durée de la note
      if(compteur % 2 == 0)
      {    
        digitalWrite(ledPin1, !ledState);    
        delay(duree);
        digitalWrite(ledPin1, ledState);    
      } 
      else
      {    
        digitalWrite(ledPin2, !ledState);      
        delay(duree);
        digitalWrite(ledPin2, ledState);   
      }       
      noTone(buzzerPin); 
      delay(50); 
      // On increment le compteur
      compteur++;
    }
}

////////////////////////// Les chansons ////////////////////////

//******* Starwars coté obscure**************** 
void firstSection()
{
  beep(NOTE_A4, 500);
  beep(NOTE_A4, 500);    
  beep(NOTE_A4, 500);
  beep(NOTE_F4, 350);
  beep(NOTE_C5, 150);  
  beep(NOTE_A4, 500);
  beep(NOTE_F4, 350);
  beep(NOTE_C5, 150);
  beep(NOTE_A4, 650); 
  delay(500); 
  beep(NOTE_E5, 500);
  beep(NOTE_E5, 500);
  beep(NOTE_E5, 500);  
  beep(NOTE_F5, 350);
  beep(NOTE_C5, 150);
  beep(NOTE_GS4, 500);
  beep(NOTE_F4, 350);
  beep(NOTE_C5, 150);
  beep(NOTE_A4, 650); 
  delay(500);
}
 
void secondSection()
{
  beep(NOTE_A5, 500);
  beep(NOTE_A4, 300);
  beep(NOTE_A4, 150);
  beep(NOTE_A5, 500);
  beep(NOTE_GSH, 325);
  beep(NOTE_G5, 175);
  beep(NOTE_FS5, 125);
  beep(NOTE_F5, 125);    
  beep(NOTE_FS5, 250); 
  delay(325); 
  beep(NOTE_AS, 250);
  beep(NOTE_DS5, 500);
  beep(NOTE_D5, 325);  
  beep(NOTE_CS5, 175);  
  beep(NOTE_C5, 125);  
  beep(NOTE_AS4, 125);  
  beep(NOTE_C5, 250);   
  delay(350);
}

void Starwars() 
{    
  firstSection();  
  secondSection(); 
  //Variant 1
  beep(NOTE_F4, 250);  
  beep(NOTE_GS4, 500);  
  beep(NOTE_F4, 350);  
  beep(NOTE_A4, 125);
  beep(NOTE_C5, 500);
  beep(NOTE_A4, 375);  
  beep(NOTE_C5, 125);
  beep(NOTE_E5, 650); 
  delay(500);  
  secondSection(); 
  //Variant 2
  beep(NOTE_F4, 250);  
  beep(NOTE_GS4, 500);  
  beep(NOTE_F4, 375);  
  beep(NOTE_C5, 125);
  beep(NOTE_A4, 500);  
  beep(NOTE_F4, 375);  
  beep(NOTE_C5, 125);
  beep(NOTE_A4, 650); 
  delay(650);
  compteur = 0;
}

//******* GameOfThrones ****************
void GameOfThrones()
  {
    for(int i=0; i<3; i++)
    {
    beep(NOTE_G4, 500);     
    beep(NOTE_C4, 500);    
    beep(NOTE_DS4, 250);    
    beep(NOTE_F4, 250);    
    }
    for(int i=0; i<3; i++)
    {
    beep(NOTE_G4, 500);    
    beep(NOTE_C4, 500);    
    beep(NOTE_E4, 250);    
    beep(NOTE_F4, 250);    
    }
        beep(NOTE_G4, 500);
        beep(NOTE_C4, 500);        
        beep(NOTE_DS4, 250);
        beep(NOTE_F4, 250);
        beep(NOTE_D4, 500);        
    for(int i=0; i<2; i++)
    {
    beep(NOTE_G3, 500);
    beep(NOTE_AS3, 250);
    beep(NOTE_C4, 250);
    beep(NOTE_D4, 500);    
    }//
        beep(NOTE_G3, 500);
        beep(NOTE_AS3, 250);
        beep(NOTE_C4, 250);
        beep(NOTE_D4, 1000);
        beep(NOTE_F4, 1000);
        beep(NOTE_AS3, 1000);
        beep(NOTE_DS4, 250);
        beep(NOTE_D4, 250);
        beep(NOTE_F4, 1000);
        beep(NOTE_AS3, 1000);
        beep(NOTE_DS4, 250);
        beep(NOTE_D4, 250);
        beep(NOTE_C4, 500);
    for(int i=0; i<2; i++)
    {
    beep(NOTE_GS3, 250);
    beep(NOTE_AS3, 250);
    beep(NOTE_C4, 500);
    beep(NOTE_F3, 500);    
    }
          beep(NOTE_G4, 1000);
          beep(NOTE_C4, 1000);
          beep(NOTE_DS4, 250);
          beep(NOTE_F4, 250);
          beep(NOTE_G4, 1000);
          beep(NOTE_C4, 1000);
          beep(NOTE_DS4, 250);
          beep(NOTE_F4, 250);
          beep(NOTE_D4, 500);          
    for(int i=0; i<3; i++)
    {
    beep(NOTE_G3, 500);
    beep(NOTE_AS3, 250);
    beep(NOTE_C4, 250);
    beep(NOTE_D4, 500);
    }
  compteur = 0;
}


//******* Happy Birthday ****************
void HappyBirthday()
{
  beep(NOTE_G3, 200);
  beep(NOTE_G3, 200);
  beep(NOTE_A3, 500);
  beep(NOTE_G3, 500);
  beep(NOTE_C4, 500);
  beep(NOTE_B3, 1000);
  beep(NOTE_G3, 200);
  beep(NOTE_G3, 200);
  beep(NOTE_A3, 500);
  beep(NOTE_G3, 500);
  beep(NOTE_D4, 500);
  beep(NOTE_C4, 1000);
  beep(NOTE_G3, 200);
  beep(NOTE_G3, 200);
  beep(NOTE_G4, 500);
  beep(NOTE_E4, 500);
  beep(NOTE_C4, 500);
  beep(NOTE_B3, 500);
  beep(NOTE_A3, 750);
  beep(NOTE_F4, 200);
  beep(NOTE_F4, 200);
  beep(NOTE_E4, 500);
  beep(NOTE_C4, 500);
  beep(NOTE_D4, 500);
  beep(NOTE_C4, 1000); 
  compteur = 0;
}