LED Ring Synchronization
Relevant source files
The following files were used as context for generating this wiki page:
The LED Ring Synchronization system is responsible for controlling a WS2812B 16-LED ring connected to a Raspberry Pi Zero 2 W. This feature allows the LED ring to pulse in synchronization with audio beat data, display static colors, or perform breathing animations based on commands sent from a companion phone application. The synchronization is achieved through a dedicated Python daemon that interfaces with the hardware via SPI (Serial Peripheral Interface) on GPIO 10.
Sources: README.md:1-12, CLAUDE.md:65-68
Hardware and Communication Architecture
The LED ring is physically wired to the Raspberry Pi Zero 2 W using the SPI bus. The software architecture utilizes a multi-layered approach where high-level commands are generated by the phone app, transmitted via WebSocket to the Pi app, and then processed by a low-level Python daemon.
Technical Specifications
- Hardware: WS2812B 16-LED Ring.
- Interface: GPIO 10 (SPI).
- SPI Frequency: 2.4 MHz (2,400,000 Hz).
- Control Protocol: Unidirectional flow from Phone Client to Pi Server.
Sources: README.md:12-14, apps/pi_app/led_daemon.py:12-14, CLAUDE.md:28-30
System Data Flow
The following diagram illustrates the command flow from the user interface to the physical LED hardware.
The Pi app utilizes flutter_bloc for state management, specifically the LedBloc which handles LedCommand models defined in the core package.
Sources: README.md:28-40, CLAUDE.md:32-34, apps/pi_app/led_daemon.py:85-98
LED Daemon Implementation
The led_daemon.py script serves as the low-level driver. It employs a multi-threaded architecture to separate the command intake (stdin) from the hardware rendering loop.
Rendering Logic
The daemon encodes standard RGB values into the specific bit-timing format required by WS2812B controllers. Since WS2812B uses a specific pulse-width modulation protocol, each data bit is encoded into three SPI bits to simulate the required waveforms.
| Component | Description |
|---|---|
NUM_LEDS | Constant set to 16 for the ring size. |
_encode_byte | Converts 1 byte of color into 3 SPI bytes (3 bits per WS bit). |
_pixel | Combines G, R, and B encoded bytes (GRB order). |
_send_frame | Transfers the full buffer to the SPI bus followed by a reset signal. |
Sources: apps/pi_app/led_daemon.py:12-36
Animation Modes
The system supports four distinct operational modes processed within the _mode_loop function.
The breathe mode specifically uses a sine envelope with a period of approximately 2 seconds at 30 frames per second.
Sources: apps/pi_app/led_daemon.py:46-77
Data Models and Configuration
Synchronization relies on the LedCommand model shared via the bt_speaker_core package.
LED Command Parameters
The daemon expects a JSON object via standard input with the following fields:
| Field | Type | Description |
|---|---|---|
mode | String | "off", "static", "beat_sync", or "breathe". |
r | Integer | Red component (0-255). |
g | Integer | Green component (0-255). |
b | Integer | Blue component (0-255). |
brightness | Integer | Master brightness scalar (0-255). |
Sources: apps/pi_app/led_daemon.py:53-57, CLAUDE.md:12-14
Python SPI Integration
def main():
spi = spidev.SpiDev()
spi.open(0, 0)
spi.max_speed_hz = 2400000
spi.mode = 0b00
# ... thread initialization ...
Sources: apps/pi_app/led_daemon.py:80-84
Summary
LED Ring Synchronization provides real-time visual feedback for the BT Speaker system. By offloading the timing-sensitive WS2812B signaling to a Python daemon using SPI, the system ensures smooth animations (30-60 Hz) without blocking the main Flutter UI thread on the Raspberry Pi Zero 2 W hardware.
Sources: README.md:43-46, CLAUDE.md:65-68