docs: add README and SD card layout
Context:
The README is the primary usable deliverable: how to install
ESP-IDF + ESP-ADF, build, flash, and prepare the microSD card, plus an
honest list of what is unverified.
Changes:
- README.md: prerequisites (ADF_PATH), build/flash, provisioning,
module table, child-safety notes, and a thorough "A verifier sur le
materiel" section (build, BSP<->ADF I2S, radio URLs, emoji fonts...)
- sdcard/README.md + sdcard/sounds/manifest.json example
Impact:
A reader can stand the project up and knows exactly what still needs
validation on real hardware.
This commit is contained in:
@@ -1,3 +1,219 @@
|
||||
# lisael-box
|
||||
# Lisael Box
|
||||
|
||||
Lisael Box — firmware ESP32-S3-BOX-3 : webradios FR enfants + boite a sons SD + coin calme + horloge. ESP-IDF/ESP-ADF/LVGL.
|
||||
Firmware **ESP32-S3-BOX-3** pour une "boîte" à destination d'un jeune enfant :
|
||||
webradios FR enfants, boîte à sons (microSD), coin calme / respiration,
|
||||
horloge / veilleuse, et jeux adaptés. UI tactile **LVGL**, sans lecture
|
||||
obligatoire (gros boutons + icônes), volume **bridé** (plafond enfant).
|
||||
|
||||
> ⚠️ **Statut : point de départ soigné, NON compilé / NON flashé.** Ce dépôt a
|
||||
> été écrit sans la carte ni ESP-IDF/ESP-ADF installés. Le code suit les APIs
|
||||
> ESP-IDF 5.x, le BSP `esp-box-3` (v3.2.x, LVGL 9) et ESP-ADF de façon
|
||||
> idiomatique, mais **rien n'a pu être bâti**. Voir la section
|
||||
> [À vérifier sur le matériel](#à-vérifier-sur-le-matériel) — elle est honnête
|
||||
> et exhaustive.
|
||||
|
||||
## Matériel cible
|
||||
|
||||
- **ESP32-S3-BOX-3** : ESP32-S3 (8 Mo PSRAM octal, 16 Mo flash), LCD 320×240
|
||||
tactile capacitif, codec audio **ES8311** + HP, micro **ES7210**, **microSD**,
|
||||
IMU (ICM-42670). Pas de capteur température/humidité externe sur la carte de
|
||||
base (un add-on AHT30 existe ; non supposé présent ici).
|
||||
|
||||
## Stack logicielle
|
||||
|
||||
- **ESP-IDF 5.4 / 5.5** (nouvelles APIs : `driver/i2c_master.h`,
|
||||
`driver/temperature_sensor.h`, `esp_netif_sntp.h`, …).
|
||||
- **BSP `espressif/esp-box-3` `^3.2.0`** (managed component) — écran, tactile,
|
||||
LVGL port, codec ES8311 (`esp_codec_dev`), microSD (FATFS), boutons, IMU.
|
||||
Tire transitivement `esp_lvgl_port ^2`, **LVGL 9.x**, `esp_codec_dev ~1.5`.
|
||||
- **LVGL 9** pour l'UI tactile.
|
||||
- **ESP-ADF** pour l'audio en streaming/décodage (webradio + lecture SD).
|
||||
ESP-ADF **n'est pas** un managed component : il s'installe à part (voir
|
||||
ci-dessous).
|
||||
- **`espressif/cjson`** pour le `manifest.json` SD et les réponses open-meteo.
|
||||
|
||||
## Arborescence
|
||||
|
||||
```
|
||||
lisael-box/
|
||||
├── CMakeLists.txt # projet ; injecte ESP-ADF via ADF_PATH
|
||||
├── partitions.csv # 16 MB : factory 4 MB + storage FAT
|
||||
├── sdkconfig.defaults # défauts communs
|
||||
├── sdkconfig.defaults.esp32s3# PSRAM octal, 16 MB flash, 240 MHz, LVGL/PSRAM
|
||||
├── main/
|
||||
│ ├── CMakeLists.txt # sources + REQUIRES (BSP, ADF guardé)
|
||||
│ ├── idf_component.yml # managed deps (esp-box-3, cjson)
|
||||
│ ├── Kconfig.projbuild # menuconfig : WiFi, lat/lon météo, volume
|
||||
│ ├── lisael_config.h # constantes partagées (TZ, NVS keys, volume)
|
||||
│ ├── app_main.c # séquence de boot + tâches
|
||||
│ ├── net/
|
||||
│ │ ├── wifi.{c,h} # STA + reconnexion + creds NVS/menuconfig
|
||||
│ │ ├── sntp_time.{c,h} # SNTP + fuseau Europe/Paris
|
||||
│ │ └── weather.{c,h} # open-meteo.com (HTTPS, sans clé) périodique
|
||||
│ ├── audio/
|
||||
│ │ ├── audio_player.{c,h}# façade codec ES8311 + volume bridé (NVS)
|
||||
│ │ ├── radio_pipeline.{c,h} # ESP-ADF http_stream→decoder→i2s
|
||||
│ │ └── sd_player.{c,h} # ESP-ADF fatfs→mp3→i2s + parse manifest.json
|
||||
│ ├── ui/
|
||||
│ │ ├── ui.{c,h} # shell LVGL + routeur de modes + styles
|
||||
│ │ ├── fr_date.{c,h} # date FR en lettres (sans setlocale)
|
||||
│ │ └── icons.{c,h} # glyphes/emoji des tuiles et boutons
|
||||
│ └── modes/
|
||||
│ ├── home.c # accueil : date FR, heure, météo, capteur, tuiles
|
||||
│ ├── radio.c # écran webradios + volume/stop
|
||||
│ ├── radios.h # liste de stations (URLs à vérifier)
|
||||
│ ├── soundbox.c # grille SD depuis manifest.json
|
||||
│ ├── calm.c # bulle de respiration (anim LVGL)
|
||||
│ ├── clock.c # horloge/veilleuse + histoire du soir
|
||||
│ └── games.c # Memory + Compter + Coloriage (stub)
|
||||
└── sdcard/
|
||||
├── README.md # disposition attendue de la carte
|
||||
└── sounds/manifest.json # exemple de manifeste
|
||||
```
|
||||
|
||||
## Prérequis
|
||||
|
||||
1. **ESP-IDF 5.4+** installé et exporté :
|
||||
```bash
|
||||
. $IDF_PATH/export.sh
|
||||
```
|
||||
2. **ESP-ADF** installé séparément (audio streaming/décodage). ESP-ADF n'est
|
||||
pas distribué via le registre de composants ; on le clone et on exporte
|
||||
`ADF_PATH` :
|
||||
```bash
|
||||
git clone --recursive https://github.com/espressif/esp-adf.git ~/esp/esp-adf
|
||||
export ADF_PATH=~/esp/esp-adf
|
||||
# (esp-adf fournit aussi $ADF_PATH/export.sh qui définit ADF_PATH)
|
||||
```
|
||||
Le `CMakeLists.txt` racine ajoute `$ADF_PATH/components` à
|
||||
`EXTRA_COMPONENT_DIRS`. **Si `ADF_PATH` est absent**, le projet *configure*
|
||||
quand même (UI/horloge/respiration), mais les modules audio (webradio,
|
||||
lecture SD) ne sont pas liés et renvoient `ESP_ERR_NOT_SUPPORTED` à
|
||||
l'exécution (gardes `__has_include("audio_pipeline.h")`).
|
||||
|
||||
> ⚠️ **Compatibilité de versions à vérifier.** ESP-ADF s'épingle
|
||||
> historiquement à des versions précises d'ESP-IDF, et son abstraction
|
||||
> `audio_board` peut entrer en conflit avec le BSP `esp-box-3` (les deux
|
||||
> veulent gérer l'I2S/le codec). Voir
|
||||
> [À vérifier sur le matériel](#à-vérifier-sur-le-matériel).
|
||||
|
||||
## Build & flash
|
||||
|
||||
```bash
|
||||
# 1. cibler l'ESP32-S3
|
||||
idf.py set-target esp32s3
|
||||
|
||||
# 2. configurer le WiFi et la météo (lat/lon, ville)
|
||||
idf.py menuconfig
|
||||
# -> "Lisael Box configuration" -> Wi-Fi / Weather / Audio
|
||||
|
||||
# 3. build (télécharge les managed components dans managed_components/)
|
||||
idf.py build
|
||||
|
||||
# 4. flash + monitor (macOS : le port apparaît en /dev/cu.usbmodem*)
|
||||
idf.py -p /dev/cu.usbmodem* flash monitor
|
||||
# (Linux : -p /dev/ttyUSB0 ou /dev/ttyACM0 ; Ctrl+] pour quitter le monitor)
|
||||
```
|
||||
|
||||
### Provisioning Wi-Fi
|
||||
|
||||
Deux voies :
|
||||
|
||||
- **menuconfig** : `Lisael Box configuration → Wi-Fi → SSID / password`. Les
|
||||
valeurs partent dans le binaire (pratique en dev).
|
||||
- **NVS à l'exécution** : `lisael_wifi_save_credentials()` écrit les clés
|
||||
`wifi_ssid` / `wifi_pass` dans le namespace `lisael` ; elles **priment** sur
|
||||
les valeurs menuconfig au boot suivant. Brancher un petit portail captif ou
|
||||
une commande console reste **à faire** (les valeurs menuconfig suffisent pour
|
||||
démarrer).
|
||||
|
||||
### Météo
|
||||
|
||||
`api.open-meteo.com` (gratuit, sans clé, HTTPS via le CA bundle mbedTLS). La
|
||||
latitude/longitude viennent de NVS (`lat`/`lon`) sinon des défauts menuconfig
|
||||
(Paris). Rafraîchissement toutes les `CONFIG_LISAEL_WEATHER_REFRESH_MIN` min.
|
||||
En cas d'échec réseau, l'accueil affiche `—`.
|
||||
|
||||
## Carte microSD
|
||||
|
||||
Voir [`sdcard/README.md`](sdcard/README.md). En bref : FAT32, dossier
|
||||
`/sounds/` contenant des `.mp3` + un `manifest.json`
|
||||
(`[{file, title, emoji}]`). Les clips seront générés plus tard par TTS FR.
|
||||
|
||||
## Modes
|
||||
|
||||
| Mode | Fichier | Implémenté | Notes |
|
||||
|------|---------|-----------|-------|
|
||||
| Accueil | `modes/home.c` | ✅ | date FR en lettres, heure (SNTP), météo (open-meteo), temp interne de la puce (fallback), 5 tuiles |
|
||||
| 📻 Webradios | `modes/radio.c` + `audio/radio_pipeline.c` | ✅ code / ⚠️ URLs | pipeline ESP-ADF `http_stream→decoder→i2s` ; **URLs de stations à confirmer** (`modes/radios.h`) |
|
||||
| 🔊 Boîte à sons | `modes/soundbox.c` + `audio/sd_player.c` | ✅ | grille depuis `manifest.json` ; pipeline `fatfs→mp3→i2s` |
|
||||
| 🫧 Coin calme | `modes/calm.c` | ✅ | bulle qui gonfle (~4 s) / dégonfle (~6 s) en boucle, "Inspire…/Souffle…", ambiance SD optionnelle |
|
||||
| 🕐 Horloge/veilleuse | `modes/clock.c` | ✅ | grande heure + date FR, fond sombre doux, backlight tamisé, bouton "Histoire du soir" |
|
||||
| 🎮 Jeux | `modes/games.c` | ✅ Memory + Compter / 🚧 Coloriage stub | feedback positif uniquement, pas de défaite ; Coloriage est un stub documenté (canvas tactile = plus lourd sur 320×240) |
|
||||
|
||||
## Sécurité enfant
|
||||
|
||||
- **Volume bridé** : plafond `CONFIG_LISAEL_VOLUME_MAX_PERCENT` (défaut 60 %),
|
||||
appliqué partout via `audio_player.c` (`clamp_volume`), persisté en NVS.
|
||||
- **Pas de lecture requise** : icônes/emoji + gros boutons (≥ 56 px).
|
||||
- **Jeux sans échec** : aucun score punitif, retour "Bravo ! ⭐".
|
||||
|
||||
## À vérifier sur le matériel
|
||||
|
||||
Liste honnête de ce qui **n'a pas pu être testé** et doit être validé sur une
|
||||
vraie carte :
|
||||
|
||||
1. **Compilation.** Le code n'a jamais été bâti (ni ESP-IDF ni ESP-ADF
|
||||
installés ici). Attendez-vous à des ajustements : noms exacts de composants
|
||||
managés ESP-ADF dans `REQUIRES` (`audio_pipeline`, `audio_stream`,
|
||||
`esp-adf-libs`, `audio_sal`) et includes/macros décodeur exacts
|
||||
(`DEFAULT_ESP_MP3_DECODER_CONFIG` vs `…_AUDIO_DECODER_CONFIG` varie selon la
|
||||
révision ADF). cJSON = composant **built-in `json`** d'ESP-IDF.
|
||||
2. **Coexistence BSP ↔ ESP-ADF sur l'I2S/codec.** C'est **le** point chaud. Le
|
||||
BSP `esp-box-3` initialise l'I2S et le codec ES8311 (`bsp_audio_init` /
|
||||
`bsp_audio_codec_speaker_init`), et ESP-ADF `i2s_stream` veut lui aussi
|
||||
posséder le port I2S. Deux approches connues, à arbitrer sur le matériel :
|
||||
- **(A)** ADF possède l'I2S : configurer `i2s_stream` avec le port/pins du
|
||||
BOX-3 et **ne pas** appeler `bsp_audio_init` ; n'utiliser le handle codec
|
||||
du BSP que pour le volume/gain. *(approche codée ici)*
|
||||
- **(B)** Le BSP possède l'I2S : écrire le PCM décodé vers le codec via
|
||||
`esp_codec_dev_write()` depuis un `raw_stream` au lieu de `i2s_stream`.
|
||||
Les configs `i2s_std` (port, broches, horloge via `i2s_stream_set_clk`)
|
||||
dans `radio_pipeline.c` / `sd_player.c` portent des `TODO(hardware)` — elles
|
||||
doivent matcher les valeurs du BSP de votre version.
|
||||
3. **Version ESP-ADF compatible** avec votre ESP-IDF 5.4/5.5 (ADF s'épingle
|
||||
souvent à une révision IDF précise). L'abstraction `audio_board` d'ADF peut
|
||||
doublonner le BSP — c'est pourquoi `board.h` n'est *pas* inclus.
|
||||
4. **URLs des webradios** (`modes/radios.h`) : ce sont des **placeholders
|
||||
plausibles** (Radio Pomme d'Api, Radio Doudou, etc.). Confirmez chaque flux
|
||||
(`curl -I` → `audio/mpeg` ou `audio/aac`) et préférez du MP3 HTTP simple.
|
||||
5. **Polices / emoji LVGL.** Les fonts Montserrat par défaut **ne contiennent
|
||||
pas** les emoji ; les glyphes 📻🔊🫧… risquent d'apparaître en "tofu". Pour
|
||||
les rendre, embarquez une police emoji (subset Noto via `lv_font_conv`) ou
|
||||
remplacez par le set monochrome `LV_SYMBOL_*` (déjà utilisé pour les flèches
|
||||
et contrôles). Les accents FR (é, à, û, î) demandent une police Latin-1 — à
|
||||
vérifier aussi.
|
||||
6. **Tailles de police montserrat** (`lv_font_montserrat_28/48`) : activez-les
|
||||
dans `menuconfig → Component config → LVGL → Font usage` si elles ne sont
|
||||
pas dans le set par défaut.
|
||||
7. **Backlight / luminosité.** `bsp_display_brightness_set()` /
|
||||
`bsp_display_backlight_on()` supposés présents dans le BSP — à confirmer sur
|
||||
votre version (sinon le mode veilleuse ne tamise pas).
|
||||
8. **Capteurs.** Seule la température **interne de la puce**
|
||||
(`driver/temperature_sensor.h`) est lue, en fallback : ce n'est PAS la
|
||||
température de la pièce. L'IMU ICM-42670 et l'éventuel AHT30 ne sont pas
|
||||
exploités (le BSP expose `bsp_sensor_init` — à brancher si voulu).
|
||||
9. **Profondeur de pile des tâches.** `net`/`weather` à 8192 o (TLS+HTTP+JSON),
|
||||
`tick` à 4096 o, tâche LVGL gérée par le BSP — à surveiller au monitor
|
||||
(`uxTaskGetStackHighWaterMark`) sous charge audio + UI.
|
||||
10. **Mémoire de jeu (`games.c`).** Les états Memory/Compter sont alloués via
|
||||
`lv_malloc` et vivent le temps de l'écran (non libérés à la destruction) —
|
||||
OK pour une app mono-écran longue durée, à durcir si les écrans sont
|
||||
recréés en boucle.
|
||||
11. **Politique supply-chain.** Conformément au dépôt parent, les deps doivent
|
||||
à terme être miroitées/épinglées par hash contre la baseline auditée
|
||||
(`idf_component.yml` ne fige aujourd'hui que les versions sémantiques).
|
||||
|
||||
## Licence
|
||||
|
||||
À définir (aligner sur la politique de l'org `electron-rare`).
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
# Carte microSD — disposition attendue
|
||||
|
||||
Formatez la carte en **FAT32** et copiez l'arborescence suivante à la racine :
|
||||
|
||||
```
|
||||
/ (racine de la carte microSD, montée sur /sdcard)
|
||||
└── sounds/
|
||||
├── manifest.json # décrit la grille de la "Boîte à sons"
|
||||
├── miaou.mp3
|
||||
├── ouaf.mp3
|
||||
├── coucou.mp3
|
||||
├── bravo.mp3
|
||||
├── histoire.mp3 # joué par le bouton "Histoire du soir" (Horloge)
|
||||
└── calme.mp3 # ambiance douce optionnelle (Coin calme)
|
||||
```
|
||||
|
||||
## `manifest.json`
|
||||
|
||||
Tableau JSON d'objets `{ file, title, emoji }` :
|
||||
|
||||
```json
|
||||
[
|
||||
{ "file": "miaou.mp3", "title": "Le chat", "emoji": "🐱" }
|
||||
]
|
||||
```
|
||||
|
||||
- `file` : nom du fichier **sous `/sounds/`** (pas de chemin absolu).
|
||||
- `title` : libellé affiché sous le bouton.
|
||||
- `emoji` : icône du bouton (rendu si une police emoji est embarquée — voir
|
||||
README principal, section "À vérifier sur le matériel").
|
||||
|
||||
Le firmware lit jusqu'à `LISAEL_SD_MAX_SOUNDS` (32) entrées. Sans carte ou sans
|
||||
`manifest.json`, l'écran "Boîte à sons" affiche un message d'invite au lieu
|
||||
d'une erreur.
|
||||
|
||||
## Format audio
|
||||
|
||||
Fichiers **MP3** (le pipeline SD utilise le décodeur MP3 d'ESP-ADF). Les clips
|
||||
seront générés plus tard par TTS FR. Préférez un débit modéré (ex. 128 kbit/s,
|
||||
44.1 kHz) pour un décodage léger sur l'ESP32-S3.
|
||||
|
||||
Les fichiers `histoire.mp3` et `calme.mp3` sont chargés par nom en dur
|
||||
(respectivement par l'écran Horloge et le Coin calme) ; ils peuvent aussi
|
||||
figurer dans le `manifest.json` pour apparaître dans la grille.
|
||||
@@ -0,0 +1,8 @@
|
||||
[
|
||||
{ "file": "miaou.mp3", "title": "Le chat", "emoji": "🐱" },
|
||||
{ "file": "ouaf.mp3", "title": "Le chien", "emoji": "🐶" },
|
||||
{ "file": "coucou.mp3", "title": "Coucou", "emoji": "👋" },
|
||||
{ "file": "bravo.mp3", "title": "Bravo", "emoji": "⭐" },
|
||||
{ "file": "histoire.mp3", "title": "Histoire", "emoji": "📖" },
|
||||
{ "file": "calme.mp3", "title": "Calme", "emoji": "🫧" }
|
||||
]
|
||||
Reference in New Issue
Block a user