ti-pote/docs/robot-client.md
ordinarthur 4ac7bd16d2 add doc
2026-04-01 17:10:18 +02:00

285 lines
14 KiB
Markdown

# Robot Client (`apps/robot-client`)
## Vue d'ensemble
Le **robot-client** est l'application TypeScript qui tourne directement sur le Raspberry Pi Zero 2 W du robot. C'est le "cerveau logiciel local" de Ti-Pote : il fait le pont entre le hardware (ESP32-S3 via UART) et le backend cloud (via WebSocket). En phase de développement, cette même app est simulée sur un PC de dev, permettant de travailler sans le robot physique.
### Positionnement dans l'architecture
```
┌──────────────┐ UART ┌──────────────────────┐ WebSocket ┌──────────────┐
│ ESP32-S3 │ ◄────────────►│ robot-client │ ◄─────────────►│ Backend │
│ (firmware) │ │ (Node.js / Pi) │ │ Cloud │
│ │ │ │ │ (NestJS) │
│ • Audio I2S │ │ • Orchestration │ │ │
│ • Servos │ │ • Wake word │ │ • STT/TTS │
│ • LEDs │ │ • WiFi management │ │ • LLM │
│ • Capteurs │ │ • Config locale │ │ • Services │
└──────────────┘ │ • Domotique bridge │ └──────────────┘
│ • OTA updates │
└──────────────────────┘
```
## Runtime et contraintes
| Paramètre | Choix |
|-----------|-------|
| Runtime | **Node.js** (LTS) |
| Langage | TypeScript strict |
| OS | Raspberry Pi OS Lite (headless) |
| RAM disponible | ~300 Mo (sur 512 Mo, après OS) |
| Stockage | microSD 32 Go |
| Réseau | WiFi 802.11 b/g/n + BLE 4.2 |
### Pourquoi Node.js ?
Node.js est le choix le plus cohérent avec le reste du stack (backend NestJS, simulateur). L'écosystème npm offre des librairies matures pour tout ce dont on a besoin : WebSocket (socket.io-client), serialport (UART), mDNS, BLE, HTTP serveur local. La consommation mémoire est gérable sur le Pi Zero 2 W si on fait attention aux dépendances.
## Architecture interne
Le robot-client suit lui aussi une architecture modulaire avec des services découplés. Chaque module peut être activé/désactivé selon la configuration du robot.
```
apps/robot-client/
├── src/
│ ├── main.ts # Point d'entrée, bootstrap
│ ├── config/
│ │ ├── robot.config.ts # Config locale (device ID, tokens, WiFi...)
│ │ └── hardware.config.ts # Config hardware (UART port, baudrate...)
│ │
│ ├── transport/
│ │ ├── cloud-socket.ts # Client WebSocket vers le backend cloud
│ │ ├── uart-bridge.ts # Communication UART avec l'ESP32
│ │ └── local-server.ts # Serveur HTTP/WS local pour le setup
│ │
│ ├── services/
│ │ ├── wake-word.service.ts # Détection wake word (OpenWakeWord via subprocess)
│ │ ├── wifi.service.ts # Gestion WiFi (scan, connect, AP mode)
│ │ ├── bluetooth.service.ts # BLE pour le provisioning
│ │ ├── camera.service.ts # Capture image (CSI camera)
│ │ ├── ota.service.ts # Mises à jour OTA (self + ESP32)
│ │ ├── health.service.ts # Monitoring, heartbeat, diagnostics
│ │ └── automation/
│ │ ├── automation.manager.ts # Gestionnaire de bridges domotiques
│ │ ├── bridges/
│ │ │ ├── bridge.interface.ts # Interface commune pour tous les bridges
│ │ │ ├── hue.bridge.ts # Philips Hue (API REST locale)
│ │ │ ├── homeassistant.bridge.ts # Home Assistant (WebSocket API)
│ │ │ └── mqtt.bridge.ts # MQTT générique (Zigbee2MQTT, etc.)
│ │ └── discovery.service.ts # Découverte mDNS des hubs domotiques
│ │
│ ├── setup/
│ │ ├── captive-portal.ts # AP mode + portail captif pour config WiFi
│ │ ├── ble-provisioning.ts # Provisioning BLE (méthode principale)
│ │ └── setup-flow.ts # Orchestration du flux de premier setup
│ │
│ └── utils/
│ ├── logger.ts # Logging local + remote
│ ├── network.ts # Utilitaires réseau (IP, connectivity check)
│ └── system.ts # Infos système (CPU, RAM, température)
├── scripts/
│ ├── install.sh # Installation des dépendances système (Node, OpenWakeWord...)
│ └── setup-ap.sh # Configuration du point d'accès WiFi
├── package.json
└── tsconfig.json
```
## Fonctionnalités clés
### 1. Communication cloud (WebSocket)
Le robot-client maintient une connexion WebSocket persistante avec le backend cloud. C'est le même protocole que le simulateur web — le backend ne fait pas la différence. En cas de déconnexion, le client tente automatiquement de se reconnecter avec un backoff exponentiel.
Le client gère aussi la bufferisation : si la connexion tombe pendant un échange audio, il peut informer l'utilisateur via les LEDs (passage en orange) et basculer en mode dégradé local.
### 2. Communication hardware (UART)
Le robot-client communique avec l'ESP32-S3 via le port série UART à 921600 baud. Il implémente le protocole de frames binaire décrit dans [hardware.md](hardware.md) : réception des chunks audio du micro (AUDIO_UP), envoi des chunks TTS (AUDIO_DOWN), commandes servo/LED, et heartbeat.
La librairie `serialport` npm est utilisée pour l'accès au port série.
### 3. Configuration WiFi
Le robot doit pouvoir être configuré sur n'importe quel réseau WiFi, même par un utilisateur non-technique. Deux mécanismes complémentaires sont prévus.
#### Méthode principale : BLE Provisioning
Le mécanisme de setup préféré. L'utilisateur ouvre l'app web/mobile Ti-Pote sur son téléphone et scanne les robots disponibles en BLE. L'app envoie les credentials WiFi (SSID + mot de passe) au robot via BLE. Le robot se connecte au WiFi et confirme la connexion.
Avantage : l'utilisateur n'a pas besoin de changer de réseau WiFi sur son téléphone pendant le setup.
```
Téléphone (App Ti-Pote) Robot (Pi Zero 2 W)
│ │
│ ─── BLE scan ──────────────────► │
│ ◄── BLE advertise (Ti-Pote-XXXX) │
│ │
│ ─── Connect BLE ──────────────► │
│ ─── Send WiFi credentials ────► │
│ │ → connecte au WiFi
│ ◄── WiFi connected (IP, status) │ → contacte le backend cloud
│ │
│ ─── Send device token ─────────► │
│ ◄── Setup complete ──────────── │
```
#### Méthode fallback : AP Mode + Captive Portal
Si le BLE n'est pas disponible (navigateur desktop, BLE désactivé), le robot peut créer son propre point d'accès WiFi.
1. Au premier démarrage (ou après un reset réseau), le Pi crée un hotspot WiFi : `Ti-Pote-XXXX` (où XXXX est un identifiant unique).
2. L'utilisateur se connecte à ce hotspot depuis son téléphone/PC.
3. Un captive portal s'ouvre automatiquement (ou manuellement à `http://192.168.4.1`).
4. Le portail affiche les réseaux WiFi disponibles. L'utilisateur choisit son réseau et entre le mot de passe.
5. Le robot se connecte au réseau, désactive le hotspot, et confirme via une page de succès.
```
┌────────────────────┐
│ Captive Portal │
│ (page web locale) │
│ │
│ ┌──────────────┐ │
│ │ WiFi: Maison │ │
│ │ MDP: ******* │ │
│ │ [Connecter] │ │
│ └──────────────┘ │
│ │
│ ► Réseau 1 │
│ ► Réseau 2 │
│ ► Réseau 3 │
└────────────────────┘
```
Technologies utilisées : `hostapd` (AP mode), `dnsmasq` (DHCP + DNS redirect), serveur HTTP local Express/Fastify pour le portail.
### 4. Mode simulation (développement)
En phase de développement, le robot-client tourne sur un PC de dev au lieu du Pi physique. Les modules hardware (UART, WiFi, BLE, caméra) sont remplacés par des mocks :
- **UART** → mockée par le simulateur web (qui joue le rôle de l'ESP32 : capture micro via WebRTC, playback via Web Audio API)
- **WiFi** → pas nécessaire en mode dev (le PC est déjà sur le réseau)
- **BLE** → désactivé en mode dev
- **Caméra** → remplacée par la webcam du PC ou des images statiques
Un flag d'environnement `ROBOT_MODE=simulator|physical` détermine quels modules sont chargés.
```bash
# Mode développement (sur PC, avec le simulateur web comme frontend)
ROBOT_MODE=simulator pnpm dev:robot
# Mode production (sur le Pi physique)
ROBOT_MODE=physical pnpm start:robot
```
### 5. Premier setup (onboarding)
Le flux de premier démarrage du robot :
```
┌─────────────────────────────────────────────────────────────────────┐
│ PREMIER DÉMARRAGE │
│ │
│ 1. Power on │
│ └─► LEDs en respiration violette (mode setup) │
│ │
│ 2. WiFi pas configuré ? │
│ ├─► Active BLE advertising ("Ti-Pote-XXXX") │
│ └─► Active AP mode en fallback (30s sans connexion BLE) │
│ │
│ 3. Credentials WiFi reçus (BLE ou captive portal) │
│ └─► Tente la connexion WiFi │
│ ├─► Succès → LEDs en vert │
│ └─► Échec → LEDs en rouge, retour étape 2 │
│ │
│ 4. Connexion au backend cloud │
│ └─► Envoie device_id + firmware version │
│ ├─► Nouveau device → le backend génère un device token │
│ └─► Device connu → reprise de session │
│ │
│ 5. Setup terminé │
│ └─► LEDs en bleu, robot prêt │
│ └─► "Bonjour, je suis Ti-Pote ! Je suis prêt." │
└─────────────────────────────────────────────────────────────────────┘
```
### 6. OTA Updates
Le robot-client supporte les mises à jour over-the-air :
- **Self-update** : le backend notifie le client qu'une nouvelle version est disponible. Le client télécharge le nouveau package, le vérifie (checksum), et se redéploie (process manager type PM2 ou systemd).
- **ESP32 update** : le Pi peut flasher l'ESP32 via UART (bootloader ESP32). Le firmware est téléchargé depuis le cloud et poussé vers l'ESP32.
- **Rollback** : si le nouveau firmware ne démarre pas correctement (pas de heartbeat dans les 60 secondes), le système revient à la version précédente.
## Transition depuis le simulateur
Le `apps/simulator` actuel (React + Vite) reste en place comme outil de développement. Il joue désormais le rôle d'interface visuelle pour le robot-client en mode simulateur :
```
Mode développement actuel :
Browser (simulator) ──WebSocket──► Backend cloud
Mode développement futur :
Browser (simulator) ──WebSocket──► robot-client (PC) ──WebSocket──► Backend cloud
(simule le Pi)
Mode production :
ESP32 (hardware) ───UART──► robot-client (Pi) ──WebSocket──► Backend cloud
```
Le simulateur web évolue pour devenir un "frontend de debug" du robot-client, plutôt qu'un client direct du backend. Cela permet de tester la logique complète du robot-client (wake word, bufferisation, mode dégradé, domotique) sans hardware.
## Dépendances système (Pi Zero 2 W)
```bash
# Runtime
node >= 20 LTS
npm / pnpm
# Wake word
python3
pip: openwakeword
# WiFi AP mode
hostapd
dnsmasq
# Bluetooth
bluez
noble (npm, bindings BLE)
# Port série
librairie native serialport (npm)
# Caméra
libcamera-apps (rpicam-still, rpicam-vid)
```
## Variables d'environnement
```bash
# Mode de fonctionnement
ROBOT_MODE=simulator|physical
# Backend cloud
CLOUD_URL=wss://api.tipote.dev/ws/robot
DEVICE_TOKEN=eyJ...
# Hardware (mode physical uniquement)
UART_PORT=/dev/ttyS0
UART_BAUDRATE=921600
# WiFi AP (mode physical uniquement)
AP_SSID=Ti-Pote-XXXX
AP_CHANNEL=6
# Domotique
HUE_BRIDGE_IP=192.168.1.x
HUE_API_KEY=xxxxx
HOMEASSISTANT_URL=http://homeassistant.local:8123
HOMEASSISTANT_TOKEN=xxxxx
MQTT_BROKER_URL=mqtt://192.168.1.x:1883
```