QLAB Buddy
Free firmware for inexpensive off-the-shelf ESP32 development boards, powered by QLab 5 (or Go Button).
- The Buddy A touchscreen showing the current state of QLab 5 (or Go Button).
- The RF Relay Learns a radio-frequency remote and emulates it on command of QLab 5 (or Go Button).
- Network Devices Motors, servos, etc; all driven straight from QLab 5 (or Go Button).
Pair each device to the buddy once, and your cues reach it directly from then on. One flash from this page, or try the whole rig in your browser first.
QLAB Buddy
A wireless touchscreen that shows your show live — cue name, timer, and connection status on a bright 3.5″ display. Put it anywhere: the tech table, the wings, your hand. Works with QLab 5 on a Mac, or Go Button on an iPhone with no Mac at all. Pair wireless RF and WiFi props — motor controllers, sub-GHz relays, and more — and fire them straight from your cues.
#timer/ directives baked into QLab notes.Hardware
The three buttons
Everything the buddy does day-to-day is on the touchscreen, but the board has three physical buttons and each one is worth knowing before a show.
| Button | Press | What happens |
|---|---|---|
| BOOT | Tap | Sends /go — advances one cue, exactly like pressing GO on screen. Ignored in VIEW mode. |
| BOOT | Hold 5 s | Sends /panic — fades out and stops everything. Deliberately long: it was 3 s, and accidental brushes against gear were panicking shows. A hold you abandon at 3–4 s does nothing rather than firing a stray /go, and the button is locked out for 1.2 s afterwards so chatter on release can’t walk the playhead. |
| PWR | Short press | Toggles the backlight. The firmware keeps running and keeps drawing behind a dark screen — cues, timers and prop firing are all unaffected. Useful for a blackout in the wings. |
| RST | Press | Hard-resets the chip. Everything in RAM goes and the boot splash runs; paired devices, saved WiFi and the locked workspace all survive, since those live in flash. |
RF Relay
A networked sub-GHz capture and replay node. Point a 315, 390, 418, 433.92, 868.35, or 915 MHz fixed-code remote at it, learn the code via OSC or the onboard encoder, then replay it directly from QLab on every subsequent show. Once provisioned the RF Relay fires independently — the buddy is only needed for initial WiFi SYNC and monitoring. There’s no Go Button device tag for it yet (see OSC reference); trigger it from QLab directly.
Designed for battery-powered remotes that control props or practical effects that can’t be wired: wireless LED controllers, RF dimmers, ceiling fans, and any fixed-code PT2262/EV1527 remote.
#timer/ directives in cue notes. Activates after 30 s idle.Pairing with the buddy
The RF Relay pairs exactly like every other prop — hotspot, boot, one tap on SYNC. The full walkthrough lives in Pairing Props with the Buddy.
Cabled to a Mac (LAN)
Over USB-C the relay appears as a USB Ethernet device at 192.168.9.1, port 8000 — the address in its status bar. Patch a Network cue straight at it: no WiFi, no router, no buddy. Sub-millisecond and RF-immune, so it’s the best showtime path. The .9 subnet is deliberate (the buddy uses .7), so both can be cabled to one Mac.
Hardware
Amazon →
Display UI
| Screen | Description |
|---|---|
| Home | 25-slot list — slot number, name, frequency, captured code. Short press fires. Long press opens Setup. |
| Setup | Per-slot menu: LEARN / FIRE / RENAME / DELETE. Long press back. |
| Delete confirm | Selecting DELETE opens a CANCEL/DELETE toggle screen — scroll to switch, press to confirm. No more accidental holds. |
| Listening | 30 s capture window. Auto-scans 315 / 390 / 418 / 433 / 868 / 915 MHz. Short press cancels. |
| CAPTURED | Code, frequency, and protocol confirmed. Auto-returns to Home. |
| NO SIGNAL | Nothing received in 30 s. Auto-returns to Setup. |
| Rename | Character picker (A–Z, 0–9). Short press places, long press saves. |
| Screensaver | QLab elapsed timer or #timer/ countdown. Activates after 30 s idle. Any input exits. |
OSC reference
/ping and /identify requires the passcode as the message’s first argument, as a string — e.g. /rf/learn/1 "1234", not bare /rf/learn/1. Get it wrong or omit it and the board drops the message with no reply and nothing in Serial — it looks exactly like the command doesn’t exist. /ping works either way, which is why “ping replies but learn does nothing” almost always means a missing or wrong passcode, not a broken command.
| Address | What it does |
|---|---|
/rf/N | Fire stored slot N (1–25) for the default 1 s hold. Optional float or int argument overrides the hold duration in seconds — e.g. /rf/1 with arg 0.5 fires for 0.5 s. |
/rf/N/TIME | Fire slot N for a hold time encoded in the path — e.g. /rf/1/3 (3 s), /rf/1/00:00.03 (3 s), /rf/1/00:01.30 (90 s). Lets a QLab Network cue set duration without an OSC argument. |
/rf/learn/N | Arm capture for slot N at its stored frequency (433.92 MHz by default if never learned). 15 s window — hold the remote button to capture. (The onboard Setup → LEARN menu gives 30 s; an OSC-armed learn gets half that, since the board is deaf to every other OSC message for the whole window and a stray fire cue landing during it would otherwise be silently dropped.) Multi-band auto-scan is onboard-menu only — this OSC command always arms a single fixed frequency, never scans. |
/rf/learn/N/MHZ | Arm capture for slot N at an explicit frequency, same 15 s window — e.g. /rf/learn/1/433. Accepts any numeric MHz value (315, 433.92, 868.35, 915, etc.). |
/rf/freq/MHZ | Set the active band and re-drive the antenna switch — e.g. /rf/freq/315, /rf/freq/433, /rf/freq/868, /rf/freq/915. |
/rf/stop | Cancel an armed learn without saving. |
/ping | Connectivity check — board replies /pong (and sends /hello). |
/identify | 3× white LED flash — confirms which board you’re targeting. This is what the buddy’s PING button sends; the relay and the Universal Controller show PING there rather than TEST, because it identifies without firing anything. |
If Install fails: this board has no BOOT button — BOOT/GPIO0 is the encoder knob’s own push-button, so the knob you turn to scroll also clicks straight in. Hold it pressed in (don’t turn it), plug in USB-C or press RST while still holding, keep holding a couple of seconds past power-up, then release and click Connect and Install.
Reading the Status Dot
Bottom-left of every view. The bar tints dark blue whenever a show is actually open, the label names the path the buddy is using, and the dot tells you how ready that path is. Grouped by path below, so you can see what each one looks like before and after the internet check confirms.
| Status bar | Path | What it means |
|---|---|---|
| Show open, internet confirmed | Blue dot + gold ring — the healthy showtime state. Blue only ever means a show is open, readable from across a tech table without parsing the label. | |
| Show open, internet not confirmed | Blue dot + white ring. Cues still work fine — the buddy reaches QLab over the local network regardless; the internet check only affects the clock and OTA. | |
| On WiFi, no show found yet | Always gold on this path, confirmed internet or not — gold here means “network is fine, still looking”, not “ready to run”. | |
| Cabled to a Mac | Always white on this path, confirmed internet or not. Internet still works behind the scenes for the clock and OTA; it just isn’t surfaced here. | |
| Hotspot / Pairing, nothing joined | White — the buddy’s own hotspot is up and nothing has joined it yet. | |
| Hotspot / Pairing, device joined | Gold — a device has joined. During Pair Devices this is your cue that the prop has arrived and SYNC is ready to tap. | |
| No network, never set up | Grey — no network has ever been saved. What a freshly flashed buddy shows until you enter WiFi credentials on the device (Devices tab → SETTINGS → WiFi). | |
| No network, connection lost | Red — a network was configured and has been lost. Plug in USB-C or re-enter WiFi credentials. The difference from grey is the point: grey is “never set up”, red is “something broke”. |
Pairing Props with the Buddy
Every prop pairs the same way — RF Relay, Universal Controller, and both Card Fountains. The buddy raises a temporary hotspot, the prop joins it on boot, and one tap hands over your real WiFi credentials plus a permanent address. You only do this once per prop.
- Start pairing mode. On the buddy, tap Devices → Pair Devices. This turns on the hotspot and begins scanning.
- Power on the prop. It scans for the “QLAB Buddy” hotspot on every boot and joins automatically. The hotspot just needs to be active when the prop boots — there is no countdown to beat.
- Tap SYNC on the buddy when the prop appears in the list. This pushes your main WiFi credentials, assigns a static IP, and completes the pairing. The prop reboots onto your network.
Factory re-pair
To move a prop to a different buddy or a different network, wipe its stored credentials: hold the board’s button while powering on, and keep holding for 3 seconds after power-up. That clears NVS and reboots the prop into SYNC mode, ready to pair again. The button is sampled during boot, so holding it after the boot sequence finishes does nothing.
| Board | Hold this | What you’ll see |
|---|---|---|
| RF Relay | The encoder knob, pressed straight in — this board has no BOOT button | A red DELETED confirmation screen, then it reboots into SYNC mode. A successful pair shows a green PAIRED screen. |
| Universal Controller | BOOT — GPIO 0 on NULLLAB, GPIO 9 on Romeo Mini | Status LED returns to its unprovisioned pattern. |
| Card Fountain — Handheld | BOOT | Nothing — this board has no status LED. Just hold through the power-up. |
| Card Fountain — De Kolta | BOOT | Nothing — this board has no status LED. Just hold through the power-up. |
OSC passcode
There is nothing to set on the prop — the buddy syncs the Go Button passcode to every paired prop by itself, and re-syncs whenever you change it. See Passcode auto-sync to props.
Firmware Updates (OTA)
Every board on the rig — the buddy and every paired prop — updates itself wirelessly. There are two separate update paths, and the one thing worth knowing up front is that the buddy never actually relays firmware to a prop; it only points the prop at where to get its own.
Updating the buddy itself
Settings → Software shows the buddy’s own version, with Check Again and Install buttons. It checks for its own updates automatically in the background (roughly every 15 minutes) but nothing installs until you tap Install — there is no silent auto-update.
Updating a paired prop
Open Devices on the buddy. Any prop with a newer version available shows an UPDATE chip next to it — the buddy checks each paired prop’s version roughly every 10 minutes. Tap it once; there is no confirmation step. The chip goes outlined while the update is in flight, then flashes DONE or ERR.
Verification and rollback
Every OTA binary is SHA-256 verified as it downloads, on both the buddy and every prop — a mismatch aborts before anything is written, so a corrupted or interrupted download can’t half-install. Once flashed, the new firmware has to run cleanly for 60 seconds before it’s marked good; if it crashes or hangs before then, the bootloader itself reboots back into the previous version automatically. None of this needs anything from you — a bad update reverts on its own.
Workspace Lock
The buddy auto-selects the first workspace it finds on the network — whether that’s a QLab workspace or a Go Button show. In a more complex rig you can lock it to exactly the right one.
Workspace picker
Go to Settings → Workspace to open the picker. Every workspace the buddy has discovered on the current network appears as a row. Short-tap a workspace to select it as the active workspace — the buddy stays on that selection through WiFi drops, reboots, and restarts.
To hard-lock onto a specific host device, hold a workspace row for 2.5 seconds. The row fills as you hold. When it completes, the buddy locks to that workspace and that device IP — it won’t wander to another host on the network even if something else responds first. The locked row turns blue and pins to the top of the list with a LOCKED badge. Hold the same row again to unlock.
If the locked host goes offline, its row stays pinned at the top with the LOCKED badge — Offline shows in place of the address, so you can always see it is still locked and unlock it on purpose. The buddy waits quietly for that host to come back, ignoring everything else on the network.
The picker stays open after a tap or lock so you can review the state. Tap the ‹ Back pill (top-right) to return to Settings.
OSC Access
Tap Settings → OSC Access to open the OSC Access page. This is where all OSC permission and passcode settings live.
- Mode (VIEW / CTRL) — CTRL (default) lets the buddy send GO, PANIC, STOP, and cue-navigation commands. Switch to VIEW to make the buddy read-only: it monitors the show live but all control buttons and chevron navigation are silenced. Useful at the tech table or for a second monitor display you don’t want to accidentally trigger.
- QLab passcode — if your QLab workspace has an OSC passcode set under Workspace Settings → Network → OSC Access, enter the same numeric code here. The buddy sends it automatically on every connect. Leave blank if no passcode is set.
- Go Button passcode — if your Go Button show has an OSC passcode set, enter the matching numeric code here. This is the only place you enter it: the buddy shares it with every paired prop automatically (see Passcode auto-sync to props below). Leave blank if no passcode is set.
Passcode auto-sync to props
When OSC is passcode-locked, a prop only fires for a matching passcode, so it won’t fire for an unauthorized sender on a shared network. You never type that passcode into the prop itself. Enter it once on the buddy — Settings → OSC Access → Go Button passcode — and the buddy pushes it to every paired prop: Card Fountain, De Kolta, RF Relay, and Universal Controller alike.
- Set once, shared everywhere — the buddy sends the stored Go Button passcode to each paired prop in the background. Newly discovered props, props that reconnect, and props pairing via SYNC all receive it — no per-prop entry and no re-pairing.
- Changes re-push automatically. Change the passcode on the buddy, from the numeric pad or by tapping x to clear it, and the new value reaches every paired prop right away. Clearing it propagates too, so props stop expecting a passcode and keep firing.
- Self-healing on reconnect — if a prop was offline when you changed the passcode, or a push was missed, it re-syncs silently the next time it reports in. Each prop advertises a fingerprint of its stored passcode; when the buddy sees a mismatch it re-pushes the current code on its own. Cue triggering just keeps working — no manual action needed.
Show Timers
The buddy can put a clock on screen next to the cue name — a countdown to a curtain, a stopwatch across an act, or the show-elapsed time. How you drive it depends on which app is running the show, so both are here together.
From QLab — cue-note directives
Drop directives into a cue’s Notes field — the buddy reads them and strips them from the visible text, so the operator never sees them on the QLab screen. Both : and . work as separators.
| Directive | What it does |
|---|---|
#timer/HH:MM.SS |
Countdown from the specified time (amber). Goes negative and turns red after expiry. Hours are optional — #timer/05.00 and #timer/00:05.00 both mean five minutes. |
#timer/start |
Count-up stopwatch from 0:00.00 (green). Drop on the first cue of the show; pair with #timer/end on the last. |
#timer/pause |
Freeze the timer in its current color. Amber if time remains, red if over, green if counting up. Tap the timer area on the buddy to clear manually. |
#timer/resume |
Un-pause exactly where it stopped — no gap, no double-counting. |
#timer/end |
Show over. Freezes the display with the final run time. Green if you came in at or under the duration; red if you ran over. Count-up timers (no original duration set) always freeze green. |
#timer/clear |
Resets all timers and clears any pause-freeze. Drop this on a pre-show reset cue. |
From Go Button — show timer
Go Button handles timing differently from QLab — there are no cue notes, so there are no #timer/ directives. What the buddy shows depends on whether that show file has a duration set:
- No duration set — Go Button counts up. The buddy shows elapsed time in green, from 00:00.00.
- Duration set — Go Button counts down. The buddy shows time remaining in amber. Once the show runs over, it flips to red and shows how far over.
It resets to 00:00.00 when the show resets in Go Button. The buddy detects motion from Go Button’s own elapsed time, so firing the first cue is what starts the clock.
On the RF Relay
The RF Relay mirrors the same timer on its own screen once it has been idle for 30 seconds, so a relay sitting in the wings doubles as a second clock. Nothing to configure — see RF Relay.
Simulator
Run a show in your browser, with no hardware at all. A virtual QLab drives a QLAB Buddy and an RF Relay — and neither device is a mockup: each runs its actual firmware, compiled to WebAssembly, on one shared clock, so what you see on both is genuinely the same moment.
Teach the relay a remote, then hit GO. The cue tagged for RF fires the relay while the buddy advances, because QLab reaches both devices independently — which is exactly how a real rig behaves. The director panel underneath goes deeper: the buddy’s three physical buttons, VIEW-only OSC access, #timer/ directives, battery and radio states, and the relay’s full setup menu — every control drives the real firmware, never a re-enactment.
Flash Firmware
Buy the board in QLAB Buddy above, plug it in over USB-C, and install it right here: the page identifies which board it is and arms the matching installer itself, so you never pick a firmware. After flashing, give the buddy WiFi on the device (Devices tab → SETTINGS → WiFi), then jump to Using with QLab 5 or Using with Go Button for your show setup.
Auto-install: plug in and flash
Plug the board in over USB-C and leave it. The page watches for it, reads what it is, and arms the matching installer by itself — then one click flashes it. Two boards share each chip (Buddy/RF Relay are both ESP32-S3; three props are ESP32-C3), so esptool can’t refuse a mismatched flash on chip grounds alone; asking the board is what makes picking the right firmware automatic. That answer is remembered by every other Install button on the page too, so clicking one for a different device gets stopped before it overwrites anything.
The browser asks permission once. Chrome only lets a page open a serial port after you pick it from its own dialog, so the very first board needs one click on Scan. After that this page can watch that port on its own — plug a board in and it identifies itself with nothing to click.
A brand-new board only narrows the choice, it can’t fully identify it. A blank board prints nothing, but its USB chip ID still rules boards out — a native-USB chip means it isn’t the NULLLAB Universal Controller (the one board with no native USB), and vice versa. Reading the rest requires firmware that’s already running, so pick from the sections above when the scan can’t go further.
Using with QLab 5
#timer/ directives. QLab 4 gives you GO/PANIC/STOP and a blue dot, but Stage view stays blank. Get QLab 5 → (free, opens .qlab4 files via File → Import).
OSC setup in QLab
Two settings in every workspace. Do this once per show file and it sticks in the .qlab5 file.
-
Enable OSC input on port 53000 Workspace Settings → Network → OSC Controls (or OSC Receiver).
Tick Enable Network OSC. Set the listen / input port to 53000. -
Allow control Workspace Settings → Network → OSC Access.
Check Allow OSC connections. In the No Passcode row, tick Control. Save.
Three ways to connect
- USB-C — sub-millisecond latency, completely RF-immune. Best choice for showtime. The buddy presents as a USB-NCM Ethernet device at
192.168.7.1— your Mac’s WiFi stays the default route, so QLab and internet work normally alongside it. - WiFi — join the same network as your QLab Mac. Discovery is automatic, typically connects within 2–3 seconds.
- Hotspot — the buddy hosts its own AP if you have no router. Used for prop pairing too. Credentials below.
QLAB Buddy
Passwordqlabbuddy
Cabled with no WiFi at all
USB-C to the QLab Mac works with WiFi off entirely — no router, no venue network. QLab reaches the buddy over the cable at 192.168.7.1 the same way it would over WiFi.
Universal Controller BETA
A wireless OSC prop controller for live shows. Pair it with the buddy, give it a static IP, and drive motors and servos directly from QLab cues or Go Button. Two board options: the NULLLAB Maker-ESP32 gives you four DC motor channels and four servo channels; the Romeo Mini ESP32-C3 is a smaller form factor with two motor channels and four servo channels.
Build anything that moves: turntables, lifts, reveals, automated set pieces, puppet rigs. If it takes a motor or a servo, the Universal Controller drives it from your cue stack.
Pairing with the buddy
The Universal Controller pairs exactly like every other prop — hotspot, boot, one tap on SYNC. The full walkthrough lives in Pairing Props with the Buddy.
Hardware
DFRobot →
Amazon →
Amazon →
LED status
| Pattern | Meaning |
|---|---|
| Idle | |
| Blue | QLab show file open and ready. |
| Green | WiFi connected, no QLab file open. |
| Boot | |
| White pulse ×3 | Boot animation, board is starting up. |
| Yellow | Connecting to WiFi. |
| Magenta blink | On buddy hotspot, waiting for SYNC or receiving credentials. |
| Pairing | |
| 3× green flash | Credentials saved, board rebooting onto your WiFi. |
| Active | |
| White breathing | Motor running. |
| 3× white flash | /identify received, confirms which board you’re targeting. |
| Error | |
| Red blink | WiFi dropped, waiting to reconnect. |
| Yellow | Actively reconnecting to WiFi. |
OSC reference
| Custom Message | What it does |
|---|---|
| Motors | |
/m1/start/80/00:00.03 | Run M1 forward at 80 % for 3 s |
/m1/start/80 | Run M1 forward at 80 % for the default 5 s |
/m1/start | Run M1 forward at the defaults (75 % / 5 s) |
/m1/reverse/80/00:00.03 | Run M1 in reverse at 80 % for 3 s |
/m1/stop | Halt M1 immediately |
/m1/test | Quick test fire — 75 % / 5 s |
| Servos | |
/s1/90 | Move servo 1 to 90° instantly |
/s1/90/00:00.03 | Move servo 1 to 90° over 3 s |
/s1/angle/90 | Same as /s1/90 — explicit keyword form, with or without a duration |
/s1/velocity/30 | Sweep servo 1 continuously at 30°/s (stops at 0° or 180°) |
/s1/velocity/0 | Stop the sweep, hold at the current position |
/s1/bounce | Bounce servo 1 back and forth until stopped — add a number (/s1/bounce/60) to set °/s |
/s1/center | Move servo 1 to 90° (centre) |
/s1/stop | Halt servo 1 at the current position |
/s1/pulse/1500 | Set servo 1 to raw pulse width 1500 µs (advanced) |
| Whole board | |
/stop | Emergency stop — halts every motor on the board at once |
/ping | Connectivity check — the board replies /pong |
/identify | Flash the LED 3× white — confirms which board you’re talking to. This is what the buddy’s PING button sends. |
Card Fountain — Handheld ALPHA
Pocket-sized Card Fountain. Fires on the same OSC interface as the De Kolta. Pairs over the buddy’s hotspot, gets a static IP, and takes /start from QLab Network Cues or Go Button cue tags.
Pairing with the buddy
The Handheld pairs exactly like every other prop — hotspot, boot, one tap on SYNC. The full walkthrough lives in Pairing Props with the Buddy.
Hardware
DFRobot →
Amazon →
Amazon →
Amazon →
Card Fountain — De Kolta ALPHA
Stage Card Fountain. Pairs over the buddy’s hotspot; QLab drives it over OSC. Cosine ramp for smooth motor acceleration. Static IP survives reboots and router restarts.
Pairing with the buddy
The De Kolta pairs exactly like every other prop — hotspot, boot, one tap on SYNC. The full walkthrough lives in Pairing Props with the Buddy.
Hardware
DFRobot →
Amazon →
Amazon →
Amazon →
Device Triggering from QLab
Every prop patches into QLab the same way — pair it, add a Network Patch, fire OSC from a Network cue. What you send depends on which prop:
Which commands for which prop
| Prop | Commands | |
|---|---|---|
| Card Fountain | /start · /reverse · /stop · /test |
Full reference ↓ |
| RF Relay | /rf/N · /rf/learn/N · /rf/freq/MHZ · /rf/stop |
Full reference → |
| Universal Controller | /m1–m4/start · /s1–s4/angle · /stop |
Full reference → |
Patch it into QLab
-
Pair the prop to the buddy first Before you can trigger a prop from QLab, it needs to be paired to the buddy — see Pairing Props with the Buddy. Once paired, the prop gets a static IP from the buddy and appears in the Devices view.
-
Find the prop’s IP on the buddy Go to the Devices view. The IP appears under each paired prop’s name (e.g.
192.168.1.62). This address is permanent across reboots, router restarts, and DHCP churn — QLab patches stay valid forever. -
Add a Network Patch in QLab Workspace Settings → Network → OSC Controls → + (add).
Name: anything you want (e.g.Handheld Fountain).
Network Patch: tap + Add Network Patch if needed. Type:TCP(see the box below — orUDPon prop firmware older than 0.12.0), Host:<prop IP>, Port:8000. -
Add a Network cue Toolbar → + → Network. Set the destination to your prop patch. In the Custom Message field, type the OSC address + arguments separated by spaces.
UDP sends each Network cue once, with no confirmation and no retry — if that one packet is lost on busy venue WiFi, the cue is silently missed and QLab still shows it as done. Setting the patch Type to TCP fixes this: delivery is guaranteed and acknowledged, and if the prop is unreachable QLab shows a visible connection error instead of pretending the cue fired.To switch an existing show: Workspace Settings → Network → OSC Controls → find your prop’s patch → change Type from
UDP to TCP. That’s the only change — same IP, same port 8000, and every cue already using that patch is upgraded at once. Nothing about your cues or the prop’s behavior changes.Requires prop firmware 0.12.0 or newer (currently in Beta Firmware). Older prop firmware is UDP-only — keep the patch on
UDP and use the burst technique in the reliability tip below.
Card Fountain commands
| Custom Message | What it does |
|---|---|
/start/80/00:00.03 |
Run motor forward at 80 % for 3 s. Cosine ramp in/out. |
/start |
Run forward at default velocity and duration (75 %, 5 s). |
/reverse/80/00:00.03 |
Run motor in reverse at 80 % for 3 s. |
/stop |
Stop immediately. Safe to fire even if nothing is running. |
/test |
Quick test fire — 75 % for 5 s. Handy for a soundcheck cue. |
/ping |
Connectivity check. Prop replies /pong. Use as a pre-show health check. |
Device Triggering from Go Button
How it works: Go Button can’t send OSC out to a device on its own, so the buddy does it for you. Put a short trigger tag in a cue’s name. The buddy watches Go Button’s live cue display — the moment the playhead leaves the tagged cue and advances to the next one, the buddy fires the matching prop. In practice this happens right as GO is pressed, since pressing GO is what advances the playhead. The buddy is the bridge between Go Button and the prop — it must be powered on and connected to the show (it only needs USB power, not a computer). The tag uses the prop’s IP address directly, so it always targets the right device regardless of device type or how many props are on the network.
192.168.86.62). This address is permanent — it never changes after pairing, so paste it once and it works forever.
-
Add the tag to a Go Button cue name In Go Button, rename the cue so its name contains the trigger tag. The tag can sit anywhere — the cue name can still read naturally:
Big Reveal #192.168.86.62/80%/3s -
Press GO When GO is pressed, Go Button advances the playhead to the next cue. The buddy sees the displayed cue name leave the tagged cue and immediately fires the prop — never when you merely open, reopen, or scrub to the cue. Pressing GO on the same tagged cue again re-triggers the prop, so a repeated reveal works every time.
Tag syntax
| Tag in the cue name | What it does |
|---|---|
#192.168.86.62/start |
Fire the prop at that IP at default velocity and duration (75 %, 5 s). Motor props (Handheld, De Kolta, Universal Controller). |
#192.168.86.62/80%/3s |
Fire at 80 % for 3 s. (Velocity first, then duration — implicit start.) Motor props. |
#192.168.86.62/reverse/80%/3s |
Run motor in reverse at 80 % for 3 s. Motor props. |
#192.168.86.62/stop |
Stop the prop immediately. Motor props. |
#192.168.86.62/test |
Quick test fire — 75 % for 5 s. Handy for a soundcheck cue. Motor props. |
Big Reveal #192.168.1.2/80%/3s #192.168.1.9/stop |
Two tags in one cue name — fires both props simultaneously. |
/start, /reverse, /stop, or (M1-alias) /test message — addresses the RF Relay’s firmware doesn’t subscribe to at all, so a tag aimed at an RF Relay’s IP is accepted and sent, but does nothing at the device. Trigger the RF Relay from QLab directly instead — see RF Relay OSC reference.
The pieces
- IP address — the prop’s static IP from the buddy’s Devices view. Works with Handheld, De Kolta, and Universal Controller, and with multiple props of the same type — each has a unique IP.
- Velocity —
0to100. The%sign is optional (75and75%are identical). - Duration —
3s(seconds),500ms, or decimal seconds (2.5s). Always include the unit suffix: a bare number with nos/ms(e.g.3000) is parsed as seconds, not milliseconds —3000means 3000 s, clamped down to the 60 s firing cap, not a quick 3 s burst.
Don’t tag the last cue
- Fix — keep at least one cue after the tagged cue: a 1-second blackout, a silent memo, or an
ENDmarker. Trailing / cleanup cues are normal show-building practice and this is the only reliable solution.
Magic API
The Magic API page turns the buddy into a live data monitor. Point it at up to three HTTP endpoints — a wiki, a bridge relay, or a lyric/word service — and the buddy polls them every 10 seconds and shows the results on screen. No QLab required, no cues involved: it’s always-on ambient data for the stage.
Setting URLs
Each section is configured with its own URL. URLs are stored on the buddy and survive reboots.
- Open the API page on the buddy and navigate to the section you want to configure (WIKITEST, BRIDGE, or ELIPS).
- Double-tap the section header (e.g. tap “WIKITEST” twice quickly). The on-screen keyboard appears.
- Type or paste your endpoint URL and confirm. Both
http://andhttps://are supported. URLs can be up to 127 characters. Certificate errors on HTTPS are bypassed — self-signed certs work fine. - The buddy polls immediately and then every 10 seconds while the API tab is open. To refresh a section right away instead of waiting for the next poll, tap anywhere in that section.
Endpoint types
The three slots each speak a slightly different protocol, designed to match common data services used in theatrical production. Every slot makes the same request — a plain GET on the URL you give it — and they differ only in what they read out of the reply:
| Slot | Reads from the response | Shown as |
|---|---|---|
| WIKITEST | the raw body, verbatim — no JSON parsing | Value |
| BRIDGE | JSON value or rawValue | Value |
| ELIPS | JSON artist, song, and word or lyric or selected or title | Artist, Song, Word |
WIKITEST — plain text
The simplest format. The buddy makes a GET request and displays the raw response body as the Value field. No JSON parsing — whatever the server returns is shown verbatim.
Use this for any lightweight endpoint that returns a single string: a custom show-state server, a script cue counter, a simple webhook that writes a word to a text endpoint, etc.
BRIDGE — JSON value relay
Designed for wkt.pw-compatible bridge services and any JSON endpoint that wraps a value in a standard envelope. The buddy reads the value or rawValue field from the JSON response body and shows it as Value.
Either field name is accepted — if both are present, value takes priority. Any other keys in the response are ignored.
ELIPS — artist / song / word
A richer format for live lyric feeds, prompter services, or any endpoint that streams the current word or line being spoken/sung on stage. The buddy reads three fields and shows them as separate labeled rows.
For the Word field the buddy tries each key in order until it finds one that exists in the response — so the same endpoint works whether it calls the current word word, lyric, selected, or title. Fields that are absent or empty are left blank on screen.
Status & errors
Each section independently shows its connection state. Values appear white when data is live, red on an error, and grey when no URL has been set yet. If the buddy has no internet at all, the entire page displays “No Internet” in red.
| Message shown | What it means |
|---|---|
Set URL — double-tap header | No URL configured for this slot yet. |
Can't connect | Network error reaching the server (DNS failure or refused connection). |
No response | The buddy connected but got no reply in time — the request timed out. |
Wrong URL or code | Server returned 401 or 403 — check the URL or any access token. |
Link not found | Server returned 404 — the path doesn’t exist. |
Server busy | Server returned 5xx — try again or check the service. |
Check the link | Response arrived but JSON parsing failed — confirm the endpoint returns valid JSON in the expected format. |
No Internet (full page) | The buddy’s WiFi is connected but has no internet route. |
Debug Console NEW
Watch any buddy or prop’s live log in the browser — no drivers, no terminal app. Plug in several at once and they share one timeline, each with its own colour tag, so you can see exactly how two devices react to the same event.
Not connected.
Two devices at once: add each via Add Device. With Auto-connect on, both reopen by themselves next time. Mark (or M) drops a numbered marker the instant you press the physical button, so both devices’ reactions can be read against the same moment. Click a device’s chip to mute it; the ✕ closes it.
Capturing starts on connect — nothing to configure. The last ~4,000 lines are kept and survive a disconnect, so you can still Copy, Download or Report after unplugging. Every line is matched against ~120 error patterns taken from the devices’ own firmware, and hits collect as clickable chips so you don’t have to read the whole log. Report Issue on GitHub pre-fills a bug report; nothing is sent until you click Submit there, since a log can contain your WiFi network name.
Beta Firmware TESTING
Pre-release builds staged for hardware testing before they merge into main.
Every device’s test build lives here in one place — whenever firmware is being
worked on, it lands in this section first, gets verified on real hardware, and only then
ships. The section empties itself automatically once the reviewed firmware merges into
main and the production release supersedes it.
New panel size in testing
The in-review buddy firmware adds a larger screen option alongside the existing 3.5″ board, so the same firmware and the same interface can run on whichever size suits the booth. The layout is the identical design, scaled up — not a different screen with more crammed onto it.
Troubleshooting
Find your symptom. Each fix links to the full explanation rather than repeating it, so there is only ever one place a given fact is maintained.
Flashing fails
- “Browser not supported” — Web Serial only exists in desktop Chrome, Edge, or Opera. Firefox and Safari cannot flash.
- The port isn’t listed — try another cable (many USB-C cables are charge-only, with no data lines), and quit anything holding the port: Arduino IDE,
screen, the Debug Console. On a Mac,ls /dev/cu.usbmodem*shows whether the board enumerated at all. - It fails partway, or the board reboot-loops — unplug, hold BOOT, plug back in, keep holding a moment, then retry. The RF Relay has no BOOT button — see its install hint for the encoder-knob equivalent.
- It flashed, but the screen stays blank — power-cycle. After a clean flash the buddy boots to the Devices view and waits for WiFi credentials; it is working, it just has nowhere to connect yet. Enter them on the device: Devices tab → SETTINGS → WiFi.
QLab won’t connect
The status dot tells you how far it got — see Reading the Status Dot. A dot that never turns blue means no show was found.
- Confirm the buddy and the Mac are on the same network, or cabled together over USB-C.
- Check QLab’s side: Workspace Settings → Network → OSC Controls, Network OSC input enabled on port 53000. Full walkthrough in OSC setup in QLab.
- On a different subnet, or a venue with AP isolation, enter the Mac’s IP by hand — Workspace picker → Enter IP Manually.
- Running QLab 4? It has no push-update OSC API, so the stage view stays blank even when the dot is blue. QLab 5 is a free upgrade at qlab.app and imports
.qlab4files via File → Import.
Connected, but GO / PANIC / STOP do nothing
- Check the buddy’s own mode first. Settings → OSC Access set to VIEW silences every control button and the chevron navigation, deliberately. Tap CTRL to restore control — see OSC Access.
- If it is already on CTRL, QLab is refusing the commands: Workspace Settings → Network → OSC Access → No Passcode row, tick Control. If the workspace has a passcode, enter the matching code on the buddy.
Go Button show doesn’t appear
- UDP Reply Port must be 53001 — Settings → Connections in Go Button. This is the single most common miss by a wide margin: Go Button hears the buddy’s query but answers on the wrong port, so the buddy sees silence. Same fix if the show timer sticks at 00:00:00.
- Both devices must be on the same WiFi network — discovery is UDP broadcast, which does not cross subnets or VLANs.
- Go Button needs a show loaded and the session active; the buddy only discovers running shows.
- If OSC Access in Go Button has a passcode, enter the same code on the buddy — a mismatch makes Go Button reject the queries silently. See OSC Access.
- Tap the workspace name in the buddy’s status bar to force a fresh scan.
A prop doesn’t appear in Devices
- Tap Pair Devices on the buddy, then power-cycle the prop — it only looks for the hotspot at boot. Full flow in Pairing Props with the Buddy.
- If the row sits on WAIT for more than 60 s, move the prop closer to the buddy and power-cycle it again.
- Already paired to a different buddy or network? Do a factory re-pair.
A prop won’t fire from a cue
Start with TEST on the prop’s row in Devices. If the prop fires from TEST, the prop and the pairing are both fine and the problem is in the cue — which halves what you have to check.
- From QLab: confirm the Network Patch is the prop’s IP on port 8000, and UDP (or TCP on prop firmware 0.12.0+). Send
/pingfirst to prove you can reach it. See Device Triggering from QLab. - From a Go Button cue name — is the tagged cue the last cue? This is the most common cause by far — a tag on the last cue has no next cue to advance to, so it never fires. See Don’t tag the last cue for the fix.
- The tag must carry the prop’s IP, not a name:
#192.168.86.62/80%/3s. The IP is permanent after pairing and is shown under the prop’s name in Devices. Verbs and syntax: Tag syntax. - The buddy has to be powered and connected to the show — it is the bridge between Go Button and the prop. USB power alone is enough; no computer needed.
- Passcodes are not the cause. The buddy syncs them to every paired prop by itself, including props that were offline when it changed — see Passcode auto-sync to props.
Release History
Highlights from earlier versions
- Touch + reliability fixes (0.9.8–0.9.9) — Touch unresponsiveness fixed: AP-subnet probe packets were firing on every draw cycle, blocking touch reads for ~30 ms; removed. Pulsing-dot redraws rate-limited to 12 fps to stop unnecessary screen contention. Tab bar dead zone between LIVE / DEVICES / API tabs eliminated (8 px gap now split 4 px each side). Prop LED broadcast: the buddy now sends
/qlab/connectedand/qlab/disconnectedto all paired props the instant a QLab workspace opens or closes — props update their LED immediately without waiting for the next poll cycle. Universal Controller TEST button now sends/identify(3× white LED flash) instead of/start, so you can confirm which board you’re targeting without triggering a motor. Auto-timezone fix:autoTimezone()was blocking the main loop for up to 1.5 s every 60 s in venues without internet; moved to a background core. Go Button: fix session no longer triggers discovery backoff. - Go Button cue triggering + passcode auto-sync (0.9.5) — Tag a Go Button cue name with a device tag (e.g.
#192.168.86.62/start) and the matching Card Fountain fires the moment Go Button advances the playhead past that cue. Go Button can’t send OSC to a device itself, so the buddy bridges it — the buddy watches Go Button’s live cue display and fires the prop the instant the playhead leaves the tagged cue, even right after a buddy reboot before the device has reported in. The buddy’s stored Go Button OSC passcode auto-syncs to every paired prop — set it once and each Card Fountain receives it; if a prop was offline when you changed it, it self-heals on its next reconnect via a passcode fingerprint check, so cue triggering just keeps working. Cue-name display strips the#IP/verbtag when a readable note precedes it, and long names no longer overrun the navigation arrows. Fixes: props now fire only when a cue is actually GO’d — never when you merely open or reopen the show, and using the on-screen chevrons to scrub no longer mis-fires a tagged prop. Closing a picked Go Button file blanks the cue / stage / timer almost instantly instead of holding the stale display ~1.5 s. Paired devices now survive a buddy reboot (and a deleted-then-re-paired device stays paired). The Devices-view UPDATE button is refused with a clear HOTSPOT hint while the hotspot is on, since a prop on the hotspot has no internet route to pull the firmware. Time-picker and OSC PIN-pad keys no longer stay visually stuck in the pressed state. Note: don’t make a tagged cue the last cue — the trigger fires when the playhead advances past the tagged cue to the next one, so there must be a next cue; keep at least one trailing cue (a blackout, memo, or “END” marker) after it. - OSC Access page + workspace picker redesign (0.9.3) — Settings → OSC Access replaces the old Workspace IP row and moves VIEW / CTRL out of the settings sub-header into its own dedicated page. The OSC Access page adds on-device numeric passcode entry for both QLab and Go Button — no more compile-time constant. Workspace picker now stays open after a tap or lock instead of jumping to the live view; locked rows pin to the top in blue with a LOCKED badge; a ghost row keeps the locked entry visible when the host is offline. Manual IP entry moved into the picker empty state as an “Enter IP Manually” escape hatch.
- VIEW / CTRL + workspace picker (0.9.2) — VIEW / CTRL segmented control added to Settings sub-header. Go Button show timer, workspace picker initial implementation, chevron cue navigation.
- Go Button connectivity fixes (0.9.1) — Two bugs fixed: (1) iPhones and iPads running Go Button couldn’t connect to the buddy’s hotspot. Root cause: in WIFI_AP_STA mode the AP auto-syncs its channel to the router channel; if the router uses 40 MHz (HT40), the AP beacons at 40 MHz too — iOS 14+ associates then immediately drops. Fixed by forcing the AP interface to HT20 (20 MHz) after softAP starts. Macs and Card Fountain props are unaffected. (2) When Go Button disconnected with QLab open but no file loaded, the buddy would auto-connect to QLab, immediately get a “no file” thump, disconnect, and repeat every 4 s — producing a visible QLAB↔WiFi status flicker. Fixed by arming the closed-file guard defensively in
gbGoIdle(). A genuine QLab file open still disarms the guard within one poll cycle via the existing fast-path. - UI polish + bug fixes (0.8.33) — IP Pool arrows register correctly. OTA dot + version centered on splash. Connection dot restored to all views. OTA dot colors: grey = up to date, green = update available, red = update available but no WiFi.
- IP Pool, status indicators, WiFi (0.8.32) — IP Pool selector (AUTO or four isolated 40-address slices). OTA update dot on splash persisted in NVS. Pairing label replaces HOTSPOT when pairing is active. Hotspot toggle shows “OFF — hold to enable.” WiFi auto-connects after save. WiFi indicator stuck-white fix.
- Device flicker fix + API URL pre-fill (0.8.31) — stale timeout restored to 6 s (main WiFi) / 8 s (hotspot). WIKITEST and BRIDGE URL keyboards pre-fill the constant prefix.
- Single-tap navigation + faster device presence (0.8.29) — Fixed double-tap regression. Prop
/helloreduced to 400 ms; buddy probe at 1.2 s of silence; stale/offline threshold 3.5 s. - OSC commands, IP stability, keyboard (0.8.28) — Props respond to
/start,/reverse,/stop, and/test. Connect time ~1 s; disconnect ~4 s. Same prop always gets same static IP on re-pair. Pool raised to 32 devices (160 addresses). - Ghost arrow fix on USB power banks (0.8.24) — Touch threshold dichotomy: real-host (0x55) vs floating-power (0x70). Detector is
usbNcmIsConnected() && usbNcmLeaseGiven()so power banks, wall chargers, and OSes without USB-NCM always get the ghost-resistant threshold. - QLab host lock (0.8.22) — Once the buddy talks to a QLab, that’s THE QLab for the session. Survives heartbeat blips, foreign-Mac overtakes, file switches, 5-min socket stalls, DHCP renumbering, and WiFi flaps.
- Wireless OTA end-to-end — SHA-256 verify + 60 s health-gate rollback. Direct Connect IP, press-and-hold prev/next, 5 s connection recovery.
What’s next
Multi-performer / multi-tenancy
- Team key — short shared secret so multiple performers on one venue WiFi don’t cross-adopt props or API URLs.
- Per-buddy hotspot SSID — suffix with MAC so two buddies in the same room don’t both broadcast plain “QLAB Buddy.”
Pairing & IP coordination
- ARP pre-check — ping a candidate IP before handing it to a prop to avoid collisions with non-paired LAN devices.
- Multi-prop pairing — pool DHCP so more than one prop can pair in a single SYNC window.
Integration & polish
- Custom theme — built-in presets (Classic Amber, High-Contrast, Cool Blue…).
- Larger paired-device list — raise MAX_FOUND / MAX_KNOWN for venues running 8+ props.
Known limitations
- One prop pairs at a time — the buddy tracks a single in-flight pairing. Starting a second SYNC while the first prop is still migrating (up to 60 s) is blocked so neither device’s identity gets corrupted. Pair them one-by-one; the hotspot itself supports up to 4 simultaneous clients, so this is a pairing-workflow limit, not a hotspot capacity one.
- USB-only buddy can’t reach WiFi-side props — enter WiFi creds on the buddy so it can see the prop’s static IP.
- Static-IP pool can collide with non-paired LAN devices — reserve .50–.209 on your router, or move DHCP to .210+.
- Two buddies in the same room both broadcast “QLAB Buddy” — pair one at a time, or keep the other’s hotspot off.
- Go Button mode: GO / PANIC / STOP unavailable — cue advancement is controlled by Go Button on the iOS device.
- Go Button: don’t tag the last cue — the trigger fires when the playhead advances to the next cue, so a device tag on the final cue fires nothing. Keep a trailing cue (a blackout, memo, or “END” marker) after it. See Device Triggering from Go Button.
- Environmental (out of scope): router AP-isolation, captive portals, 5 GHz-only WiFi, mesh band-steering.