diff --git a/composants/byPanda/alarme.py b/composants/byPanda/alarme.py new file mode 100644 index 0000000..3797530 --- /dev/null +++ b/composants/byPanda/alarme.py @@ -0,0 +1,246 @@ +import time +import RPi.GPIO as GPIO + +GPIO.setmode(GPIO.BOARD) +GPIO.setwarnings(False) + + +class SystemeAlarme: + def __init__(self): + """ + Initialise les composants liés à l'alarme. + + Cette classe gère uniquement la logique locale de sécurité : + - le capteur PIR + - le buzzer + - la LED RGB de statut + - le clavier 4x4 + + Elle ne dépend d'aucune autre partie du projet. + """ + + # ----------------------------- + # Définition des pins physiques + # ----------------------------- + self.pinPir = 22 + self.pinBuzzer = 12 + + self.pinLedRouge = 11 + self.pinLedVerte = 13 + self.pinLedBleue = 15 + + # Clavier 4x4 + # 4 lignes + 4 colonnes + self.lignes = [29, 31, 33, 35] + self.colonnes = [37, 32, 36, 38] + + # Disposition classique d'un clavier 4x4 + self.touches = [ + ["1", "2", "3", "A"], + ["4", "5", "6", "B"], + ["7", "8", "9", "C"], + ["*", "0", "#", "D"] + ] + + # ----------------------------- + # Variables de fonctionnement + # ----------------------------- + self.codeSecret = "1234" + self.codeSaisi = "" + + # Etats possibles : + # - desarme + # - arme + # - alarme + self.etat = "desarme" + + # Anti-rebond clavier + self.derniereLecture = 0 + self.delaiLecture = 0.25 + + self.initialiserGPIO() + self.mettreAJourEtat() + + def initialiserGPIO(self): + """Configure les broches du Raspberry Pi pour l'alarme.""" + GPIO.setup(self.pinPir, GPIO.IN) + GPIO.setup(self.pinBuzzer, GPIO.OUT, initial=GPIO.LOW) + + GPIO.setup(self.pinLedRouge, GPIO.OUT, initial=GPIO.LOW) + GPIO.setup(self.pinLedVerte, GPIO.OUT, initial=GPIO.LOW) + GPIO.setup(self.pinLedBleue, GPIO.OUT, initial=GPIO.LOW) + + for pin in self.lignes: + GPIO.setup(pin, GPIO.OUT, initial=GPIO.LOW) + + for pin in self.colonnes: + GPIO.setup(pin, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) + + def definirCouleur(self, rouge, vert, bleu): + """ + Allume la LED RGB selon la couleur voulue. + + Paramètres : + - rouge : True / False + - vert : True / False + - bleu : True / False + """ + GPIO.output(self.pinLedRouge, GPIO.HIGH if rouge else GPIO.LOW) + GPIO.output(self.pinLedVerte, GPIO.HIGH if vert else GPIO.LOW) + GPIO.output(self.pinLedBleue, GPIO.HIGH if bleu else GPIO.LOW) + + def mettreAJourEtat(self): + """ + Met à jour les sorties selon l'état actuel du système. + + - desarme : LED verte, buzzer éteint + - arme : LED bleue, buzzer éteint + - alarme : LED rouge, buzzer allumé + """ + if self.etat == "desarme": + self.definirCouleur(False, True, False) + GPIO.output(self.pinBuzzer, GPIO.LOW) + + elif self.etat == "arme": + self.definirCouleur(False, False, True) + GPIO.output(self.pinBuzzer, GPIO.LOW) + + elif self.etat == "alarme": + self.definirCouleur(True, False, False) + GPIO.output(self.pinBuzzer, GPIO.HIGH) + + def armer(self): + """Passe le système en mode armé.""" + self.etat = "arme" + self.codeSaisi = "" + self.mettreAJourEtat() + print("Alarme activée.") + + def desarmer(self): + """Passe le système en mode désarmé.""" + self.etat = "desarme" + self.codeSaisi = "" + self.mettreAJourEtat() + print("Alarme désactivée.") + + def declencherAlarme(self): + """ + Déclenche l'alarme si un mouvement est détecté alors + que le système est armé. + """ + if self.etat != "alarme": + self.etat = "alarme" + self.codeSaisi = "" + self.mettreAJourEtat() + print("Intrusion détectée : alarme déclenchée.") + + def lireClavier(self): + """ + Scanne le clavier 4x4. + + Retour : + - la touche détectée + - None si aucune touche n'est pressée + """ + maintenant = time.time() + + if maintenant - self.derniereLecture < self.delaiLecture: + return None + + for indexLigne, ligne in enumerate(self.lignes): + GPIO.output(ligne, GPIO.HIGH) + + for indexColonne, colonne in enumerate(self.colonnes): + if GPIO.input(colonne) == GPIO.HIGH: + GPIO.output(ligne, GPIO.LOW) + self.derniereLecture = maintenant + + # Petite attente pour éviter la lecture multiple + time.sleep(0.05) + + return self.touches[indexLigne][indexColonne] + + GPIO.output(ligne, GPIO.LOW) + + return None + + def validerCode(self): + """ + Vérifie le code saisi. + + Si le code est correct : + - alarme désarmée -> armée + - alarme armée -> désarmée + - alarme déclenchée -> désarmée + + Si le code est faux : + - on efface la saisie + """ + if self.codeSaisi == self.codeSecret: + if self.etat == "desarme": + self.armer() + else: + self.desarmer() + else: + print("Code incorrect.") + self.codeSaisi = "" + + def traiterClavier(self, touche): + """ + Gère la logique du clavier : + - chiffres : ajout au code saisi + - * : efface la saisie + - # : valide le code + """ + if touche is None: + return + + print("Touche appuyée :", touche) + + if touche == "*": + self.codeSaisi = "" + print("Saisie effacée.") + return + + if touche == "#": + self.validerCode() + return + + if touche.isdigit(): + if len(self.codeSaisi) < 8: + self.codeSaisi += touche + print("Code en cours :", "*" * len(self.codeSaisi)) + + def surveillerPIR(self): + """ + Vérifie le capteur de mouvement. + + Si un mouvement est détecté alors que l'alarme est armée, + on passe en état d'alarme. + """ + mouvement = GPIO.input(self.pinPir) == GPIO.HIGH + + if self.etat == "arme" and mouvement: + self.declencherAlarme() + + def mettreAJour(self): + """ + Fonction appelée en boucle dans le programme principal. + + Elle : + - lit le clavier + - traite la touche appuyée + - surveille le PIR + - synchronise LED et buzzer avec l'état courant + """ + touche = self.lireClavier() + self.traiterClavier(touche) + self.surveillerPIR() + self.mettreAJourEtat() + + def cleanup(self): + """ + Remet les sorties dans un état propre à la fermeture. + """ + GPIO.output(self.pinBuzzer, GPIO.LOW) + self.definirCouleur(False, False, False) diff --git a/composants/byPanda/board1main.py b/composants/byPanda/board1main.py new file mode 100644 index 0000000..4f8e05f --- /dev/null +++ b/composants/byPanda/board1main.py @@ -0,0 +1,31 @@ +import time +from alarme import SystemeAlarme +from porterfid import SystemePorteRFID + +# ------------------------------------------------------------ +# board1main.py +# ------------------------------------------------------------ +# Ce fichier lance uniquement la logique locale de la board 1. +# Il ne dépend pas du site web, de la base de données ni de Flask. +# Son rôle est simplement de faire tourner : +# - le système d'alarme +# - le système de porte RFID +# ------------------------------------------------------------ + +alarme = SystemeAlarme() +porte = SystemePorteRFID() + +try: + while True: + # Mise à jour des deux modules locaux + alarme.mettreAJour() + porte.mettreAJour() + time.sleep(0.05) + +except KeyboardInterrupt: + print("\nArrêt manuel du programme.") + +finally: + # On remet les sorties dans un état propre avant de quitter + alarme.cleanup() + porte.cleanup() diff --git a/composants/byPanda/porterfid.py b/composants/byPanda/porterfid.py new file mode 100644 index 0000000..991b72a --- /dev/null +++ b/composants/byPanda/porterfid.py @@ -0,0 +1,110 @@ +import time +import threading +import RPi.GPIO as GPIO +from mfrc522 import SimpleMFRC522 + +GPIO.setmode(GPIO.BOARD) +GPIO.setwarnings(False) + + +class SystemePorteRFID: + def __init__(self): + """ + Initialise le système local d'accès par RFID. + + Cette classe gère uniquement : + - le lecteur RFID + - la LED qui symbolise l'ouverture de porte + + Elle ne pilote pas l'alarme et ne dépend pas du site web. + """ + self.pinLed = 40 + GPIO.setup(self.pinLed, GPIO.OUT, initial=GPIO.LOW) + + self.lecteur = SimpleMFRC522() + + # Remplacer ces identifiants par les vrais UID autorisés + self.badgesAutorises = { + 123456789012: "Admin", + 987654321098: "Utilisateur" + } + + self.badgeDetecte = None + self.derniereOuverture = 0 + self.delaiEntreScans = 2 + + # Ce thread sert à ne pas bloquer la boucle principale + # pendant l'attente d'un badge RFID. + self.threadLecture = threading.Thread(target=self.boucleLectureRFID, daemon=True) + self.threadLecture.start() + + def boucleLectureRFID(self): + """ + Boucle secondaire qui attend les badges RFID. + + La méthode read() est bloquante selon la bibliothèque utilisée, + donc on la place dans un thread séparé pour que le reste du + programme continue de tourner normalement. + """ + while True: + try: + badgeId, _ = self.lecteur.read() + self.badgeDetecte = badgeId + except Exception as erreur: + print("Erreur RFID :", erreur) + time.sleep(1) + + def badgeAutorise(self, badgeId): + """Retourne True si le badge est autorisé.""" + return badgeId in self.badgesAutorises + + def ouvrirPorte(self): + """ + Simule l'ouverture de la porte avec la LED. + + Ici la LED reste allumée 2 secondes pour représenter + l'accès autorisé. + """ + print("Porte ouverte.") + GPIO.output(self.pinLed, GPIO.HIGH) + time.sleep(2) + GPIO.output(self.pinLed, GPIO.LOW) + + def traiterBadge(self, badgeId): + """ + Vérifie si le badge présenté est autorisé. + Si oui, on ouvre la porte. + Sinon, on affiche un refus. + """ + print("Badge détecté :", badgeId) + + if self.badgeAutorise(badgeId): + print("Accès autorisé pour", self.badgesAutorises[badgeId]) + self.ouvrirPorte() + else: + print("Accès refusé.") + + def mettreAJour(self): + """ + Fonction appelée en boucle dans le programme principal. + + Elle récupère le dernier badge lu par le thread RFID + et le traite si nécessaire. + """ + if self.badgeDetecte is None: + return + + if time.time() - self.derniereOuverture < self.delaiEntreScans: + return + + badgeId = self.badgeDetecte + self.badgeDetecte = None + self.derniereOuverture = time.time() + + self.traiterBadge(badgeId) + + def cleanup(self): + """ + Eteint la LED de porte lors de la fermeture du programme. + """ + GPIO.output(self.pinLed, GPIO.LOW)