# 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 ``` **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 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 python3 -m venv ./venv source ./venv/bin/activate pip install --upgrade pip pip install -r requirements.txt ``` ### 3) Configuration série (.env) Créer un fichier `.env` à la racine du projet : ```bash cd ~/pont-bascule-connector nano .env ``` Exemple : ```env 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` ### 4) Droits port série (dialout) Vérifier les devices : ```bash 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 sudo usermod -aG dialout malio sudo reboot ``` --- ## Lancer l'API (mode manuel) ```bash source /home/malio/venv/bin/activate # ou ./venv/bin/activate uvicorn app.main:app --host 0.0.0.0 --port 8000 ``` Test local : ```bash curl http://127.0.0.1:8000/health ``` --- ## Lancer l'API au démarrage (systemd) ### 1) Créer le service ```bash sudo nano /etc/systemd/system/pont-bascule-api.service ``` Contenu (adapter les chemins si nécessaire) : ```ini [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 ``` ### 2) Activer et démarrer ```bash sudo systemctl daemon-reload sudo systemctl enable --now pont-bascule-api sudo systemctl status pont-bascule-api --no-pager ``` ### 3) Logs ```bash journalctl -u pont-bascule-api -f ``` --- ## API — Endpoints ### Santé **GET** `/health` Exemple : ```bash curl http://127.0.0.1:8000/health ``` ### Dernière réponse (debug) **GET** `/last` ### Envoi trame "Esclave" **POST** `/send/esclave` ```bash curl -X POST http://127.0.0.1:8000/send/esclave ``` ### Envoi trame "DSD" **POST** `/send/dsd` ```bash curl -X POST http://127.0.0.1:8000/send/dsd ``` ### Envoi trame custom (hex) **POST** `/send/custom` ```bash 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 { "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 tailscale status tailscale ip -4 ``` Exemple : IP Tailscale du Pi `100.122.43.54`. ### 2) Appeler l'API via Tailscale (simple) ```bash curl http://100.122.43.54:8000/health curl -X POST http://100.122.43.54:8000/send/esclave ``` ### 3) Option recommandée : exposer sans port avec `tailscale serve` Sur le Raspberry : ```bash sudo tailscale serve --http=80 localhost:8000 sudo tailscale serve status ``` Ensuite : ```bash curl http://100.122.43.54/health curl -X POST http://100.122.43.54/send/esclave ``` ### 4) SSH via Tailscale ```bash tailscale ssh malio@raspberrypi ``` --- ## Dépannage rapide ### API down ```bash sudo systemctl status pont-bascule-api --no-pager journalctl -u pont-bascule-api -n 100 --no-pager ``` ### Port série introuvable ```bash ls /dev/ttyUSB* /dev/ttyACM* 2>/dev/null dmesg | tail -n 50 ``` ### Permission refusée ```bash 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) 1. **Exposer l'API uniquement via Tailscale :** - faire écouter uvicorn en local seulement (`--host 127.0.0.1`) - utiliser `tailscale serve` comme reverse proxy 2. **Ajouter un token API** si besoin (header `Authorization`) 3. **Ajouter une route `/weight`** qui parse la chaîne `response_ascii` et renvoie `weight` + `unit` + `ticket` proprement. --- ## Autres recommandations (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": "..." } ``` ### 3) 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`.