// // ######################## // # CHALET CONNECTE # // ######################## // # Guy Magli 26.05.2019 # // # 23.01.2020 # // ######################## // # Conception, cablages # // # mises a jour, # // # contact... # // # www.magli.fr # // ######################## // // Afin d'eviter les problemes d'affichage et d'impression les signes diacritiques // ont ete retires des commentaires et des chaines de caracteres. // // ! ======================== ! // ! LIBRAIRIES ET PARAMETRES ! // ! ======================== ! // #include #include #include // Capteur temperature, pression, humidite. #include // Broches 20 et 21. #include // Envoi de courriels et lecture des parametres sur le web. #include // Pour le reboot. #include // Pour sauvegarder les minutes meme en cas de reboot. // // ! ================================================= ! // ! VARIABLES GLOBALES, CONSTANTES ET INITIALISATIONS ! // ! ================================================= ! // // : -------------------- : // : Le bouclier Internet : // : -------------------- : // // Adresse Mac du bouclier. // Cette adresse figure sur le bouclier Internet. // Elle peut etre modifiee. // Elle doit etre modifiee si vous avez deja une carte reseau avec la meme adresse MAC. // Par exemple { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xEE }; puis { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xEF }; byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xEF }; // Adresse IP du bouclier. IPAddress ip(192, 168, 1, 40); // Trouvee grace a IPSCAN. Voir www.advanced-ip-scanner.com/fr int iPort = 25; // Changer selon la box, voir www.commentcamarche.net/faq/ 893-parametres-de-serveurs-pop-imap-et-smtp-des-principaux-fai char cServerEnvoi[] = "smtp.bbox.fr"; // A changer selon votre box. Voir la page ci-dessous. char cServerLecture[] = "www.xxxxx.eu"; // Adresse de votre serveur. EthernetClient cClientEnvoi; // Pour l'envoi des mails. EthernetClient cClientLecture; // Pour la lecture des parametres. // // : ----------------------------------------------------------------------- : // : Accessoire de mesure de la temperature, de la pression et de l'humidite : // : ----------------------------------------------------------------------- : // Adafruit_BME280 bme; int iTemperatureAlerterCourriel = 0; // Temperature a partir de laquelle on adresse une alerte par courriel, int iTemperatureAllumageGyrophare = 0; // on allume le gyrophare, int iTemperatureAllumageChauffage = 0; // on allume le chauffage, int iTemperatureExtinctionGyrophare = 0; // on eteint le gyrophare, int iTemperatureExtinctionChauffage = 0; // et on eteint le chauffage. int iGyrophare = 6; // Broches du relais du gyrophare int iChauffage = 7; // et du chauffage. bool bGyrophareAllume = false; // Le gyrophare est eteint. bool bChauffageAllume = false; // Le chauffage est eteint. int iMinutesEnvoiCourrielAlerte = 0; int iTentativesConnexion = 0; // Pour gerer ls reboot en cas de problemes de connexion. int iTentativesLecture = 0; // Id. int iReponseServeur = 0; // Pour la communication Internet. String cLigne = ""; // La ligne qui a ete lue sur le Web. int iCaracteresLus = 0; // Compte les caracteres recus par Internet. int iDebut = 0; // Premiere position trouvee dans une chaine de caracteres. int iFin = 0; // Seconde position trouvee. String cPluriel = ""; // Pour un eventuel accord du nombre. int t; // Pour les boucles diverses. // // ! ===== ! // ! SETUP ! // ! ===== ! // void setup() { // // Pour la communication via la console. // Serial.begin(9600); Serial.println(" "); Serial.println(": -------------------------------------- :"); Serial.println(": BOITIER DE MAINTIEN HORS GEL DU CHALET :"); Serial.println(": -------------------------------------- :"); Serial.println(" "); // // Extinction du gyrophare au demarrage. // pinMode(iGyrophare, OUTPUT); digitalWrite(iGyrophare, LOW); bGyrophareAllume = false; Serial.println("<= Extinction du gyrophare."); Serial.println(" "); // // Extinction du chauffage au demarrage. // pinMode(iChauffage, OUTPUT); digitalWrite(iChauffage, LOW); bChauffageAllume = false; Serial.println("<= Extinction du chauffage."); Serial.println(" "); // // Debut de la connexion Internet. // Ethernet.begin(mac); // // Delai d'initialisation du bouclier. // delay(3000); // // Initialisation de la communication. // iReponseServeur = 0; while (iReponseServeur != 1) { // // Commande GET de lecture du fichier texte contenant les parametres. // iReponseServeur = cClientLecture.connect(cServerLecture, 80); cClientLecture.println("GET /Xxxxx/Chalet.txt HTTP/1.1"); cClientLecture.println("host: xxxxx.eu"); cClientLecture.println(); delay(4000); iTentativesConnexion++; if (iTentativesConnexion >= 5) { reboot(); } Serial.print("Connexion Internet, tentative : "); Serial.print(iTentativesConnexion); Serial.print(", reponse : "); Serial.print(iReponseServeur); if (iReponseServeur == 1) { Serial.println(" Succes"); } if (iReponseServeur == 0) { Serial.println(" Erreur indeterminee"); } if (iReponseServeur == -1) { Serial.println(" Hors delai de reponse"); } if (iReponseServeur == -2) { Serial.println(" Serveur invalide"); } if (iReponseServeur == -3) { Serial.println(" Reponse tronquee"); } if (iReponseServeur == -4) { Serial.println(" Reponse invalide"); } delay(3000); } // // Interprete les donnees d'initialisation. // // // On cherche dans la ligne lue les reperes pour les differentes temperatures, // #@ et $@ qui encadrent la valeur en dessous de laquelle il faut adresser un courriel, // #AG et $AG et #EG et $EC pour allumer et eteindre le gyrophare, // et #AC et $AC et #EC et $EC pour allumer et eteindre le chauffage. // Serial.println(" "); cLigne = ""; iTentativesLecture = 0; while (cLigne.length() < 10) { cLigne = ""; int iLen = cClientLecture.available(); iTentativesLecture++; Serial.print("Tentative de lecture des donnees (setup) : "); Serial.println(iTentativesLecture); if (iTentativesLecture >= 20) { reboot(); } while (iLen > 0) // Il y a des donnees en attente. { byte buffer[80]; int t; if (iLen > 80) iLen = 80; cClientLecture.read(buffer, iLen); for (t = 0; t < 80; t++) { cLigne = cLigne + (char)(buffer[t]); // Stockage des donnes. iCaracteresLus++; } iLen = cClientLecture.available(); } delay(3000); } Serial.println(" "); iDebut = cLigne.indexOf("#@"); iFin = cLigne.indexOf("$@"); iTemperatureAlerterCourriel = cLigne.substring(iDebut + 2, iFin).toInt(); iDebut = cLigne.indexOf("#AG"); iFin = cLigne.indexOf("$AG"); iTemperatureAllumageGyrophare = cLigne.substring(iDebut + 3, iFin).toInt(); iDebut = cLigne.indexOf("#AC"); iFin = cLigne.indexOf("$AC"); iTemperatureAllumageChauffage = cLigne.substring(iDebut + 3, iFin).toInt(); iDebut = cLigne.indexOf("#EG"); iFin = cLigne.indexOf("$EG"); iTemperatureExtinctionGyrophare = cLigne.substring(iDebut + 3, iFin).toInt(); iDebut = cLigne.indexOf("#EC"); iFin = cLigne.indexOf("$EC"); iTemperatureExtinctionChauffage = cLigne.substring(iDebut + 3, iFin).toInt(); Serial.print("Un courriel d'alerte sera envoye des que la temperature passera en dessous de "); Serial.print(iTemperatureAlerterCourriel); Serial.println(" degre.s."); Serial.print("Le gyrophare sera allume quand la temperature passera en dessous de "); Serial.print(iTemperatureAllumageGyrophare); Serial.print(" degre.s et il sera eteint quand elle repassera au dessus de "); Serial.print(iTemperatureExtinctionGyrophare); Serial.println(" degre.s."); Serial.print("Le chauffage sera allume quand la temperature passera en dessous de "); Serial.print(iTemperatureAllumageChauffage); Serial.print(" degre.s et il sera eteint quand elle repassera au dessus de "); Serial.print(iTemperatureExtinctionChauffage); Serial.println(" degre.s."); Serial.println("Test du thermometre"); Serial.println(" "); // // Test du thermometre. // if (bme.begin()) { Serial.println("-> Le thermometre fonctionne correctement."); Serial.println(" "); } else { Serial.println("-> Le thermometre NE fonctionne PAS correctement."); Serial.println(" "); // // Extinction du gyrophare par mesure de securite si le thermometre ne fonctionne pas. // digitalWrite(iGyrophare, LOW); bGyrophareAllume = false; Serial.println("<= Maintien de l'extinction du gyrophare par mesure de securite."); Serial.println(" "); // // Extinction du chauffage par mesure de securite. // digitalWrite(iChauffage, LOW); bChauffageAllume = false; Serial.println("<= Maintien de l'extinction du chauffage par mesure de securite."); Serial.println(" "); } Serial.print("La temperature actuelle est de "); Serial.print(bme.readTemperature()); Serial.println(" degre.s."); Serial.println(" "); Serial.print("Actuellement, le gyrophare est "); if (bGyrophareAllume == true) { Serial.println("en marche."); Serial.println(" "); } else { Serial.println("arrete."); Serial.println(" "); } Serial.print("Actuellement, le chauffage est "); if (bChauffageAllume == true) { Serial.println("en marche."); Serial.println(" "); } else { Serial.println("arrete."); Serial.println(" "); } // Mettre ici, eventuellement, un envoi de courriel initial pour indiquer le lancement du dispositif. // (Ne raison du nombre assez important de reboots -et donc d'envoi de courriels- j'ai supprime cette partie.) } // // ! ==== ! // ! LOOP ! // ! ==== ! // void loop() { // // Commande GET de lecture du fichier texte contenant les parametres. // iReponseServeur = 0; while (iReponseServeur != 1) { iReponseServeur = cClientLecture.connect(cServerLecture, 80); cClientLecture.println("GET /Xxxxx/Chalet.txt HTTP/1.1"); cClientLecture.println("host: xxxxx.eu"); cClientLecture.println(); delay(4000); iTentativesConnexion++; if (iTentativesConnexion >= 5) { reboot(); } Serial.print("Connexion Internet, tentative : "); Serial.print(iTentativesConnexion); Serial.print(", reponse : "); Serial.print(iReponseServeur); if (iReponseServeur == 1) { Serial.println(" Succes"); } if (iReponseServeur == 0) { Serial.println(" Erreur indeterminee"); } if (iReponseServeur == -1) { Serial.println(" Hors delai de reponse"); } if (iReponseServeur == -2) { Serial.println(" Serveur invalide"); } if (iReponseServeur == -3) { Serial.println(" Reponse tronquee"); } if (iReponseServeur == -4) { Serial.println(" Reponse invalide"); } delay(3000); } // // S'il y a des donnees en attente, on les importe et on les memorise. // cLigne = ""; iTentativesLecture = 0; while (cLigne.length() < 10) { cLigne = ""; int iLen = cClientLecture.available(); iTentativesLecture++; Serial.print("Tentative de lecture des donnees (loop) : "); Serial.println(iTentativesLecture); if (iTentativesLecture > 20) { reboot(); } while (iLen > 0) // Il y a des donnees en attente { byte buffer[80]; int t; if (iLen > 80) iLen = 80; cClientLecture.read(buffer, iLen); for (t = 0; t < 80; t++) { cLigne = cLigne + (char)(buffer[t]); // Stockage iCaracteresLus++; } iLen = cClientLecture.available(); } delay(3000); } // // On cherche dans la ligne lue les reperes pour les differentes temperatures // #@ et $@ qui encadrent la valeur en dessous de laquelle il faut adresser un courriel, // #AG et $AG et #EG et $EC pour allumer et eteindre le gyrophare, // et #AC et $AC et #EC et $EC pour allumer et eteindre le chauffage. // iDebut = cLigne.indexOf("#@"); iFin = cLigne.indexOf("$@"); iTemperatureAlerterCourriel = cLigne.substring(iDebut + 2, iFin).toInt(); iDebut = cLigne.indexOf("#AG"); iFin = cLigne.indexOf("$AG"); iTemperatureAllumageGyrophare = cLigne.substring(iDebut + 3, iFin).toInt(); iDebut = cLigne.indexOf("#AC"); iFin = cLigne.indexOf("$AC"); iTemperatureAllumageChauffage = cLigne.substring(iDebut + 3, iFin).toInt(); iDebut = cLigne.indexOf("#EG"); iFin = cLigne.indexOf("$EG"); iTemperatureExtinctionGyrophare = cLigne.substring(iDebut + 3, iFin).toInt(); iDebut = cLigne.indexOf("#EC"); iFin = cLigne.indexOf("$EC"); iTemperatureExtinctionChauffage = cLigne.substring(iDebut + 3, iFin).toInt(); Serial.print("Un courriel d'alerte sera envoye des que la temperature passera en dessous de "); Serial.print(iTemperatureAlerterCourriel); Serial.println(" degre.s."); Serial.print("Le gyrophare sera allume quand la temperature passera en dessous de "); Serial.print(iTemperatureAllumageGyrophare); Serial.print(" degre.s et il sera eteint quand elle repassera au dessus de "); Serial.print(iTemperatureExtinctionGyrophare); Serial.println(" degre.s."); Serial.print("Le chauffage sera allume quand la temperature passera en dessous de "); Serial.print(iTemperatureAllumageChauffage); Serial.print(" degre.s et il sera eteint quand elle repassera au dessus de "); Serial.print(iTemperatureExtinctionChauffage); Serial.println(" degre.s."); Serial.print("La temperature actuelle est de "); Serial.print(bme.readTemperature()); Serial.println(" degre.s."); Serial.println(" "); Serial.println(" "); // // Mesure des temperatures en continu // if (bme.begin()) { Serial.println("-> Le thermometre fonctionne correctement."); Serial.println(" "); Serial.print("La temperature actuelle est de "); Serial.print(bme.readTemperature()); Serial.println(" degre.s."); Serial.println(" "); Serial.print("Actuellement, le chauffage est "); if (bChauffageAllume == true) { Serial.println("en marche."); Serial.println(" "); } else { Serial.println("arrete."); Serial.println(" "); } Serial.print("Actuellement, le gyrophare est "); if (bGyrophareAllume == true) { Serial.println("en marche."); Serial.println(" "); } else { Serial.println("arrete."); Serial.println(" "); } // // Envoi d'un courriel d'alerte si la temperature descend sous un seuil. // if (bme.readTemperature() <= iTemperatureAlerterCourriel) { Serial.print("La temperature ("); Serial.print(bme.readTemperature()); Serial.print(") est descendue sous le seuil d'envoi d'un courriel d'alerte ("); Serial.print(iTemperatureAlerterCourriel); Serial.println(")."); Serial.print(" "); // On n'envoie pas plus d'un courriel toutes les heures. iMinutesEnvoiCourrielAlerte = EEPROM.read(1); Serial.print("Le dernier courriel d'alerte a ete envoye il y a : "); Serial.print(iMinutesEnvoiCourrielAlerte); Serial.println(" minutes."); Serial.print("L'heure de debut etait : "); Serial.println("."); Serial.print(" "); // Si aucun courriel d'alerte n'a encore ete envoye ou s'il a ete envoye depuis plus de deux heures. if ((iMinutesEnvoiCourrielAlerte == 0) || (iMinutesEnvoiCourrielAlerte >= 60)) // Valeur en minutes. { Serial.println("Nous envoyons donc les courriels."); Serial.println(" "); EnvoiCourrielAlerte("xxxxxxxxxxx@gmail.com", "Xxxxxx"); EnvoiCourrielAlerte("xxxxxxxxx@gmail.com", "Xxx"); EEPROM.write(1, 1); } } // Allumage du gyrophare si la temperature descend sous un seuil. if ((bme.readTemperature() <= iTemperatureAllumageGyrophare) & (bGyrophareAllume == false)) { digitalWrite(iGyrophare, HIGH); bGyrophareAllume = true; Serial.print("=> Allumage du gyrophare a "); Serial.print(bme.readTemperature()); Serial.print(" degre.s sur "); Serial.println(iTemperatureAllumageGyrophare); Serial.println(" "); } // Allumage du chauffage si la temperature descend sous un seuil. if ((bme.readTemperature() <= iTemperatureAllumageChauffage) & (bChauffageAllume == false)) { delay(3000); digitalWrite(iChauffage, HIGH); bChauffageAllume = true; Serial.print("=> Allumage du chauffage a "); Serial.print(bme.readTemperature()); Serial.print(" degre.s sur "); Serial.println(iTemperatureAllumageChauffage); Serial.println(" "); } // Extinction du gyrophare lorsque la temperature remonte au dessus d'un seuil. if ((bme.readTemperature() >= iTemperatureExtinctionGyrophare) & (bGyrophareAllume == true)) { digitalWrite(iGyrophare, LOW); bGyrophareAllume = false; Serial.print("<= Extinction du gyrophare a "); Serial.print(bme.readTemperature()); Serial.print(" degre.s sur "); Serial.println(iTemperatureExtinctionGyrophare); Serial.println(" "); } // Extinction du chauffage lorsque la temperature remonte au dessus d'un seuil. if ((bme.readTemperature() >= iTemperatureExtinctionChauffage) & (bChauffageAllume == true)) { digitalWrite(iChauffage, LOW); bChauffageAllume = false; Serial.print("<= Extinction du chauffage a "); Serial.print(bme.readTemperature()); Serial.print(" degre.s sur "); Serial.println(iTemperatureExtinctionChauffage); Serial.println(" "); } } else { Serial.println("-> Le thermometre NE fonctionne PAS correctement."); Serial.println(" "); // // Extinction du gyrophare par mesure de securite. // digitalWrite(iGyrophare, LOW); bGyrophareAllume = false; Serial.println("<= Extinction du gyrophare par mesure de securite."); Serial.println(" "); // // Extinction du chauffage par mesure de securite. // digitalWrite(iChauffage, LOW); bChauffageAllume = false; Serial.println("<= Extinction du chauffage par mesure de securite."); Serial.println(" "); } Serial.println("(60 secondes d'attente)"); for (t = 0; t < 60; t++) //Inutile de surcharger le reseau ! { delay(1000); Serial.print(">"); } Serial.println(" "); } // // : ---------------------------- : // : ENVOI D'UN COURRIEL D'ALERTE : // : ---------------------------- : // byte EnvoiCourrielAlerte(String cDestinataire, String cPrenom) { Serial.print("Envoi du courriel d'alerte a "); Serial.print(cPrenom); Serial.println(" pour l'informer du passage sous le seuil de la temperature."); String cTexteCourriel = ""; // Le texte du courriel. if (cClientEnvoi.connect(cServerEnvoi, iPort) == 1) { Serial.println("-> Connexion Internet correcte."); Serial.println(" "); } else { Serial.println("-> Connexion Internet INCORRECTE pour l'envoi du courriel d'alerte."); Serial.println(" "); return 0; } if (!receptionCaracteres()) return 0; cClientEnvoi.println("EHLO 192.168.1.40"); // Arduino IP if (!receptionCaracteres()) return 0; cClientEnvoi.println("MAIL From: "); if (!receptionCaracteres()) return 0; cClientEnvoi.print("RCPT To: <"); cClientEnvoi.print(cDestinataire); cClientEnvoi.println(">"); if (!receptionCaracteres()) return 0; cClientEnvoi.println("DATA"); if (!receptionCaracteres()) return 0; cClientEnvoi.print("To: <"); cClientEnvoi.print(cDestinataire); cClientEnvoi.println(">"); cClientEnvoi.println("From: [Ne pas repondre] "); cClientEnvoi.print("Subject: ALERTE TEMPERATURE du chalet (id="); cClientEnvoi.print(millis() / 100); cClientEnvoi.println(")\r\n"); cTexteCourriel = "Attention " + cPrenom + ".\r\n\r\n"; cTexteCourriel.concat("La temperature dans le chalet est descendue au dessous du seuil fixe ("); cTexteCourriel.concat(iTemperatureAlerterCourriel); cTexteCourriel.concat(".\r\n\r\n"); cTexteCourriel.concat("Elle est actuellement de : "); bme.begin(); cTexteCourriel.concat(bme.readTemperature()); cTexteCourriel.concat(" degre.s.\r\n"); cTexteCourriel.concat("Ce courriel pourra, selon, le cas etre complete d'une alerte visuelle.\r\n\r\n"); cTexteCourriel.concat("Le gyrophare se met automatiquement en route au dessous de "); cTexteCourriel.concat(iTemperatureAllumageGyrophare); cTexteCourriel.concat(".\r\n\r\n"); cTexteCourriel.concat("Le chauffage se met automatiquement en route au dessous de "); cTexteCourriel.concat(iTemperatureAllumageChauffage); cTexteCourriel.concat(".\r\n\r\n"); cTexteCourriel.concat("Actuellement, le gyrophare est "); if (bGyrophareAllume == true) { cTexteCourriel.concat("en marche.\r\n\r\n"); } else { cTexteCourriel.concat("arrete.\r\n\r\n"); } cTexteCourriel.concat("Actuellement, le chauffage est "); if (bChauffageAllume == true) { cTexteCourriel.concat("en marche.\r\n\r\n"); } else { cTexteCourriel.concat("arrete.\r\n\r\n"); } cTexteCourriel.concat("Meme si la temperature descend encore sous le seuil d'alerte aucun courriel ne sera plus envoye durant environ une heure.\r\n\r\n"); cClientEnvoi.println(cTexteCourriel); cClientEnvoi.println("."); if (!receptionCaracteres()) return 0; cClientEnvoi.println("QUIT"); if (!receptionCaracteres()) return 0; cClientEnvoi.stop(); return 1; Serial.println("-> Courriel d'alerte envoye."); Serial.println(" "); } // // : ------------------------ : // : RECEPTION DES CARACTERES : // : ------------------------ : // byte receptionCaracteres() { byte codeDeReponse; byte octet; int compteur = 0; while (!cClientEnvoi.available()) { delay(1); compteur++; // Si on ne recoit rien if (compteur > 10000) { cClientEnvoi.stop(); return 0; } } codeDeReponse = cClientEnvoi.peek(); while (cClientEnvoi.available()) { octet = cClientEnvoi.read(); } if (codeDeReponse >= '4') { abandon(); return 0; } return 1; } // // : ------- : // : ABANDON : // : ------- : // void abandon() { byte octet = 0; int compteur = 0; cClientEnvoi.println(F("QUIT")); while (!cClientEnvoi.available()) { delay(1); compteur++; // Si on ne recoit rien if (compteur > 10000) { cClientEnvoi.stop(); return; } } while (cClientEnvoi.available()) { octet = cClientEnvoi.read(); } cClientEnvoi.stop(); } // // : ------ : // : REBOOT : // : ------ : // void reboot() { wdt_enable(WDTO_15MS); while (1) { } }