asso:si:serrure

Différences

Ci-dessous, les différences entre deux révisions de la page.

Lien vers cette vue comparative

Prochaine révision
Révision précédente
asso:si:serrure [2026/10/12 22:03] – Création : serrure de la porte (Raspberry Pi gatepi, lecteur Wiegand, bouton garage) nrineauasso:si:serrure [2026/10/12 22:15] (Version actuelle) – Ajout du code source du Pi (secrets masqués) : gate-service.sh, gate.c, bouton garage, services nrineau
Ligne 26: Ligne 26:
 | Accès | ''ssh pi@gatepi'' ; mot de passe dans le [[asso:si:keepass|keepass]] du bureau | | Accès | ''ssh pi@gatepi'' ; mot de passe dans le [[asso:si:keepass|keepass]] du bureau |
 | Lecteur | Lecteur de badges Wiegand 26 bits (fils rouge, noir, vert, blanc, jaune) | | Lecteur | Lecteur de badges Wiegand 26 bits (fils rouge, noir, vert, blanc, jaune) |
 +| Serrure | **HUA XING QIANG**, serrure électronique en applique, avec antenne (récepteur radio intégré) |
 +| Boîtier | Boîtier imprimé en 3D (rouge) fixé à côté de la porte ; le Pi est monté sur une plaque à pastilles |
 +| Alimentation | Convertisseur DC-DC réglable **LM2596** (bornier à vis en entrée) qui fournit le 5 V du Pi |
 +| Bouton garage | Petit bouton-poussoir sur le flanc droit du boîtier (fils bleu et violet) |
 +| Connexions | Deux connecteurs sur le côté gauche du boîtier pour les câbles du lecteur et de la serrure |
  
 L'ancienne adresse ''192.168.42.128'' n'est plus valable depuis que l'atom distribue les adresses. L'ancienne adresse ''192.168.42.128'' n'est plus valable depuis que l'atom distribue les adresses.
 +
 +==== Photos ====
 +
 +{{ :asso:si:serrure_boitier_ensemble.jpg?300|Boîtier du Raspberry Pi ouvert}}
 +{{ :asso:si:serrure_alimentation_dcdc.jpg?300|Convertisseur DC-DC LM2596 et Raspberry Pi}}
 +{{ :asso:si:serrure_connecteur_gpio.jpg?300|Câblage sur le connecteur GPIO}}
 +{{ :asso:si:serrure_huaxingqiang.jpg?300|Serrure HUA XING QIANG sur la porte}}
  
 ==== Brochage GPIO ==== ==== Brochage GPIO ====
