Files
lisael-box/docs/superpowers/specs/2026-06-21-lisael-box-routines-design.md
L'électron rare baea6ccdd1 docs(merlin): transfers viz spec + routines note
Merlin Studio v2: in-progress + completed transfer journal (duration, throughput, retry failed) alongside the routines tab.
2026-06-21 08:48:59 +02:00

10 KiB
Raw Permalink Blame History

Lisael Box — Mode « Routines » illustrées (design)

  • Date : 2026-06-21
  • Repo : electron-rare/lisael-box (firmware) + Tower lisael-content + Merlin Studio (merlin.saillant.cc)
  • Statut : design validé, prêt pour le plan d'implémentation
  • Livraison : 2 lots — Firmware Mode Routine + chantier Merlin Studio v2 (onglet Routines ici, et la visu des transferts livrée dans le même chantier web/Tower).

1. But

Donner à Lisael (6 ans) un mode Routine illustrée à étapes : une séquence de pictogrammes (s'habiller → petit-déj → dents → cartable…) qu'il avance une par une, avec une voix qui annonce chaque étape et un « Bravo ! » final. Deux routines au lancement : Matin et Soir. Le contenu (étapes, emojis, textes, ordre) est édité par un parent depuis Merlin Studio (web) et poussé sur la box en WiFi — aucun reflash pour changer une routine.

2. Non-objectifs (YAGNI)

  • Pas de minuteur par étape ni d'avance automatique (décision : avance manuelle).
  • Pas de routines libres au-delà de Matin/Soir (deux routines fixes).
  • Pas de logique horaire (la box ne devine pas matin/soir : l'enfant choisit).
  • Pas de photos perso (emojis Twemoji uniquement).
  • Pas de persistance de progression entre sessions (chaque ouverture repart au début).
  • Pas de généralisation du pipeline de contenu Merlin (on étend, on ne refactore pas lisael_content.py en système de types génériques).

3. Décisions de cadrage (verrouillées)

Sujet Décision
Concept Routine illustrée à étapes (gros picto + nom plein écran)
Routines Matin + Soir (2, fixes)
Source du contenu Pilotée par Merlin Studio (web) → manifest SD + assets poussés en WiFi
Interaction Gros bouton « C'est fait ✓ » + voix TTS par étape + son de validation + Bravo final
Déclenchement Tuile « Routine » à l'accueil → choix Matin/Soir
Pictogrammes Twemoji choisis dans Merlin → .bin RGB565A8 généré par Tower
Édition web Éditer les étapes de Matin/Soir : texte + emoji, ajouter/supprimer/réordonner
Voix fr (= « siwis » de la gateway, voix validée pour le jeu Lire)
Reset Repart de l'étape 1 à chaque ouverture, pas de persistance
Cap ~12 étapes / routine, structures en PSRAM
Livraison Tout d'un bloc (firmware + Tower + web), test final global

4. Architecture d'ensemble

Merlin Studio (web)          Tower (lisael-content)              Box (firmware)
merlin.saillant.cc           clems@192.168.0.120                ESP32-S3-BOX-3
─────────────────            ──────────────────────            ─────────────────
Onglet "Routines"   ──POST──▶ routines.json (vérité)
 édite Matin/Soir             + Twemoji→.bin (RGB565A8)
 (texte+emoji,                + TTS→.mp3 (voix "fr")
  ordre, +/)                 + manifest SD
                              ───push WiFi (:8080/put)──▶  /sdcard/routines/*
                                  (delta, comme podcasts)        routines.json + .bin + .mp3
                                                                       │
                                                          mode Routine lit le manifest

Réutilise trois briques déjà en prod : le jeu Lire (pictos Twemoji .bin + TTS .mp3 + cache PSRAM), l'autopush Tower (box_register + compute_delta

  • push_batch vers :8080/put), et Merlin Studio (UI + API + Basic Auth).

5. Contenu sur la SD — /sdcard/routines/

Manifest unique + assets, tout poussé par Tower :

{ "routines": [
  { "id":"matin", "title":"Le matin", "icon":"matin.bin", "steps":[
      {"text":"Habille-toi",          "icon":"matin_01.bin", "audio":"matin_01.mp3"},
      {"text":"Petit-déjeuner",       "icon":"matin_02.bin", "audio":"matin_02.mp3"},
      {"text":"Brosse tes dents",     "icon":"matin_03.bin", "audio":"matin_03.mp3"},
      {"text":"Prépare ton cartable", "icon":"matin_04.bin", "audio":"matin_04.mp3"} ]},
  { "id":"soir",  "title":"Le soir",  "icon":"soir.bin",  "steps":[ ... ] }
]}

Règles :

  • pictos .bin RGB565A8 (même format/pipeline que Lire), audio .mp3 mono.
  • noms de fichiers ASCII (matin_01) ; les libellés accentués vivent dans le JSON (UTF-8, rendu par Fredoka) — leçon LFN/codepage FAT du jeu Lire.
  • icon de routine = vignette du choix Matin/Soir (Twemoji ☀️ / 🌙).

6. Firmware — mode LISAEL_MODE_ROUTINE

Câblage

  • main/ui/ui.h : ajouter LISAEL_MODE_ROUTINE à l'enum + lv_obj_t *lisael_screen_routine(void);.
  • main/modes/routine.c : nouvel écran.
  • main/ui/assets/lisael_ic_routine.c : icône de tuile (Twemoji bakée, /🗓️, RGB565A8).
  • main/modes/home.c : ajouter la tuile au carrousel d'accueil + route dans ui.c.

Écrans

  1. Choix : deux grosses cartes Matin (☀️) / Soir (🌙), tap → ouvre la routine.
  2. Étape plein écran : gros picto (cache PSRAM + FS S:, comme lire.c) + nom (Fredoka) + indicateur de progression 3/6 (pastilles) + bouton géant « C'est fait ✓ » + bouton retour Accueil.
    • à l'arrivée sur une étape : annonce vocale (aac_player lit le .mp3, type MP3).
    • tap « C'est fait » : son de validation (court) → étape suivante.
    • dernière étape validée : écran Bravo (animation étoiles lisael_ic_star + son) → retour Accueil.

Données & RAM

  • main/audio/routine_manifest.{c,h} : parse /sdcard/routines/routines.json → structures lisael_routine_t / lisael_step_t, allouées en PSRAM (heap_caps_*(MALLOC_CAP_SPIRAM)), caps 2 routines × 12 étapes.
  • Cache picto PSRAM : lire chaque .bin une fois en PSRAM et le présenter comme lv_image_dsc_t RAM (data = buf+12), pour éviter la relecture SD à chaque frame (piège perf corrigé dans lire.c).
  • Reset : lisael_screen_routine() repart toujours de l'étape 0.

7. Tower — génération + push

Dans tower/lisael_content.py (module partagé), ajouter le support « routines » :

  • vérité : routines.json côté Tower (/home/clems/lisael-content/), édité par l'API web.
  • pictos : pour chaque étape, télécharger le Twemoji de l'emoji → resize → .bin RGB565A8 via LVGLImage (pipeline du jeu Lire).
  • voix : annonce TTS via la gateway (POST <gateway>/v1/audio/speech, model=tts-1, voice=fr) → .mp3 mono. Gateway = origine tailnet Tower :9300 (⚠️ Cloudflare bloque l'UA Python-urllib sur les domaines publics → appel direct tailnet, ou User-Agent: Mozilla/5.0).
  • manifest SD : composer routines.json (chemins .bin/.mp3 relatifs).
  • push : étendre box_register pour annoncer aussi routines/* dans have, et réutiliser compute_delta + push_batch vers :8080/put?name=routines/<f>.
  • cache : ne régénérer un .bin/.mp3 que si l'emoji/texte a changé (hash du couple emoji+texte dans le nom ou un index), comme le suffixe _a48 des podcasts.

8. Merlin Studio — onglet « Routines »

  • tower/merlin_ui.html : nouvel onglet, 2 sections Matin / Soir. Par étape : champ texte + sélecteur d'emoji + bouton supprimer ; + ajouter une étape ; réordonner (flèches ↑↓) ; bouton Enregistrer & pousser.
  • tower/lisael_server.py : GET /api/routines (lit routines.json), POST /api/routines (sauve + régénère pictos/TTS + met à jour le manifest staging), POST /api/routines/push (push delta à la box). Sous la Basic Auth existante.
  • Sélecteur d'emoji : palette d'emojis enfant courants (dents/habits/repas/bain/dodo/ cartable/jouets…) + saisie libre d'un emoji.

9. Gestion d'erreurs (dégradés gracieux)

  • pas de routines.json sur SD → écran « Demande à un parent de préparer tes routines ».
  • picto manquant → case colorée avec 1ʳᵉ lettre (fallback du jeu Lire).
  • audio manquant pour une étape → étape affichée sans voix (non bloquant).
  • TTS gateway indisponible → Tower garde texte+picto, audio absent (non bloquant).
  • push échoué (WiFi faible) → réessai au prochain register (pattern existant) ; fichier partiel supprimé côté box (unlink, durcissement file-server existant).

10. Tests

  • Hôte : test/host/test_routine_manifest.c (JSON valide / malformé / vide / caps dépassés) ; test Tower de génération + delta (calqué sur test_delta.py).
  • Sur carte (box branchée en USB) : boot-burst USB-JTAG + écoute UDP — manifest chargé, pictos affichés, voix jouée par étape, son de validation, Bravo final.

11. Pré-requis build (dette à régler avant de compiler)

Le sdkconfig (gitigné) a été perdu avec /tmp. sdkconfig.defaults* ne contient PAS tous les réglages critiques. Avant le premier build neuf, reconfigurer (ou remonter dans sdkconfig.defaults, recommandé) : FATFS_LFN_HEAP + FATFS_MAX_LFN=255, LV_USE_FS_STDIO + lettre S (83), MBEDTLS_EXTERNAL_MEM_ALLOC + MBEDTLS_DYNAMIC_BUFFER, ESP_WIFI_TX_BUFFER_TYPE=1, SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY, + les creds WiFi (ou override NVS). Sans ces réglages : manifest illisible, pictos invisibles, reset TLS. À traiter dans le plan (ce sera la première tâche, sinon rien ne tourne).

12. Critères d'acceptation

  1. Une tuile « Routine » apparaît au carrousel d'accueil ; tap → choix Matin/Soir.
  2. Choisir Matin enchaîne les étapes : gros picto + nom, voix qui annonce, bouton « C'est fait ✓ », son de validation, jusqu'au Bravo final.
  3. Le contenu vient de /sdcard/routines/routines.json (aucun contenu en dur figé).
  4. Depuis Merlin, ajouter/supprimer/réordonner une étape + changer texte/emoji, puis « Enregistrer & pousser » → la box reflète le changement après push (au boot/register).
  5. Dégradés gracieux vérifiés : pas de manifest, picto/audio manquant, push partiel.
  6. Tests hôte verts (parser + delta Tower).