Un Raspberry Pi lit les badges sur un lecteur Wiegand placé à la porte et pilote la serrure motorisée. Il gère aussi un bouton qui ouvre le garage en passant par l'atom. Voir aussi ouverture du garage et réseau du local.
Relevé du 13/10/2026.
Schéma réalisé avec Archify ; source : docs/serrure/archify/serrure.architecture.json dans le dépôt AdminLova.
| Élément | Valeur |
|---|---|
| Carte | Raspberry Pi 3 Model B+ |
| Système | Debian 12 (bookworm) |
| Nom / adresse | gatepi · 192.168.42.20 (réservée dans /etc/dnsmasq.d/static.conf de l'atom) |
| Réseau | Wi-Fi (l'Ethernet n'est pas branché) |
| Accès | ssh pi@gatepi ; mot de passe dans le keepass du bureau |
| 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.
Relevé dans le code (numérotation wiringPi convertie avec gpio readall).
| Fonction | Programme | BCM | Broche physique |
|---|---|---|---|
| Lecteur : Data0 (fil vert) | reader | GPIO17 | 11 |
| Lecteur : Data1 (fil blanc) | reader | GPIO18 | 12 |
| Lecteur : bip à chaque lecture (fil jaune) | reader | GPIO26 | 37 |
| Bips d'état (ouvert, fermé, erreur, garage) | gate | GPIO4 | 7 |
| Serrure : fermer (impulsion de 1 s) | gate close | GPIO14 (TxD) | 8 |
| Serrure : ouvrir (impulsion de 1 s) | gate open | GPIO15 (RxD) | 10 |
| Bouton garage (vers la masse) | garage_button | GPIO16 | 36 |
Les fils Data0 et Data1 du lecteur sont en 5 V : ils doivent être ramenés à 3,3 V avant les GPIO du Pi.
⚠️ Ne pas activer le port série du Pi (enable_uart=1 ou raspi-config → Serial) : la serrure est branchée sur les broches du port série (GPIO14/15), et les messages de démarrage la feraient basculer.
Dossier /home/pi/rpi-wiegand-reader, dérivé de rpi-wiegand-reader.
| Fichier | Rôle | Lancé par |
|---|---|---|
reader (reader.c) | Décode les badges et ajoute une ligne au fichier output | service wg-reader |
gate-service.sh | Vérifie le badge dans perm.list, ouvre ou ferme, prévient Framateam et le site | service gate-service |
gate (gate.c) | gate open, gate close, gate beepo / beepc / beepe / beepg | gate-service.sh, garage_button.sh |
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 | — |
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 dépôt d'origine.
#!/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
Read 26 bits ; l'identifiant du badge est le 7ᵉ champ.OPEN_DELAY (4 s) : même badge → ouverture, rien → fermeture.
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).
/* * 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(); } }
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.
/* * 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); } }
#!/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
# /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
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.
ssh pi@gatepi sudo systemctl status gate-service wg-reader journalctl -u gate-service -f # suivre les badges en direct cd ~/rpi-wiegand-reader sudo ./gate open # ouvrir la porte (attention : réel) sudo ./gate close # fermer la porte sudo tmux attach -t button # bouton garage (sortir : Ctrl-B puis D)
journalctl -u gate-service | tail → relever l'identifiant sur la ligne unauthorized code <ID>.<ID> <pseudo> dans ~/rpi-wiegand-reader/perm.list. C'est pris en compte immédiatement.~/garage/perm.list sur l'atom (voir ouverture du garage) : les deux listes sont séparées.gate-service.sh.output grossit sans fin (1,6 Mo) : prévoir une rotation.sudo timedatectl set-timezone Europe/Paris.