Différences
Ci-dessous, les différences entre deux révisions de la page.
| Les deux révisions précédentes Révision précédente Prochaine révision | Révision précédente | ||
| asso:si:si_garage [2023/02/20 20:34] – fredervish | asso:si:si_garage [2026/10/12 22:04] (Version actuelle) – La route locale est aussi appelée par le bouton garage du Pi de la porte (gatepi) nrineau | ||
|---|---|---|---|
| Ligne 1: | Ligne 1: | ||
| - | ===== Système d' | + | ===== Système d' |
| - | Le système d' | + | Le boîtier rose fixé près du garage |
| - | Pré-requis: | + | * **Framateam (Mattermost)** : commandes slash dans les canaux autorisés → ouvrir le garage, ouvrir / fermer la porte arrière ; |
| + | * **Badge RFID** : un badge de la liste ouvre le **garage** uniquement. | ||
| + | |||
| + | {{ : | ||
| + | |||
| + | //Schéma réalisé avec [[https:// | ||
| + | |||
| + | ==== Électronique ==== | ||
| + | |||
| + | {{ : | ||
| + | |||
| + | Schéma Fritzing modifiable : {{: | ||
| + | |||
| + | {{ : | ||
| + | {{ : | ||
| + | {{ : | ||
| + | |||
| + | ^ Broche Arduino ^ Fonction ^ Fonctionnement ^ | ||
| + | | D9 | Bouton de la télécommande du garage | Mise à la masse pendant 800 ms = appui (ouvre **ou** ferme) | | ||
| + | | D10 | Télécommande de la serrure : **ouvrir** | Impulsion 5 V de 2,5 s | | ||
| + | | D11 | Télécommande de la serrure : **fermer** | Impulsion 5 V de 2,5 s | | ||
| + | | USB | Liaison avec l'Atom | Série 9600 bauds, puce CH340 | | ||
| + | |||
| + | La télécommande du garage (PCB vert HXQ920BT-4) n'a plus de pile : elle est alimentée par l' | ||
| + | |||
| + | Anciennement, | ||
| + | |||
| + | ==== Firmware Arduino (LOV garage opener v3) ==== | ||
| + | |||
| + | Protocole : un caractère sur le port série (9600 bauds). '' | ||
| + | |||
| + | <code cpp> | ||
| + | #include < | ||
| + | |||
| + | Servo servo; | ||
| + | |||
| + | void setup() { | ||
| + | pinMode(10, OUTPUT); | ||
| + | digitalWrite(10, | ||
| + | pinMode(11, OUTPUT); | ||
| + | digitalWrite(11, | ||
| + | |||
| + | Serial.begin(9600); | ||
| + | Serial.println(" | ||
| + | |||
| + | / | ||
| + | servo.write(0); | ||
| + | delay(400); | ||
| + | servo.detach(); | ||
| + | pinMode(9, INPUT); | ||
| + | digitalWrite(9, | ||
| + | } | ||
| + | |||
| + | void loop() { | ||
| + | int c = 0; | ||
| + | c = Serial.read(); | ||
| + | if (c == ' | ||
| + | Serial.println(" | ||
| + | / | ||
| + | servo.write(30); | ||
| + | delay(1000); | ||
| + | servo.write(0); | ||
| + | delay(400); | ||
| + | servo.detach(); | ||
| + | pinMode(9, OUTPUT); | ||
| + | digitalWrite(9, | ||
| + | delay(800); | ||
| + | pinMode(9, INPUT); | ||
| + | digitalWrite(9, | ||
| + | Serial.println(" | ||
| + | } else if (c == ' | ||
| + | //Serial data for shorting the remote controller of backdoor | ||
| + | Serial.println(" | ||
| + | digitalWrite(10, | ||
| + | delay(2500); | ||
| + | digitalWrite(10, | ||
| + | Serial.println(" | ||
| + | } else if (c == ' | ||
| + | //Serial data for shorting the remote controller of backdoor | ||
| + | Serial.println(" | ||
| + | digitalWrite(11, | ||
| + | delay(2500); | ||
| + | digitalWrite(11, | ||
| + | Serial.println(" | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | Vérifié le 10/10/2026 : ce code correspond au firmware présent sur la carte. | ||
| + | |||
| + | ==== Serveur (Atom) ==== | ||
| + | |||
| + | Pré-requis : | ||
| < | < | ||
| - | apt install python3-flask python3-serial | + | apt install python3-flask python3-serial |
| adduser lov dialout | adduser lov dialout | ||
| </ | </ | ||
| - | Code du serveur | + | Fichiers dans ''/ |
| - | < | + | |
| - | from flask import Flask | + | ^ Fichier ^ Rôle ^ |
| + | | '' | ||
| + | | '' | ||
| + | | '' | ||
| + | | '' | ||
| + | | '' | ||
| + | |||
| + | **nginx** écoute sur le port **4280** et ne transmet au serveur Flask que les URL secrètes (voir ''/ | ||
| + | |||
| + | ^ Route ^ Déclenchée par ^ Action ^ Contrôle ^ | ||
| + | | ouverture locale (GET) | '' | ||
| + | | garage (POST) | commande slash Framateam | garage | canal garage ou canal porte | | ||
| + | | entrer (POST) | commande slash Framateam | ouvre la porte arrière + message « X a ouvert le local » | canal porte | | ||
| + | | sortir (POST) | commande slash Framateam | ferme la porte arrière + message « X a fermé le local » | canal porte | | ||
| + | |||
| + | ==== Code du serveur | ||
| + | |||
| + | Version en service sur l'atom ('' | ||
| + | |||
| + | < | ||
| + | from flask import Flask, request, jsonify | ||
| app = Flask(__name__) | app = Flask(__name__) | ||
| import serial | import serial | ||
| - | ARDUINO = '/ | + | ARDUINO = '/ |
| + | GARAGE_CHANNEL = '< | ||
| + | PORTE_CHANNEL = '< | ||
| - | @app.route('/ | + | def send_command(c): |
| - | def open(): | + | |
| try: | try: | ||
| with serial.Serial(port=ARDUINO, | with serial.Serial(port=ARDUINO, | ||
| Ligne 24: | Ligne 137: | ||
| if ' | if ' | ||
| return " | return " | ||
| - | f.write(b' | + | f.write(c) |
| f.flush() | f.flush() | ||
| resp = f.readline().decode(' | resp = f.readline().decode(' | ||
| Ligne 30: | Ligne 143: | ||
| except serial.serialutil.SerialException as e: | except serial.serialutil.SerialException as e: | ||
| return str(e) | return str(e) | ||
| + | |||
| + | # uncondtionnally open the garage (for local usage) | ||
| + | @app.route('/< | ||
| + | def local_open(): | ||
| + | return send_command(b' | ||
| + | |||
| + | @app.route('/< | ||
| + | def open(): | ||
| + | user_name = request.form.get(' | ||
| + | channel_id = request.form.get(' | ||
| + | if channel_id != GARAGE_CHANNEL and channel_id != PORTE_CHANNEL: | ||
| + | return "this is not the command you are looking for" | ||
| + | return send_command(b' | ||
| + | |||
| + | @app.route('/< | ||
| + | def enter(): | ||
| + | user_name = request.form.get(' | ||
| + | channel_id = request.form.get(' | ||
| + | if channel_id != PORTE_CHANNEL: | ||
| + | return "this is not the command you are looking for" | ||
| + | r = send_command(b' | ||
| + | return jsonify({ | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | }] | ||
| + | }) | ||
| + | |||
| + | @app.route('/< | ||
| + | def leave(): | ||
| + | user_name = request.form.get(' | ||
| + | channel_id = request.form.get(' | ||
| + | if channel_id != PORTE_CHANNEL: | ||
| + | return "this is not the command you are looking for" | ||
| + | r = send_command(b' | ||
| + | return jsonify({ | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | }] | ||
| + | }) | ||
| if __name__ == ' | if __name__ == ' | ||
| - | app.run(host=' | + | app.run(host=' |
| </ | </ | ||
| - | < | + | Fonctionnement : |
| - | #include <Servo.h> | + | * '' |
| + | * '' | ||
| + | * '' | ||
| + | * '' | ||
| + | * Le serveur n' | ||
| - | Servo servo; | + | ==== Code du lecteur de badges (gate-service.sh) ==== |
| - | void setup() { | + | <code bash> |
| - | | + | #!/bin/bash |
| - | Serial.println(" | + | |
| - | servo.attach(9); | + | cd $(dirname "$0") |
| - | servo.write(0); | + | LOG=./rfid.log |
| - | | + | |
| - | servo.detach(); | + | |
| - | } | + | |
| - | void loop() { | + | (while true ; do python3 rfid.py ; sleep 1 ; done) & |
| - | int c = 0; | + | |
| - | c = Serial.read(); | + | validate_id() { |
| - | | + | awk "/^$1 / { print \$2 }" ./perm.list |
| - | | + | |
| - | | + | |
| - | Serial.println("opening..."); | + | |
| - | servo.attach(9); | + | |
| - | servo.write(30); | + | |
| - | delay(1000); | + | |
| - | servo.write(0); | + | |
| - | delay(400); | + | |
| - | servo.detach(); | + | |
| - | Serial.println(" | + | |
| } | } | ||
| + | |||
| + | while true; do | ||
| + | inotifywait -qq -e modify " | ||
| + | date | ||
| + | l=$(tail -n 1 " | ||
| + | if ! egrep -q ' | ||
| + | echo " | ||
| + | continue | ||
| + | fi | ||
| + | c=$(cut -d ' ' -f 2 <<< | ||
| + | name=$(validate_id " | ||
| + | if [ -z " | ||
| + | echo " | ||
| + | continue | ||
| + | fi | ||
| + | echo " | ||
| + | curl http:// | ||
| + | done | ||
| </ | </ | ||
| - | Pour lancer | + | Fonctionnement : |
| + | * lance '' | ||
| + | * '' | ||
| + | * à chaque nouvelle ligne de '' | ||
| + | * si le badge est autorisé, il appelle la route locale | ||
| + | |||
| + | Lancement au démarrage, | ||
| < | < | ||
| - | sudo -u lov -g lov / | + | tmux new -s garage -d 'sudo -u lov -g lov / |
| + | tmux new -s rfid -d /home/lov/garage/ | ||
| </ | </ | ||
| + | |||
| + | Pour voir ce qui se passe : '' | ||
| + | |||
| + | ==== Ajouter un badge ==== | ||
| + | |||
| + | - Passer le badge sur le lecteur. | ||
| + | - Sur l'Atom : '' | ||
| + | - Ajouter la ligne ''< | ||
| + | |||
| + | ==== Dépannage ==== | ||
| + | |||
| + | * '' | ||
| + | * '' | ||
| + | * Le badge ne fait rien : lecteur débranché ('' | ||
| + | * L' | ||
| + | |||
| + | Documentation technique détaillée (rétro-ingénierie, | ||
| + | |||