Pont Bascule Connector — FastAPI + Serial + (optionnel) Tailscale

API HTTP (FastAPI) qui pilote un pont bascule connecté en USB (port série) sur Raspberry Pi ou Linux.

Objectif : permettre à une application/serveur distant d'interroger le pont bascule via réseau, avec une contrainte stricte : 1 requête série à la fois.


Fonctionnement global

Client (PC / serveur / app) --HTTP--> Machine (FastAPI)
                                      |
                                      | 1 appel à la fois (lock)
                                      v
                                Port série (/dev/ttyUSB0)
                                      |
                                      v
                                 Pont bascule

Accès distant (optionnel) :

  • via IP Tailscale 100.x.x.x (VPN mesh)
  • ou autre VPN / reverse-proxy selon votre infra

Prérequis

Matériel

  • Pont bascule branché en USB (ou via adaptateur USB↔RS232/RS485 selon le matériel)

Système

  • Raspberry Pi OS / Debian / Ubuntu (autres Linux OK)
  • Python 3.9+ recommandé
  • Accès SSH
  • (optionnel) Tailscale installé et connecté

Installation

1) Récupérer le projet

cd ~
git clone gitea@gitea.malio.fr:MALIO-DEV/pont-bascule-connector.git
cd pont-bascule-connector

2) Installer les dépendances système (Debian/Ubuntu/Raspberry Pi OS)

sudo apt update
sudo apt install -y python3 python3-venv python3-pip git

Si vous êtes sur un autre OS, installez Python 3 + pip + venv via votre gestionnaire de paquets.

3) Installer les dépendances Python

python3 -m venv ./.venv
source ./.venv/bin/activate
pip install --upgrade pip
pip install -r requirements.txt

Hook git (commit-msg)

Le repo contient un hook pour valider le format des messages de commit.

Activation :

git config core.hooksPath .githooks

Format attendu :

<type>(<scope optionnel>) : <message>

Types autorisés : build, chore, ci, docs, feat, fix, perf, refactor, revert, style, test.


Configuration série (.env)

Le fichier .env est chargé automatiquement au démarrage (via python-dotenv).

Créer un fichier .env à la racine du projet :

cd ~/pont-bascule-connector
nano .env

Exemple (mode reel / port serie) :

APP_MODE=serial
APP_HOST=0.0.0.0
APP_PORT=8000
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

Exemple (mode mock pour dev, sans port serie) :

APP_MODE=mock
APP_HOST=0.0.0.0
APP_PORT=8000

Notes importantes :

  • APP_MODE=serial (defaut) utilise le port serie ; APP_MODE=mock simule les reponses pour le dev.
  • APP_HOST et APP_PORT permettent de changer l'interface d'ecoute et le port HTTP.
  • 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 votre port est /dev/ttyACM0, adaptez SERIAL_PORT

Droits port série (dialout)

Vérifier les devices :

ls /dev/ttyUSB* 2>/dev/null || true
ls /dev/ttyACM* 2>/dev/null || true
dmesg | tail -n 30

Ajouter l'utilisateur courant au groupe dialout :

sudo usermod -aG dialout "$USER"
newgrp dialout

Si besoin, reconnectez-vous (ou redémarrez) pour que les droits prennent effet.


Lancer l'API (mode manuel)

source ./.venv/bin/activate
uvicorn app.main:app --host 0.0.0.0 --port 8000

Test local :

curl http://127.0.0.1:8000/health

Lancer l'API au démarrage (systemd)

1) Créer le service

sudo nano /etc/systemd/system/pont-bascule-api.service

Contenu (adapter les chemins et l'utilisateur) :

[Unit]
Description=Pont bascule API (FastAPI)
After=network-online.target
Wants=network-online.target

[Service]
User=<votre_user>
WorkingDirectory=/home/<votre_user>/pont-bascule-connector
EnvironmentFile=/home/<votre_user>/pont-bascule-connector/.env
ExecStart=/home/<votre_user>/pont-bascule-connector/.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

sudo systemctl daemon-reload
sudo systemctl enable --now pont-bascule-api
sudo systemctl status pont-bascule-api --no-pager

3) Logs

journalctl -u pont-bascule-api -f

API — Endpoints

Santé

GET /health

Exemple :

curl http://127.0.0.1:8000/health

Dernière réponse (debug)

GET /last

Envoi trame "Esclave"

POST /send/esclave

curl -X POST http://127.0.0.1:8000/send/esclave

Envoi trame "DSD"

POST /send/dsd

curl -X POST http://127.0.0.1:8000/send/dsd

Envoi trame custom (hex)

POST /send/custom

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) :

{
  "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 (optionnel)

1) Vérifier Tailscale

Sur la machine :

tailscale status
tailscale ip -4

Exemple : IP Tailscale 100.122.43.54.

2) Appeler l'API via Tailscale (simple)

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 la machine :

sudo tailscale serve --http=80 localhost:8000
sudo tailscale serve status

Ensuite :

curl http://100.122.43.54/health
curl -X POST http://100.122.43.54/send/esclave

Dépannage rapide

API down

sudo systemctl status pont-bascule-api --no-pager
journalctl -u pont-bascule-api -n 100 --no-pager

Port série introuvable

ls /dev/ttyUSB* /dev/ttyACM* 2>/dev/null
dmesg | tail -n 50

Permission refusée

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 (optionnel)

  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.


Recommandations utiles

Port série stable (udev)

Si votre port passe parfois de /dev/ttyUSB0 à /dev/ttyUSB1, on peut créer une règle udev pour avoir un nom fixe (ex: /dev/pontbascule).

Description
No description provided
Readme 52 KiB
Languages
Python 68%
Makefile 24%
Shell 8%