How the installation fits together: six players, one Q-SYS Core, five projectors and the software that drives them — sliced by network, by runtime, by control path, and by build pipeline.
One isolated static LAN — no gateway, no router, no internet, by design. All traffic is on-subnet. The only device that reaches outside is the on-site PC, and it does so outbound only.
The same system sliced by runtime rather than by folder.
| Runs on | Software | Lines | Job |
|---|---|---|---|
| Q-SYS Core | bridge_controller_master.lua v2.23.2 | — | Scheduler, UDP send, projector watchers, clock sync |
| Q-SYS Core | sync_receiver.lua v1.11.10 | — | Owns UDP :7000, marker-anchored playhead |
| Q-SYS Core | 15 further Lua controllers | — | Show latch, timers, zones, AUDAC, projector gate |
| BrightSign ×6 | Stock BA autorun.brs + .bpfx | — | Two-state machine: park image ⇄ show video |
| Wall PC | bs-provisioner renderer | 5,914 | Operator UI, grid, sync clock, Q-SYS tabs |
| Wall PC | bs-provisioner main | 2,636 | Electron main, IPC, the only reboot path |
| Wall PC | bridge-server.js | 877 | Embedded Express bridge on :5000 |
| Wall PC | dws-client.js | 524 | DWS HTTP + Digest, file push/pull, recovery |
| Wall PC | net-doctor.js | 839 | Network diagnostics, ARP sweep |
| Cloudflare | Worker index.js | 722 | Feed, OTP, telemetry ingest, command relay |
| Cloudflare | Dashboard index.html | 1,428 | This site (+ switch 181, manual 275) |
| Bench | Provisioner · bridge CLI · WAV→MP3 · 40 tools | — | SD cloning, diagnostics, authoring, deploy |
What happens between the scheduled start time and frame zero. Each step crosses a boundary between two of the layers above.
No cron and no cloud trigger. The Core has no NTP, so it syncs from the Wall's
/api/time and reads it back in UTC form — correcting drift and a
mis-set timezone at once.
Every start source — timer, wall button, web switch — converges on one latch. Two absolute buttons rather than a toggle, because a momentary toggle double-pulses on press and release.
show_button.lua v1.1.0 — Play = force ONA warm-up delay lets the projectors come up before video starts. The AND gate re-asserts power every 15 s, so a dropped command self-heals instead of leaving one room dark.
start_launcher.lua → proj_on_gate.lua v1.3.0Start on port 5000, direct to each player. The BrightAuthor state
machine moves from park image to show video at frame 0.
roSync on PTP domain 6 frame-locks the fleet, which needs firmware parity, equal fps and equal frame count. The Leader emits S0/L0 markers back to the Core.
sync_receiver.lua owns :7000The Core drives its 16 audio players from the marker feed, so sound re-anchors to picture rather than free-running.
set locate.Value, then play:Trigger()Screenshots over DWS and telemetry to the cloud — gated on whether anyone is actually looking at the dashboard, to stay inside the free-tier write budget.
outbound only| Source | Trigger | Lands at |
|---|---|---|
| Wall app | tag wall-v* | R2 → the in-app updater |
| This site | same job | Cloudflare Pages |
| Worker | manual | Cloudflare Worker |
| Autostart bundle | tag tools-autostart-v* | GitHub Release |
| Changelog + version table | push to main | Repo docs, auto-generated from tags |
| Q-SYS Lua | none | Hand-pasted — needs pins, Start Automatically, then F5 |
| BrightSign presentations | none | BrightAuthor:Connected, published Standalone → SD card |
version.json across removes it, and every Wall in the field
starts failing its update check.