chore: retire config ESP-ADF morte + MAJ README
Le firmware n'utilise plus ESP-ADF (audio via esp_codec_dev + esp-audio-player/esp_audio_codec). Supprime le bloc ADF_PATH du CMake racine et main, le commentaire idf_component.yml, et l'en-tete ADF de main/CMakeLists. Retire modes/radios.h (orphelin, URLs dans radio.c). Corrige le commentaire 'STUB' de games.c (Coloriage est implemente: canvas + flood-fill). Reecrit le README (statut compile vert, archi box+Merlin, modes a jour).
This commit is contained in:
+4
-34
@@ -1,40 +1,10 @@
|
||||
# Lisael Box — top-level project CMake for ESP-IDF 5.4/5.5
|
||||
# Target: ESP32-S3-BOX-3
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
# --- ESP-ADF integration -----------------------------------------------------
|
||||
# ESP-ADF is NOT a managed component: it is installed separately and exposed via
|
||||
# the ADF_PATH environment variable (see README "Installer ESP-ADF").
|
||||
# We add its component tree to EXTRA_COMPONENT_DIRS so that audio_pipeline,
|
||||
# http_stream, esp_decoder, fatfs_stream, i2s_stream etc. become available.
|
||||
#
|
||||
# If ADF_PATH is unset the project still configures, but the audio modules that
|
||||
# need ADF will fail to link. We surface a clear message instead of a cryptic
|
||||
# "component not found" error.
|
||||
if(DEFINED ENV{ADF_PATH})
|
||||
set(ADF_COMPONENTS "$ENV{ADF_PATH}/components")
|
||||
if(EXISTS "${ADF_COMPONENTS}")
|
||||
# On n'ajoute QUE les composants audio nécessaires (pas tout l'arbre ADF) :
|
||||
# `audio_board` épingle esp_lcd_ili9341 ^1 et entre en conflit avec le BSP
|
||||
# esp-box-3 (qui veut ^2.0.1). On exclut donc audio_board / display_service
|
||||
# et on laisse le BSP gérer écran/tactile/SD/codec.
|
||||
set(ADF_AUDIO_COMPONENTS
|
||||
esp-adf-libs audio_pipeline audio_stream audio_sal esp_dispatcher)
|
||||
foreach(_adf_c IN LISTS ADF_AUDIO_COMPONENTS)
|
||||
if(EXISTS "${ADF_COMPONENTS}/${_adf_c}")
|
||||
list(APPEND EXTRA_COMPONENT_DIRS "${ADF_COMPONENTS}/${_adf_c}")
|
||||
endif()
|
||||
endforeach()
|
||||
message(STATUS "Lisael Box: ESP-ADF audio components from ${ADF_COMPONENTS}")
|
||||
else()
|
||||
message(WARNING "Lisael Box: ADF_PATH set but ${ADF_COMPONENTS} not found")
|
||||
endif()
|
||||
else()
|
||||
message(WARNING
|
||||
"Lisael Box: ADF_PATH not set. Audio streaming (webradio / SD playback) "
|
||||
"will not link. Run '. $ADF_PATH/export.sh' or export ADF_PATH. "
|
||||
"See README.")
|
||||
endif()
|
||||
# Audio is handled WITHOUT ESP-ADF: the BSP esp-box-3 owns the I2S/ES8311 codec
|
||||
# (esp_codec_dev) and decoding goes through chmorgan/esp-audio-player +
|
||||
# espressif/esp_audio_codec (managed components, see main/idf_component.yml).
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(lisael_box)
|
||||
|
||||
@@ -1,101 +1,112 @@
|
||||
# Lisael Box
|
||||
|
||||
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).
|
||||
Firmware **ESP32-S3-BOX-3** pour une « boîte » à destination d'un jeune enfant :
|
||||
webradios FR enfants, podcasts (Radio France), boîte à sons (microSD), coin
|
||||
calme / respiration, horloge / veilleuse, apprendre à lire, routines du matin /
|
||||
soir et mini-jeux. UI tactile **LVGL 9**, sans lecture obligatoire (gros boutons
|
||||
+ icônes/emoji), 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.
|
||||
Le contenu (podcasts, vignettes, voix des routines, mots de lecture) est produit
|
||||
et poussé vers la box par **Merlin**, un petit studio web côté serveur (voir
|
||||
[`tower/`](#merlin--studio-de-contenu-tower)), exposé sur `merlin.saillant.cc`.
|
||||
|
||||
> **Statut : compile vert (ESP-IDF 5.4.4 + BSP `esp-box-3` 3.2.x), pas encore
|
||||
> validé sur la carte.** `idf.py set-target esp32s3 && idf.py build` réussit
|
||||
> (binaire ≈ 2,5 Mo, 39 % libre dans la partition app de 4 Mo). Les
|
||||
> comportements **matériels** (audio réel, tactile, IMU, backlight, SD) restent
|
||||
> à dérouler sur une vraie BOX-3 — voir [À vérifier sur le
|
||||
> matériel](#à-vérifier-sur-le-matériel).
|
||||
|
||||
## 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).
|
||||
tactile capacitif (GT911), codec audio **ES8311** + HP, micro **ES7210**,
|
||||
**microSD**, IMU **ICM-42670** (utilisée par le jeu de labyrinthe). L'add-on
|
||||
AHT30 (température/humidité) n'est pas supposé présent.
|
||||
|
||||
## Stack logicielle
|
||||
|
||||
- **ESP-IDF 5.4 / 5.5** (nouvelles APIs : `driver/i2c_master.h`,
|
||||
- **ESP-IDF 5.4 / 5.5** (APIs récentes : `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`.
|
||||
port LVGL, codec ES8311 (`esp_codec_dev`), microSD (FATFS), boutons, IMU.
|
||||
Tire transitivement `esp_lvgl_port ^2`, **LVGL 9.x**, `esp_codec_dev`.
|
||||
- **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.
|
||||
- **Audio SANS ESP-ADF.** Le BSP possède l'I2S/le codec ; le décodage passe par
|
||||
`chmorgan/esp-audio-player` (MP3) + `espressif/esp_audio_codec` (AAC/M4A des
|
||||
podcasts), et le PCM est remis au codec ES8311 via `esp_codec_dev`. Cela évite
|
||||
le conflit de versions ESP-ADF `audio_board` ↔ `esp_lcd_ili9341` avec le BSP.
|
||||
- **cJSON** : composant **built-in `json`** d'ESP-IDF (manifestes SD, réponses
|
||||
open-meteo).
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
Radio France RSS / open-meteo / TTS FR
|
||||
│
|
||||
┌────────▼─────────┐ HTTP POST /put
|
||||
merlin.saillant.cc│ Merlin (tower/) │ ────────────────────▶ Lisael Box
|
||||
(studio web) │ serveur :9555 │ (push delta) (ESP32-S3)
|
||||
└────────▲─────────┘ ◀──────────────────── :8080/put
|
||||
│ POST /register {have} file_server
|
||||
merlin_ui.html
|
||||
```
|
||||
|
||||
- **Lisael Box** (`main/`) — le firmware embarqué (UI + modes + audio + réseau).
|
||||
- **Merlin** (`tower/`) — le studio de contenu côté serveur : récupère podcasts
|
||||
et génère vignettes/voix, puis **pousse** le delta vers la box.
|
||||
|
||||
## 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
|
||||
├── CMakeLists.txt # projet (pas d'ESP-ADF)
|
||||
├── partitions.csv # 16 MB : factory 4 MB + storage FAT
|
||||
├── sdkconfig.defaults[.esp32s3]# PSRAM octal, 16 MB flash, 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
|
||||
│ ├── app_main.c # séquence de boot + tâches
|
||||
│ ├── lisael_config.h # constantes partagées (TZ, NVS keys, volume)
|
||||
│ ├── 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
|
||||
│ │ ├── wifi.{c,h} · wifi_portal.{c,h} # STA + SoftAP/portail captif
|
||||
│ │ ├── sntp_time.{c,h} # SNTP + fuseau Europe/Paris
|
||||
│ │ ├── weather.{c,h} # open-meteo.com (HTTPS, sans clé)
|
||||
│ │ ├── file_server.{c,h} # endpoint /put (réception du push Merlin)
|
||||
│ │ ├── box_register.{c,h} # POST /register {have} vers Merlin :9555
|
||||
│ │ ├── podcast.{c,h} # téléchargement des épisodes
|
||||
│ │ └── udp_log.{c,h} # sink de log UDP (debug)
|
||||
│ ├── 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
|
||||
│ │ ├── audio_player.{c,h} # façade codec ES8311 + volume bridé (NVS)
|
||||
│ │ ├── radio_pipeline.{c,h} # webradio HTTP MP3 (sans ESP-ADF)
|
||||
│ │ ├── sd_player.{c,h} # lecture MP3 SD (esp-audio-player)
|
||||
│ │ ├── aac_player.{c,h} # AAC/M4A (podcasts) via esp_audio_codec
|
||||
│ │ ├── calm_tone.{c,h} # synthèse 432 Hz du coin calme
|
||||
│ │ ├── podcast_manifest.{c,h} · routine_manifest.{c,h} # parseurs 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
|
||||
│ │ ├── ui.{c,h} # shell LVGL + routeur de modes + styles
|
||||
│ │ ├── fr_date.{c,h} # date FR en lettres (sans setlocale)
|
||||
│ │ ├── icons.{c,h}
|
||||
│ │ ├── fonts/ # Fredoka custom (accents FR), 18/24/30 px
|
||||
│ │ └── assets/ # icônes/emoji Twemoji -> descripteurs LVGL
|
||||
│ └── modes/ # home, radio, calm, clock, games, balloons,
|
||||
│ # maze, histoires, wifi_setup, lire, routine
|
||||
├── tower/ # Merlin : studio de contenu (Python, voir + bas)
|
||||
├── tools/ # gen_covers.py, gen_icons.py, gen_lire_content.py,
|
||||
│ # push_to_box.sh
|
||||
├── test/host/ # tests hôte des parseurs de manifeste
|
||||
└── sdcard/ # disposition attendue de la carte + manifeste ex.
|
||||
```
|
||||
|
||||
## 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")`).
|
||||
**ESP-IDF 5.4+** installé et exporté :
|
||||
|
||||
> ⚠️ **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).
|
||||
```bash
|
||||
. $IDF_PATH/export.sh
|
||||
```
|
||||
|
||||
C'est tout — **plus aucune dépendance ESP-ADF** à installer. Les managed
|
||||
components (`esp-box-3`, `esp-audio-player`, `esp_audio_codec`, …) sont
|
||||
téléchargés automatiquement dans `managed_components/` au premier build.
|
||||
|
||||
## Build & flash
|
||||
|
||||
@@ -103,7 +114,7 @@ lisael-box/
|
||||
# 1. cibler l'ESP32-S3
|
||||
idf.py set-target esp32s3
|
||||
|
||||
# 2. configurer le WiFi et la météo (lat/lon, ville)
|
||||
# 2. configurer le Wi-Fi et la météo (lat/lon, ville)
|
||||
idf.py menuconfig
|
||||
# -> "Lisael Box configuration" -> Wi-Fi / Weather / Audio
|
||||
|
||||
@@ -117,102 +128,98 @@ idf.py -p /dev/cu.usbmodem* flash monitor
|
||||
|
||||
### Provisioning Wi-Fi
|
||||
|
||||
Deux voies :
|
||||
Trois voies, par ordre de priorité au boot :
|
||||
|
||||
- **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).
|
||||
- **NVS à l'exécution** : `lisael_wifi_save_credentials()` écrit `wifi_ssid` /
|
||||
`wifi_pass` dans le namespace `lisael` — **prime** sur le reste.
|
||||
- **Portail captif SoftAP** : le mode `wifi_setup` (`modes/wifi_setup.c` +
|
||||
`net/wifi_portal.c`) ouvre un point d'accès et une page de saisie quand
|
||||
aucune crédential valide n'est connue.
|
||||
- **menuconfig** : `Lisael Box configuration → Wi-Fi → SSID / password`
|
||||
(pratique en dev ; les valeurs partent dans le binaire).
|
||||
|
||||
### 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.
|
||||
`api.open-meteo.com` (gratuit, sans clé, HTTPS via le CA bundle mbedTLS).
|
||||
Lat/lon depuis NVS (`lat`/`lon`) sinon défauts menuconfig (Paris).
|
||||
Rafraîchissement toutes les `CONFIG_LISAEL_WEATHER_REFRESH_MIN` min ; en cas
|
||||
d'échec réseau, l'accueil affiche `—`.
|
||||
|
||||
## 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) |
|
||||
| Mode | Fichier(s) | Notes |
|
||||
|------|-----------|-------|
|
||||
| 🏠 Accueil | `modes/home.c` | date FR en lettres, heure (SNTP), météo (open-meteo), temp interne de la puce, tuiles en carrousel |
|
||||
| 📻 Webradios | `modes/radio.c` + `audio/radio_pipeline.c` | flux HTTP MP3, stations RadioKing réelles |
|
||||
| 🔊 Boîte à sons | `audio/sd_player.c` | grille depuis `manifest.json`, MP3 SD |
|
||||
| 🎧 Histoires | `modes/histoires.c` + `audio/aac_player.c` | podcasts Radio France (covers SD + AAC/M4A) |
|
||||
| 📖 Lire | `modes/lire.c` | apprendre à lire (366 mots, `words.json`, icône + voix TTS) |
|
||||
| 🫧 Coin calme | `modes/calm.c` + `audio/calm_tone.c` | bulle de respiration + tonalité 432 Hz synthétisée |
|
||||
| 🕐 Horloge/veilleuse | `modes/clock.c` | grande heure + date FR, fond sombre, backlight tamisé, « histoire du soir » |
|
||||
| 🗓️ Routine | `modes/routine.c` + `audio/routine_manifest.c` | matin / soir : étapes picto + voix, « Bravo ! », contenu poussé par Merlin |
|
||||
| 🎮 Jeux | `modes/games.c` | Memory · Compter · **Coloriage** (canvas + flood-fill) · **Labyrinthe** (inclinaison via IMU) |
|
||||
| 🎈 Ballons | `modes/balloons.c` | pop sounds synthétisés |
|
||||
| ⚙️ Réglages Wi-Fi | `modes/wifi_setup.c` | portail captif SoftAP |
|
||||
|
||||
## Carte microSD
|
||||
|
||||
Voir [`sdcard/README.md`](sdcard/README.md). En bref : FAT32, dossiers `/sounds/`
|
||||
(MP3 + `manifest.json`), `/podcasts/`, `/routines/`, `/lire/` peuplés par le push
|
||||
Merlin. Les manifestes suivent `[{file, title, emoji}]` (et variantes par mode).
|
||||
|
||||
## Merlin — studio de contenu (`tower/`)
|
||||
|
||||
Serveur HTTP **stdlib** (`http.server`, port **9555**), déployé sur l'hôte
|
||||
`tower`. Deux faces :
|
||||
|
||||
- **Machine** : `POST /register {id, have}` (réservé LAN/tailnet). Le serveur
|
||||
calcule le **delta** (`staging \ have`, `manifest.json` en dernier) et le
|
||||
**pousse** vers la box : `POST http://<box_ip>:8080/put?name=<subdir>/<file>`.
|
||||
- **Humain** : le **studio** `merlin_ui.html` (SPA, 5 onglets : Podcasts,
|
||||
Fichiers, Box, Routines, Transferts), derrière Basic Auth optionnelle
|
||||
(`MERLIN_AUTH="user:pass"`).
|
||||
|
||||
Génération de contenu (`lisael_content.py`) : RSS Radio France → transcodage
|
||||
ffmpeg (mp3 mono 48 kbps), covers redimensionnées 160×160 RGB565 `.bin` via
|
||||
`LVGLImage.py`, voix des routines via gateway TTS FR. Rafraîchissement hebdo
|
||||
(`lisael_refresh.py` + `systemd/lisael-refresh.timer`, dimanche 04:00). Le
|
||||
serveur tourne en service `systemd/lisael-content.service` (`Restart=always`).
|
||||
|
||||
```bash
|
||||
# tests (offline) — nécessite Pillow + ffmpeg
|
||||
cd tower && python3 -m pytest -q # 23 tests
|
||||
```
|
||||
|
||||
## 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 ! ⭐".
|
||||
- **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 :
|
||||
Le code **compile** ; ce qui suit n'a pas encore été 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).
|
||||
1. **Audio réel** : sortie ES8311 (webradio MP3, podcasts AAC, MP3 SD, tonalité
|
||||
calme, pops), niveaux et plafond de volume.
|
||||
2. **Tactile / UI** : réactivité des tuiles et carrousels (le wrap infini est
|
||||
par flèches seulement, pas au swipe — voir
|
||||
`.superpowers/sdd/carousel-wrap-report.md`).
|
||||
3. **IMU (labyrinthe)** : lecture ICM-42670 via `bsp_i2c_get_handle()`, sens des
|
||||
axes, fallback « Capteur indisponible ».
|
||||
4. **Backlight / veilleuse** : `bsp_display_brightness_set()` doit tamiser en
|
||||
mode horloge.
|
||||
5. **microSD** : montage FATFS, lecture des manifestes et des assets poussés.
|
||||
6. **Réseau bout-en-bout** : provisioning (NVS / SoftAP / menuconfig), SNTP,
|
||||
open-meteo HTTPS, `register`/push delta depuis Merlin.
|
||||
7. **URLs des webradios** (`modes/radio.c`) : confirmer chaque flux (`curl -I` →
|
||||
`audio/mpeg`).
|
||||
8. **Profondeur de pile** des tâches `net`/`weather` (TLS+HTTP+JSON) sous charge
|
||||
audio + UI (`uxTaskGetStackHighWaterMark`).
|
||||
9. **Politique supply-chain** : `idf_component.yml` ne fige aujourd'hui que les
|
||||
versions sémantiques ; à miroiter/épingler par hash contre la baseline
|
||||
auditée avant toute prod (conformément au dépôt parent).
|
||||
|
||||
## Licence
|
||||
|
||||
|
||||
+3
-20
@@ -1,14 +1,8 @@
|
||||
# Lisael Box — main component.
|
||||
#
|
||||
# ESP-ADF components (audio_pipeline, esp-adf-libs, audio_stream, esp_decoder,
|
||||
# audio_board ...) are resolved at configure time from ADF_PATH via
|
||||
# EXTRA_COMPONENT_DIRS (top-level CMakeLists.txt). They are listed in REQUIRES
|
||||
# so the linker pulls them in; if ADF_PATH is missing the build fails loudly
|
||||
# in the audio sources only.
|
||||
#
|
||||
# The audio REQUIRES are guarded: when ADF is absent we still let the UI / net
|
||||
# layers build for inspection. Set LISAEL_WITH_ADF=OFF to compile without audio
|
||||
# (UI/clock/breathing only) for quick syntax checks on a machine without ADF.
|
||||
# Audio uses NO ESP-ADF: playback/decoding is done with the managed components
|
||||
# chmorgan/esp-audio-player (MP3) and espressif/esp_audio_codec (AAC/M4A), with
|
||||
# PCM handed to the BSP ES8311 codec via esp_codec_dev. See idf_component.yml.
|
||||
|
||||
set(srcs
|
||||
"app_main.c"
|
||||
@@ -72,17 +66,6 @@ set(reqs
|
||||
espressif__esp_audio_codec # AAC/M4A simple decoder (podcast self-test)
|
||||
)
|
||||
|
||||
# ESP-ADF audio components. Names follow the ADF master component tree.
|
||||
# Guarded so a no-ADF inspection build still configures.
|
||||
if(DEFINED ENV{ADF_PATH})
|
||||
list(APPEND reqs
|
||||
audio_pipeline
|
||||
audio_stream
|
||||
esp-adf-libs
|
||||
audio_sal
|
||||
)
|
||||
endif()
|
||||
|
||||
idf_component_register(
|
||||
SRCS ${srcs}
|
||||
INCLUDE_DIRS "."
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
# Managed component dependencies for Lisael Box.
|
||||
# Fetched automatically into managed_components/ on `idf.py build`.
|
||||
#
|
||||
# NOTE: ESP-ADF is NOT listed here — it is not distributed via the component
|
||||
# registry. It is installed separately and pulled in through ADF_PATH /
|
||||
# EXTRA_COMPONENT_DIRS (see top-level CMakeLists.txt and README).
|
||||
# Audio uses NO ESP-ADF (no audio_pipeline / i2s_stream): decoding is done with
|
||||
# chmorgan/esp-audio-player + espressif/esp_audio_codec listed below, feeding the
|
||||
# BSP ES8311 codec via esp_codec_dev. This avoids the ESP-ADF audio_board <->
|
||||
# esp_lcd_ili9341 version conflict with the BOX-3 BSP.
|
||||
#
|
||||
# Versions below reflect the esp-box-3 BSP v3.2.x line, which itself depends on
|
||||
# LVGL 9.x, esp_lvgl_port ^2 and esp_codec_dev ~1.5. We let the BSP pin the
|
||||
|
||||
+4
-4
@@ -1,4 +1,4 @@
|
||||
// Jeux mode for Lisael Box: a small menu -> Memory, Compter, (Coloriage stub).
|
||||
// Jeux mode for Lisael Box: a small menu -> Memory, Compter, Coloriage, Maze.
|
||||
//
|
||||
// Design rule: no losing, no scores that shame — only positive feedback (a star
|
||||
// / "Bravo !"). All games are LVGL-only and fit the 320x240 screen.
|
||||
@@ -9,9 +9,9 @@
|
||||
// * Compter : an addition. Two groups of apples (1..10 each) with a "+" sign;
|
||||
// tap the number button with the total (up to 20). New round on
|
||||
// success.
|
||||
// * Coloriage: documented STUB (free-draw on a canvas is heavier on 320x240
|
||||
// and needs an lv_canvas + touch drawing loop; left as a labelled
|
||||
// placeholder screen). See note in build_coloring_stub().
|
||||
// * Coloriage: an lv_canvas (RGB565 in PSRAM) with a 6-colour palette + white
|
||||
// eraser; tapping a region scanline flood-fills it. See
|
||||
// build_coloring() / col_flood().
|
||||
|
||||
#include "ui/ui.h"
|
||||
#include "ui/icons.h"
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
// Webradio station list for Lisael Box.
|
||||
//
|
||||
// ⚠️ À VÉRIFIER / COMPLÉTER: the stream URLs below are PLAUSIBLE placeholders.
|
||||
// French kids' webradio stream endpoints change and are not all publicly
|
||||
// documented. Before shipping, confirm each URL actually returns an MP3/AAC
|
||||
// stream (e.g. `curl -I <url>` should show audio/mpeg or audio/aac, or use the
|
||||
// station's official "écouter en direct" / TuneIn stream link). Prefer plain
|
||||
// HTTP MP3 streams for the lightest decode path on the ESP32-S3.
|
||||
#pragma once
|
||||
|
||||
typedef struct {
|
||||
const char *name; // display name
|
||||
const char *emoji; // small icon
|
||||
const char *url; // stream URL (MP3 or AAC)
|
||||
} lisael_radio_station_t;
|
||||
|
||||
// NOTE: keep this list short — each station is one big touch button.
|
||||
static const lisael_radio_station_t LISAEL_RADIOS[] = {
|
||||
// Radio Pomme d'Api (Bayard) — kids' radio.
|
||||
{ "Pomme d'Api", "\U0001F34E", // 🍎
|
||||
"https://radio.pommedapi.example/stream.mp3" }, // TODO verify
|
||||
|
||||
// Radio Doudou — lullabies / very young children.
|
||||
{ "Radio Doudou", "\U0001F9F8", // 🧸
|
||||
"http://stream.radiodoudou.example/doudou.mp3" }, // TODO verify
|
||||
|
||||
// Mmuz Kids / Mouv'Kids style FR kids music.
|
||||
{ "Muz Kids", "\U0001F3B5", // 🎵
|
||||
"http://stream.muzkids.example/kids.mp3" }, // TODO verify
|
||||
|
||||
// Une histoire et Oli (France Inter) — stories podcast stream proxy.
|
||||
{ "Histoires", "\U0001F4D6", // 📖
|
||||
"http://stream.histoires.example/oli.mp3" }, // TODO verify
|
||||
|
||||
// Comptines FR.
|
||||
{ "Comptines", "\U0001F3B6", // 🎶
|
||||
"http://stream.comptines.example/fr.mp3" }, // TODO verify
|
||||
};
|
||||
|
||||
#define LISAEL_RADIOS_COUNT \
|
||||
(sizeof(LISAEL_RADIOS) / sizeof(LISAEL_RADIOS[0]))
|
||||
Reference in New Issue
Block a user