Files
pont-bascule-connector/README.md
matthieu fce468cf76 feat(api): FastAPI bridge for pont bascule via serial + Tailscale access
- Add FastAPI service exposing /health, /last, /send/esclave, /send/dsd, /send/custom
- Implement SerialBridge aligned with legacy Tkinter behavior (open delay 2s, post-write 0.5s, read in_waiting once)
- Enforce single in-flight serial request (non-blocking lock, returns 409 BUSY)
- Add environment-based serial configuration (.env + systemd EnvironmentFile)
- Document installation, systemd service, and Tailscale usage (direct IP and tailscale serve)
2026-01-09 15:34:47 +00:00

318 lines
6.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 dinterroger 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 lAPI 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 <URL_DE_TON_REPO> 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 lutilisateur au groupe dialout :
bash
Copier le code
sudo usermod -aG dialout malio
sudo reboot
Lancer lAPI (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 lAPI 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 lopération
error : message derreur 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, lAPI 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 lAPI 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 lAPI 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 lAPI**
Aujourdhui 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 menvoies 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