Projet – Épreuve Synthèse

14 novembre 2021

Énoncé 2021.11.28.01 du projet de l’épreuve synthèse

Version finale de l’énoncé.

Pondération : 50%

Date de remise: mardi le 14 décembre 2021


À chaque semaine, ce document fournira des informations supplémentaires pour la réalisation du projet de session.


Contexte de réalisation

À partir

Programmer un système d’alarme en utilisant tous les connaissances acquises durant le cours.



Tâches pour la semaine 01 du projet (15 novembre 2021)

(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.


Remise de l’étape 01

Il faut remettre cette étape du projet, sur LEA avant le dimanche, 21 novembre 23h55.


Astuces de la semaine 01

1.1 – Exemple de défilement d’un long message sur la ligne 2 du LCD

/*
    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 ****

1.2 – Utilisation de millis() pour un délai non bloquant

/*
    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 *****

1.3 – Éteindre l’écran LCD après une période d’inactivité

/*
    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 *****

1.4 – Version minimale de l’étape 01 (- 7%)

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,

  1. Afficher sur le LCD:
    1. Ligne 1: ‘Alarme 420-1C4‘,
    2. Ligne 2: ‘Dormez en paix
  2. Attendre 10 secondes puis
  3. Afficher, (en utilisant le défilement sur une seule ligne) le message suivant:
    1. Systeme d’alarme 420-1C4. Nous sommes lundi le 99 novembre 2021 et il est hh:mm:ss. Pour la configuration du systeme, appuyer sur le bouton de panique…
  4. Si le bouton est appuyé,
    1. Allumer la DEL
    2. Afficher
      1. sur la ligne 1: ‘Entrer le code:’
      2. sur la ligne 2: ‘ 1 2 3 4 *OK


Tâches pour la semaine 02 du projet (22 novembre)

  1. Verrouiller/Déverrouiller le système d’alarme.
  2. Gérer le détecteur de mouvement et une température trop élevée.
  3. Envoyer des codes d’événement sur le petit vibreur (buzzer).

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:

  • ‘*1 2 3 4 OK
  • À la prochaine utilisation du bouton, la ligne 2 du LCD va afficher ceci:

  • ‘ 1 *2 3 4 OK
  • 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é:


    2 – Gestion du détecteur de mouvement et de la température

    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

    Version minimale de l’étape 02 (- 5%)

    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

    // 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);
    }
    

    Remise de l’étape 02

    Il faut remettre cette étape du projet, sur LEA avant le vendredi, 3 décembre.




    Tâches pour la semaine 03 du projet (29 novembre)

    Objectif de cette étape – Mesurer votre degré d’adaptation et d’intiative.

    3 – Relier le système d’alarme à une centrale de surveillance.

    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.


    3.1 – Pour réaliser cette étape, il faut:

    3.2 – Utilisation d’un type ‘enum‘ pour l’identification des messages

    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.

    3.2.1 – Exemple d’utilisation du type ‘CodesAlarme’ et transmission de messages entre deux Arduino:

    Pour le Arduino UNO (Système d’alarme)

    /*
        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);
    
    }

    Pour le Arduino MEGA(Centrale de contrôle)

    /*
    
      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 ....");
    
        
      }
    }
    

    3.3 – Gestion des événements entre UNO et MEGA

    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énementDétailalarme
    evenement_systeme_enligneEnvoyer à la fin de la fonction setup()2 beeps rapides
    evenement_alarme_inactiveEnvoyer après le délai du msg ‘Dormez en paix’1 long beep
    evenement_alarme_activeEnvoyer après avoir afficher
    l’écran de l’étape 2.1
    Afficher dans la
    console du Mega:

    Système activé
    evenement_code_valideEnvoyer si le code du système
    est valide
    1 beep rapide
    evenement_code_invalideEnvoyer si le code du système
    est invalide
    3 beeps rapides
    evenement_dectection_mouvementEnvoyer si le détecteur de mouvement
    est activé.
    Note: ne pas renvoyer tant que
    le détecteur demeure actif
    4 beeps rapides
    evenement_temperature_depasseeEnvoyer 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



    Semaine 04 – Compléter le projet (6 décembre)

    Remise du projet: mardi le 14 décembre 2021


    Document par Alain Boudreault – aka VE2CUY version 2021.11.28.01