If you want to make a Raspberry Pi play Spotify as a dedicated headless streamer, the first rule is to ignore the onboard 3.5mm audio jack. The Pi’s built-in audio is PWM-based, yielding a dismal signal-to-noise ratio (SNR) and a constant background hiss that ruins dynamic range. To get true hi-fi audio, you must route the digital I2S bus directly to an external Digital-to-Analog Converter (DAC) HAT, and use a lightweight daemon like raspotify (a wrapper for librespot) to handle the Spotify Connect protocol.

This guide targets the Raspberry Pi 4 Model B (4GB variant) running Raspberry Pi OS Bookworm. We explicitly avoid the Raspberry Pi 5 for this specific build because the Pi 5 relocated several I2S and PCM pins to the new J2 header, breaking physical and electrical compatibility with 90% of legacy audio HATs on the market without cumbersome ribbon adapters.

Why the Pi's Built-In Audio Fails (And What to Buy)

The Raspberry Pi generates analog audio by pulsing a digital pin (PWM) and relying on a basic RC filter. This results in an SNR of roughly 60dB and high Total Harmonic Distortion (THD). For a Spotify Connect endpoint, you need an I2S (Inter-IC Sound) DAC, which takes the raw digital audio stream directly from the Pi’s CPU and converts it using dedicated silicon.

Audio Output Method Interface SNR (Typical) THD+N Est. Price (2026)
Pi Onboard 3.5mm Jack PWM / RC Filter ~60 dB High (Audible Hiss) $0 (Built-in)
Adafruit I2S 3W Bonnet I2S (MAX98357A) ~95 dB 0.04% $24.95
HiFiBerry DAC+ Standard I2S (PCM5102A) 112 dB 0.0019% $49.90
Allo Boss DAC V2 I2S (PCM5122) 120 dB 0.0008% $79.00

For this build, we are using the HiFiBerry DAC+ Standard. It uses the Texas Instruments PCM5102A chip, requires no external power supply (it pulls 5V from the Pi’s GPIO header), and outputs standard line-level RCA audio to your amplifier or powered monitors.

Parts List & GPIO Pin Mapping

Beyond the DAC, we are adding three physical tactile switches to control playback without needing to unlock your phone. Because the I2S protocol claims specific hardware PCM pins on the Pi 4, we must map our buttons to available, non-conflicting GPIOs.

Bill of Materials

  • Compute: Raspberry Pi 4 Model B (4GB or 8GB)
  • OS: Raspberry Pi OS Lite (Bookworm, 64-bit)
  • Audio: HiFiBerry DAC+ Standard (or DAC+ Pro)
  • Controls: 3x 12mm Tactile Pushbuttons (Normally Open)
  • Wiring: 22 AWG stranded hookup wire, female-to-female Dupont jumpers
  • Power: Official 27W USB-C Power Supply (critical to prevent brownouts when the DAC draws peak current)

Pin Mapping Table

Function BCM GPIO Physical Pin Notes / Constraints
I2S Bit Clock (BCLK) GPIO 18 Pin 12 Reserved by HAT (PCM_CLK)
I2S Left/Right Clock GPIO 19 Pin 35 Reserved by HAT (PCM_FS)
I2S Data In (DIN) GPIO 21 Pin 40 Reserved by HAT (PCM_DIN)
Button: Play / Pause GPIO 5 Pin 29 Pulled HIGH internally, ground to trigger
Button: Skip Next GPIO 6 Pin 31 Pulled HIGH internally, ground to trigger
Button: Skip Previous GPIO 13 Pin 33 Pulled HIGH internally, ground to trigger
Wiring Tip: Wire one leg of each tactile button to a shared Ground (GND) pin (e.g., Pin 39), and the other leg to the respective BCM GPIO pins (5, 6, 13). The Pi’s internal pull-up resistors will handle the rest; no external resistors are required.

Software Setup: I2S Configuration & Raspotify

Raspberry Pi OS Bookworm changed the boot partition mount point and shifted to PipeWire for desktop audio. For a headless Spotify streamer, we bypass PipeWire and let raspotify talk directly to ALSA.

Step 1: Enable the I2S Overlay

SSH into your Pi and edit the boot configuration file. Note the Bookworm path (/boot/firmware/ instead of the legacy /boot/):

