Objetivo y caso de uso
Qué construirás: Un reproductor de audio en red I2S utilizando Raspberry Pi Zero 2 W y un DAC PCM5102A.
Para qué sirve
- Transmisión de audio de alta calidad a través de la red local utilizando I2S.
- Integración con sistemas de automatización del hogar mediante MQTT para control remoto.
- Reproducción de listas de reproducción desde servidores de medios como Plex o Jellyfin.
- Uso como dispositivo de audio en proyectos de arte interactivo.
Resultado esperado
- Latencia de audio inferior a 100 ms desde la solicitud hasta la reproducción.
- Capacidad de manejar hasta 10 flujos de audio simultáneos sin pérdida de calidad.
- Consumo de CPU por debajo del 30% durante la reproducción continua.
- Estabilidad de conexión con menos del 1% de paquetes perdidos en la red.
Público objetivo: Usuarios avanzados en Linux y Python; Nivel: Avanzado
Arquitectura/flujo: Raspberry Pi Zero 2 W conectado a un DAC PCM5102A mediante I2S, utilizando GStreamer para la reproducción de audio y MQTT para la comunicación.
Nivel: Avanzado
Prerrequisitos
Sistema operativo y toolchain exactos
Este caso práctico se ha diseñado y verificado con la siguiente toolchain. Te recomiendo mantener estas versiones para reproducibilidad:
- Sistema operativo: Raspberry Pi OS Bookworm 64-bit (Debian 12)
- Kernel Linux: 6.6.y (rama estable para Raspberry Pi OS Bookworm)
- Python: 3.11.2 (intérprete del sistema)
- pip: 23.0.1
- Virtualenv (módulo venv de Python 3.11)
- GCC: 12.2.0 (para compilar módulos nativos si fuese necesario)
- ALSA (alsa-lib/alsa-utils): 1.2.8
- GStreamer: 1.22.x (binarios gstreamer1.0 proporcionados por Debian 12)
- Device Tree overlay: hifiberry-dac (para PCM5102A vía I2S)
- OpenSSL: 3.0.x (dep. de muchos clientes/servidores, ya incluida en Bookworm)
Comandos para verificar rápidamente:
uname -a
python3 --version
pip3 --version
gcc --version
aplay --version
gst-launch-1.0 --version
cat /etc/os-release
Notas:
– Usaremos Python 3.11 (Bookworm lo trae por defecto).
– GStreamer 1.22.x en Bookworm aporta decodificadores modernos y estabilidad.
– ALSA 1.2.8 garantiza compatibilidad con el overlay de PCM5102A.
Conocimientos previos
- Manejo de terminal Linux en Raspberry Pi.
- Conocimientos de sonido digital (PCM/I2S, muestreo, formatos).
- Nociones de redes (TCP/IP, HTTP, streaming).
- Python avanzado (asyncio, subprocess, o GStreamer via PyGObject).
Materiales
- 1x Raspberry Pi Zero 2 W + PCM5102A DAC
- Raspberry Pi Zero 2 W (SoC quad‑core ARM Cortex‑A53)
- DAC I2S basado en PCM5102A (módulo sin control I2C, entrada I2S pura: BCLK/LRCK/DATA)
- 1x Tarjeta microSD (16 GB o superior, Clase A1/A2 recomendada)
- 1x Fuente de alimentación 5 V/2.5 A con cable micro‑USB (estable)
- 1x Cabecera GPIO soldada (40 pines) y jumpers Dupont hembra‑hembra
- 2x Altavoces amplificados o amplificador + altavoces pasivos
- 1x LED + resistencia 330 Ω (opcional, para estado de reproducción en GPIO 13)
- 1x Botón momentáneo (opcional, para play/pause en GPIO 26)
- Conectividad de red:
- Wi‑Fi 2.4 GHz (integrado en la Zero 2 W)
- Opcionalmente, adaptador USB OTG Ethernet para mayor fiabilidad
- Herramientas:
- Soldador (si la cabecera no está ya instalada)
- PC para preparar la microSD con Raspberry Pi Imager
Notas sobre alimentación del módulo PCM5102A:
– Muchos módulos PCM5102A traen regulador (p. ej., AMS1117‑3.3) y admiten 5 V en su pin VIN. Si tu módulo incluye regulador, alimenta con 5 V.
– Si es un breakout “limpio” sin regulador (sólo el chip + pasivos), alimenta únicamente con 3.3 V desde la Raspberry Pi (pin 1 o 17). Verifica el serigrafiado/hoja de datos de tu módulo antes de conectar.
Preparación y conexión
Preparación del sistema (Bookworm 64‑bit, Python 3.11, I2S)
- Flashea Raspberry Pi OS Bookworm 64‑bit con Raspberry Pi Imager.
- En el primer arranque, configura:
- Zona horaria y teclado.
- Wi‑Fi (si no usas Ethernet).
- Activa SSH si lo necesitas: sudo raspi-config (System Options → SSH → Enable).
-
Actualiza el sistema:
bash
sudo apt update
sudo apt full-upgrade -y
sudo reboot -
Habilita el overlay de I2S para PCM5102A (hifiberry-dac) en /boot/firmware/config.txt:
bash
sudo nano /boot/firmware/config.txt
Añade al final (o ajusta si ya están presentes):
dtparam=audio=off
dtoverlay=hifiberry-dac
Guarda, cierra y reinicia:
bash
sudo reboot -
Verifica que ALSA ve el DAC I2S:
bash
aplay -l
Debes ver una tarjeta similar a: -
card 0: snd_rpi_hifiberry_dac [snd_rpi_hifiberry_dac], device 0: …
Si aparece como card 1, lo anotaremos para la configuración del dispositivo ALSA en el software. -
Instala herramientas y bibliotecas del sistema necesarias (GStreamer, ALSA, Python GI, etc.):
bash
sudo apt install -y \
python3-venv python3-pip python3-gi gir1.2-gst-1.0 \
gstreamer1.0-tools gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad \
alsa-utils ffmpeg \
python3-gpiozero python3-rpi.gpio
Notas:
– Usaremos gpiozero para un LED de estado opcional.
– GStreamer nos dará decodificación de AAC/MP3/OGG/FLAC vía plugins good/bad (evita dolores de cabeza con codecs).
Cableado: Raspberry Pi Zero 2 W ↔ PCM5102A (I2S)
Conecta las señales I2S y alimentación. Asegúrate de usar masas comunes y evitar cables muy largos para BCLK/LRCK/DATA.
Tabla de pines (Raspberry Pi Zero 2 W → PCM5102A):
| Función I2S | Raspberry Pi GPIO (BCM) | Pin físico (J8) | PCM5102A pin habitual | Notas |
|---|---|---|---|---|
| BCLK (bit clock) | GPIO18 (PCM_CLK) | 12 | BCK / BCLK | Señal principal de reloj |
| LRCK (WS/Frame) | GPIO19 (PCM_FS) | 35 | LRCK / LCK / WS | Word select (izq/der) |
| DATA (SD / DIN) | GPIO21 (PCM_DOUT) | 40 | DIN | Datos hacia el DAC |
| GND | GND | 6, 9, 14, 20, 25, 30, 34, 39 | GND | Masa común |
| 3V3 / 5V | 3V3 (pin 1/17) o 5V (pin 2/4) | 1/17 o 2/4 | VCC / VIN | Verifica si tu módulo acepta 5V o solo 3.3V |
- PCM_DIN (input del DAC) debe ir al PCM_DOUT de la Pi (GPIO21/pin 40).
- PCM5102A no necesita MCLK (usa PLL interna con BCLK/LRCK).
- Si usas un LED de estado:
- LED Anodo → GPIO13 (pin 33) a través de resistencia 330 Ω.
- LED Cátodo → GND.
Prueba rápida de audio con ALSA
Antes de software personalizado, valida el camino I2S con un tono:
# Identifica la tarjeta (ajusta -D hw:0,0 o hw:1,0 según aplay -l)
speaker-test -c 2 -r 44100 -D hw:0,0 -t sine
# Ctrl+C para parar
Si escuchas el tono en los altavoces, el camino I2S está OK.
Código completo
Crearemos un reproductor de audio de red (HTTP/HTTPS, Icecast, streams directos) que decodifica con GStreamer y envía PCM a ALSA en el dispositivo I2S (PCM5102A). Tendrá:
- Pipeline de GStreamer con uridecodebin → audioconvert → audioresample → volume → alsasink (device=hw:X,Y).
- Servidor HTTP con aiohttp para controlar:
- POST /play con JSON { «uri»: «…» }
- POST /stop
- PUT /volume?val=0..1
- GET /status
- LED de actividad (opcional) con gpiozero para estados: buscando buffer, reproduciendo, detenido.
Estructura del proyecto:
- ~/i2s-network-audio-player/
- .venv/ (entorno virtual)
- player.py
- config.yaml (opcional)
- i2s-player.service (systemd, opcional)
player.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import argparse
import asyncio
import json
import os
import signal
import sys
from contextlib import suppress
from typing import Optional
import gi
gi.require_version("Gst", "1.0")
from gi.repository import Gst, GObject # noqa: E402
try:
from gpiozero import LED
except Exception:
LED = None # LED opcional, si no hay GPIOZero instalado
from aiohttp import web
Gst.init(None)
class I2SNetworkPlayer:
def __init__(self, alsa_device: str, initial_uri: Optional[str] = None, use_led_pin: Optional[int] = None):
self.alsa_device = alsa_device
self.current_uri = initial_uri
self.pipeline = None
self.loop = asyncio.get_event_loop()
self._status = {
"state": "stopped",
"uri": None,
"volume": 1.0,
"alsa_device": alsa_device
}
self.led = None
if LED and use_led_pin is not None:
self.led = LED(use_led_pin)
self.led.off()
def _update_led(self, state: str):
if not self.led:
return
# Estados LED:
# - playing: encendido
# - buffering/starting: parpadeo lento
# - stopped/error: apagado
if state == "playing":
self.led.on()
elif state in ("buffering", "starting"):
# parpadeo lento: 1 Hz
self.led.blink(on_time=0.5, off_time=0.5)
else:
self.led.off()
def build_pipeline(self, uri: str, volume: float):
# Limpia pipeline existente
if self.pipeline:
self.pipeline.set_state(Gst.State.NULL)
self.pipeline = None
pipeline = Gst.Pipeline.new("i2s_net_audio_player")
src = Gst.ElementFactory.make("uridecodebin", "src")
if not src:
raise RuntimeError("No se pudo crear uridecodebin (falta plugin GStreamer).")
src.set_property("uri", uri)
convert = Gst.ElementFactory.make("audioconvert", "convert")
resample = Gst.ElementFactory.make("audioresample", "resample")
vol = Gst.ElementFactory.make("volume", "volume")
vol.set_property("volume", volume)
caps = Gst.Caps.from_string("audio/x-raw,format=S16LE,channels=2,rate=44100")
capsfilter = Gst.ElementFactory.make("capsfilter", "caps")
capsfilter.set_property("caps", caps)
sink = Gst.ElementFactory.make("alsasink", "sink")
sink.set_property("device", self.alsa_device)
sink.set_property("sync", True) # sincroniza timestamps
# sink.set_property("buffer-time", 500000) # 500ms (ajustar para latencia/robustez)
for elem in (convert, resample, vol, capsfilter, sink):
if not elem:
raise RuntimeError("Falta un elemento de GStreamer. Verifica plugins instalados.")
pipeline.add(elem)
# uridecodebin es dinámico: conectamos su pad 'src' cuando esté listo
def on_pad_added(_src, pad):
sink_pad = convert.get_static_pad("sink")
if not sink_pad.is_linked():
pad.link(sink_pad)
src.connect("pad-added", on_pad_added)
pipeline.add(src)
# Enlaza elementos fijos
assert convert.link(resample)
assert resample.link(vol)
assert vol.link(capsfilter)
assert capsfilter.link(sink)
# Gestión de mensajes del bus (estado, errores, EOS)
bus = pipeline.get_bus()
bus.add_signal_watch()
def on_message(_bus, msg):
t = msg.type
if t == Gst.MessageType.EOS:
self._status["state"] = "stopped"
self._update_led("stopped")
pipeline.set_state(Gst.State.NULL)
elif t == Gst.MessageType.ERROR:
err, dbg = msg.parse_error()
self._status["state"] = "error"
self._status["error"] = str(err)
if dbg:
self._status["debug"] = dbg
self._update_led("stopped")
pipeline.set_state(Gst.State.NULL)
elif t == Gst.MessageType.STATE_CHANGED:
if msg.src == pipeline:
old, new, _ = msg.parse_state_changed()
if new == Gst.State.PLAYING:
self._status["state"] = "playing"
self._update_led("playing")
elif new == Gst.State.PAUSED:
self._status["state"] = "paused"
self._update_led("buffering")
elif new in (Gst.State.READY, Gst.State.NULL):
self._status["state"] = "stopped"
self._update_led("stopped")
return True
bus.connect("message", on_message)
self.pipeline = pipeline
async def play(self, uri: str):
self.current_uri = uri
self._status["uri"] = uri
self._status["state"] = "starting"
self._update_led("starting")
if not self.pipeline:
self.build_pipeline(uri, self._status["volume"])
else:
# reconstruye pipeline para nueva URI
self.build_pipeline(uri, self._status["volume"])
self.pipeline.set_state(Gst.State.PLAYING)
async def stop(self):
if self.pipeline:
self.pipeline.set_state(Gst.State.NULL)
self._status["state"] = "stopped"
self._update_led("stopped")
async def set_volume(self, val: float):
val = max(0.0, min(1.5, val)) # permite boost leve hasta 150% si se desea
self._status["volume"] = val
if self.pipeline:
vol_elem = self.pipeline.get_by_name("volume")
if vol_elem:
vol_elem.set_property("volume", val)
def status(self):
return dict(self._status)
async def create_app(player: I2SNetworkPlayer):
routes = web.RouteTableDef()
@routes.get("/status")
async def status(_request):
return web.json_response(player.status())
@routes.post("/play")
async def play(request):
data = await request.json()
uri = data.get("uri")
if not uri:
return web.json_response({"error": "Falta 'uri'."}, status=400)
await player.play(uri)
return web.json_response({"ok": True, "uri": uri})
@routes.post("/stop")
async def stop(_request):
await player.stop()
return web.json_response({"ok": True})
@routes.put("/volume")
async def volume(request):
qs = request.rel_url.query
v = qs.get("val")
if v is None:
return web.json_response({"error": "Falta 'val' en querystring."}, status=400)
try:
val = float(v)
except ValueError:
return web.json_response({"error": "Valor no numérico."}, status=400)
await player.set_volume(val)
return web.json_response({"ok": True, "volume": val})
app = web.Application()
app.add_routes(routes)
return app
def parse_args():
p = argparse.ArgumentParser(description="i2s-network-audio-player para Raspberry Pi Zero 2 W + PCM5102A")
p.add_argument("--device", default="hw:0,0", help="Dispositivo ALSA (ej: hw:0,0 o hw:1,0)")
p.add_argument("--uri", default=None, help="URI inicial a reproducir (http(s)://, icecast, etc.)")
p.add_argument("--port", type=int, default=8080, help="Puerto HTTP de control")
p.add_argument("--led-pin", type=int, default=None, help="GPIO BCM para LED de estado (opcional)")
return p.parse_args()
async def main_async():
args = parse_args()
player = I2SNetworkPlayer(alsa_device=args.device, initial_uri=args.uri, use_led_pin=args.led_pin)
app = await create_app(player)
runner = web.AppRunner(app)
await runner.setup()
site = web.TCPSite(runner, host="0.0.0.0", port=args.port)
await site.start()
# Reproduce URI inicial si fue proporcionada
if args.uri:
await player.play(args.uri)
# Mantén el bucle corriendo hasta señal de terminación
stop_event = asyncio.Event()
def _handle_sig(*_):
stop_event.set()
for sig in (signal.SIGINT, signal.SIGTERM):
signal.signal(sig, _handle_sig)
await stop_event.wait()
await player.stop()
await runner.cleanup()
def main():
try:
asyncio.run(main_async())
except KeyboardInterrupt:
pass
return 0
if __name__ == "__main__":
sys.exit(main())
Puntos clave del código:
– uridecodebin detecta y decodifica automáticamente el formato de la URI (MP3/AAC/FLAC/OGG).
– capsfilter fuerza la salida a PCM 16 bits, 44.1 kHz estéreo, típico de streams musicales.
– alsasink apunta a hw:X,Y (por defecto hw:0,0) que mapea al “snd_rpi_hifiberry_dac”.
– Aiohttp expone una API de control simple para play/stop/status/volumen.
– LED de estado opcional en GPIO13.
Compilación/flash/ejecución
No hay compilación en sentido estricto, pero configuraremos el entorno y lanzaremos el servicio. Todos los comandos son para Raspberry Pi OS Bookworm 64‑bit con Python 3.11.
1) Crear entorno de trabajo y venv
# Directorio del proyecto
cd ~
mkdir -p i2s-network-audio-player
cd i2s-network-audio-player
# Entorno virtual con Python 3.11
python3 -m venv .venv
source .venv/bin/activate
# Actualiza pip dentro del venv
pip install --upgrade pip==23.0.1
# Instala dependencias Python de la app (aiohttp, pyyaml si quieres añadir config)
pip install aiohttp==3.9.5 PyYAML==6.0.2
Nota: PyGObject (gi) y GStreamer los instalamos por apt previamente; no uses pip para PyGObject en la Pi salvo que sepas lo que haces.
2) Guardar el script
nano player.py
# (pega el código anterior y guarda)
chmod +x player.py
3) Validación de GStreamer en CLI
Antes de usar Python, asegúrate que el pipeline base funciona:
# Sustituye la URI por un stream válido; por ejemplo FIP (AAC):
gst-launch-1.0 uridecodebin uri=https://icecast.radiofrance.fr/fip-hifi.aac ! audioconvert ! audioresample ! \
audio/x-raw,format=S16LE,channels=2,rate=44100 ! alsasink device=hw:0,0 sync=true
Si oyes audio, la ruta GStreamer → ALSA → I2S funciona.
4) Ejecutar el reproductor
Ejemplo: iniciar con una URI y LED en GPIO13
source .venv/bin/activate
python ./player.py --device hw:0,0 --port 8080 \
--uri "https://icecast.radiofrance.fr/fip-hifi.aac" \
--led-pin 13
- Accede a http://
:8080/status para ver el estado. - Cambia de stream:
bash
curl -X POST http://<IP>:8080/play \
-H 'Content-Type: application/json' \
-d '{"uri":"http://ice1.somafm.com/groovesalad-128-mp3"}' - Ajusta volumen (0.0 a 1.5):
bash
curl -X PUT "http://<IP>:8080/volume?val=0.8" - Detener:
bash
curl -X POST http://<IP>:8080/stop
5) Arranque automático con systemd (opcional)
Crea la unidad:
nano i2s-player.service
Contenido:
[Unit]
Description=I2S Network Audio Player (Raspberry Pi Zero 2 W + PCM5102A)
After=network-online.target sound.target
Wants=network-online.target
[Service]
Type=simple
User=pi
WorkingDirectory=/home/pi/i2s-network-audio-player
Environment=PYTHONUNBUFFERED=1
ExecStart=/home/pi/i2s-network-audio-player/.venv/bin/python /home/pi/i2s-network-audio-player/player.py --device hw:0,0 --port 8080 --led-pin 13 --uri https://icecast.radiofrance.fr/fip-hifi.aac
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
Instala y habilita:
sudo cp i2s-player.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now i2s-player.service
sudo systemctl status i2s-player.service
Para ver logs:
journalctl -u i2s-player.service -f
Validación paso a paso
1) Verifica que el overlay I2S está cargado
- dmesg | grep -i hifiberry
- aplay -l debe listar “snd_rpi_hifiberry_dac”.
Si no aparece, revisa /boot/firmware/config.txt y que reiniciaste la Pi.
2) Prueba ALSA con tono
- speaker-test -D hw:0,0 -c 2 -r 44100 -t sine
- Debes oír tono alternando canales izquierdo/derecho.
- Sin ruido ni chasquidos a 44.1 kHz.
3) Prueba GStreamer en CLI
- gst-launch-1.0 con una URI comprobada (ver arriba). Si se reproduce, el pipeline base está correcto.
4) Verifica API y flujo con la app Python
- GET /status:
- Debe devolver JSON con state (“playing”, “stopped”, “starting” o “error”), URI, volumen y ALSA device.
- POST /play:
- Respuesta 200 y ok:true. Debes oír audio tras 1–3 s (buffering + decodificación).
- PUT /volume:
- Cambia volumen percibido de forma suave.
- POST /stop:
- Corta la reproducción, state pasa a “stopped”.
- LED (si instalado):
- “starting”/“buffering”: parpadeo.
- “playing”: encendido fijo.
- “stopped”/“error”: apagado.
5) Confirmaciones técnicas
-
Uso de CPU:
bash
top -p $(pgrep -f player.py | tr '\n' ' ')
En Zero 2 W debería ser moderado (10–35%) según códec/bitrates. -
Latencia:
-
Observa tiempo hasta escuchar audio tras POST /play. Debería rondar 1–3 s. Ajustable con buffer-time y propiedades de alsasink/queue si buscas menos latencia (compromete robustez).
-
Sin dropouts:
- Con Wi‑Fi estable, no deberían ocurrir cortes. Si ves underruns (XRUN) en logs, consulta la sección de troubleshooting.
Troubleshooting
1) No aparece la tarjeta “snd_rpi_hifiberry_dac” en aplay -l
– Causas:
– Falta de overlay en /boot/firmware/config.txt.
– Escribiste dtoverlay=mal (nombre incorrecto).
– No reiniciaste.
– Solución:
– Edita y añade:
dtparam=audio=off
dtoverlay=hifiberry-dac
– sudo reboot
– Verifica cables I2S (aunque no impiden enumeración, sí es buena práctica revisar).
2) No se oye nada con speaker-test
– Causas:
– Dispositivo ALSA incorrecto (hw:1,0 en vez de hw:0,0).
– Cableado I2S incorrecto (BCLK/LRCK/DATA invertidos).
– Alimentación del módulo PCM5102A errónea (módulo sin regulador conectado a 5 V).
– Solución:
– aplay -l para identificar card, ajusta -D hw:X,0.
– Revisa tabla de pines; la línea DATA debe ser desde GPIO21 (PCM_DOUT) de la Pi al DIN del DAC.
– Verifica VCC del módulo; si no tiene regulador, usa 3.3 V.
3) Chasquidos o audio entrecortado
– Causas:
– Buffer insuficiente, Wi‑Fi inestable, CPU saturada.
– Solución:
– Conéctate a 2.4 GHz con buena señal, o usa Ethernet USB.
– Ajusta buffers de GStreamer:
– Añade “queue” entre elementos:
uridecodebin ! queue max-size-buffers=0 max-size-time=400000000 ! …
– Aumenta buffer-time en alsasink (p. ej., 600 ms).
– Evita streams con bitrates muy altos si la red es limitada.
4) “No se pudo crear uridecodebin” o “falta plugin”
– Causas:
– Faltan paquetes de GStreamer.
– Solución:
– Reinstala:
bash
sudo apt install -y python3-gi gir1.2-gst-1.0 \
gstreamer1.0-tools gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad
5) Volumen sin efecto
– Causas:
– Propiedad “volume” no conectada (elemento no está en pipeline) o GStreamer usa ruta alternativa.
– Solución:
– Confirma que “volume” se inserta después de resample/convert y antes de alsasink.
– En logs de GStreamer (GST_DEBUG=2) verifica el grafo real.
6) LED no enciende
– Causas:
– No instalaste python3-gpiozero/python3-rpi.gpio.
– LED conectado al pin incorrecto o invertido.
– Solución:
– Instala dependencias:
bash
sudo apt install -y python3-gpiozero python3-rpi.gpio
– Verifica conexión: GPIO13 (BCM), anodo al GPIO con resistencia, cátodo a GND.
7) Distorsión al 100% de volumen
– Causas:
– Overdrive digital o saturación en el amplificador aguas abajo.
– Solución:
– Limita a 0.9–1.0 en la API de volumen.
– Ajusta ganancia en el amplificador/altavoces.
8) La app no arranca en systemd
– Causas:
– Ruta incorrecta a Python o player.py en ExecStart.
– Falta del venv.
– Solución:
– Verifica paths, vuelve a activar venv y reinstala dependencias.
– Revisa logs:
bash
journalctl -u i2s-player.service -b -e
Mejoras/variantes
- Entrada múltiples URIs y lista de reproducción:
- Amplía la API para soportar POST /enqueue, POST /next, GET /queue.
- Soporte de mDNS/Avahi y SSDP:
- Anuncia el servicio HTTP para descubrimiento automático en LAN.
- UPnP/DLNA o AirPlay (RAOP):
- Usa GStreamer con plugins adecuados o integra con shairport-sync (en este caso tu Pi Zero 2 W seguiría usando PCM5102A como salida ALSA por defecto).
- Botones físicos:
- GPIO para Play/Pause, Next/Prev, Mute. Con gpiozero.Button es trivial.
- Pantalla OLED I2C (SSD1306):
- Muestra estado, volumen, título de la pista si el stream emite metadatos ICY.
- Latencia ultrabaja:
- Reduce buffer-time, usa pipelines específicos y QoS; en entornos Wi‑Fi esto sacrificará robustez.
- Configuración persistente:
- Carga config.yaml con ALSA device, volumen por defecto, URI inicial, puertos, etc.
- Resampling de alta calidad:
- Ajusta audioresample (quality=10) y caps a 48 kHz si tu resto de cadena lo prefiere.
Checklist de verificación
Marca cada ítem al completar:
- [ ] Usas Raspberry Pi OS Bookworm 64‑bit con Python 3.11.2, pip 23.0.1 y kernel 6.6.y.
- [ ] Has añadido en /boot/firmware/config.txt: dtparam=audio=off y dtoverlay=hifiberry-dac.
- [ ] Tras reiniciar, aplay -l muestra snd_rpi_hifiberry_dac (card X).
- [ ] Con speaker-test -D hw:X,0 escuchas tono estéreo sin artefactos.
- [ ] Has instalado GStreamer 1.22.x y plugins base/good/bad via apt.
- [ ] Has creado venv (.venv), instalado aiohttp y probado player.py.
- [ ] GET /status responde con JSON; POST /play reproduce un stream audible.
- [ ] PUT /volume modifica el nivel; POST /stop detiene la reproducción.
- [ ] LED de estado en GPIO13 funciona como se espera (opcional).
- [ ] Has verificado el consumo de CPU y ausencia de dropouts en uso normal.
- [ ] (Opcional) El servicio systemd arranca automáticamente y logs están limpios.
Resumen final
Con la Raspberry Pi Zero 2 W y un PCM5102A (vía overlay hifiberry-dac), has construido un i2s-network-audio-player robusto que:
– Recibe audio por red (HTTP/HTTPS/Icecast),
– Decodifica con GStreamer 1.22,
– Entrega PCM 16‑bit 44.1 kHz por I2S al PCM5102A,
– Y expone una API HTTP para control en LAN.
Toda la configuración, materiales, conexiones, código y comandos son coherentes con el modelo “Raspberry Pi Zero 2 W + PCM5102A DAC” y el objetivo del proyecto.
Encuentra este producto y/o libros sobre este tema en Amazon
Como afiliado de Amazon, gano con las compras que cumplan los requisitos. Si compras a través de este enlace, ayudas a mantener este proyecto.




