2026-06-10 10:27:34 +02:00
|
|
|
|
# lightcontrol
|
|
|
|
|
|
|
|
|
|
|
|
Rust tooling for the **c-base mainhall** lighting rig. One TUI for animated
|
|
|
|
|
|
presets driving the whole hall via `dmxbackend`, plus two focused one-shot
|
|
|
|
|
|
binaries for the wall panels and the SonicPulse LED bars.
|
|
|
|
|
|
|
|
|
|
|
|
Talks to:
|
|
|
|
|
|
|
|
|
|
|
|
- **dmxbackend** at `dmx.cbrp3.c-base.org:8000` (REST + WebSocket) for the
|
|
|
|
|
|
48 DMX-routed fixtures.
|
|
|
|
|
|
- **matelight** at `matelight.cbrp3.c-base.org:8081` (`/monitor` WS,
|
|
|
|
|
|
subprotocol `matemon`) for live pixel sync of the hall lights to whatever
|
|
|
|
|
|
is playing on the Mate-crate display.
|
|
|
|
|
|
- **ArtNet** node at `10.0.0.146:6454` (default) for the Stairville
|
|
|
|
|
|
SonicPulse LED bars that aren't on `dmxbackend`.
|
|
|
|
|
|
|
|
|
|
|
|
The local microphone, optionally, for beat-driven scenes.
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## Quick start
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
# prerequisites (Fedora):
|
|
|
|
|
|
sudo dnf install rustup alsa-lib-devel pkgconf-pkg-config
|
|
|
|
|
|
rustup-init -y && source "$HOME/.cargo/env"
|
|
|
|
|
|
|
|
|
|
|
|
# build everything
|
|
|
|
|
|
cargo build --release
|
|
|
|
|
|
|
|
|
|
|
|
# fire up the TUI against the live rig
|
|
|
|
|
|
./target/release/lightcontrol
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
On Debian/Ubuntu the equivalent of `alsa-lib-devel` is
|
|
|
|
|
|
`libasound2-dev`. If you don't want microphone support and can't or won't
|
|
|
|
|
|
install ALSA dev libs, see [No-mic build](#no-mic-build) below.
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## Build
|
|
|
|
|
|
|
|
|
|
|
|
The default build enables the `mic` cargo feature, which pulls in
|
|
|
|
|
|
[`cpal`](https://crates.io/crates/cpal) and through it the system ALSA
|
|
|
|
|
|
library. The TUI's beat-detector and the `BeatPulse` preset rely on it.
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
cargo build --release # default: TUI + panels + bars + mic
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### No-mic build
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
cargo build --release --no-default-features
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Identical behaviour except the BeatPulse preset shows a slow cyan
|
|
|
|
|
|
"waiting for mic" heartbeat instead of reacting to audio. Useful on
|
|
|
|
|
|
build hosts that don't have ALSA dev headers, or on machines with no
|
|
|
|
|
|
audio input.
|
|
|
|
|
|
|
|
|
|
|
|
### Quick checks
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
cargo test # unit tests for presets + detector
|
|
|
|
|
|
cargo clippy --all-targets -- -D warnings
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## Binaries
|
|
|
|
|
|
|
|
|
|
|
|
### `lightcontrol` — main TUI
|
|
|
|
|
|
|
|
|
|
|
|
Animated presets driving every fixture in the mainhall through
|
|
|
|
|
|
`dmxbackend`'s `/api/v1/websocket_state/` at 25 fps.
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
./target/release/lightcontrol # interactive TUI
|
|
|
|
|
|
./target/release/lightcontrol --preset rainbows # launch straight in
|
|
|
|
|
|
./target/release/lightcontrol --check # dump fixture list
|
|
|
|
|
|
./target/release/lightcontrol --host dmx.example:8000 # different backend
|
|
|
|
|
|
./target/release/lightcontrol --matelight ws://.../monitor # custom mate
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Environment variables:
|
|
|
|
|
|
- `DMX_HOST` — overrides `--host`
|
|
|
|
|
|
- `MATELIGHT_URL` — overrides `--matelight`
|
|
|
|
|
|
|
|
|
|
|
|
The TUI shows a header (host, fixture count, FPS, matelight + mic status),
|
|
|
|
|
|
a live **beat detector panel** (flash bar + BPM + envelope sparkline), and
|
|
|
|
|
|
the preset list with the active preset highlighted.
|
|
|
|
|
|
|
|
|
|
|
|
#### Keys
|
|
|
|
|
|
|
|
|
|
|
|
| Key | Preset |
|
|
|
|
|
|
|-----|--------|
|
|
|
|
|
|
| `1` | Rainbows |
|
|
|
|
|
|
| `2` | Blue House |
|
|
|
|
|
|
| `3` | 70th Disco |
|
|
|
|
|
|
| `4` | Jungle |
|
|
|
|
|
|
| `5` | Lasertec |
|
|
|
|
|
|
| `6` | Police |
|
|
|
|
|
|
| `7` | Mirror Ball (blue fade) |
|
|
|
|
|
|
| `8` | Morning Light (sunrise) |
|
|
|
|
|
|
| `9` | Orange/Green Mirror Ball |
|
|
|
|
|
|
| `m` | Matelight Sync (live) |
|
|
|
|
|
|
| `s` | Gabba Strobe (white, fast) |
|
|
|
|
|
|
| `r` | Red Strobe (fast) |
|
|
|
|
|
|
| `L` | Blue Strobe (fast) — capital L; `b` is blackout |
|
|
|
|
|
|
| `p` | Pink Strobe (fast) |
|
|
|
|
|
|
| `k` | Beat Pulse (mic-driven) |
|
|
|
|
|
|
| `0` / `b` | Blackout |
|
|
|
|
|
|
| `↑` / `↓` + `Enter` | Navigate list |
|
|
|
|
|
|
| `q` / `Esc` / `Ctrl-C` | Quit |
|
|
|
|
|
|
|
|
|
|
|
|
#### `--preset` CLI aliases
|
|
|
|
|
|
|
|
|
|
|
|
Lower-cased and matched verbatim — useful for shell scripting / OBS
|
|
|
|
|
|
integration:
|
|
|
|
|
|
|
|
|
|
|
|
| Preset | Aliases |
|
|
|
|
|
|
|--------|---------|
|
|
|
|
|
|
| Rainbows | `rainbow`, `rainbows`, `1` |
|
|
|
|
|
|
| Blue House | `blue`, `bluehouse`, `blue-house`, `2` |
|
|
|
|
|
|
| 70th Disco | `disco`, `disco70`, `70th`, `70th-disco`, `3` |
|
|
|
|
|
|
| Jungle | `jungle`, `4` |
|
|
|
|
|
|
| Lasertec | `lasertec`, `laser`, `5` |
|
|
|
|
|
|
| Police | `police`, `polizei`, `6` |
|
|
|
|
|
|
| Mirror Ball | `mirror`, `mirrorball`, `mirror-ball`, `discoball`, `kugel`, `7` |
|
|
|
|
|
|
| Morning Light | `morning`, `sunrise`, `dawn`, `sonnenaufgang`, `morgen`, `8` |
|
|
|
|
|
|
| Orange/Green Ball | `orangegreen`, `orange-green`, `orange`, `green`, `kürbis`, `9` |
|
|
|
|
|
|
| Matelight Sync | `matelight`, `mate`, `sync`, `m` |
|
|
|
|
|
|
| Gabba Strobe | `gabba`, `strobe`, `strobo`, `stroboscope`, `s` |
|
|
|
|
|
|
| Red Strobe | `red`, `rot`, `redstrobe`, `r` |
|
|
|
|
|
|
| Blue Strobe | `blau`, `bluestrobe`, `blue-strobe`, `l` |
|
|
|
|
|
|
| Pink Strobe | `pink`, `magenta`, `pinkstrobe`, `p` |
|
|
|
|
|
|
| Beat Pulse | `beat`, `beatpulse`, `kick`, `mic`, `audio`, `k` |
|
|
|
|
|
|
| Blackout | `off`, `blackout`, `0` |
|
|
|
|
|
|
|
|
|
|
|
|
#### Beat detector panel
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
┌─ beat detector ────────────────┬─ envelope (last ~4 s) ─────────────────┐
|
|
|
|
|
|
│ BEAT █████████████ │ ▂▃▆▇▆▃▁▂▇▆▃▁▂▇▆▃▁▂▃▆▇▆▃▁▂▇▆▃▁▂▇▆▃▁▂ │
|
|
|
|
|
|
│ level ████████████░░░░░░░░ │ │
|
|
|
|
|
|
│ 128.4 BPM beats=47 last 0.08s ago │ │
|
|
|
|
|
|
└────────────────────────────────┴────────────────────────────────────────┘
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
The flash bar fires on every detected beat in the current per-beat hue
|
|
|
|
|
|
palette. The level bar is the live short-window envelope. The sparkline
|
|
|
|
|
|
plots the last ~4 seconds of envelope at 20 Hz, scaled with a ×2.5
|
|
|
|
|
|
boost so quiet rooms still show shape.
|
|
|
|
|
|
|
|
|
|
|
|
**Pre-show diagnostic:** put on the playlist, look at the panel. Solid
|
|
|
|
|
|
BPM + regular flashes → detector locked. No flashes → mic not picking
|
|
|
|
|
|
up, check `pavucontrol` recording tab. Constant strobing → too much
|
|
|
|
|
|
noise, raise `MIN_DELTA` in `src/beat.rs` (default `0.020`, try
|
|
|
|
|
|
`0.035`).
|
|
|
|
|
|
|
|
|
|
|
|
### `panels` — Showtec PAR 56 wall panels (one-shot)
|
|
|
|
|
|
|
|
|
|
|
|
Hits the `LED Par 56` fixtures via `dmxbackend`'s WebSocket, sends one
|
|
|
|
|
|
frame, exits. Useful for setup checks or quick scene calls from a
|
|
|
|
|
|
script.
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
./target/release/panels --color red # red wash
|
|
|
|
|
|
./target/release/panels --color "#1a3f8f" --dim 180 # custom hex + dimmer
|
|
|
|
|
|
./target/release/panels --color off # blackout
|
|
|
|
|
|
./target/release/panels --dry-run --color magenta # print, don't send
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Colors: `red`, `green`, `blue`, `white`, `yellow`, `orange`, `magenta`,
|
|
|
|
|
|
`cyan`, `off`, or any `#RRGGBB`.
|
|
|
|
|
|
|
|
|
|
|
|
### `bars` — Stairville SonicPulse LED Bar 10 (ArtNet direct)
|
|
|
|
|
|
|
|
|
|
|
|
The bars aren't routed through `dmxbackend`, so this one talks ArtNet
|
|
|
|
|
|
UDP directly to the node at `10.0.0.146:6454` (4 bars × 39 channels =
|
|
|
|
|
|
156 channels in universe 2).
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
./target/release/bars --color magenta # paint all 4 bars
|
|
|
|
|
|
./target/release/bars --color "#ff5500" --dim 200
|
|
|
|
|
|
./target/release/bars --poll # ArtPoll discovery
|
|
|
|
|
|
./target/release/bars --sweep 0-15 # walk universes 0-15 to find them
|
|
|
|
|
|
./target/release/bars --broadcast --color blue # broadcast if unicast fails
|
|
|
|
|
|
./target/release/bars --dry-run --color red # print the packet, don't send
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
If `--color` does nothing visible: try `--poll` first to confirm the
|
|
|
|
|
|
node is on the LAN, then `--sweep` to find which universe the bars
|
|
|
|
|
|
actually listen on. The factory default is 0; many nodes are set to
|
|
|
|
|
|
different universes per harness.
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## Architecture
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
┌──────────────────────────┐
|
|
|
|
|
|
│ c-base mainhall lights │
|
|
|
|
|
|
└──────────────────────────┘
|
|
|
|
|
|
▲ ▲
|
|
|
|
|
|
DMX over │ │ ArtNet (UDP/6454)
|
|
|
|
|
|
│ │ → 10.0.0.146 → 4 SonicPulse bars
|
|
|
|
|
|
│ │
|
|
|
|
|
|
┌────────────────────┴──┐ │
|
|
|
|
|
|
│ dmxbackend (aiohttp) │ │
|
|
|
|
|
|
│ dmx.cbrp3:8000 │ │
|
|
|
|
|
|
│ REST /api/v1/... │ │
|
|
|
|
|
|
│ WS /api/v1/ │ │
|
|
|
|
|
|
│ websocket_state │ │
|
|
|
|
|
|
└────────┬──────────────┘ │
|
|
|
|
|
|
│ │
|
|
|
|
|
|
┌────────┴────────────────┴──┐
|
|
|
|
|
|
│ lightcontrol (this repo) │
|
|
|
|
|
|
│ │
|
|
|
|
|
|
│ ┌─────────┐ ┌─────────┐ │ ws://matelight:8081
|
|
|
|
|
|
│ │ engine │ │ matelight ─────────────/monitor (matemon)
|
|
|
|
|
|
│ │ (presets│ │ feeder │ │ binary RGB frames →
|
|
|
|
|
|
│ │ @25fps)│ │ │ │ drive hall via sample
|
|
|
|
|
|
│ └────┬────┘ └─────────┘ │
|
|
|
|
|
|
│ │ │ local microphone (cpal)
|
|
|
|
|
|
│ │ ┌─────────┐ │ ←───────────────────────
|
|
|
|
|
|
│ ├──────┤ beat │ │ energy-onset detector
|
|
|
|
|
|
│ │ │ detector│ │ → BeatPulse preset
|
|
|
|
|
|
│ │ └─────────┘ │
|
|
|
|
|
|
│ ▼ │
|
|
|
|
|
|
│ binaries: │
|
|
|
|
|
|
│ lightcontrol (TUI) │
|
|
|
|
|
|
│ panels (one-shot) │
|
|
|
|
|
|
│ bars (ArtNet, ad-hoc) │
|
|
|
|
|
|
└────────────────────────────┘
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Source layout:
|
|
|
|
|
|
- `src/lib.rs` — shared types (Fixture/Element/Channel), HTTP client,
|
|
|
|
|
|
re-exports.
|
|
|
|
|
|
- `src/presets.rs` — the 15 animated presets + render dispatch.
|
|
|
|
|
|
- `src/matelight.rs` — matelight `/monitor` WebSocket feeder + frame
|
|
|
|
|
|
sampler.
|
|
|
|
|
|
- `src/beat.rs` — cpal capture + energy-onset beat detector (feature
|
|
|
|
|
|
`mic`).
|
|
|
|
|
|
- `src/main.rs` — TUI binary, engine loop, key handling.
|
|
|
|
|
|
- `src/bin/panels.rs` — wall panels one-shot.
|
|
|
|
|
|
- `src/bin/bars.rs` — SonicPulse ArtNet driver.
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## Tuning notes
|
|
|
|
|
|
|
|
|
|
|
|
- **Mic input device.** On laptops with both built-in mic and a desk
|
|
|
|
|
|
feed, PulseAudio/PipeWire may pick the wrong one. Fix in
|
|
|
|
|
|
`pavucontrol` → Recording tab while `lightcontrol` is running.
|
|
|
|
|
|
- **Beat sensitivity.** `src/beat.rs` constants:
|
|
|
|
|
|
- `THRESHOLD` (default `1.35`) — ratio of short to long envelope.
|
|
|
|
|
|
Higher = stricter.
|
|
|
|
|
|
- `MIN_DELTA` (default `0.020`) — absolute energy rise required.
|
|
|
|
|
|
Suppresses warmup-window false triggers and quiet-room noise.
|
|
|
|
|
|
- `MIN_INTERVAL_MS` (default `200`) — caps detected BPM at 300.
|
|
|
|
|
|
- **Matelight unreachable.** TUI header will show
|
|
|
|
|
|
`matelight: connecting…` or the last error. `lightcontrol` reconnects
|
|
|
|
|
|
every 3 s. The Matelight preset falls back to a slow dim-white pulse
|
|
|
|
|
|
meanwhile.
|
|
|
|
|
|
- **Photosensitivity.** Gabba/Red/Blue/Pink strobes are hard 12.5 Hz
|
|
|
|
|
|
square-wave alternations at the engine's Nyquist rate. Don't fire
|
|
|
|
|
|
them at a crowd that hasn't been warned.
|
2026-06-10 10:31:11 +02:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## Related projects
|
|
|
|
|
|
|
|
|
|
|
|
### c-base backend & infrastructure
|
|
|
|
|
|
|
|
|
|
|
|
- **[c-base/dmxbackend](https://github.com/c-base/dmxbackend)** — the
|
|
|
|
|
|
aiohttp service this tool talks to. ArtNet/HTTP-WebSocket/MQTT bridge
|
|
|
|
|
|
for the mainhall lights. Hosts the fixture API at
|
|
|
|
|
|
`dmx.cbrp3.c-base.org:8000` and the live state WebSocket at
|
|
|
|
|
|
`/api/v1/websocket_state/`.
|
|
|
|
|
|
- **[c-base/c-base-map](https://github.com/c-base/c-base-map)** —
|
|
|
|
|
|
interactive map of c-base areas and interfaces. Useful context for
|
|
|
|
|
|
where the fixtures actually sit in space.
|
|
|
|
|
|
- **[code.c-base.org/t/dmx-mainhall-foo](https://code.c-base.org/t/dmx-mainhall-foo)** —
|
|
|
|
|
|
this repo's home.
|
|
|
|
|
|
|
|
|
|
|
|
### Matelight ecosystem
|
|
|
|
|
|
|
|
|
|
|
|
- **[jaseg/matelight](https://github.com/jaseg/matelight)** — the
|
|
|
|
|
|
canonical project. WS2801-in-Mate-bottles, 40×16 display, CRAP
|
|
|
|
|
|
protocol (UDP/1337). Foundation everything else builds on.
|
|
|
|
|
|
- **[c-base/matelight](https://github.com/c-base/matelight)** — c-base
|
|
|
|
|
|
fork of the above.
|
|
|
|
|
|
- **[c-base/matelight-pixel](https://github.com/c-base/matelight-pixel)**
|
|
|
|
|
|
— Python/FastAPI + React frontend served at
|
|
|
|
|
|
`matelight.cbrp3.c-base.org:8000`.
|
|
|
|
|
|
- **[code.c-base.org/c-matelight](https://code.c-base.org/c-matelight)**
|
|
|
|
|
|
— the active production org on c-base's Gitea (fresher than the
|
|
|
|
|
|
GitHub mirror). Contains `matelight`, `matelight-original`,
|
|
|
|
|
|
`matelight-pixel`, `c-matelight`.
|
|
|
|
|
|
- **[c-base/mlaudiospectrum](https://github.com/c-base/mlaudiospectrum)**
|
|
|
|
|
|
— audio-spectrum analyzer that paints the Matelight from mic input.
|
|
|
|
|
|
Conceptually adjacent to our `BeatPulse` preset; if you want to push
|
|
|
|
|
|
audio reactivity into the Matelight itself (instead of the hall),
|
|
|
|
|
|
this is the prior art.
|
|
|
|
|
|
|
|
|
|
|
|
### Other c-base lighting
|
|
|
|
|
|
|
|
|
|
|
|
- **[c-base/roboclub-lighting](https://github.com/c-base/roboclub-lighting)**
|
|
|
|
|
|
— separate lighting project for the roboclub area, C/MCU-side.
|
|
|
|
|
|
|
|
|
|
|
|
### Documentation
|
|
|
|
|
|
|
|
|
|
|
|
- **[cbag3.c-base.org/artefact/matelight](https://cbag3.c-base.org/artefact/matelight)**
|
|
|
|
|
|
— c-base artefact guide entry for the Matelight (hardware history,
|
|
|
|
|
|
build notes).
|
|
|
|
|
|
- **[c-base/artefact-guide](https://github.com/c-base/artefact-guide)**
|
|
|
|
|
|
— source of the artefact guide above.
|