LEGO RADIO× RASPBERRY PI · PLAN D'INTÉGRATION · SET 10334

Transformer la radio rétro LEGO en vraie radio Spotify : un Pi Zero 2 W caché à l'intérieur, headless, son propre haut-parleur, pilotée par les boutons LEGO.

LEGO Retro Radio — set 10334
LEGO® 10334 · Retro Radio
VOLUME / PLAY
TUNING / NEXT
AudioLe Pi joue le son (raspotify + ampli I2S)
ÉcranHeadless — aucun écran
ContrôlesEncodeurs rotatifs derrière les boutons LEGO
ModèleRaspberry Pi Zero 2 W
AlimentationCâble USB à l'arrière, comme un cordon secteur

FM 86.0Architecture

Deux processus indépendants tournent sur le Pi :

  • raspotify (librespot packagé) : fait apparaître le Pi comme appareil Spotify Connect nommé « LEGO Radio ». Il gère tout le flux audio, survit aux reboots et aux coupures Wi-Fi.
  • lego_radio_gpio.py (nouveau daemon) : lit les encodeurs via gpiozero et appelle l'API Web Spotify (spotipy, PKCE — même auth que le TUI existant) en ciblant l'appareil « LEGO Radio ».
                        ┌────────────────────────────────────┐
                        │        LEGO Retro Radio 10334      │
                        │                                    │
 Spotify Web API ◄──────┼── lego_radio_gpio.py (daemon)      │
 (play/pause/next/vol)  │        ▲ gpiozero                  │
                        │        │                           │
                        │   2× encodeurs KY-040              │
                        │   (derrière les boutons LEGO)      │
                        │                                    │
 Spotify Connect ───────┼─► raspotify (librespot)            │
 (flux audio)           │        │ I2S                       │
                        │   Ampli MAX98357A ──► HP 3W        │
                        │                  (derrière grille) │
                        │   Pi Zero 2 W ◄── 5V USB (arrière) │
                        └────────────────────────────────────┘

Le TUI lego_radio.py reste utilisable sur le bureau. Sa logique Spotify (auth PKCE, Liked Songs, contrôle de lecture) est extraite dans un module partagé spotify_core.py utilisé par les deux interfaces.

FM 88.0Matériel · ~45–55 €

