LED Controller Daemon (led_daemon.py)
Relevant source files
The following files were used as context for generating this wiki page:
The LED Controller Daemon is a Python-based background service responsible for driving a 16-LED WS2812B RGB ring connected to a Raspberry Pi Zero 2 W. It serves as the hardware interface layer, translating high-level commands received from the Flutter-based pi_app into low-level SPI signals required by the LED hardware.
Within the broader architecture, this daemon operates alongside a WebSocket server that handles incoming requests from the companion phone application. While the Flutter UI handles visual feedback on the HDMI display, this daemon ensures the physical LED hardware synchronizes with audio beats and user-defined lighting modes.
Sources: apps/pi_app/led_daemon.py:1-4, README.md:1-20, CLAUDE.md:6-18
System Architecture and Data Flow
The daemon employs a multi-threaded architecture to separate command ingestion from hardware rendering. It utilizes a shared state dictionary protected by a threading lock to communicate between the main input loop and the rendering loop.
Command Pipeline
- Input (Main Thread): Reads JSON-formatted commands from
sys.stdin. - State Management: Updates a shared
statedictionary under athreading.Lock. - Rendering (Mode Loop Thread): A dedicated daemon thread continuously reads the state and generates the corresponding SPI signals for the LEDs.
The diagram shows the unidirectional flow of control data from standard input to the physical hardware.
Sources: apps/pi_app/led_daemon.py:85-110
Hardware Communication (SPI & WS2812B)
The daemon communicates with the WS2812B ring via the spidev library. Since WS2812B LEDs require precise timing for their data protocol, the daemon implements a software-based encoding scheme that maps every data bit to three SPI bits at a frequency of 2.4 MHz.
SPI Configuration
- Bus/Device: SPI 0, 0
- Clock Speed: 2,400,000 Hz (2.4 MHz)
- SPI Mode:
0b00 - Reset Pulse: 50 bytes of
0x00to signal the end of a frame.
Sources: apps/pi_app/led_daemon.py:11-15, apps/pi_app/led_daemon.py:86-90
Data Encoding Details
Each color channel (Green, Red, Blue - GRB order) is encoded using the _encode_byte function. This function expands a single 8-bit color value into 24 SPI bits (3 bytes), where a logic '1' is represented as 0b110 and logic '0' as 0b100.
| Logic Level | SPI Bit Pattern | Implementation |
|---|---|---|
| WS2812B Bit 1 | 0b110 | High for ~66% of bit period |
| WS2812B Bit 0 | 0b100 | High for ~33% of bit period |
Sources: apps/pi_app/led_daemon.py:18-31
Supported LED Lighting Modes
The daemon processes specific modes defined in the incoming JSON commands. Each mode dictates how the _mode_loop calculates individual pixel values before calling _send_frame.
Mode Definitions
| Mode | Description | Timing / Logic |
|---|---|---|
off | Turns all 16 LEDs off. | 0.1s update interval |
static | Sets all LEDs to a fixed color. | 0.1s update interval |
beat_sync | Synchronizes LED intensity with audio pulses. | 0.1s update interval |
breathe | Smoothly fades the LED intensity in and out. | 30 FPS using a Sine wave |
Sources: apps/pi_app/led_daemon.py:53-82
Breathing Effect Logic
The breathe mode uses a sine-wave envelope to modulate brightness over a 2-second period (60 frames at 30 FPS).
# Sine envelope calculation
breath = (math.sin(t * 2 * math.pi / 60) + 1) / 2
combined = int(brightness * breath)
Sources: apps/pi_app/led_daemon.py:69-77
Integration and Communication Protocol
The daemon is designed to be spawned by the main pi_app (Flutter). The Flutter application sends JSON payloads to the daemon's stdin.
JSON Command Schema
The daemon expects a JSON object with the following fields:
| Field | Type | Description |
|---|---|---|
mode | String | Lighting mode (off, static, beat_sync, breathe) |
r | Integer | Red component (0-255) |
g | Integer | Green component (0-255) |
b | Integer | Blue component (0-255) |
brightness | Integer | Global brightness scaling (0-255) |
The sequence shows how commands propagate from the phone client to the physical hardware via the intermediate Pi services.
Sources: README.md:27-37, CLAUDE.md:27-33, apps/pi_app/led_daemon.py:93-110
Conclusion
The led_daemon.py acts as a specialized hardware driver within the bt_speaker ecosystem. By utilizing SPI bit-banging techniques and a multi-threaded Python architecture, it provides low-latency lighting effects (static, breathing, and beat-sync) on the Raspberry Pi Zero 2 W while remaining decoupled from the main Flutter UI application.