Programmer un système d’alarme en utilisant tous les connaissances acquises durant le cours.
(1)NOTE: Pour ne pas causer de débordements de la console série ou injecter des délais inutiles dans l’application, il est suggéré d’utiliser la technique du solutionnaire de l’examen pour l’affichage de traces dans la console série, c-a-d, l’utilisation d’un minuteur (timer) et d’une fonction associée à son interruption.
Il faut remettre cette étape du projet, sur LEA avant le dimanche, 21 novembre 23h55.
/* Fichier: exemple.defilement.lcd.ino Auteur: Alain Boudreault Date: 2021.11.21 ------------------------------------------------------------- Description: Défiler un très long texte sur la ligne no 2 de l'écran LCD. ------------------------------------------------------------- M-A-J: ------------------------------------------------------------- */ #include "rgb_lcd.h" #define LCD_LONGUEUR_LIGNE 16 #define LCD_NOMBRE_LIGNE 2 #define LCD_LIGNE_DE_DEFILEMENT 1 #define LCD_DEBUT_LIGNE 0 #define DELAI_DEFILEMENT 250 #define UN_CAR_ESPACE " " #define SEIZE_ESPACES " " #define UNE_CITATION_DE_HUGO "Il fit de la sorte un assez long chemin ..." #define LONGUEUR_LONGUE_CHAINE 100 char unLongMessage[LONGUEUR_LONGUE_CHAINE]; rgb_lcd lcd; // ********************** void setup() // ********************** { char unTitre[] = "420-1C4"; lcd.begin (LCD_LONGUEUR_LIGNE, LCD_NOMBRE_LIGNE); lcd.setCursor((LCD_LONGUEUR_LIGNE / 2 - strlen(unTitre) / 2) - 1, 0 /* ligne no 1 */); // Centrer le titre lcd.print(unTitre); // Afficher un titre sur la première ligne du LCD } // setup // ********************** void loop() // ********************** { static unsigned long int iteration = 0; // Construire la chaine à défiler sur l'écran LCD // Le message à afficher débute et se termine avec 16 caractères ' ' // Cela évite une gestion lourde du curseur sprintf(unLongMessage, "%s%s - Iteration: %lu%s", SEIZE_ESPACES, UNE_CITATION_DE_HUGO, ++iteration, SEIZE_ESPACES); // Afficher le message à partir de toutes les positions des caractères du message - longueur du LCD for (int positionDansLeMessage = 0; positionDansLeMessage <= strlen(unLongMessage) - LCD_LONGUEUR_LIGNE; positionDansLeMessage++) { afficherCaracteres(positionDansLeMessage, unLongMessage); } // for } // loop /* ===================================================================== Afficher LCD_LONGUEUR_LIGNE - 1 caractères d'une chaine de caractères reçue dans 'uneChaineDeCaracteres', à partir d'une position dans la chaine reçue par 'positionAImprimmer', décaler le tout vers la gauche en affichant un espace " ", puis attendre un peu (DELAI_DEFILEMENT). ===================================================================== */ void afficherCaracteres(int positionAImprimmer, char uneChaineDeCaracteres[]) { lcd.setCursor(LCD_DEBUT_LIGNE, LCD_LIGNE_DE_DEFILEMENT); // Afficher seulement 16 caractères de la chaine de caractères for (int positionCaractereAImprimer = positionAImprimmer; positionCaractereAImprimer <= positionAImprimmer + (LCD_LONGUEUR_LIGNE - 1); positionCaractereAImprimer++) { lcd.print(uneChaineDeCaracteres[positionCaractereAImprimer]); } lcd.print(UN_CAR_ESPACE); // puis, décaler le tout vers la gauche ... delay(DELAI_DEFILEMENT); // Attendre un peu, dans le but de ralentir le défilement ... } // afficherCaracteres // **** FIN DU FICHIER ****
/* Fichier: 5secondes.ino Auteur: Alain Boudreault Date: 2021.11.21 ----------------------------------------------------- Description: Exemple d'un délai non bloquant avec l'utilisation de la fonction millis() ----------------------------------------------------- M-A-J: ----------------------------------------------------- */ #define UN_DELAI 5000 #define MESSAGE_DELAI "5 secondes sont passees..." // ************************** void setup() { Serial.begin(9600); Serial.println("Debut du programme\n"); } // setup // ************************** void loop() { if (delaiAtteint()) Serial.println(MESSAGE_DELAI); } // loop // ************************** bool delaiAtteint() { static unsigned long int tempsDepart = millis(); if (millis() - tempsDepart > UN_DELAI) { // Nous avons atteint le delai // Replacer tempsDepart au moment courant tempsDepart = millis(); return true; } else return false; } // delaiAtteint() // ***** FIN DU FICHIER *****
/* Fichier: eteindreLCD.apres.delai.inactiviteBouton.ino Auteur: Alain Boudreault Date: 2021.11.21 ----------------------------------------------------- Description: Éteindre l'écran LCD s'il n'y a pas d'utilisation du bouton pendant la période de delai definie par UN_DELAI. ----------------------------------------------------- M-A-J: ----------------------------------------------------- */ #include "rgb_lcd.h" #define UN_DELAI 5000 #define MESSAGE "Le bouton n'a pas été appuyé dans la période de delai." #define BOUTON 3 #define MSG_LCD "Appuyer le btn" #define LCD_OFF 0,0,0 #define LCD_ON 255,255,255 rgb_lcd lcd; unsigned long int departDelaiBouton; // ************************** void setup() { Serial.begin(9600); Serial.println("Debut du programme\n"); pinMode(BOUTON, INPUT); lcd.begin(16, 2); lcd.print(MSG_LCD); departDelaiBouton = millis(); } // setup // ************************** void loop() { static bool boutonAppuye = false; // Lire l'état du bouton if (digitalRead(BOUTON)) { // Le bouton est enfoncé boutonAppuye = true; // Attendre que le bouton soit relaché avec de continuer while (digitalRead(BOUTON)); } // if digitalRead(BOUTON) // Éteindre le LCD si le bouton est inactif pendant la période de délai if (delaiAtteint() && !boutonAppuye) { // Le bouton n'a pas été appuyé dans la période de delai Serial.println(MESSAGE); // Éteindre le LCD lcd.setRGB(LCD_OFF); } // delaiAtteint() && !boutonAppuye // Allumer le LCD, peut importe son état, si le bouton a été appuyé if (boutonAppuye) { Serial.println("Bouton appuyé, le delai pour éteindre le LCD à été réinitialisé."); lcd.setRGB(LCD_ON); boutonAppuye = false; // Réinitialiser le temps de départ du delai d'utilisation du bouton departDelaiBouton = millis(); } // if boutonAppuye } // loop // ************************** bool delaiAtteint() { if (millis() - departDelaiBouton > UN_DELAI) { // Nous avons atteint le delai // Replacer tempsDepart au moment courant departDelaiBouton = millis(); return true; } else return false; } // delaiAtteint() // ***** FIN DU FICHIER *****
Pour une pénalité de 7% (correction à partir de 93/100) il est possible de réaliser cette version minimale de l’étape 01:
Au démarrage de l’application,
Au démarrage de l’application, le système d’alarme est déverrouillé (hors fonction). C-A-D, qu’il ne traite pas les deux événements suivants:
1.1 – Pour activer le système d’alarme, il faut renseigner le code de déverrouillage à partir:
À chaque utilisation du bouton, le caractère ‘*’ change de position pour indiquer l’action courante.
Par exemple, à la première utilisation du bouton, la ligne 2 du LCD va afficher ceci:
À la prochaine utilisation du bouton, la ligne 2 du LCD va afficher ceci:
L’utilisation du potentiomètre permet de faire varier la valeur du nombre suivant le caractère ‘*’ entre 0 et 9.
1.2 – Validation du code du système d’alarme
Lorsque le caractère ‘*’ est déplacé devant ‘OK’, le code du système d’alarme est validé.
Le code du système d’alarme est défini par la constante ‘const unsigned int codeSystemeAlarme = 4203′
Si le code entré:
Les fonctions suivantes sont exécutées que SI LE SYSTÈME D’ALARME EST EN MODE VERROUILLÉ (ACTIF)
2.1 – Afficher ce qui suit sur l’écran LCD
Tmp: 99c Mov:OFF hh:mm:ss
2.2 – Si le détecteur de mouvement devient actif
Faire vibrer le ‘buzzer‘ cinq (5) fois en deux (2) secondes. Attention, ne pas refaire vibrer le ‘buzzer‘ tant que son état demeure sur ‘ON‘.
Tant que l’état du détecteur de mouvement est à ‘ON‘
Afficher ce qui suit sur l’écran LCD
Tmp: 99c Mov:ON hh:mm:ss
Note: Il faut réactualiser la valeur hh:mm:ss au moins à chaque seconde.
Facultatif, 3 points extras, faire clignoter (deux fois par seconde) ‘ON‘ sur le LCD tant que l’état du détecteur de mouvement est à ‘ON‘
2.3 – Si la température dépasse ‘const char temperatureMaximum‘
Faire vibrer le ‘buzzer‘ deux (2) fois en une (1) seconde.
Attention, ne pas refaire vibrer le ‘buzzer‘ plus qu’une fois par minute. Note: Cette fonctionnalité vaut 5/100.
Tant que la température dépasse la ‘temperatureMaximum‘
Afficher ce qui suit sur l’écran LCD
Tmp:*99c* Mov:ON hh:mm:ss
NOTE: Dans cette version de l’étape 2, il n’y a pas de menu de navigation pour renseigner le code du système d’alarme. Le code de validation est compris entre 0 et 9
1.1 – Pour activer le système d’alarme, il faut renseigner le code de déverrouillage à partir:
1.2 – Validation du code du système d’alarme
Le potentiomètre permet de faire varier la valeur du code entre 0 et 9.
Le bouton permet de valider que le code entré est égal à ‘7‘
Le code du système d’alarme est défini par la constante ‘const unsigned int codeSystemeAlarme = 7′
Si le code entré:
Pour la suite, passé à: 2 – Gestion du détecteur de mouvement et de la température
// Exemple d'une solution pour la version minimale. ;-) #define CODE_SYSTEME_ALARME 7 bool systemeArme = false; if (!systemeArme) { Serial << "!systemeArme\n"; int PotMap = map(analogRead(POT), 0, 1023, 9, 0); ecran.setCursor(0, 0); ecran.print("Entrez le code: "); ecran.setCursor(0, 1); ecran.print(PotMap); ecran.print(" *OK"); if (digitalRead(BOUTON)) { while (digitalRead(BOUTON)); // Attendre que le bouton soit relaché avant de continuer if(PotMap == CODE_SYSTEME_ALARME){ systemeArme = true; ecran.clear(); ecran.print("etape 03"); } else { // Mauvais code digitalWrite(BROCHE_DU_BUZZER, HIGH); delay(500); digitalWrite(BROCHE_DU_BUZZER, 0); }
Il faut remettre cette étape du projet, sur LEA avant le vendredi, 3 décembre.
Règle générale, les systèmes d’alarme sont reliés, en utilisant les câbles du système téléphonique, par réseau cellulaire ou par le réseau Internet, à une centrale de surveillance à distance.
Si un évènement nécessitant une intervention des services d’urgence survient, la centrale de surveillance recevera un code d’intervention et pourra agir en conséquence.
Nous simulerons ici ce type de topologie en utilisant le système d’alarme (UNO) relié à la centrale (MEGA) par le port UART de ces derniers.
Ajouter au dossier de votre projet, le fichier ‘code.alarme.h‘ suivant:
/* Fichier: code.alarme.h Auteur: Alain Boudreault Date: 2021.11.21 --------------------------------------------------------- Description: Définir un type enum à utiliser pour les différents états du système d'alarme. Note: Inclure ce fichier dans le dossier de votre projet. Puis, programmer la syntaxe suivante pour utiliser ce type: codesAlarme code; ... code = evenement_systeme_enligne; --------------------------------------------------------- M-A-J: --------------------------------------------------------- */ // S'assurer que ce fichier ne sera inclus qu'une seule fois lors de la compilation du projet #ifndef LES_CODES_ERREUR_H #define LES_CODES_ERREUR_H enum codesAlarme { evenement_temperature_depassee, evenement_dectection_mouvement, evenement_code_invalide, evenement_code_valide, evenement_alarme_active, evenement_alarme_inactive, evenement_systeme_enligne }; #endif // LES_CODES_ERREUR_H_ _
NOTE: Pour que ce fichier soit disponible pour tous vos projets, il est possible de créer un dossier ‘mestrucs’ dans le dossier Documents/Arduino/libraries. Par exemple, C:\Users\Alain\Documents\Arduino\libraries\mestrucs et y placer le fichier.h.
/* Simulation d'un système d'alarme UNO, qui envoie des événements à une centrale de controle (MEGA) via le protocole UART. Le UNO est connecté par son port D8 au port UART2 du MEGA. Étant donné que le UNO n'a qu'un seul port UART et qu'il est utilisé pour déboger l'application, la librairie 'SoftwareSerial' est utilisée pour simuler un port UART à partir d'une GPIO. */ #include <SoftwareSerial.h> #include "code.alarme.h" // Le RX du UNO doit être connecté au TX du MEGA et TX -> RX // Il faut donc inverser les signaux: SoftwareSerial mySerial(9, 8); // RX, TX codesAlarme unCodeAlarme = evenement_systeme_enligne; void setup() { Serial.begin(9600); while (!Serial) { ; // S'assurer que le port serie est pret. Ceci n'est pas requis pour un SoftwareSerial. } Serial.println("Demarrage du systeme d'alarme..."); // Initialiser le port serie 'SoftwareSerial' mySerial.begin(9600); // Envoyer l'état du système d'alarme à la centrale (Mega) mySerial.write(unCodeAlarme); // Attention, pour envoyer un seul octet (Byte), il faut utiliser la méthode write et non pas print. delay(1000); } void loop() { unCodeAlarme = evenement_dectection_mouvement; // Envoyer un code de détection du mouvement à toutes les secondes: mySerial.write(unCodeAlarme); delay(1000); }
/* Code pour l'Arduino Mega. Réception de codes d'événement, envoyés par le UNO, par le port UART2 */ #include "code.alarme.h" #define VITESSE_DU_PORT_SERIAL 9600 #define PORT_DE_CONNEXION_DU_UNO Serial2 void setup() { Serial.begin (VITESSE_DU_PORT_SERIAL); // Port série pour la console de débogage PORT_DE_CONNEXION_DU_UNO.begin(VITESSE_DU_PORT_SERIAL); // Port série pour la réception des codes du client UNO while (!PORT_DE_CONNEXION_DU_UNO) { ; // Par précaution, s'assurer que le port Serial2 est prêt } Serial.println("Démarrage de la centrale de surveillance..."); } void loop() { codesAlarme code; if (PORT_DE_CONNEXION_DU_UNO.available()) { code = PORT_DE_CONNEXION_DU_UNO.read(); // afficher la valeur du byte reçu Serial.print("\nCode = "); Serial.print(code, DEC); Serial.println(""); // Tester les événements du système d'alarme if (code == evenement_systeme_enligne) Serial.print("Systeme client en ligne !"); if (code == evenement_dectection_mouvement) Serial.print("DANGER: Il y a intrusion ...."); } }
Le ‘buzzer‘ étant maintenant connecté au Arduino Mega, pour le faire sonner, il faut envoyer des codes d’état du ‘UNO‘ vers le ‘Mega‘.
Voici la liste des états à traiter:
Code de l’événement | Détail | alarme |
evenement_systeme_enligne | Envoyer à la fin de la fonction setup() | 2 beeps rapides |
evenement_alarme_inactive | Envoyer après le délai du msg ‘Dormez en paix’ | 1 long beep |
evenement_alarme_active | Envoyer après avoir afficher l’écran de l’étape 2.1 | Afficher dans la console du Mega: Système activé |
evenement_code_valide | Envoyer si le code du système est valide | 1 beep rapide |
evenement_code_invalide | Envoyer si le code du système est invalide | 3 beeps rapides |
evenement_dectection_mouvement | Envoyer si le détecteur de mouvement est activé. Note: ne pas renvoyer tant que le détecteur demeure actif | 4 beeps rapides |
evenement_temperature_depassee | Envoyer si la température dépasse TEMP_ Note: ne pas renvoyer tant que la température est élevée | 5 beeps rapides |
NOTE: beep long = une seconde, beep rapide = 1/2 seconde