docs: add French translation of README
CI / firmware-native (push) Successful in 1m10s
qa-cicd-environments / qa-kxkm-s3-build (push) Successful in 5m28s
qa-cicd-environments / qa-sim-host (push) Successful in 1m38s
qa-cicd-environments / qa-kxkm-s3-memory-budget (push) Successful in 18m36s

Context: the project README is written in English while the client
(KompleX KapharnaüM) and field technicians are French-speaking.

Approach: full translation of README.md into README.fr.md, keeping
tables, badges, code blocks and anchors identical.

Changes:
- Add README.fr.md (complete French translation)
- Cross-link to the English version at the top of the file

Impact: French-speaking users get first-class documentation without
diverging from the English source of truth.
This commit is contained in:
L'électron rare
2026-07-04 12:58:53 +02:00
parent f55093d6fe
commit bbb2102166
+381
View File
@@ -0,0 +1,381 @@
<p align="center">
<img src="BMU.jpeg" alt="PCB BMU" width="480" />
</p>
<h1 align="center">KXKM Batterie Parallelator</h1>
<p align="center">
<strong>Battery Management Unit pour packs 24-30V en parallèle</strong><br>
Jusqu'à 32 batteries &middot; ESP32-S3 &middot; BLE + MQTT + InfluxDB &middot; App iOS compagnon
</p>
<p align="center">
<a href="https://github.com/KomplexKapharnaum/KXKM_Batterie_Parallelator/actions/workflows/ci.yml">
<img src="https://github.com/KomplexKapharnaum/KXKM_Batterie_Parallelator/actions/workflows/ci.yml/badge.svg" alt="CI" />
</a>
<a href="https://github.com/KomplexKapharnaum/KXKM_Batterie_Parallelator/actions/workflows/sim-host-tests.yml">
<img src="https://github.com/KomplexKapharnaum/KXKM_Batterie_Parallelator/actions/workflows/sim-host-tests.yml/badge.svg" alt="QA" />
</a>
<img src="https://img.shields.io/badge/ESP--IDF-v5.4-blue" alt="ESP-IDF" />
<img src="https://img.shields.io/badge/license-GPLv3-green" alt="Licence" />
<img src="https://img.shields.io/badge/tests-26%20passed-brightgreen" alt="Tests" />
</p>
> 🇬🇧 English version: [README.md](README.md)
---
## Vue d'ensemble
Le **KXKM Batterie Parallelator** est une Battery Management Unit (BMU) embarquée qui met en parallèle, en toute sécurité, plusieurs packs de batteries (24-30V) pour des installations scéniques hors réseau. Chaque batterie est surveillée individuellement (tension, courant, température) et peut être déconnectée en quelques microsecondes via des interrupteurs MOSFET.
Conçu par [L'Electron Rare](https://lelectronrare.fr) pour [KompleX KapharnaüM](https://komplex-kapharnaum.net) (Villeurbanne, France) — une compagnie d'arts vivants qui déploie de la scénographie numérique dans l'espace public, sans alimentation secteur.
### Fonctionnalités clés
| Fonctionnalité | Description |
|----------------|-------------|
| Protection par batterie | Détection sous/sur-tension, surintensité, déséquilibre |
| Reconnexion automatique | Backoff exponentiel avec verrouillage permanent après 5 défauts |
| Équilibrage doux (soft-balancing) | Duty-cycling avec mesure opportuniste de R_int |
| Intégration Victron | Instant Readout BLE + émulation GATT SmartShunt + scanner de périphériques |
| Interface tactile | Écran LVGL 320x240 avec grille batteries, SOH, graphiques |
| App iOS compagnon | Supervision temps réel en BLE, cache hors-ligne, accès par rôles |
| Télémétrie cloud | MQTT + InfluxDB + Grafana avec persistance hors-ligne |
| Mises à jour OTA | Double partition avec rollback automatique |
---
## Démarrage rapide
```bash
# Firmware ESP-IDF (production)
cd firmware-idf
source ~/esp/esp-idf/export.sh
idf.py build
idf.py -p /dev/cu.usbmodem* flash monitor
# PlatformIO (Arduino legacy)
pio run -e kxkm-s3-16MB --target upload
# Lancer tous les tests (aucun matériel requis)
pio test -e sim-host # 13 tests PlatformIO
cd firmware-idf && idf.py build # 13 tests host ESP-IDF
# Stack cloud (serveur kxkm-ai)
cd kxkm-api && cp .env.example .env && docker compose up -d
```
---
## Architecture
```
┌──────────────────────────┐
│ ESP32-S3-BOX-3 │
│ Écran LVGL 320x240 │
│ NimBLE + WiFi │
└─────────┬────────────────┘
│ I2C 50kHz
┌───────────────┼───────────────┐
│ │ │
┌─────▼─────┐ ┌─────▼─────┐ ┌─────▼─────┐
│ TCA9535 │ │ TCA9535 │ │ ...×8 │
│ 4× MOSFET │ │ 4× LED │ │ │
└─────┬─────┘ └───────────┘ └───────────┘
┌─────▼─────┐
│ INA237 ×4 │ ← par TCA (tension + courant)
└───────────┘
┌──────────────────────────────────────────────────────┐
│ Stack Cloud │
│ BMU ──MQTT──► Mosquitto ──► Telegraf ──► InfluxDB │
│ │ │
│ iPhone ◄──BLE──► BMU Grafana ◄───────────┘ │
│ iPhone ◄──REST──► FastAPI ◄─────────────────┘ │
└──────────────────────────────────────────────────────┘
```
### Composants firmware (ESP-IDF, 25 modules)
| Composant | Rôle |
|-----------|------|
| `bmu_protection` | Machine à états : 5 états, 5 critères, verrouillage permanent |
| `bmu_balancer` | Soft-balancing par duty-cycling + mesure R_int |
| `bmu_ina237` | 16× moniteurs de puissance INA237 (shunt 2mΩ) |
| `bmu_tca9535` | 8× expanders GPIO (interrupteurs + LEDs) |
| `bmu_ble` | NimBLE 4 services GATT + Victron SmartShunt |
| `bmu_ble_victron` | Victron Instant Readout (advertising chiffré AES-CTR) |
| `bmu_ble_victron_scan` | Scanner BLE central pour périphériques Victron |
| `bmu_display` | LVGL : grille batteries, infos pack, SOH, graphiques, swipe |
| `bmu_influx` + `_store` | Client InfluxDB + persistance hors-ligne FAT/SD |
| `bmu_mqtt` | ESP-MQTT avec identifiants d'authentification |
| `bmu_vedirect` | Parseur UART Victron VE.Direct (chargeur solaire) |
| `bmu_rint` | Mesure de résistance interne par impulsion |
| `bmu_web` | HTTP + WebSocket avec auth par token + rate limiting |
| `bmu_config` | Config runtime NVS + clés périphériques Victron |
| `bmu_storage` | NVS, FAT, SPIFFS, carte SD, USB MSC |
| `bmu_ota` | Mise à jour firmware OTA avec rollback |
### Logique de protection
```
Nb_switch < 5 → Reconnexion immédiate dès que la condition disparaît
Nb_switch == 5 → Délai de 10 secondes avant reconnexion
Nb_switch > 5 → Verrouillage permanent (LED rouge clignotante) jusqu'au redémarrage
```
| Seuil | Défaut | Configurable |
|-------|--------|:------------:|
| Tension min | 24 000 mV | via Kconfig + NVS |
| Tension max | 30 000 mV | via Kconfig + NVS |
| Courant max | 10 000 mA | via Kconfig + NVS |
| Déséquilibre | 1 000 mV | via Kconfig + NVS |
| Délai de reconnexion | 10 000 ms | via Kconfig |
| Limite de commutations | 5 | via Kconfig |
### Comportement des LEDs
| État | LED |
|------|-----|
| Connectée | Vert fixe |
| Déconnectée | Rouge fixe |
| Erreur | **Rouge clignotant** (~1 Hz) |
| Verrouillée | Rouge fixe |
---
## App iOS compagnon
<p align="center">
<strong>KXKM BMU</strong> &mdash; SwiftUI + CoreBluetooth
</p>
| Fonctionnalité | Transport |
|----------------|-----------|
| Tableau de bord batteries temps réel | BLE (poll 2s) |
| Détail batterie + graphique de tension | BLE |
| Interrupteur ON/OFF + reset | BLE (selon rôle) |
| Config protection (pas de 0,025 V/A) | BLE |
| Config WiFi de la BMU | BLE |
| Journal d'audit + filtrage | SQLDelight local |
| Mode hors-ligne avec indicateur de cache | Bascule automatique |
| Tableau de bord SOH + R_int | BLE + REST |
| Scan des périphériques Victron | BLE passif |
**3 rôles** : Admin (complet), Technicien (contrôle), Observateur (lecture seule) — PIN + Face ID.
**Code** : `iosApp/` (Xcode, CoreBluetooth) + `kxkm-bmu-app/` (module Kotlin partagé KMP)
---
## Intégration BLE Victron
La BMU interagit avec l'écosystème Victron via trois mécanismes :
| Mécanisme | Direction | Protocole |
|-----------|-----------|-----------|
| Instant Readout | BMU → VictronConnect | Advertising chiffré AES-CTR (PID 0xA389) |
| GATT SmartShunt | BMU → tout client BLE | 9 caractéristiques en lecture seule (V, I, SOC, Ah, TTG, T, alarme) |
| Scanner de périphériques | Périphériques Victron → BMU | Scan BLE passif, déchiffrement AES, cache 8 périphériques |
Types d'enregistrements Victron supportés : Solar (0x01), Battery (0x02), Inverter (0x03), DC-DC (0x04).
---
## Infrastructure cloud
Stack Docker sur le serveur `kxkm-ai` :
| Service | Port | Rôle |
|---------|------|------|
| Mosquitto | 1883, 9001 | Broker MQTT (authentifié) |
| InfluxDB 2.7 | 8086 | Stockage séries temporelles (org=kxkm, bucket=bmu) |
| Telegraf | - | Passerelle MQTT → InfluxDB |
| FastAPI | 8400 | API REST (sync, historique, audit) |
| Grafana 11 | 3001 | 3 tableaux de bord (live, flotte, solaire) |
**Installation** : `cp .env.example .env` — renseigner tous les secrets (clé API, token InfluxDB, identifiants MQTT, mot de passe Grafana, origines CORS). Tous les secrets passent par des variables d'environnement `${...}`, `chmod 600 .env`.
**Résilience hors-ligne** : quand le WiFi/InfluxDB est injoignable, la télémétrie est persistée en flash FAT interne (`/fatfs/influx/`) avec rotation sur 2 fichiers (512 Ko chacun). Rejeu automatique à la reconnexion.
---
## Tests
**26 tests au total** — aucun matériel requis.
### PlatformIO sim-host (13 tests)
```bash
pio test -e sim-host
```
| Suite | Tests | Couvre |
|-------|:-----:|--------|
| `test_protection` | 10 | Machine à états V/I/déséquilibre/verrouillage |
| `test_battery_route_validation` | - | Bornes d'index + vérifications d'état |
| `test_influx_buffer_codec` | - | Encodage line-protocol InfluxDB |
| `test_web_mutation_rate_limit` | 3 | Fenêtre glissante + multi-IP |
| `test_web_route_security` | 9 | Token en temps constant + Bearer |
| `test_ws_auth_flow` | 7 | Auth combinée + rate limit |
| `test_mqtt_influx_codec` | 3 | JSON MQTT + parsing des topics |
| `test_emulation_bench` | - | Benchmark d'émulation |
### Tests host ESP-IDF (13 tests)
```bash
cd firmware-idf && idf.py build
```
| Suite | Tests | Couvre |
|-------|:-----:|--------|
| `test_protection` | 13 | Machine à états complète (ESP-IDF) |
| `test_victron_gatt` | 8 | Encodage GATT (V, SOC, alarme, TTG, T) |
| `test_victron_scan` | 5 | Parsing des trames, expiration, MAC, CID |
| `test_ble_victron` | - | Encodage des trames batterie/solaire |
| `test_vedirect_parser` | - | Parsing des trames VE.Direct |
| `test_rint` | - | Calcul de R_int |
| `test_config_labels` | - | Gestion des étiquettes batterie |
| `test_vrm_topics` | - | Génération des topics VRM |
### Pipelines CI/CD
| Pipeline | Déclencheur | Gate |
|----------|-------------|------|
| `ci.yml` | push/PR | 13 tests sim-host au vert |
| `sim-host-tests.yml` | push/PR | + build S3 + RAM ≤75% + Flash ≤85% |
| `esp-idf-ci.yml` | push/PR | + tests host ESP-IDF + flash ≤85% des 2 Mo OTA |
**Occupation flash actuelle** : 81 % (1,69 Mo / 2 Mo de partition OTA) — 19 % de marge.
---
## Audit de sécurité
**Tous les constats résolus** au 2026-04-07.
<details>
<summary><strong>Phase 1 — Migration (mars 2026) : 7/7 résolus</strong></summary>
| ID | Constat | Statut |
|----|---------|--------|
| CRIT-001 | Appels de fonctions obsolètes | ✅ |
| CRIT-002 | Variable globale Nb_INA non fiable | ✅ |
| CRIT-003 | Accès I2C sans mutex | ✅ |
| CRIT-004 | Initialisation de mutex manquante | ✅ |
| HIGH-005 | Race condition WebSocket V/I | ✅ |
| HIGH-006 | Race condition reconnect_time | ✅ |
| HIGH-007 | Globales non initialisées | ✅ |
</details>
<details>
<summary><strong>Phase 2 — Audit approfondi (mars-avril 2026) : 13/13 résolus</strong></summary>
| ID | Sévérité | Constat | Correctif |
|----|----------|---------|-----------|
| CRIT-A | Critique | Incohérence d'unités mV/V | ESP-IDF : mV partout |
| CRIT-B | Critique | Déséquilibre comparé à la config au lieu du max de flotte | bmu_protection |
| CRIT-C | Critique | Deadlock dans le switch web | bmu_web |
| CRIT-D | Critique | Routes non authentifiées | bmu_web_security |
| HIGH-1 | Haute | Surintensité négative ignorée | Facteur bidirectionnel |
| HIGH-2 | Haute | WebSocket non authentifié | Token sur la première trame |
| HIGH-3 | Haute | XSS dans /log | Vérification null cJSON |
| HIGH-4 | Haute | MQTT en clair | Auth Mosquitto + .env |
| HIGH-5 | Haute | Changement de vitesse I2C non protégé | I2CLockGuard |
| HIGH-7 | Haute | battery_voltages[] public | state_mutex |
| HIGH-8 | Haute | Crash de la tâche Influx si la SD échoue | Repli influx_store |
| MED-1 | Moyenne | Pas de verrouillage permanent (F08) | BMU_STATE_LOCKED |
| MED-010 | Moyenne | Harmonisation mV/V | mV partout |
</details>
<details>
<summary><strong>Audit infrastructure (avril 2026) : 5/5 résolus</strong></summary>
| Constat | Correctif |
|---------|-----------|
| Accès anonyme Mosquitto | Auth + fichier de mots de passe |
| Secrets en dur dans docker-compose | Tout via .env (chmod 600) |
| Token InfluxDB par défaut | Régénéré (44 caractères aléatoires) |
| CORS allow_origins=["*"] | Restreint via variable d'environnement |
| Perte de données hors-ligne | Persistance FAT/SD + rejeu |
</details>
---
## Matériel
### Révisions du PCB
| Version | Statut | Fichiers |
|---------|--------|----------|
| BMU v1 | Fabriquée | `hardware/PCB/` |
| BMU v2 | Prête pour production (ERC 0, BOM 107) | `hardware/pcb-bmu-v2/` |
### Circuits intégrés clés
| CI | Boîtier | Rôle | Adresse I2C |
|----|---------|------|-------------|
| INA237 | VSSOP-10 | Moniteur de puissance (V/I/P) | 0x40-0x4F (16 max) |
| TCA9535 | TSSOP-24 | Expander GPIO (interrupteurs + LEDs) | 0x20-0x27 (8 max) |
| ISO1540 | SOIC-8 | Isolateur I2C galvanique | - |
| IRF4905 | D2PAK | MOSFET canal P (55V, 74A) | - |
### Cartographie I2C
```
TCA_0 (0x20) → INA 0x40-0x43 → Batteries 1-4
TCA_1 (0x21) → INA 0x44-0x47 → Batteries 5-8
TCA_2 (0x22) → INA 0x48-0x4B → Batteries 9-12
TCA_3 (0x23) → INA 0x4C-0x4F → Batteries 13-16
```
Contrainte topologique : `Nb_TCA × 4 == Nb_INA` — toute incohérence déclenche le fail-safe (tout OFF).
---
## Structure du projet
```
KXKM_Batterie_Parallelator/
├── firmware-idf/ # Firmware ESP-IDF 5.4 (25 composants)
│ ├── components/ # Composants ESP-IDF modulaires
│ ├── main/ # Point d'entrée app_main
│ └── test/ # Tests host Unity (13 suites)
├── firmware/ # Firmware PlatformIO/Arduino legacy
│ ├── src/ # Modules source
│ ├── test/ # Tests sim-host (8 suites, 13 tests)
│ └── lib/INA237/ # Fork local du driver INA237
├── iosApp/ # App iOS SwiftUI (CoreBluetooth)
├── kxkm-bmu-app/ # Module Kotlin partagé KMP
│ └── shared/ # Logique métier multiplateforme
├── kxkm-api/ # Stack Docker (Mosquitto, InfluxDB, FastAPI, Grafana)
├── hardware/ # Schémas KiCad + Gerbers (v1 + v2)
├── specs/ # Gates Kill_LIFE (S0-S3)
├── docs/ # Specs, plans, gouvernance, recherche
├── grafana/dashboards/ # 3 dashboards Grafana JSON
└── scripts/ # CI, QA, pipeline ML
```
---
## À propos de KompleX KapharnaüM
[KompleX KapharnaüM](https://komplex-kapharnaum.net) est une compagnie d'arts vivants basée à Villeurbanne (Lyon, France), active depuis plus de 20 ans dans les arts de rue, le spectacle vivant et la création numérique. La compagnie développe sa propre plateforme matérielle embarquée (K32, à base d'ESP32) et maintient des outils open source sur [GitHub](https://github.com/KomplexKapharnaum).
---
## Licence
[GNU General Public License v3.0](LICENSE)
---
<p align="center">
<sub>Construit avec ESP-IDF, LVGL, NimBLE, SwiftUI, FastAPI, InfluxDB et Grafana.</sub><br>
<sub>Développé par <a href="https://lelectronrare.fr">L'Electron Rare</a> pour <a href="https://komplex-kapharnaum.net">KompleX KapharnaüM</a></sub>
</p>