285 lines
14 KiB
Markdown
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
|
|
```
|