Skip to content

Music Display (Raspberry Pi LED Matrix)

RGB LED matrix display for Raspberry Pi showing album art (via HEOS/Denon or Navidrome), MBTA transit predictions, and sports scores.

Overview

┌─────────────────────────────────────────────────────────────┐
│                    RASPBERRY PI 4                           │
│                    10.89.97.151                             │
│                                                             │
│  ┌─────────────────┐    ┌─────────────────┐                │
│  │  music-display  │    │ music-display   │                │
│  │    service      │    │    web UI       │                │
│  │  (display.py)   │    │   (web.py)      │                │
│  │                 │    │   :8080         │                │
│  └────────┬────────┘    └─────────────────┘                │
│           │                                                 │
│           ▼                                                 │
│  ┌─────────────────┐                                       │
│  │  64x64 RGB LED  │                                       │
│  │     Matrix      │                                       │
│  │  (Adafruit HAT) │                                       │
│  └─────────────────┘                                       │
└─────────────────────────────────────────────────────────────┘
       │              │                   │
       │ HEOS CLI     │ Subsonic API      │ MBTA API
       ▼              ▼                   ▼
┌────────────┐  ┌──────────────┐   ┌──────────────┐
│   Denon    │  │  Navidrome   │   │  MBTA API    │
│  Receiver  │  │ (arr-stack)  │   │  (public)    │
└────────────┘  └──────────────┘   └──────────────┘

Hardware

  • Raspberry Pi 4 (any RAM size)
  • 64x64 RGB LED Matrix (HUB75 interface)
  • Adafruit RGB Matrix HAT or Bonnet

Network

Service Address
Pi SSH pi@10.89.97.151
Web UI http://10.89.97.151:8080

Features

Album Art Display

  • Shows currently playing track from Navidrome
  • Artist name overlay (configurable position)
  • Smooth transitions between tracks

MBTA Transit Widget

  • Live train/bus predictions
  • Route colors and direction filtering
  • Configurable stops, routes, time windows
  • Bus support (yellow badges with route + destination)
  • Rail support (route color badges with stop name)

Idle Modes

When nothing is playing: - Clock - Current time and date - Transit - MBTA predictions - Static - Last album art - Dim - Low brightness - Blank - Display off

File Structure

/home/pi/music-display/
├── display.py           # Main display service
├── web.py               # Web UI (Flask, port 8080)
├── config.json          # User configuration
├── stops.db             # SQLite cache for MBTA stops
├── core/
│   ├── config.py        # Configuration management
│   ├── manager.py       # Widget lifecycle manager
│   └── renderer.py      # Matrix display output (black_threshold here)
├── widgets/
│   ├── base.py          # Widget base class
│   ├── music.py         # Album art widget
│   ├── clock.py         # Clock widget
│   ├── transit.py       # MBTA transit widget
│   └── sports.py        # ESPN sports scores widget
└── sources/
    ├── navidrome.py     # Navidrome/Subsonic API client
    ├── heos.py          # HEOS/Denon receiver client
    └── mbta.py          # MBTA API client + stops cache

Systemd Services

# Main display service
sudo systemctl status music-display
sudo systemctl restart music-display

# Web UI service
sudo systemctl status music-display-web
sudo systemctl restart music-display-web

Deployment

From Proxmox Host

# Deploy latest code
ssh pi@10.89.97.151 "cd /home/pi/music-display && git pull origin feature-ui && sudo systemctl restart music-display-web"

# Or use the intent
/intents:deploy-music-display

Manual on Pi

cd /home/pi/music-display
git pull origin feature-ui
sudo systemctl restart music-display-web
sudo systemctl restart music-display  # If main.py changed

Configuration

All settings managed via Web UI at http://10.89.97.151:8080

Display Settings

  • Brightness - LED brightness (10-100%)
  • Rotation - Display rotation (0/90/180/270)
  • Text Position - Artist overlay position (top/bottom)
  • Idle Mode - What to show when not playing
  • Black Threshold - Crush near-black pixels to pure black (0-255, default 0)

Black Threshold

Album art often has regions that are almost black but not quite. On an LED matrix, these near-black pixels still emit light, appearing as grey rather than true black (LED off). The black_threshold setting fixes this by clamping any pixel where R, G, and B are all below the threshold to pure black (0,0,0).

Recommended values: - 0 - Disabled (default) - 15-25 - Subtle, preserves dark detail - 30-40 - Aggressive, maximizes true blacks

Configuration:

{
  "display": {
    "black_threshold": 20
  }
}

This setting hot-reloads within 2 seconds when config.json changes.

MBTA Transit Settings

  • Stops - Search and add MBTA stops
  • Routes - Filter by specific routes
  • Directions - Filter inbound/outbound per route
  • Time Window - Min/max minutes to show
  • Rows - Number of predictions to display

Matrix Hardware

  • Hardware Mapping - adafruit-hat / adafruit-hat-pwm
  • GPIO Slowdown - Pi generation (1-4)
  • PWM Bits - Color depth vs flicker tradeoff

Operations

Check Status

# Service status
ssh pi@10.89.97.151 'systemctl status music-display music-display-web'

# View logs
ssh pi@10.89.97.151 'journalctl -u music-display -n 50'
ssh pi@10.89.97.151 'journalctl -u music-display-web -n 50'

Restart Services

ssh pi@10.89.97.151 'sudo systemctl restart music-display'
ssh pi@10.89.97.151 'sudo systemctl restart music-display-web'

View Config

ssh pi@10.89.97.151 'cat /home/pi/music-display/config.json | jq'

Troubleshooting

Display Flickering

  1. Try adafruit-hat-pwm hardware mapping (requires audio disabled)
  2. Increase GPIO slowdown for Pi 4
  3. Reduce PWM bits (7 = less flicker, 11 = more colors)

MBTA Not Loading Stops

  1. Click "Load Stops" button in Web UI
  2. Wait ~30 seconds for 8000+ stops to cache
  3. Search should work after cache loads

Web UI Not Accessible

ssh pi@10.89.97.151 'sudo systemctl restart music-display-web'
ssh pi@10.89.97.151 'journalctl -u music-display-web -n 20'

Source Code

Repository: https://github.com/jakecelentano/music-display Branch: feature-ui (current development)

Music Sources

The display supports multiple music sources for "now playing" data:

HEOS (Default)

Gets playback info directly from the Denon receiver via HEOS CLI protocol. This is the authoritative source for what's actually playing.

Benefits: - Shows what's actually playing on the receiver - Works with any input (Spotify, AirPlay, DLNA, Navidrome, etc.) - No scrobble confusion from multiple clients

Cover Art Fallback Chain: 1. HEOS metadata image URL (if provided) 2. Navidrome album search (local library) 3. iTunes Search API (free, public)

Configuration:

{
  "music": {
    "source": "heos"
  },
  "heos": {
    "host": "10.89.97.15",
    "port": 1255
  }
}

Uses Subsonic API to get now-playing from scrobbles. Useful if not using HEOS.

Configuration:

{
  "music": {
    "source": "navidrome"
  },
  "navidrome": {
    "url": "https://navidrome.example.com",
    "username": "user",
    "password": "pass"
  }
}