sudo nano /boot/firmware/config.txt

Add the following line at the very bottom of the file to load the HiFiBerry device tree overlay:

dtoverlay=hifiberry-dac

Reboot the Pi (sudo reboot). After rebooting, verify the DAC is recognized by ALSA:

aplay -l

You should see card 0: sndrpihifiberry [snd_rpi_hifiberry_dac].

Step 2: Install and Configure Raspotify

Install the raspotify package, which registers your Pi as a Spotify Connect target on your local network.

sudo apt-get install curl apt-transport-https
sudo curl -sSL https://dtcooper.github.io/raspotify/key.asc | sudo tee /usr/share/keyrings/raspotify_key.asc > /dev/null
echo 'deb [signed-by=/usr/share/keyrings/raspotify_key.asc] https://dtcooper.github.io/raspotify raspotify main' | sudo tee /etc/apt/sources.list.d/raspotify.list
sudo apt-get update
sudo apt-get install raspotify

Edit the raspotify config to force it to use the I2S DAC:

sudo nano /etc/raspotify/conf

Find the LIBRESPOT_DEVICE line, uncomment it, and set it to your ALSA hardware ID:

LIBRESPOT_DEVICE="hw:sndrpihifiberry"

Restart the service: sudo systemctl restart raspotify. Open the Spotify app on your phone; your Pi should now appear in the 'Devices' menu.

Python Control Script (GPIO Buttons to Spotify API)

To make the physical buttons work, we use the Spotify Web API via the spotipy library. Note: Controlling playback via the API requires a Spotify Premium account.

First, install the dependencies:

sudo apt-get install python3-gpiozero python3-pip
pip3 install spotipy --break-system-packages

Create the control script:

sudo nano /home/pi/spotify_gpio.py
import time
import signal
import sys
import spotipy
from spotipy.oauth2 import SpotifyOAuth
from gpiozero import Button
from gpiozero.pins.pigpio import PiGPIOFactory

# --- PIN DEFINITIONS ---
PIN_PLAY_PAUSE = 5
PIN_NEXT = 6
PIN_PREV = 13

# --- SPOTIFY API CREDENTIALS ---
# Create an app at https://developer.spotify.com/dashboard
CLIENT_ID = 'your_actual_client_id'
CLIENT_SECRET = 'your_actual_client_secret'
REDIRECT_URI = 'http://127.0.0.1:8080/callback'
USERNAME = 'your_spotify_username'

SCOPE = 'user-modify-playback-state user-read-playback-state'

def graceful_exit(signum, frame):
    print('\nExiting gracefully...')
    sys.exit(0)

signal.signal(signal.SIGINT, graceful_exit)
signal.signal(signal.SIGTERM, graceful_exit)

def get_spotify_client():
    try:
        auth_manager = SpotifyOAuth(
            client_id=CLIENT_ID,
            client_secret=CLIENT_SECRET,
            redirect_uri=REDIRECT_URI,
            scope=SCOPE,
            username=USERNAME,
            cache_path='/home/pi/.spotify_cache'
        )
        token_info = auth_manager.get_cached_token()
        if not token_info or auth_manager.is_token_expired(token_info):
            token_info = auth_manager.refresh_access_token(token_info['refresh_token'])
        
        return spotipy.Spotify(auth=token_info['access_token'])
    except Exception as e:
        print(f'Auth Error: {e}')
        return None

sp = get_spotify_client()
if not sp:
    sys.exit('Failed to authenticate. Run script manually once to complete OAuth flow.')

# Use pigpio for better debounce handling
factory = PiGPIOFactory()
btn_play = Button(PIN_PLAY_PAUSE, pull_up=True, bounce_time=0.05, pin_factory=factory)
btn_next = Button(PIN_NEXT, pull_up=True, bounce_time=0.05, pin_factory=factory)
btn_prev = Button(PIN_PREV, pull_up=True, bounce_time=0.05, pin_factory=factory)

def toggle_play():
    try:
        playback = sp.current_playback()
        if playback and playback['is_playing']:
            sp.pause_playback()
        else:
            sp.start_playback()
    except spotipy.exceptions.SpotifyException as e:
        print(f'Playback Error: {e}')

