/*
------------------------------------------------------------------------------------------------
Projet: Projet de session 2022
Nom du ficher : ALARME 420-1C4 inc
Auteur: Samuel Therrien-Côté
Date: 2022.11.22
----------------------------------------------------------------------------------------------
Description:
ÉTAPE 1:
Programme et montage reprodruisant les fonctionnalités d'un système d'alarme
connecté à une centrale de contrôle. Permettant d'afficher l'heure en temps réel et
l'envoi de messages entre les deux systèmes.
ÉTAPE 2:
Active le système d'alarme, se met en alerte et demande un PIN si il y a du mouvement
vérifie la validité du code entré. Envoi l'événement à la centrale
------------------------------------------------------------------------------------------------
M-A-J: 2022-12-08 -- Ajout d'un clavier numérique et détection de mouvement,
activation du système et communication des événements à la centrale.
À venir: lecture de la température et de l’humidité,
----------------------------------------------------------------------------------------------
*/
#include "mesdefines.h"
// Déclaration des objets à portée globale:
TM1637 affichageHorloge(CLK, DIO);
DS1307 horloge;
Adafruit_ILI9341 ecranCentraleSurveillance = Adafruit_ILI9341(TFT_CS, TFT_DC);
LiquidCrystal_I2C ecranPrincipal(LCD_ADRESSE_I2C, LCD_NB_COLONNE, LCD_NB_LIGNE);
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);
// ********************************************************
void setup()
// ********************************************************
{
Serial.begin(UART_VITESSE_CONSOLE);
#ifdef DEBUG
Serial << F(MSG_DEMARRAGE_DU_PROJET);
Serial << F(MSG_PROJET_PAR );
Serial << F(MSG_DEBUT_INIT);
#endif
initialisationDesAppareils();
#ifdef DEBUG
Serial << F(MSG_FIN_INIT);
#endif
afficherPageEcran(centraleEcranOuverture);
afficherPageEcran(ecranOuverture, UNE_SECONDE * 2);
afficherPageEcran(ecranInstruction);
// Début de Lecture du bouton et affichage de l'heure en mode non bloquant
attenteConnexion();
// Fin de Lecture du bouton
if (connexionALaCentrale()) {
afficherPageEcran(systemeEnLigne);
}
Serial1.write(evenement_alarme_active); // met le système actif
attendreSansDelai(UNE_SECONDE);
} // setup
// ********************************************************
void loop()
// ********************************************************
{
lireEvenementAlarme();
afficherTemps();
} // loop()
// ********************************************************
void afficherPageEcran(Ecrans ecran, int delai = 0)
// ********************************************************
{
switch (ecran)
{
case ecranOuverture:
afficherEcranOuverture();
break;
case centraleEcranOuverture:
afficherCentraleEcranOuverture();
break;
case ecranInstruction:
afficherEcranInstruction();
break;
case systemeEnLigne:
afficherSystemeEnLigne();
break;
case ecranActif:
afficherEcranActif();
break;
case ecranPIN:
afficherEcranPin();
break;
/*
à venir
case ecranAuteur:
*/
}
attendreSansDelai(delai);
} // afficherPageEcran
// Initialise tout les appareils utilisés ainsi que les ports de communication.
// ********************************************************
void initialisationDesAppareils()
// ********************************************************
{
ecranCentraleSurveillance.begin(); // Initialiser l'écran de la centrale
ecranPrincipal.begin(LCD_NB_COLONNE, LCD_NB_LIGNE); // Initialiser l'écran du systeme
ecranPrincipal.backlight();
horloge.begin(); // Initialiser l'horloge
affichageHorloge.init(); // Initialiser l'affichage de l'horloge
affichageHorloge.set(LUMINOSITE_HEURE);
pinMode(BOUTON, INPUT); //Initialisation du bouton
Serial1.begin(UART_VITESSE_CONNEXION); //Initialisation du UART du système d alarme
Serial3.begin(UART_VITESSE_CONNEXION); //Initialisation du UART de la centrale
pinMode(PIN_LED, OUTPUT);
pinMode(SENSOR_PIN, INPUT);
} // initialisationDesAppareils
// Verifie si le systeme est en ligne
// retourne true si oui, false si le systeme est hors ligne
// ********************************************************
boolean connexionALaCentrale()
// ********************************************************
{
if (Serial3.available())
{
byte messageRecu = Serial3.read();
if (messageRecu == evenement_systeme_enligne)
{
#ifdef DEBUG
Serial << F(MSG_CONSOLE_EN_LIGNE);
Serial << F(MSG_CONSOLE_CONNEXION);
#endif
return true;
}
else
{
#ifdef DEBUG
Serial << F(MSG_CONSOLE_ERREUR_EVENEMENT);
#endif
return false;
}
}
else {
#ifdef DEBUG
Serial << F(MSG_CONSOLE_ERREUR_CONNEXION);
#endif
return false;
}
} // connexionALaCentrale
// Affiche l'heure sur le 4 digit display
// ********************************************************
void afficherTemps()
// ********************************************************
{
horloge.getTime();
static int pointClignote = 0;
affichageHorloge.point(pointClignote % 2); // Fait clignoté le : à tout les secondes
affichageHorloge.display(PREMIER_CHIFFRE_HORLOGE, horloge.hour / DIVISEUR_AFFICHAGE_TEMPS);
affichageHorloge.display(DEUXIEME_CHIFFRE_HORLOGE, horloge.hour % DIVISEUR_AFFICHAGE_TEMPS);
affichageHorloge.display(TROISIEME_CHIFFRE_HORLOGE, horloge.minute / DIVISEUR_AFFICHAGE_TEMPS);
affichageHorloge.display(QUATRIEME_CHIFFRE_HORLOGE, horloge.minute % DIVISEUR_AFFICHAGE_TEMPS);
static unsigned long ancreDansMilli = MILLI; // Calcul du 1seconde pour le clignotement des deux points
if (MILLI - ancreDansMilli >= UNE_SECONDE )
{
ancreDansMilli = MILLI;
pointClignote++;
}
} // afficherTemps
// Attend que l'utilisateur appuie sur le bouton pour mettre le systeme en ligne
// affiche le temps en continue
void attenteConnexion()
{
boolean boutonPeser = false;
while (boutonPeser == false)
{
afficherTemps(); // affiche le temps en continu
if (digitalRead(BOUTON))
{
delay(REBOND_BOUTON);
while (digitalRead(BOUTON)) // Attend que le bouton soit relacher
{
afficherTemps(); // affiche le temps en continu
delay(REBOND_BOUTON);
}
Serial1.write(evenement_systeme_enligne); // envoi de l'événement du système vers la centrale
#ifdef DEBUG
Serial << F(MSG_TENTATIVE_CONNEXION);
#endif
attendreSansDelai(UNE_SECONDE);
boutonPeser = true;
}
}
} // attenteConnexion
// Affiche l'écran d'ouverture du systeme
void afficherEcranOuverture()
{
ecranPrincipal.clear(); // efface l'écran
ecranPrincipal.setCursor(DEBUT_DE_LIGNE, ECRAN_PRINCIPALE_LIGNE1);
ecranPrincipal << F(MSG_NOM_PROJET);
ecranPrincipal.setCursor(DEBUT_DE_LIGNE, ECRAN_PRINCIPALE_LIGNE2);
ecranPrincipal << F(LIGNE_DANS_ECRAN_PRINCIPAL);
ecranPrincipal.setCursor(DEBUT_DE_LIGNE, ECRAN_PRINCIPALE_LIGNE3);
ecranPrincipal << F(MSG_VERSION);
ecranPrincipal.setCursor(DEBUT_DE_LIGNE, ECRAN_PRINCIPALE_LIGNE4);
ecranPrincipal << F(MSG_PAR_MATRICULE);
}// afficherEcranOuverture
// Afficher l'écran d'ouverture de la centrale
void afficherCentraleEcranOuverture()
{
ecranCentraleSurveillance.setTextColor(ILI9341_RED); // met la couleur rouge pour le titre
ecranCentraleSurveillance.setTextSize(TAILLE_TITRE_ECRAN_CENTRALE);
ecranCentraleSurveillance.setCursor(HAUTEUR_CENTRALE_TITRE, DEBUT_CENTRALE_TITRE);
ecranCentraleSurveillance << F(MSG_CENTRALE_TITRE);
ecranCentraleSurveillance.drawLine(DEBUT_DE_LIGNE, HAUTEUR_LIGNE_CENTRALE, LONGUEUR_LIGNE_CENTRALE, HAUTEUR_LIGNE_CENTRALE, ILI9341_WHITE);
ecranCentraleSurveillance.setTextColor(ILI9341_YELLOW); // Change la couleur du texte pour jaune
ecranCentraleSurveillance.setTextSize(TAILLE_TEXTE_ECRAN_CENTRALE);
ecranCentraleSurveillance.setCursor(DEBUT_DE_LIGNE, HAUTEUR_MSG_CONNEXION);
ecranCentraleSurveillance << F(MSG_CENTRALE_ATTENTE_CONNEXION);
ecranCentraleSurveillance.setCursor(DEBUT_DE_LIGNE, HAUTEUR_MSG_CONNEXION + 20); // +20 pour aller a la prochaine ligne
ecranCentraleSurveillance << F(MSG_CONNEXION_DE_QUI);
} // afficherCentraleEcranOuverture
// Afficher l'écran d'instruction sur l'écran du systeme
void afficherEcranInstruction()
{
ecranPrincipal.clear(); // efface l'écran
ecranPrincipal << F(INSTRUCTION_LIGNE1);
ecranPrincipal.setCursor(DEBUT_DE_LIGNE, ECRAN_PRINCIPALE_LIGNE2);
ecranPrincipal << F(INSTRUCTION_LIGNE2);
ecranPrincipal.setCursor(DEBUT_DE_LIGNE, ECRAN_PRINCIPALE_LIGNE3);
ecranPrincipal << F(INSTRUCTION_LIGNE3);
ecranPrincipal.setCursor(DEBUT_DE_LIGNE, ECRAN_PRINCIPALE_LIGNE4);
ecranPrincipal << F(INSTRUCTION_LIGNE4);
} //afficherEcranInstruction
// Affiche les écrans système en ligne
void afficherSystemeEnLigne()
{
// écran principale
ecranPrincipal.clear(); // efface l'écran
ecranPrincipal.setCursor(DEBUT_DE_LIGNE, ECRAN_PRINCIPALE_LIGNE1);
ecranPrincipal << F(ECRAN_EN_LIGNE_TEXTE_LIGNE1);
ecranPrincipal.setCursor(DEBUT_DE_LIGNE, ECRAN_PRINCIPALE_LIGNE2);
ecranPrincipal << F(LIGNE_DANS_ECRAN_PRINCIPAL);
ecranPrincipal.setCursor(DEBUT_DE_LIGNE, ECRAN_PRINCIPALE_LIGNE3);
ecranPrincipal << F(ECRAN_EN_LIGNE_TEXTE_LIGNE4);
ecranPrincipal.setCursor(DEBUT_DE_LIGNE, ECRAN_PRINCIPALE_LIGNE4);
ecranPrincipal << F(LIGNE_DANS_ECRAN_PRINCIPAL);
// écran de la centrale
ecranCentraleSurveillance.setTextColor(ILI9341_YELLOW); // changer de couleur pour jaune
ecranCentraleSurveillance.setTextSize(TAILLE_TEXTE_ECRAN_CENTRALE);
ecranCentraleSurveillance.setCursor(DEBUT_DE_LIGNE, HAUTEUR_MSG_CONFIRMATION);
ecranCentraleSurveillance << F(MESSAGE_CONFIRMATION_SYSTEME);
} // afficherSystemeEnLigne
// Affiche l'écran de systeme actif.
void afficherEcranActif()
{
// permet de transformer un int en nom de jour ou de mois(string)
char* nomDesJours[] = {"", "Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi"};
char* nomDesMois[] = {"", "JANVER ", "FEVRIER ", "MARS ", "AVRIL ", "MAI ", "JUIN ", "JUILLET ", "AOUT ", "SEPTEMBRE", "OCTOBRE ", "NOVEMBRE", "DECEMBRE "};
char chaineDeCaracteres[MAX_CHAR_CHAINE_LCD];
// Permet de calculer le temps en seconde/jour/minute/heure en relation avec l'ouverture du programme
unsigned long secondes = (MILLI / DIVISEUR_SECONDES) % MODULO_TEMPS;
unsigned long minutes = (MILLI / DIVISEUR_MINUTES) % MODULO_TEMPS;
unsigned long heures = (MILLI / DIVISEUR_HEURES) % MODULO_TEMPS;
unsigned long jours = (MILLI / DIVISEUR_JOURS);
// Affichage de l'écran principal de l'écran actif
ecranPrincipal.setCursor(DEBUT_DE_LIGNE, ECRAN_PRINCIPALE_LIGNE1);
ecranPrincipal << F(MSG_NOM_PROJET);
ecranPrincipal.setCursor(DEBUT_DE_LIGNE, ECRAN_PRINCIPALE_LIGNE2);
ecranPrincipal << F(LIGNE_DANS_ECRAN_PRINCIPAL);
ecranPrincipal.setCursor(DEBUT_DE_LIGNE, ECRAN_PRINCIPALE_LIGNE3);
snprintf(chaineDeCaracteres, MAX_CHAR_CHAINE_LCD, "%s %02d %s", nomDesJours[horloge.dayOfWeek], horloge.dayOfMonth, nomDesMois[horloge.month]);
ecranPrincipal << chaineDeCaracteres;
ecranPrincipal.setCursor(DEBUT_DE_LIGNE, ECRAN_PRINCIPALE_LIGNE4);
snprintf(chaineDeCaracteres, MAX_CHAR_CHAINE_LCD, "Actif: %03lu, %02lu:%02lu:%02lu", jours, heures, minutes, secondes);
ecranPrincipal << chaineDeCaracteres;
}// afficherEcranActif
// Affiche l'écran principale de demande de PIN
void afficherEcranPin()
{
ecranPrincipal.clear();
ecranPrincipal.cursor();
ecranPrincipal.setCursor(DEBUT_DE_LIGNE, ECRAN_PRINCIPALE_LIGNE1);
ecranPrincipal << F(MSG_ECRAN_PIN_MOVEMENT);
ecranPrincipal.setCursor(DEBUT_DE_LIGNE, ECRAN_PRINCIPALE_LIGNE2);
ecranPrincipal << LIGNE_DANS_ECRAN_PRINCIPAL;
ecranPrincipal.setCursor(DEBUT_DE_LIGNE, ECRAN_PRINCIPALE_LIGNE3);
ecranPrincipal << F(MSG_ECRAN_CODE);
ecranPrincipal.setCursor(DEBUT_DE_LIGNE, ECRAN_PRINCIPALE_LIGNE4);
ecranPrincipal << "_";
} // afficherEcranPin
// permet à la centrale de lire l'événement envoyé par le systeme d'alarme.
void lireEvenementAlarme()
{
byte messageRecu;
if (Serial3.available()) // Vérifie si l'on a recu un message
{
messageRecu = Serial3.read(); // Lis le message recu
}
switch (messageRecu) // Le compare avec les différents événements et applique la bonne fonction
{
case evenement_alarme_active:
evenementAlarmeActive();
break;
case evenement_detection_movement:
evenementDetectionMovement();
break;
case evenement_code_valide:
evenementAlarmeActive();
break;
case evenement_code_invalide:
evenementAlarmeActive();
break;
}
} // lireEvenementAlarme
// affiche l'écran Pin, lis le code entré et vérifie s'il est valide
void evenementDetectionMovement()
{
#ifdef DEBUG
Serial << F(MSG_CONSOLE_MOVEMENT); // Affiche l'événement dans la console
Serial << F(MSG_CONSOLE_LECTURE_CODE);
Serial << F(MSG_CONSOLE_DELAI_CODE);
#endif
int nombreEntrees = 0; // Calcul le nombre de chiffres entrés
int codeCalculator = 0; // Calcul le total des chiffres entrés
bool codeAttente = true;
afficherPageEcran(ecranPIN); // Affiche l'écran de pin
unsigned long tempsEcoule = MILLI; // Prend le temps présent pour calculer le temps maximum pour mettre le code
while (codeAttente == true) // Boucle dans laquelle on demande d'entrer le code
{
afficherTemps(); // Affiche le temps en continu
blinkCursor(); // Fait clignoté le curseur
char key = keypad.getKey();
if (MILLI - tempsEcoule > TEMPS_MAXIMUM_ENTRE_CODE )
{
Serial1.write(evenement_code_invalide); // Envoi l'événement code invalide à la centrale
attendreSansDelai(TEMPS_VALIDATION_CODE); // Attend pour assurer la réception du message
ecranPrincipal.noCursor(); // Enleve le curseur de l'écran
#ifdef DEBUG
Serial << F(MSG_CONSOLE_TEMPS_ECOULE); // Affiche que le temps est écouler dans la console
#endif
codeAttente = false; // Ferme la boucle d'attente de code
}
if (key != NO_KEY) // Vérifie si une touche a été enfoncé
{
nombreEntrees++; // Calcul le nombre de chiffres entrés
codeCalculator += key - CALCUL_CHIFFRE_CHAR_TO_INT; // Calcul le total des chiffres entrés
ecranPrincipal << key; // Imprime chaque entrées à l'écran de déboggage
#ifdef DEBUG
Serial << key - CALCUL_CHIFFRE_CHAR_TO_INT;
#endif
if (nombreEntrees == NOMBRE_CHIFFRE_CODE) // Vérifie si le nombre d'entrées maximum est atteind
{
#ifdef DEBUG
Serial << F(MSG_CONSOLE_CODE) << codeCalculator; // Affiche le code total dans la console
Serial << F(MSG_CONSOLE_VALIDATION_CODE); // Affiche un message d'attente de validation dans la console
#endif
if (codeCalculator == TOTAL_CODE_VALIDE) // Si le code est valide
{
Serial1.write(evenement_code_valide); // Envoi l'événement code valide à la centrale
attendreSansDelai(TEMPS_VALIDATION_CODE); // Attend pour être sur que le message s'est rendu
#ifdef DEBUG
Serial << F(MSG_CONSOLE_CODE_VALIDE); // Affiche validation de code valide dans la console
#endif
ecranPrincipal.noCursor(); // Éteind le curseur pour ne pas affecter les prochains écrans
codeAttente = false; // Ferme la boucle d'attente de code
}
else
{
Serial1.write(evenement_code_invalide); // Envoi l'événement code invalide à la centrale
attendreSansDelai(TEMPS_VALIDATION_CODE); // Attend pour assurer la réception du message
ecranPrincipal.noCursor(); // Éteind le curseur pour ne pas affecter les prochains écrans
#ifdef DEBUG
Serial << F(MSG_CONSOLE_CODE_INVALIDE); // Affiche que le code est invalide dans la console
#endif
codeAttente = false; // Ferme la boucle d'attente de code
}
}
}
}
} // evenementDetectionMovement
// active le systeme d'alarme. Vérifie en continue s'il y a du mouvement
void evenementAlarmeActive()
{
bool actif = true;
while (actif == true)
{
afficherTemps(); // Affiche le temps en continu
afficherPageEcran(ecranActif); // affiche la page écran actif
int mouvementDetecte = 0;
mouvementDetecte = digitalRead(SENSOR_PIN); // Vérifie s'il y a du mouvement
if (mouvementDetecte == HIGH) { // Si du mouvement est détecté
digitalWrite(PIN_LED, HIGH); // Allume la del
Serial1.write(evenement_detection_movement); // Envoi l'événement à la central
actif = false; // Sort du système actif
}
else
{
digitalWrite(PIN_LED, LOW); // éteind la del
if (mouvementDetecte == HIGH) {
mouvementDetecte = LOW;
}
}
}
} // evenementAlarmeActive
// Fais clicnoter le curseur de l'écran principale
void blinkCursor() {
if (MILLI / TEMPS_CURSOR_BLINK % 2 ) // Vérifie combien de temps est passé
{
ecranPrincipal.cursor(); // Allume le curseur
}
else
{
ecranPrincipal.noCursor(); // Éteind le curseur
}
} // blinkCursorForFun
// Permet de faire un delai sans affecter l'affichage du temps
void attendreSansDelai(int tempsDemande)
{
unsigned long ancreDansMilli = MILLI;
while (MILLI - ancreDansMilli < tempsDemande )
afficherTemps(); // Affiche le temps en continu durant le délai
} // attendreSansDelai