Ligne 56: Ligne 68:
 | ''garage_button'' et ''garage_button.sh'' | Attend l'appui sur le bouton, joue un air, attend 8 s et demande à l'atom d'ouvrir le garage | ''/etc/rc.local'' (session tmux ''button'') | | ''garage_button'' et ''garage_button.sh'' | Attend l'appui sur le bouton, joue un air, attend 8 s et demande à l'atom d'ouvrir le garage | ''/etc/rc.local'' (session tmux ''button'') |
 | ''perm.list'' | Badges autorisés, une ligne ''ID pseudo'' | — | | ''perm.list'' | Badges autorisés, une ligne ''ID pseudo'' | — |
 +
 +==== Code source ====
 +
 +Version en service sur le Pi (''/home/pi/rpi-wiegand-reader''). Les adresses sensibles sont masquées : ''<url-webhook-framateam>'' (webhook entrant Framateam), ''<url-etat-ouvert>'' / ''<url-etat-ferme>'' (page d'état du site) et ''<chemin-local>'' (route secrète du garage sur l'atom). Les vraies valeurs sont dans les fichiers sur le Pi.
 +
 +''reader.c'' (décodage Wiegand) est identique au [[https://github.com/alperenguman/rpi-wiegand-reader/blob/master/reader.c|dépôt d'origine]].
 +
 +=== gate-service.sh : décision ouvrir / fermer ===
 +
 +<code bash>
 +#!/bin/bash
 +
 +cd $(dirname "$0")
 +LOG=./output
 +OPEN_DELAY=4
 +
 +validate_id() {
 + awk "/^$1 / { print \$2 }" ./perm.list
 +}
 +
 +gate_error() {
 + ./gate beepe
 +}
 +
 +gate_open() {
 + ./gate beepo
 + ./gate open
 + curl --connect-timeout 3 <url-etat-ouvert> || true
 + curl --connect-timeout 3 -i -X POST -H 'Content-Type: application/json' -d '{"text": "Local ouvert par @'"$1"' :)"}' <url-webhook-framateam> || true
 +}
 +
 +gate_close() {
 + ./gate beepc
 + ./gate close
 + curl --connect-timeout 3 <url-etat-ferme> || true
 + curl --connect-timeout 3 -i -X POST -H 'Content-Type: application/json' -d '{"text": "Local fermé par @'"$1"' :)"}' <url-webhook-framateam> || true
 +}
 +
 +# at boot make sure we are closed
 +./gate close
 +
 +while true; do
 + inotifywait -qq -e modify "$LOG"
 + date
 + l=$(tail -n 1 "$LOG")
 + if ! grep -q 'Read 26 bits' <<< "$l" ; then
 + echo "invalid read: $l"
 + continue
 + fi
 + c=$(awk '{ print $7 }' <<< "$l")
 + if [ -z "$c" ] ; then
 + echo "empty code"
 + continue
 + fi
 + name=$(validate_id "$c")
 + if [ -z "$name" ] ; then
 + echo "unauthorized code $c"
 + gate_error
 + continue
 + fi
 + echo "identified $name"
 + if inotifywait -qq -t "$OPEN_DELAY" -e modify "$LOG" ; then
 + l=$(tail -n 1 "$LOG")
 + if ! grep -q 'Read 26 bits' <<< "$l" ; then
 + echo "invalid read 2: $l"
 + gate_error
 + continue
 + fi
 + c2=$(awk '{ print $7 }' <<< "$l")
 + if [ "$c" != "$c2" ] ; then
 + echo "code 1 doesn't match code 2"
 + continue
 + fi
 + echo "calling open"
 + gate_open "$name"
 + else
 + echo "calling close"
 + gate_close "$name"
 + fi
 +done
 +</code>
 +
 +  * Une ligne est valide si elle contient ''Read 26 bits'' ; l'identifiant du badge est le 7ᵉ champ.
 +  * Après un badge autorisé, le script attend une 2ᵉ lecture pendant ''OPEN_DELAY'' (4 s) : même badge → ouverture, rien → fermeture.
 +
 +=== gate.c : commande de la serrure et des bips ===
 +
 +Compilation : ''gcc gate.c -o gate -lwiringPi -lpthread -lrt''. Les numéros de broches sont en numérotation **wiringPi** (15 = GPIO14, 16 = GPIO15, 7 = GPIO4).
 +
 +<code c>
 +/*
 + * linked with -lpthread -lwiringPi -lrt
 + */
 +
 +#include <stdio.h>
 +#include <stdlib.h>
 +#include <wiringPi.h>
 +#include <time.h>
 +#include <unistd.h>
 +#include <memory.h>
 +
 +#define PIN_C 15
 +#define PIN_O 16
 +#define PIN_B 7
 +
 +int gate_close() {
 +    pinMode(PIN_C, OUTPUT);
 +    digitalWrite(PIN_C, 1);
 +    delay(1000); 
 +    digitalWrite(PIN_C, 0);
 +    pinMode(PIN_C, INPUT);
 +    pullUpDnControl(PIN_C, PUD_OFF);
 +}
 +
 +int gate_open() {
 +    pinMode(PIN_O, OUTPUT);
 +    digitalWrite(PIN_O, 1);
 +    delay(1000); 
 +    digitalWrite(PIN_O, 0);
 +    pinMode(PIN_O, INPUT);
 +    pullUpDnControl(PIN_O, PUD_OFF);
 +}
 +
 +/* close sound */
 +int gate_beepc() {
 +    pinMode(PIN_B, OUTPUT);
 +    digitalWrite(PIN_B, 0);
 +    delay(150); 
 +    digitalWrite(PIN_B, 1);
 +    pinMode(PIN_B, INPUT);
 +    pullUpDnControl(PIN_B, PUD_OFF);
 +    delay(100); 
 +    pinMode(PIN_B, OUTPUT);
 +    digitalWrite(PIN_B, 0);
 +    delay(150); 
 +    digitalWrite(PIN_B, 1);
 +    pinMode(PIN_B, INPUT);
 +    pullUpDnControl(PIN_B, PUD_OFF);
 +}
 +
 +/* open sound */
 +int gate_beepo() {
 +    pinMode(PIN_B, OUTPUT);
 +    digitalWrite(PIN_B, 0);
 +    delay(1000); 
 +    digitalWrite(PIN_B, 1);
 +    pinMode(PIN_B, INPUT);
 +    pullUpDnControl(PIN_B, PUD_OFF);
 +}
 +
 +/* error sound */
 +int gate_beepe() {
 +    for (int i = 0; i < 10; i++) {
 +     pinMode(PIN_B, OUTPUT);
 +     digitalWrite(PIN_B, 0);
 +     delay(50); 
 +     digitalWrite(PIN_B, 1);
 +     pinMode(PIN_B, INPUT);
 +     pullUpDnControl(PIN_B, PUD_OFF);
 +     delay(50); 
 +     }
 +}
 +
 +/* garage sound */
 +int gate_beepg() {
 +    pinMode(PIN_B, OUTPUT);
 +
 +#define TADA(on, off)  digitalWrite(PIN_B, 0); delay(on); digitalWrite(PIN_B, 1); delay(off);
 +#define C TADA(80, 120)
 +#define CA TADA(120, 80)
 +
 +    for(int i = 0; i < 4; i++) {
 +     CA C C
 +     CA C C
 +     CA C C
 +     CA C CA C
 +    }
 +
 +
 +    pinMode(PIN_B, INPUT);
 +    pullUpDnControl(PIN_B, PUD_OFF);
 +}
 +
 +void main(int argc, char* argv[]) {
 +    wiringPiSetup() ;
 +
 +    if (argc >=2 && strncmp(argv[1], "open", 4) == 0) {
 +    gate_open(); 
 +    } else if (argc >=2 && strncmp(argv[1], "beepc", 5) == 0) {
 +    gate_beepc(); 
 +    } else if (argc >=2 && strncmp(argv[1], "beepo", 5) == 0) {
 +    gate_beepo(); 
 +    } else if (argc >=2 && strncmp(argv[1], "beepe", 5) == 0) {
 +    gate_beepe(); 
 +    } else if (argc >=2 && strncmp(argv[1], "beepg", 5) == 0) {
 +    gate_beepg(); 
 +    } else {
 +    gate_close(); 
 +    }
 +}
 +</code>
 +
 +=== garage_button.c et garage_button.sh : bouton garage ===
 +
 +''garage_button'' utilise la numérotation **BCM** (''wiringPiSetupGpio'') : la broche 16 est GPIO16. Le programme se termine dès que le bouton est maintenu 0,5 s ; le script joue alors l'air ''beepg'', attend 8 s et appelle l'atom.
 +
 +<code c>
 +/*
 + * linked with -lpthread -lwiringPi -lrt
 + */
 +
 +#include <stdio.h>
 +#include <stdlib.h>
 +#include <wiringPi.h>
 +#include <time.h>
 +#include <unistd.h>
 +#include <memory.h>
 +
 +#define PIN_BUTTON 16
 +
 +void main(int argc, char* argv[]) {
 +    wiringPiSetupGpio() ;
 +    pinMode(PIN_BUTTON, INPUT);
 +    pullUpDnControl(PIN_BUTTON, PUD_UP);
 +
 +    /* wait for the button to be released */
 +    while(digitalRead(PIN_BUTTON) == 0) {
 + delay(100);
 +    }
 +    //puts("waiting for the button...");
 +
 +    while(1) {
 +     if(digitalRead(PIN_BUTTON) == 0) {
 +     delay(500);
 + if(digitalRead(PIN_BUTTON) == 0) {
 + exit(0);
 + }
 +     }
 +     delay(100);
 + }
 +}
 +</code>
 +
 +<code bash>
 +#!/bin/bash
 +
 +cd /home/pi/rpi-wiegand-reader
 +
 +while true; do
 +    ./garage_button
 +    ./gate beepg
 +    echo "button pressed, wwaiting for 15s..."
 +    sleep 8
 +    echo "opening garage..."
 +    curl -v http://192.168.42.1:4280/<chemin-local> || true
 +done
 +</code>
 +
 +=== Services systemd ===
 +
 +<code ini>
 +# /etc/systemd/system/gate-service.service
 +[Unit]
 +Description=LOV gate service
 + 
 +[Service]
 +Type=simple
 +User=root
 +WorkingDirectory=/home/pi/rpi-wiegand-reader
 +ExecStart=/home/pi/rpi-wiegand-reader/gate-service.sh
 +Restart=on-failure
 + 
 +[Install]
 +WantedBy=multi-user.target
 +
 +# /etc/systemd/system/wg-reader.service
 +[Unit]
 +Description=Wiegand badge reader
 + 
 +[Service]
 +Type=simple
 +User=root
 +WorkingDirectory=/home/pi/rpi-wiegand-reader
 +ExecStart=/home/pi/rpi-wiegand-reader/reader
 +Restart=on-failure
 + 
 +[Install]
 +WantedBy=multi-user.target
 +</code>
 +
 +Le bouton garage n'est pas un service : il est lancé par ''/etc/rc.local'' avec ''tmux new -s button -d /home/pi/rpi-wiegand-reader/garage_button.sh''.
  
 ==== Commandes utiles ==== ==== Commandes utiles ====
Ligne 86: Ligne 389:
 ===== À vérifier sur place ===== ===== À vérifier sur place =====
  
-  * Modèle du lecteur Wiegand et de la serrure. +  * Modèle du lecteur Wiegand et tension de l'alimentation en entrée du convertisseur DC-DC. 
-  * Ce que commandent GPIO14/15 (relais, transistors ou télécommande de la serrure). L'Arduino du [[asso:si:si_garage|boîtier garage]] commande aussi une porte arrière : s'agit-il de la même serrure ?+  * Ce que commandent GPIO14/15 (entrées filaires de la serrure, relais ou transistors). La serrure a une antenne : l'Arduino du [[asso:si:si_garage|boîtier garage]], qui commande une « porte arrière » par la télécommande de la serrure (D10/D11), pilote probablement cette même serrure par radio. À confirmer.
   * Présence de l'adaptation 5 V → 3,3 V sur Data0/Data1.   * Présence de l'adaptation 5 V → 3,3 V sur Data0/Data1.
   * Fil jaune (bip) : sur GPIO26 (broche 37), GPIO4 (broche 7), ou les deux ?   * Fil jaune (bip) : sur GPIO26 (broche 37), GPIO4 (broche 7), ou les deux ?
-  * Emplacement exact du bouton garage.+  * Confirmer que le bouton du flanc droit est bien relié à GPIO16 (broche 36).
  
  • asso/si/serrure.1791842596.txt.gz
  • Dernière modification : 2026/10/12 22:03
  • de nrineau