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

6.8 KiB
Raw Blame History

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

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