Skip to main content
This page provides a comprehensive overview of the Spotify MacroBoard source code, explaining the main components, program flow, and key functions.

Code structure

The Spotify MacroBoard code is organized into several functional sections:
  • Setup and initialization: Hardware configuration and WiFi connection
  • Display management: OLED screen updates and graphics
  • LED control: RGB LED strip animations and color transitions
  • Button handling: Input detection and action triggering
  • API communication: HTTP requests to Spotify control server
  • State management: Tracking playback state and metadata

Libraries and dependencies

The code uses several libraries for hardware control and networking:

Global objects and variables

Hardware objects

  • wifiClient: Secure HTTPS client for API communication
  • I2C: I2C bus interface for OLED display
  • display: SSD1306 OLED display object (128x64 pixels)
  • LEDs: Array of RGB LED objects (20 LEDs)

Button state tracking

Buttons are configured with internal pull-up resistors, so they read HIGH when not pressed and LOW when pressed.

Timing control

The code uses non-blocking delays to handle multiple tasks concurrently.

Playback state

Setup function

The setup() function runs once when the ESP32 boots and initializes all hardware components.
1

Configure GPIO pins

Buttons use internal pull-up resistors to eliminate the need for external resistors.
2

Initialize LED strip

LEDs start white to indicate power-on. Power is limited to 500mA for safety.
3

Initialize OLED display

I2C runs at 400kHz for faster display updates. If the display fails to initialize (wrong address, disconnected, etc.), LEDs turn orange-red and the system halts.
4

Connect to WiFi

LEDs pulse white while connecting. The pulsing animation provides visual feedback during connection.
5

Indicate successful connection

LEDs smoothly transition from white to green over 768ms to indicate successful WiFi connection.
6

Configure SSL certificate

Sets the server certificate for HTTPS verification.

Main loop

The loop() function continuously monitors buttons, updates the display, and synchronizes playback state.

Loop execution flow

  1. Button detection: Scans all 7 buttons for state changes (edge detection)
  2. WiFi monitoring: Updates signal strength indicator every 10 seconds
  3. Track sync: Fetches current track metadata every 5 seconds
  4. Progress tracking: Updates progress bar every second (increments locally if playing)
  5. LED animation: Smoothly fades LEDs when track changes (4ms per step)
The loop uses non-blocking delays with millis() timing. This allows multiple tasks to run concurrently without freezing the system.

Key functions

Button handling

Each button triggers a specific function through a function pointer array:
Button functions call updateState() with an action character:
The skip function sends a skip command, then immediately requests updated track info.

API communication

The updateState() function sends control commands to the server:
API endpoint format: GET /api/manageState/{PASSWORD}/{ACTION}Actions: playPause, back, skip, vdec, vinc, loop, shuffle

Fetching current track

The updateCurrent() function retrieves track metadata:

JSON parsing

The code uses a simple string-based JSON parser:
This lightweight parser extracts values by finding key patterns and extracting substrings, avoiding the overhead of a full JSON library.

LED color transitions

When a new track starts, LEDs smoothly transition to the album’s dominant color:
The fade takes 256 steps × 4ms = 1,024ms (about 1 second) to complete.

Display updates

The updateScreen() function draws the top status bar with WiFi signal and volume:
The status bar uses 16×16 pixel bitmap icons for WiFi signal strength and volume level.

Progress bar rendering

The updateTime() function draws the playback progress bar:

Error indication

The code uses LED colors to indicate different error states:

Memory usage

The compiled sketch has the following resource utilization on ESP32:
The code leaves 33% of flash storage free, allowing for future feature additions or larger assets.

Performance considerations

Non-blocking architecture

All timing uses millis() instead of delay(), allowing:
  • Responsive button input
  • Smooth LED animations
  • Concurrent display updates
  • Uninterrupted network communication

Update frequencies

Connection management

HTTP connections use Keep-Alive to reduce overhead:
Connections are only established when needed and reused across requests.

Customization opportunities

Adjust update intervals

Modify timing constants to change update frequencies:

Change LED brightness

Adjust LED brightness in SMBCredentials.h:

Modify button actions

Reorder or replace button functions:

Next steps

Now that you understand the code structure:

Hardware assembly

Build the physical MacroBoard

Troubleshooting

Resolve common issues