GPIO audio visualizer for Squeezelite. Reads the player's visualization shared-memory buffer and drives:
- a 7-LED bar-meter on Raspberry Pi GPIO when audio is playing
- a 32×16 WS2812 RGB matrix with a phosphor-trail waveform visualizer when audio is playing, and outdoor temperature when paused
A small embedded HTTP control surface exposes power on/off and live calibration sliders (brightness, gamma, per-channel color balance).
- Raspberry Pi (4 recommended). Both PWM channels free.
- 5 V regulated PSU sized to the matrix: ~4 A typical, 10 A worst-case for default brightness on 512 pixels. See Power budget.
- WS2812 serpentine matrix, default 4×2 panels of 8×8 (32×16 = 512 pixels), driven as two horizontal halves on PWM0 (GPIO 12) and PWM1 (GPIO 13).
- 7 indicator LEDs on GPIO 4, 17, 22, 23, 24, 25, 27 with current limiting.
- 74AHCT125 (or similar) level shifter on the WS2812 data lines.
- Squeezelite running on the same Pi with visualization SHM enabled.
- I²S DAC HAT for audio output.
/boot/config.txt (or /boot/firmware/config.txt) needs:
dtparam=audio=off
dtoverlay=<your-dac-overlay>
dtparam=audio=off frees PWM0/PWM1 for the matrix. The DAC sits on I²S (GPIO 18-21), which doesn't collide.
WS2812B pixels draw up to ~60 mA at 5 V at full white. Power scales roughly linearly with brightness × duty:
I ≈ N × 0.060 A × (brightness/255) × duty
For N = 512, default brightness = 80:
| Scenario | Brightness | Duty | Pixel current |
|---|---|---|---|
| Typical visualization | 80 | 0.40 | ~3.9 A |
| All-white burst | 80 | 1.00 | ~9.6 A |
| All-white at full brightness | 255 | 1.00 | ~30.7 A |
A 5 V / 15 A supply comfortably covers default brightness. Inject 5 V at the start of each panel row, fuse the rails, keep grounds common.
Eight 8×8 panels arranged 4×2, chained as a column-major serpentine, two halves driven independently:
| Half | Pixels | GPIO | PWM channel |
|---|---|---|---|
| Top 8 rows | 0-255 | 12 | PWM0 |
| Bottom 8 rows | 256-511 | 13 | PWM1 |
xyToIndex in internal/led/ws2812.go is the source of truth for the mapping.
make build # builds ./gometer for linux/arm64
sudo ./gometer # root required for PWM-based WS2812 accessConfiguration via --flag, env (GOMETER_*), or YAML at ~/.config/gometer/gometer.yaml. See internal/config/config.go for the full list (FFT window, EMA constants, beat boost, etc.).
By default the daemon:
- Watches
/dev/shm/squeezelite*for the visualization buffer - Drives the bar-meter on the configured GPIOs
- Drives the matrix on GPIO 12/13
- Serves the control UI on
:9001
Open http://<pi>:9001/ for the htmx-driven UI: power on/off, plus live sliders for brightness, gamma, and per-channel color balance. The sliders write directly to the running visualizer.
For scripting:
curl -X POST -d 'action=toggle' http://localhost:9001/control
curl -X POST -d 'brightness=120' http://localhost:9001/tune
curl -X POST -d 'color_r=1.0&color_g=0.78&color_b=0.78' http://localhost:9001/tune
curl http://localhost:9001/status/control actions: toggle, turn_on, turn_off, set_mode (with mode=auto/off).
/tune accepts any subset of brightness (0-255), gamma, color_r, color_g, color_b.
Point weather.url at an Ecowitt-style JSON endpoint (with tempf/humidity/solarradiation fields). When audio is silent for 10 s+, the matrix cycles through outdoor temperature, humidity, and solar readings. Empty (the default) disables.
# ~/.config/gometer/gometer.yaml
weather:
url: "https://my-weather-host.example/latest"http.enabled(defaulttrue);http.listendefaults to:9001.pinsandthresholdsslices must be the same length.color_balanceis[R, G, B](default[1.0, 0.85, 0.85]) compensating for the WS2812B's narrow-spectrum red die. Live-adjustable via the UI's sliders.led_orderismsb(default) orlsb.
- WS281x PWM driver vendored from Jon-Bright/ledctl (MIT). See
ledctl/LICENSE.md. - Squeezelite SHM layout: ralph-irving/squeezelite.
- htmx 2.x (htmx.org), BSD 2-clause, vendored.
MIT. See LICENSE.