# Pont Bascule Connector (Raspberry Pi) — FastAPI + Serial + Tailscale API HTTP (FastAPI) qui pilote un pont bascule connecté en USB (port série) sur Raspberry Pi. Objectif : permettre à une application/serveur distant d’interroger le pont bascule via réseau (Tailscale), avec une contrainte stricte : **1 requête série à la fois**. --- ## Fonctionnement global Client (PC / serveur / app) --HTTP--> Raspberry Pi (FastAPI) | | 1 appel à la fois (lock) v Port série (/dev/ttyUSB0) | v Pont bascule yaml Copier le code Accès distant : - via IP Tailscale `100.x.x.x` (VPN mesh) - optionnellement via `tailscale serve` pour exposer l’API sur le port 80 sans `:8000` --- ## Prérequis ### Raspberry Pi - Raspberry Pi OS (Lite recommandé) - Python 3 - Accès SSH - Tailscale installé et connecté ### Matériel - Pont bascule branché en USB (ou via adaptateur USB↔RS232/RS485 selon le matériel) --- ## Installation (Raspberry Pi) ### 1) Récupérer le projet ```bash cd ~ git clone pont-bascule-connector cd pont-bascule-connector 2) Environnement Python Deux options : Option A : venv global (recommandé si déjà en place) bash Copier le code python3 -m venv /home/malio/venv source /home/malio/venv/bin/activate pip install --upgrade pip pip install -r requirements.txt Option B : venv dans le projet bash Copier le code python3 -m venv ./venv source ./venv/bin/activate pip install --upgrade pip pip install -r requirements.txt Configuration série (.env) Créer un fichier .env à la racine du projet : bash Copier le code cd ~/pont-bascule-connector nano .env Exemple : env Copier le code SERIAL_PORT=/dev/ttyUSB0 SERIAL_BAUDRATE=9600 SERIAL_TIMEOUT_S=1.0 SERIAL_OPEN_DELAY_S=2.0 SERIAL_POST_WRITE_DELAY_S=0.5 Notes importantes SERIAL_OPEN_DELAY_S=2.0 et SERIAL_POST_WRITE_DELAY_S=0.5 reproduisent le comportement du script Tkinter historique : attente 2s après ouverture du port envoi trame attente 0.5s lecture une seule fois de in_waiting Si ton port est /dev/ttyACM0, adapte SERIAL_PORT. Droits port série (dialout) Vérifier les devices : bash Copier le code ls /dev/ttyUSB* 2>/dev/null || true ls /dev/ttyACM* 2>/dev/null || true dmesg | tail -n 30 Ajouter l’utilisateur au groupe dialout : bash Copier le code sudo usermod -aG dialout malio sudo reboot Lancer l’API (mode manuel) bash Copier le code source /home/malio/venv/bin/activate # ou ./venv/bin/activate uvicorn app.main:app --host 0.0.0.0 --port 8000 Test local : bash Copier le code curl http://127.0.0.1:8000/health Lancer l’API au démarrage (systemd) Créer le service : bash Copier le code sudo nano /etc/systemd/system/pont-bascule-api.service Contenu (adapter les chemins si nécessaire) : ini Copier le code [Unit] Description=Pont bascule API (FastAPI) After=network-online.target tailscaled.service Wants=network-online.target [Service] User=malio WorkingDirectory=/home/malio/pont-bascule-connector EnvironmentFile=/home/malio/pont-bascule-connector/.env ExecStart=/home/malio/venv/bin/uvicorn app.main:app --host 0.0.0.0 --port 8000 Restart=always RestartSec=2 [Install] WantedBy=multi-user.target Activer et démarrer : bash Copier le code sudo systemctl daemon-reload sudo systemctl enable --now pont-bascule-api sudo systemctl status pont-bascule-api --no-pager Logs : bash Copier le code journalctl -u pont-bascule-api -f API — Endpoints Santé GET /health Exemple : bash Copier le code curl http://127.0.0.1:8000/health Dernière réponse (debug) GET /last Envoi trame “Esclave” POST /send/esclave bash Copier le code curl -X POST http://127.0.0.1:8000/send/esclave Envoi trame “DSD” POST /send/dsd bash Copier le code curl -X POST http://127.0.0.1:8000/send/dsd Envoi trame custom (hex) POST /send/custom bash Copier le code curl -X POST http://127.0.0.1:8000/send/custom \ -H "Content-Type: application/json" \ -d '{"hex":"01 0D 0A"}' Format de réponse La réponse renvoie : response_ascii : texte décodé ASCII (souvent le poids + infos) response_hex : trame brute duration_ms : durée de l’opération error : message d’erreur si problème Exemple (indicatif) : json Copier le code { "ok": true, "mode": "serial", "port": "/dev/ttyUSB0", "baudrate": 9600, "request_hex": "01 0D 0A", "response_hex": "30 30 31 32 2E 33 34 20 6B 67", "response_ascii": "0012.34 kg", "duration_ms": 2600, "error": null } Contrainte “1 appel à la fois” (important) Le port série ne doit pas être utilisé en concurrence. Si une requête est déjà en cours, l’API renvoie : HTTP 409 message BUSY Accès à distance via Tailscale 1) Vérifier Tailscale Sur le Raspberry : bash Copier le code tailscale status tailscale ip -4 Exemple : IP Tailscale du Pi 100.122.43.54. 2) Appeler l’API via Tailscale (simple) bash Copier le code curl http://100.122.43.54:8000/health curl -X POST http://100.122.43.54:8000/send/esclave 3) Option recommandé : exposer sans port avec tailscale serve Sur le Raspberry : bash Copier le code sudo tailscale serve --http=80 localhost:8000 sudo tailscale serve status Ensuite : bash Copier le code curl http://100.122.43.54/health curl -X POST http://100.122.43.54/send/esclave 4) SSH via Tailscale bash Copier le code tailscale ssh malio@raspberrypi Dépannage rapide API down bash Copier le code sudo systemctl status pont-bascule-api --no-pager journalctl -u pont-bascule-api -n 100 --no-pager Port série introuvable bash Copier le code ls /dev/ttyUSB* /dev/ttyACM* 2>/dev/null dmesg | tail -n 50 Permission refusée bash Copier le code groups # dialout doit apparaître Pas de réponse vérifier le baudrate vérifier le port /dev/ttyUSB0 vs /dev/ttyACM0 augmenter SERIAL_POST_WRITE_DELAY_S (ex: 1.0) si la réponse arrive lentement Sécurité recommandée (à faire) Exposer l’API uniquement via Tailscale : faire écouter uvicorn en local seulement (--host 127.0.0.1) utiliser tailscale serve comme reverse proxy Ajouter un token API si besoin (header Authorization) Ajouter une route /weight qui parse la chaîne response_ascii et renvoie weight + unit + ticket proprement. yaml Copier le code --- ## 3) Autres choses que je te recommande (vraiment utiles) 1) **Sécuriser l’API** Aujourd’hui tu exposes `0.0.0.0:8000` → accessible depuis le LAN. Si tu veux “Tailscale only” (recommandé) : - Dans systemd : `--host 127.0.0.1` - Et tu actives `tailscale serve --http=80 localhost:8000` 2) **Ajouter une route `/weight`** Tu m’envoies 1 exemple de `response_ascii` (exact) et je te code un parseur robuste qui sort : ```json {"weight": 12.34, "unit": "kg", "ticket": "000123", "raw": "..."} Port série stable (udev) Si ton port passe parfois de /dev/ttyUSB0 à /dev/ttyUSB1, on peut créer une règle udev pour avoir un nom fixe, genre /dev/pontbascule. Healthcheck matériel Ajouter une route GET /serial/info qui vérifie : port existe user a accès bridge non-busy