def skip_next():
    try:
        sp.next_track()
    except spotipy.exceptions.SpotifyException as e:
        print(f'Skip Error: {e}')

def skip_prev():
    try:
        sp.previous_track()
    except spotipy.exceptions.SpotifyException as e:
        print(f'Prev Error: {e}')

btn_play.when_pressed = toggle_play
btn_next.when_pressed = skip_next
btn_prev.when_pressed = skip_prev

print('Spotify GPIO Controller active. Press Ctrl+C to exit.')
while True:
    time.sleep(1)  # Keep main thread alive

First Run Note: You must run this script manually (python3 spotify_gpio.py) once while logged in via SSH with port forwarding, or via a desktop browser, to complete the OAuth web redirect and cache the token at /home/pi/.spotify_cache. Afterward, it can run headlessly as a systemd service.

Debugging: When Raspberry Pi Play Spotify Fails

When building embedded audio systems, things break at the intersection of hardware overlays, ALSA routing, and API authentication. If your build fails, here are the first three things to check:

  1. The Device Tree Overlay: Did you put dtoverlay=hifiberry-dac in /boot/firmware/config.txt (Bookworm) instead of the legacy /boot/config.txt? If it's in the wrong file, the Pi boots with the onboard PWM audio active.
  2. Spotify Premium Status: The Spotify Connect protocol and the Web API playback endpoints strictly require a Premium subscription. Free accounts will authenticate but throw 403 errors on playback commands.
  3. ALSA Device Routing: Run aplay -l. If the Pi lists vc4-hdmi as card 0 and your DAC as card 1, raspotify might be sending audio to the HDMI port. Force the hardware ID in the raspotify config.

Common Error Strings & Ranked Causes

Error 1: ALSA lib pcm_hw.c:1829:(_snd_pcm_hw_open) Invalid value for card
Cause: The I2S overlay failed to load, or the DAC is not seated properly on the GPIO header. The ALSA string hw:sndrpihifiberry points to hardware that the kernel doesn't see.
Fix: Power down, reseat the HAT, verify the standoff alignment, and check dmesg | grep hifiberry for I2C probe failures.
Error 2: spotipy.exceptions.SpotifyException: http status: 403, code:-1 - Player command failed: Premium required
Cause: The authenticated user token belongs to a Free-tier Spotify account.
Fix: Log into the Spotify Developer Dashboard, ensure the test user added to your app's whitelist has an active Premium subscription, and delete the .spotify_cache file to force re-authentication.
Error 3: raspotify[xxxx]: connect: Connection refused (in journalctl -u raspotify)
Cause: Network isolation or Avahi/mDNS daemon failure. Spotify Connect relies on mDNS to discover devices on the local subnet.
Fix: Ensure your Pi and your phone are on the exact same VLAN/Subnet. Restart the mDNS service: sudo systemctl restart avahi-daemon.

Extending and Simplifying the Build

Depending on your use case, you may want to strip this project down to its bare essentials or scale it up into a standalone kiosk.

How to Simplify (The Headless Receiver)

If you don't care about physical buttons and just want a high-quality audio receiver for your workshop or kitchen, delete the Python script and the tactile buttons entirely. The raspotify daemon runs automatically on boot. You simply open Spotify on your phone, tap the 'Devices' icon, and select the Pi. This eliminates all API token expiration headaches and reduces the build to a pure hardware/ALSA configuration.

How to Extend (Adding Metadata Displays)

To show the current track name and album art, you can add an OLED display. However, do not use a standard I2C OLED (like the SSD1306). Many I2S DAC HATs (including some HiFiBerry variants) use the I2C bus to communicate with onboard EEPROMs or hardware volume chips, which can cause address collisions and lock up the display.

Instead, extend the build using an SPI-based display (like the ST7789 1.3-inch IPS LCD). SPI uses separate GPIO pins (MOSI, SCLK, CE0) that do not conflict with the I2S PCM bus or the I2C bus, ensuring your audio stream remains uninterrupted while you render album art via the Pillow and spotipy libraries.

For more information on Raspberry Pi hardware interfaces, consult the official Raspberry Pi configuration documentation. For API limits and scopes, refer to the Spotify Developer Web API reference. If you are exploring alternative DAC HATs, the HiFiBerry knowledge base provides exact overlay strings for their entire product lineup.