fix: Update du readme
This commit is contained in:
413
README.md
413
README.md
@@ -1,28 +1,27 @@
|
|||||||
# Pont Bascule Connector (Raspberry Pi) — FastAPI + Serial + Tailscale
|
# 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.
|
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
|
## Fonctionnement global
|
||||||
|
|
||||||
|
```
|
||||||
Client (PC / serveur / app) --HTTP--> Raspberry Pi (FastAPI)
|
Client (PC / serveur / app) --HTTP--> Raspberry Pi (FastAPI)
|
||||||
|
|
|
|
||||||
| 1 appel à la fois (lock)
|
| 1 appel à la fois (lock)
|
||||||
v
|
v
|
||||||
Port série (/dev/ttyUSB0)
|
Port série (/dev/ttyUSB0)
|
||||||
|
|
|
|
||||||
v
|
v
|
||||||
Pont bascule
|
Pont bascule
|
||||||
|
```
|
||||||
|
|
||||||
yaml
|
**Accès distant :**
|
||||||
Copier le code
|
|
||||||
|
|
||||||
Accès distant :
|
|
||||||
- via IP Tailscale `100.x.x.x` (VPN mesh)
|
- 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)
|
## Installation (Raspberry Pi)
|
||||||
|
|
||||||
### 1) Récupérer le projet
|
### 1) Récupérer le projet
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd ~
|
cd ~
|
||||||
git clone <URL_DE_TON_REPO> pont-bascule-connector
|
git clone <URL_DE_TON_REPO> pont-bascule-connector
|
||||||
cd pont-bascule-connector
|
cd pont-bascule-connector
|
||||||
2) Environnement Python
|
```
|
||||||
|
|
||||||
|
### 2) Environnement Python
|
||||||
|
|
||||||
Deux options :
|
Deux options :
|
||||||
|
|
||||||
Option A : venv global (recommandé si déjà en place)
|
#### Option A : venv global (recommandé si déjà en place)
|
||||||
|
|
||||||
bash
|
```bash
|
||||||
Copier le code
|
|
||||||
python3 -m venv /home/malio/venv
|
python3 -m venv /home/malio/venv
|
||||||
source /home/malio/venv/bin/activate
|
source /home/malio/venv/bin/activate
|
||||||
pip install --upgrade pip
|
pip install --upgrade pip
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
Option B : venv dans le projet
|
```
|
||||||
|
|
||||||
bash
|
#### Option B : venv dans le projet
|
||||||
Copier le code
|
|
||||||
|
```bash
|
||||||
python3 -m venv ./venv
|
python3 -m venv ./venv
|
||||||
source ./venv/bin/activate
|
source ./venv/bin/activate
|
||||||
pip install --upgrade pip
|
pip install --upgrade pip
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
Configuration série (.env)
|
```
|
||||||
Créer un fichier .env à la racine du projet :
|
|
||||||
|
|
||||||
bash
|
### 3) Configuration série (.env)
|
||||||
Copier le code
|
|
||||||
|
Créer un fichier `.env` à la racine du projet :
|
||||||
|
|
||||||
|
```bash
|
||||||
cd ~/pont-bascule-connector
|
cd ~/pont-bascule-connector
|
||||||
nano .env
|
nano .env
|
||||||
|
```
|
||||||
|
|
||||||
Exemple :
|
Exemple :
|
||||||
|
|
||||||
env
|
```env
|
||||||
Copier le code
|
|
||||||
SERIAL_PORT=/dev/ttyUSB0
|
SERIAL_PORT=/dev/ttyUSB0
|
||||||
SERIAL_BAUDRATE=9600
|
SERIAL_BAUDRATE=9600
|
||||||
SERIAL_TIMEOUT_S=1.0
|
SERIAL_TIMEOUT_S=1.0
|
||||||
SERIAL_OPEN_DELAY_S=2.0
|
SERIAL_OPEN_DELAY_S=2.0
|
||||||
SERIAL_POST_WRITE_DELAY_S=0.5
|
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 :
|
Vérifier les devices :
|
||||||
|
|
||||||
bash
|
```bash
|
||||||
Copier le code
|
|
||||||
ls /dev/ttyUSB* 2>/dev/null || true
|
ls /dev/ttyUSB* 2>/dev/null || true
|
||||||
ls /dev/ttyACM* 2>/dev/null || true
|
ls /dev/ttyACM* 2>/dev/null || true
|
||||||
dmesg | tail -n 30
|
dmesg | tail -n 30
|
||||||
Ajouter l’utilisateur au groupe dialout :
|
```
|
||||||
|
|
||||||
bash
|
Ajouter l'utilisateur au groupe `dialout` :
|
||||||
Copier le code
|
|
||||||
|
```bash
|
||||||
sudo usermod -aG dialout malio
|
sudo usermod -aG dialout malio
|
||||||
sudo reboot
|
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
|
source /home/malio/venv/bin/activate # ou ./venv/bin/activate
|
||||||
uvicorn app.main:app --host 0.0.0.0 --port 8000
|
uvicorn app.main:app --host 0.0.0.0 --port 8000
|
||||||
|
```
|
||||||
|
|
||||||
Test local :
|
Test local :
|
||||||
|
|
||||||
bash
|
```bash
|
||||||
Copier le code
|
|
||||||
curl http://127.0.0.1:8000/health
|
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
|
sudo nano /etc/systemd/system/pont-bascule-api.service
|
||||||
|
```
|
||||||
|
|
||||||
Contenu (adapter les chemins si nécessaire) :
|
Contenu (adapter les chemins si nécessaire) :
|
||||||
|
|
||||||
ini
|
```ini
|
||||||
Copier le code
|
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=Pont bascule API (FastAPI)
|
Description=Pont bascule API (FastAPI)
|
||||||
After=network-online.target tailscaled.service
|
After=network-online.target tailscaled.service
|
||||||
@@ -143,65 +158,78 @@ RestartSec=2
|
|||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
Activer et démarrer :
|
```
|
||||||
|
|
||||||
bash
|
### 2) Activer et démarrer
|
||||||
Copier le code
|
|
||||||
|
```bash
|
||||||
sudo systemctl daemon-reload
|
sudo systemctl daemon-reload
|
||||||
sudo systemctl enable --now pont-bascule-api
|
sudo systemctl enable --now pont-bascule-api
|
||||||
sudo systemctl status pont-bascule-api --no-pager
|
sudo systemctl status pont-bascule-api --no-pager
|
||||||
Logs :
|
```
|
||||||
|
|
||||||
bash
|
### 3) Logs
|
||||||
Copier le code
|
|
||||||
|
```bash
|
||||||
journalctl -u pont-bascule-api -f
|
journalctl -u pont-bascule-api -f
|
||||||
API — Endpoints
|
```
|
||||||
Santé
|
|
||||||
GET /health
|
---
|
||||||
|
|
||||||
|
## API — Endpoints
|
||||||
|
|
||||||
|
### Santé
|
||||||
|
|
||||||
|
**GET** `/health`
|
||||||
|
|
||||||
Exemple :
|
Exemple :
|
||||||
|
|
||||||
bash
|
```bash
|
||||||
Copier le code
|
|
||||||
curl http://127.0.0.1:8000/health
|
curl http://127.0.0.1:8000/health
|
||||||
Dernière réponse (debug)
|
```
|
||||||
GET /last
|
|
||||||
|
|
||||||
Envoi trame “Esclave”
|
### Dernière réponse (debug)
|
||||||
POST /send/esclave
|
|
||||||
|
|
||||||
bash
|
**GET** `/last`
|
||||||
Copier le code
|
|
||||||
|
### Envoi trame "Esclave"
|
||||||
|
|
||||||
|
**POST** `/send/esclave`
|
||||||
|
|
||||||
|
```bash
|
||||||
curl -X POST http://127.0.0.1:8000/send/esclave
|
curl -X POST http://127.0.0.1:8000/send/esclave
|
||||||
Envoi trame “DSD”
|
```
|
||||||
POST /send/dsd
|
|
||||||
|
|
||||||
bash
|
### Envoi trame "DSD"
|
||||||
Copier le code
|
|
||||||
|
**POST** `/send/dsd`
|
||||||
|
|
||||||
|
```bash
|
||||||
curl -X POST http://127.0.0.1:8000/send/dsd
|
curl -X POST http://127.0.0.1:8000/send/dsd
|
||||||
Envoi trame custom (hex)
|
```
|
||||||
POST /send/custom
|
|
||||||
|
|
||||||
bash
|
### Envoi trame custom (hex)
|
||||||
Copier le code
|
|
||||||
|
**POST** `/send/custom`
|
||||||
|
|
||||||
|
```bash
|
||||||
curl -X POST http://127.0.0.1:8000/send/custom \
|
curl -X POST http://127.0.0.1:8000/send/custom \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d '{"hex":"01 0D 0A"}'
|
-d '{"hex":"01 0D 0A"}'
|
||||||
Format de réponse
|
```
|
||||||
|
|
||||||
|
### Format de réponse
|
||||||
|
|
||||||
La réponse renvoie :
|
La réponse renvoie :
|
||||||
|
|
||||||
response_ascii : texte décodé ASCII (souvent le poids + infos)
|
- `response_ascii` : texte décodé ASCII (souvent le poids + infos)
|
||||||
|
- `response_hex` : trame brute
|
||||||
response_hex : trame brute
|
- `duration_ms` : durée de l'opération
|
||||||
|
- `error` : message d'erreur si problème
|
||||||
duration_ms : durée de l’opération
|
|
||||||
|
|
||||||
error : message d’erreur si problème
|
|
||||||
|
|
||||||
Exemple (indicatif) :
|
Exemple (indicatif) :
|
||||||
|
|
||||||
json
|
```json
|
||||||
Copier le code
|
|
||||||
{
|
{
|
||||||
"ok": true,
|
"ok": true,
|
||||||
"mode": "serial",
|
"mode": "serial",
|
||||||
@@ -213,105 +241,130 @@ Copier le code
|
|||||||
"duration_ms": 2600,
|
"duration_ms": 2600,
|
||||||
"error": null
|
"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**
|
Le port série ne doit pas être utilisé en concurrence.
|
||||||
Aujourd’hui tu exposes `0.0.0.0:8000` → accessible depuis le LAN.
|
|
||||||
Si tu veux “Tailscale only” (recommandé) :
|
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`
|
- Dans systemd : `--host 127.0.0.1`
|
||||||
- Et tu actives `tailscale serve --http=80 localhost:8000`
|
- Et tu actives `tailscale serve --http=80 localhost:8000`
|
||||||
|
|
||||||
2) **Ajouter une route `/weight`**
|
### 2) Ajouter une route `/weight`
|
||||||
Tu m’envoies 1 exemple de `response_ascii` (exact) et je te code un parseur robuste qui sort :
|
|
||||||
|
Tu m'envoies 1 exemple de `response_ascii` (exact) et je te code un parseur robuste qui sort :
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{"weight": 12.34, "unit": "kg", "ticket": "000123", "raw": "..."}
|
{
|
||||||
Port série stable (udev)
|
"weight": 12.34,
|
||||||
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.
|
"unit": "kg",
|
||||||
|
"ticket": "000123",
|
||||||
|
"raw": "..."
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Healthcheck matériel
|
### 3) Port série stable (udev)
|
||||||
Ajouter une route GET /serial/info qui vérifie :
|
|
||||||
|
|
||||||
port existe
|
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`.
|
||||||
|
|
||||||
user a accès
|
|
||||||
|
|
||||||
bridge non-busy
|
|
||||||
Reference in New Issue
Block a user