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.
Deux processus indépendants tournent sur le Pi :
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.
| Pièce | Rôle | Prix |
|---|---|---|
| Raspberry Pi Zero 2 W + header soudé | le cerveau | 22 € |
| Ampli I2S Adafruit MAX98357A 3W | sortie audio (le Zero n'en a pas) | 6 € |
| Haut-parleur 4Ω / 3W, ~40–50 mm | derrière la grille LEGO | 5 € |
| 2× encodeurs rotatifs KY-040 (avec poussoir) | boutons volume + tuning | 5 € |
| MicroSD 16 Go+ (A1) | OS | 8 € |
| Alim 5V / 2,5A USB + câble | « cordon secteur » à l'arrière | 8 € |
| Fils Dupont, perfboard, gaine thermo | câblage | — |
| Pin ampli | Pin Pi |
|---|---|
| VIN | 5V (pin 2) |
| GND | GND (pin 6) |
| BCLK | GPIO 18 (pin 12) |
| LRC | GPIO 19 (pin 35) |
| DIN | GPIO 21 (pin 40) |
Haut-parleur sur le bornier à vis de l'ampli. GAIN non connecté (9 dB par défaut).
| Encodeur | CLK | DT | SW | Alim |
|---|---|---|---|---|
| A — Volume (bouton gauche) | GPIO 5 | GPIO 6 | GPIO 13 | 3V3 + GND |
| B — Tuning (bouton droit) | GPIO 16 | GPIO 20 | GPIO 26 | 3V3 + GND |
Tout est assemblé et testé sur le bureau d'abord. On ne « brique » qu'une fois le logiciel solide.
legoradio, SSH activé, Wi-Fi préconfiguré).ssh legoradio.local → apt update && apt full-upgrade.# /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.
lego_radio.py → spotify_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.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.La redirection OAuth va vers 127.0.0.1:8888 — pas de navigateur sur un Pi headless.
~/.config/lego_radio/ (config + cache de tokens) sur le Pi. spotipy rafraîchit ensuite les tokens tout seul.ssh -L 8888:127.0.0.1:8888 legoradio.local et finir le flow depuis le navigateur du Mac.lego-radio-gpio.service : daemon lancé en user normal, Restart=always, After=network-online.target.journalctl -u lego-radio-gpio -f.| Risque | Parade |
|---|---|
| Spotify Premium requis pour le contrôle Connect | Déjà requis par le TUI existant ; rien ne change |
| OAuth headless | Cache de tokens copié depuis le Mac ; auto-refresh spotipy |
| Rebonds / pas manqués des encodeurs | gpiozero RotaryEncoder + file d'appels API |
| Corruption SD sur coupure de courant | Extinction par appui long ; overlayfs en option |
| librespot disparaît de la liste d'appareils | Auto-restart systemd ; le daemon re-résout l'appareil par son nom |
| Rate limits API en tournant vite le bouton | Coalescence : seul le dernier état volume/piste est envoyé, ≥ 250 ms d'écart |
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).