PièceRôlePrix
Raspberry Pi Zero 2 W + header soudéle cerveau22 €
Ampli I2S Adafruit MAX98357A 3Wsortie audio (le Zero n'en a pas)6 €
Haut-parleur 4Ω / 3W, ~40–50 mmderrière la grille LEGO5 €
2× encodeurs rotatifs KY-040 (avec poussoir)boutons volume + tuning5 €
MicroSD 16 Go+ (A1)OS8 €
Alim 5V / 2,5A USB + câble« cordon secteur » à l'arrière8 €
Fils Dupont, perfboard, gaine thermocâblage

FM 92.0Câblage

MAX98357A (I2S)

Pin ampliPin Pi
VIN5V (pin 2)
GNDGND (pin 6)
BCLKGPIO 18 (pin 12)
LRCGPIO 19 (pin 35)
DINGPIO 21 (pin 40)

Haut-parleur sur le bornier à vis de l'ampli. GAIN non connecté (9 dB par défaut).

Encodeurs

EncodeurCLKDTSWAlim
A — Volume (bouton gauche)GPIO 5GPIO 6GPIO 133V3 + GND
B — Tuning (bouton droit)GPIO 16GPIO 20GPIO 263V3 + GND

FM 95.0Mapping des contrôles

Volume · rotationVolume ± (API Spotify)
Volume · pressionPlay / Pause
Tuning · rotationTitre suivant / précédent
Tuning · pressionRe-shuffle des Liked Songs
Tuning · appui long 3 sExtinction propre (poweroff)

FM 104.0Plan logiciel — 8 phases

0PHASE

Montage sur établi (sans LEGO)

Tout est assemblé et testé sur le bureau d'abord. On ne « brique » qu'une fois le logiciel solide.

1PHASE

OS & réseau

  • Raspberry Pi OS Lite 64-bit flashé avec Raspberry Pi Imager (hostname legoradio, SSH activé, Wi-Fi préconfiguré).
  • Boot → ssh legoradio.localapt update && apt full-upgrade.
2PHASE

Audio : raspotify

# /boot/firmware/config.txt
dtparam=audio=off
dtoverlay=hifiberry-dac        # pilote le MAX98357A

# installation
curl -sL https://dtcooper.github.io/raspotify/install.sh | sh

# /etc/raspotify/conf
LIBRESPOT_NAME="LEGO Radio"
LIBRESPOT_BITRATE="320"
LIBRESPOT_INITIAL_VOLUME="40"

Test : speaker-test, puis choisir « LEGO Radio » depuis l'app Spotify du téléphone.

3PHASE

Refactor du code (sur le Mac)

  • Extraire de lego_radio.pyspotify_core.py : config, auth PKCE + cache de tokens, pagination des Liked Songs, contrôle de lecture (play/pause/next/prev/volume/ciblage d'appareil).
  • lego_radio.py garde l'UI curses et importe le core.
  • Nouveau lego_radio_gpio.py : handlers gpiozero RotaryEncoder/Button → appels au core. Débounce + file de travail pour que les appels API ne bloquent jamais les callbacks GPIO. Cible automatiquement « LEGO Radio » ; si rien ne joue, lance les Liked Songs en shuffle.
  • Tests sur l'extraction du core avec un client spotipy mocké (TDD).
4PHASE

Auth sur le Pi (PKCE headless)

La redirection OAuth va vers 127.0.0.1:8888 — pas de navigateur sur un Pi headless.

  • Choisi : s'autoriser une fois sur le Mac, puis copier ~/.config/lego_radio/ (config + cache de tokens) sur le Pi. spotipy rafraîchit ensuite les tokens tout seul.
  • Secours : ssh -L 8888:127.0.0.1:8888 legoradio.local et finir le flow depuis le navigateur du Mac.
5PHASE

Services systemd

  • lego-radio-gpio.service : daemon lancé en user normal, Restart=always, After=network-online.target.
  • Logs via journald : journalctl -u lego-radio-gpio -f.
  • Seul le cache de tokens est écrit ; overlayfs lecture seule en option plus tard (longévité SD).
6PHASE

Intégration LEGO

  • Haut-parleur monté derrière la grille avant (panneau majoritairement ajouré — le son passe bien).
  • Accouplement des axes KY-040 aux boutons rotatifs du set : axe Technic + connecteur d'axe collé/emmanché sur l'axe en D de l'encodeur — les boutons du set tournent déjà librement.
  • Pi + perfboard au fond du corps de la radio (velcro ou cage en briques) ; câble USB sorti à l'arrière, comme un cordon secteur.
  • Quelques tenons ouverts près du Pi pour l'aération (le Zero 2 W chauffe à peine à cette charge).
7PHASE

Tests & finitions

  • Cold-boot : alimentation → « LEGO Radio » visible sur Connect en ≤ 30 s, boutons opérationnels sans aucune interaction.
  • Coupure Wi-Fi : raspotify se reconnecte, le daemon réessaie avec backoff.
  • Erreurs API : 429/5xx → backoff exponentiel, jamais de crash.
  • Extinction par appui long vérifiée avant fermeture définitive (évite la corruption SD).

FM 108.0Risques & parades

RisqueParade
Spotify Premium requis pour le contrôle ConnectDéjà requis par le TUI existant ; rien ne change
OAuth headlessCache de tokens copié depuis le Mac ; auto-refresh spotipy
Rebonds / pas manqués des encodeursgpiozero RotaryEncoder + file d'appels API
Corruption SD sur coupure de courantExtinction par appui long ; overlayfs en option
librespot disparaît de la liste d'appareilsAuto-restart systemd ; le daemon re-résout l'appareil par son nom
Rate limits API en tournant vite le boutonCoalescence : seul le dernier état volume/piste est envoyé, ≥ 250 ms d'écart

Hors périmètre (YAGNI)

Batterie, écran, web UI, multi-room, « stations » présélectionnées sur le cadran (le mécanisme de tuning s'y prêterait bien plus tard : présélections → playlists).