diff --git a/README.md b/README.md index 10738b0..7c25c9d 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,27 @@ # 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**. + +**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 + | + | 1 appel à la fois (lock) + v + Port série (/dev/ttyUSB0) + | + v + Pont bascule +``` -yaml -Copier le code - -Accès distant : +**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` +- optionnellement via `tailscale serve` pour exposer l'API sur le port 80 sans `:8000` --- @@ -42,92 +41,108 @@ Accès distant : ## Installation (Raspberry Pi) ### 1) Récupérer le projet + ```bash cd ~ git clone pont-bascule-connector cd pont-bascule-connector -2) Environnement Python +``` + +### 2) Environnement Python + Deux options : -Option A : venv global (recommandé si déjà en place) +#### Option A : venv global (recommandé si déjà en place) -bash -Copier le code +```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 -Copier le code +#### Option B : venv dans le projet + +```bash 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 +### 3) Configuration série (.env) + +Créer un fichier `.env` à la racine du projet : + +```bash cd ~/pont-bascule-connector nano .env +``` + Exemple : -env -Copier le code +```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 +**Notes importantes :** -envoi trame +- `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` -attente 0.5s +### 4) Droits port série (dialout) -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 +```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 -Copier le code +Ajouter l'utilisateur au groupe `dialout` : + +```bash sudo usermod -aG dialout malio sudo reboot -Lancer l’API (mode manuel) -bash -Copier le code +``` + +--- + +## 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 -Copier le code +```bash curl http://127.0.0.1:8000/health -Lancer l’API au démarrage (systemd) -Créer le service : +``` -bash -Copier le code +--- + +## 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 -Copier le code +```ini [Unit] Description=Pont bascule API (FastAPI) After=network-online.target tailscaled.service @@ -143,65 +158,78 @@ RestartSec=2 [Install] WantedBy=multi-user.target -Activer et démarrer : +``` -bash -Copier le code +### 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 -Logs : +``` -bash -Copier le code +### 3) Logs + +```bash journalctl -u pont-bascule-api -f -API — Endpoints -Santé -GET /health +``` + +--- + +## API — Endpoints + +### Santé + +**GET** `/health` Exemple : -bash -Copier le code +```bash curl http://127.0.0.1:8000/health -Dernière réponse (debug) -GET /last +``` -Envoi trame “Esclave” -POST /send/esclave +### Dernière réponse (debug) -bash -Copier le code +**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 -Copier le code +### 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 -Copier le code +### 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 +``` + +### 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 +- `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 +```json { "ok": true, "mode": "serial", @@ -213,105 +241,130 @@ Copier le code "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) +## Contrainte "1 appel à la fois" (important) -1) **Sécuriser l’API** -Aujourd’hui tu exposes `0.0.0.0:8000` → accessible depuis le LAN. -Si tu veux “Tailscale only” (recommandé) : +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 : +### 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. +{ + "weight": 12.34, + "unit": "kg", + "ticket": "000123", + "raw": "..." +} +``` -Healthcheck matériel -Ajouter une route GET /serial/info qui vérifie : +### 3) Port série stable (udev) -port existe - -user a accès - -bridge non-busy +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`. \ No newline at end of file