Skip to content
/ SPNFY Public

Display Spotify's currently playing album art on a circular display with ESP32.

License

Notifications You must be signed in to change notification settings

Saqoosha/SPNFY

Repository files navigation

SPNFY

🌐 日本語

SPNFY Preview

Display Spotify's currently playing album art on a circular display with ESP32.

📝 Write-up: 円形ディスプレイに Spotify カバーアートを表示するデバイスを作った話 (Japanese)

Hardware

JC3636W518 - ESP32-S3 module with 1.8" 360x360 circular display (ST77916, QSPI) and touch (CST816S)

Features

  • Rotating vinyl record animation with album art
  • Smooth start/stop rotation (accelerates when playing, decelerates when paused)
  • Anti-aliased circular mask edges
  • Touch controls:
    • Center tap: Play/pause toggle
    • Left side tap: Previous track
    • Right side tap: Next track
    • Swipe left: Next track
    • Swipe right: Previous track
  • Cover art transitions (circular reveal animation)
  • Dynamic Spotify polling (faster detection at track end)
  • Rate limit handling with automatic backoff
  • SD card album art cache (~60x faster loading)
  • Device filtering: Only display playback from specific devices (see docs/DEVICE_FILTERING.md)

Project Structure

SPNFY/
├── SPNFY/               # Main ESP32 Arduino sketch
│   ├── SPNFY.ino        # Main entry point
│   ├── config.example.h # Copy to config.h with your credentials
│   ├── display.h        # Display types and declarations
│   ├── display.cpp      # Rotation rendering, compositing
│   ├── display_init.cpp # Display & backlight initialization
│   ├── display_send.cpp # Double buffering, DMA transfer
│   ├── image_processing.h/cpp # Album art buffers, mask bounds, AA
│   ├── spotify.cpp/h    # Spotify API (auth, now playing, playback)
│   ├── touch.cpp/h      # Touch input handling (tap, swipe)
│   ├── wifi_manager.cpp/h # WiFi connection
│   ├── sd_cache.cpp/h   # SD card album art cache
│   ├── font.cpp/h       # 5x7 bitmap font for status text
│   ├── log.h            # Debug logging macros
│   ├── pins.h           # Hardware pin definitions
│   ├── circle_aa_lut.h  # Auto-generated AA lookup table
│   └── vinyl_texture.h  # Vinyl record texture
├── token-retriever/     # Local server for Spotify token retrieval
├── esp32_display_test/  # Display test sketch
├── tools/               # Development tools
│   ├── arduwrap         # ESP32 serial monitor/compile wrapper
│   ├── generate_circle_aa_lut.py  # Generate AA boundary LUT
│   └── convert_image.py # Image conversion utility
├── docs/                # Documentation
└── README.md

Setup

1. Create Spotify Developer App

  1. Go to Spotify Developer Dashboard
  2. Click "Create app" → Enter app name and description
  3. Set Redirect URI: http://127.0.0.1:8888/callback
  4. Copy the Client ID

2. Get Refresh Token

cd token-retriever
mise trust && mise install
mise exec -- bun run dev

Open http://127.0.0.1:8888 in your browser and authenticate.

Required scopes: user-read-currently-playing user-read-playback-state user-modify-playback-state

3. Configure ESP32

Copy SPNFY/config.example.h to SPNFY/config.h and fill in:

  • WiFi SSID and password
  • Spotify Client ID
  • Spotify Refresh Token (from step 2)

4. Upload to ESP32

Required Arduino Libraries:

  • ESP32_Display_Panel 0.1.4 + ESP32_IO_Expander 0.0.2 - Get from manufacturer's download page in Demo_Arduino_V1.0/library/
  • JPEGDEC - From Arduino Library Manager
  • ArduinoJson - From Arduino Library Manager

⚠️ Important: You MUST use exact versions above. Latest versions from Arduino Library Manager DO NOT WORK.

Arduino ESP32 Core: 3.0.1

Using arduwrap (Recommended)

arduwrap monitors the serial port and automatically releases/reopens during compile/upload. Port is auto-detected from the serve process.

# Setup (first time)
cd tools && uv sync

# Start monitor server (separate terminal)
./tools/arduwrap serve --port /dev/cu.usbmodem1101 --baud 115200

# Compile and upload (port auto-detected from serve)
./tools/arduwrap compile \
  --fqbn esp32:esp32:esp32s3:USBMode=hwcdc,CDCOnBoot=cdc,FlashMode=qio,FlashSize=16M,PartitionScheme=huge_app,PSRAM=opi \
  SPNFY/SPNFY.ino

# Get logs
./tools/arduwrap log                 # Full log
./tools/arduwrap log -f "error" -i   # Filter (case-insensitive)
./tools/arduwrap log -n 50           # Last 50 lines

Using arduino-cli directly

arduino-cli compile \
  --fqbn esp32:esp32:esp32s3:USBMode=hwcdc,CDCOnBoot=cdc,FlashMode=qio,FlashSize=16M,PartitionScheme=huge_app,PSRAM=opi \
  SPNFY/SPNFY.ino

arduino-cli upload -p /dev/cu.usbmodem1101 \
  --fqbn esp32:esp32:esp32s3:USBMode=hwcdc,CDCOnBoot=cdc,FlashMode=qio,FlashSize=16M,PartitionScheme=huge_app,PSRAM=opi \
  SPNFY/SPNFY.ino

Architecture

Dual-Core Design

  • Core 0: Spotify API polling, HTTP requests, image download
  • Core 1: Rendering loop, touch handling, display updates

This separation ensures smooth 30 FPS animation even during network operations.

Rendering Pipeline

  1. Vinyl texture (360×360): Nearest neighbor sampling for outer ring
  2. Album art (300×300): Bilinear interpolation, scaled to 286px diameter
  3. Small composite (292×292): Pre-baked cover art + AA boundary
  4. Double buffering: Render and send in parallel for 30+ FPS

Documentation

License

MIT

About

Display Spotify's currently playing album art on a circular display with ESP32.

Resources

License

Stars

Watchers

Forks

Contributors 3

  •  
  •  
  •