QLAB Buddy

Current firmware: v… · built checking… · Release History

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.

📺Live cue viewName, notes, cue type, timer — all live.
🎬GO / PANIC / STOPBig on-screen buttons. Hold an edge to jump cues.
📱Go Button modeShow timer + cue nav. No Mac required.
🔌USB-C, WiFi, hotspotThree paths, all live at once — a silent one is dropped after 5 s so another takes over.
🎯Locked workspaceStays on your show through blips & overtakes.
🪄Wireless propsPair RF and WiFi props over the built-in hotspot — OSC passcode auto-syncs.
🔥Fire props from cuesTag a Go Button cue name — the prop fires when Go Button advances past it.
Cue-note timers#timer/ directives baked into QLab notes.
OTA + rollbackSHA-256 verified, 60 s health-gate revert.

Hardware

Hardware
Waveshare ESP32-S3 Touch LCD 3.5" ESP32-S3 · 480 × 320 capacitive touch · USB-C · 4 MB flash + 8 MB OPI PSRAM · three buttons (BOOT / PWR / RST) · ~$35
Waveshare  ·  Amazon US  ·  UK  ·  EU  ·  Canada

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.

ButtonPressWhat happens
BOOTTapSends /go — advances one cue, exactly like pressing GO on screen. Ignored in VIEW mode.
BOOTHold 5 sSends /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.
PWRShort pressToggles 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.
RSTPressHard-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.

BOOT doubles as the flashing button — see Flash Firmware. Try all three in the Simulator.

Flash firmware onto your Buddy

RF Relay

Current firmware: v… · built checking… · board: LilyGo T-Embed CC1101

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.

📡315–915 MHzA three-position antenna switch spans the range, and capture sweeps six frequencies across it: 315, 390, 418, 433.92, 868.35 and 915 MHz. The right path is selected automatically on fire.
🎛️25 code slotsStore up to 25 remotes. Each slot holds one captured code, frequency, protocol, and a custom name. Survives reboots and OTA updates.
🖥️1.9″ LCD + encoder320 × 170 ST7789 IPS display with rotary encoder. Learn, rename, delete, and fire slots entirely on-device.
🔋Battery indicatorLiPo percentage read from the BQ25896 charger over I2C using a piecewise discharge curve. Shows a gold lightning-bolt icon when USB power is present. Top-right on all screens.
OTA + rollbackWireless updates pushed from the buddy. SHA-256 verified with a 60 s health-gate rollback — bad firmware auto-reverts.
🪄SYNC pairingSame hotspot pairing flow as all other props. Receives WiFi credentials and static IP from the buddy in one tap.
🔁Static IPAssigned on first SYNC and reboot-survivable — OSC cue addresses never need updating after initial setup.
QLab timer screensaverShows the running cue’s elapsed time, or a countdown driven by #timer/ directives in cue notes. Activates after 30 s idle.
🔌Cabled straight to a MacOver USB-C it appears as a USB Ethernet device on your Mac, so QLab can drive it with no router, no WiFi and no buddy in the rig at all.

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.

On this board: there is no BOOT button — factory re-pair holds the encoder knob pressed straight in. A successful pair shows a green PAIRED screen; a wipe shows a red DELETED one.

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.

A bare cable carries no internet — only Mac-to-relay traffic. Two things need it: the status-bar clock and OTA updates. A relay that’s also paired to WiFi (the normal setup) has both regardless of the cable. Only a cable-only relay goes without: the clock stays --:-- -- and OTA is unavailable until it’s paired. Cue firing, QLab detection and the screensaver timer all work over the bare cable either way.

Hardware

Two hardware options — both run the same firmware. The Plus adds an external SMA antenna for better range and a built-in microphone (unused by current firmware). All pins and features are otherwise identical.

Board
LilyGo T-Embed CC1101 ESP32-S3-WROOM-1U-N16R8 · 16 MB flash, 8 MB PSRAM · CC1101 sub-GHz transceiver · PCB trace antenna · 1.9″ ST7789 IPS LCD (320 × 170) · rotary encoder + button · 8× WS2812B LEDs · hardware antenna band switch · side power button (hold 2 s → deep sleep) · USB-C
LilyGo → · Amazon →
Board
LilyGo T-Embed CC1101 Plus Same ESP32-S3 + CC1101 as above · external SMA antenna (better RF range across all six bands: 315 / 390 / 418 / 433.92 / 868.35 / 915 MHz) · built-in microphone (unused by current firmware) · 1.9″ ST7789 IPS LCD (320 × 170) · rotary encoder + button · 8× WS2812B LEDs · side power button · USB-C
Amazon →

Display UI

Rotate to scroll · short press to confirm/fire · long press to open setup or go back.

ScreenDescription
Home25-slot list — slot number, name, frequency, captured code. Short press fires. Long press opens Setup.
SetupPer-slot menu: LEARN / FIRE / RENAME / DELETE. Long press back.
Delete confirmSelecting DELETE opens a CANCEL/DELETE toggle screen — scroll to switch, press to confirm. No more accidental holds.
Listening30 s capture window. Auto-scans 315 / 390 / 418 / 433 / 868 / 915 MHz. Short press cancels.
CAPTUREDCode, frequency, and protocol confirmed. Auto-returns to Home.
NO SIGNALNothing received in 30 s. Auto-returns to Setup.
RenameCharacter picker (A–Z, 0–9). Short press places, long press saves.
ScreensaverQLab elapsed timer or #timer/ countdown. Activates after 30 s idle. Any input exits.

OSC reference

Port 8000 (UDP; firmware 0.12.0+ also accepts TCP on the same port — use TCP if the cue must be guaranteed). Send Network cues (e.g. a QLab Custom Message cue) directly to the board’s static IP — the relay subscribes these addresses itself. Every address below is QLab/direct-OSC only — there’s no Go Button device tag for the RF Relay yet (see Device Triggering from Go Button).

Passcode-gated commands fail silently. If a Go Button passcode is set, the buddy keeps the relay’s own copy of it current automatically (see Passcode auto-sync to props), so QLab Custom Messages you send can use it too — the sender still has to include it itself: every address below except /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.
AddressWhat it does
/rf/NFire 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/TIMEFire 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/NArm 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/MHZArm 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/MHZSet the active band and re-drive the antenna switch — e.g. /rf/freq/315, /rf/freq/433, /rf/freq/868, /rf/freq/915.
/rf/stopCancel an armed learn without saving.
/pingConnectivity check — board replies /pong (and sends /hello).
/identify3× 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.

One slot fires at a time. There’s a single sub-GHz radio on board, so firing a new slot while an earlier hold is still running doesn’t queue or run alongside it — it immediately takes over the radio and replaces the transmission in progress. If two cues might land close together, give the first a hold time short enough to finish before the second one fires.

Workflow: before the show, fire /rf/learn/N/MHZ (or use the onboard LEARN option), hold the remote button until captured. During the show, fire /rf/N or /rf/N/TIME from your cue stack exactly like any other prop trigger.

Flash firmware onto your RF Relay

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.

The dot answers one question — is a show open? Blue means yes, and nothing else is blue. Whether the internet check has confirmed is a separate fact, and it rides the ring and label colour on every path.

Status barPathWhat it means
Blue dot with a gold ring, labeled QLAB 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.
Blue dot with a white ring, labeled QLAB 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.
Gold dot, labeled WiFi 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”.
White dot, labeled LAN 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.
Gold dot, labeled Pairing 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.
Grey dot, labeled WiFi 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 → SETTINGSWiFi).
Red dot, labeled 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”.

Every dot above is a real screenshot from the firmware simulator, not a mockup — see it live in the Simulator.


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.

  1. Start pairing mode. On the buddy, tap Devices → Pair Devices. This turns on the hotspot and begins scanning.
  2. 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.
  3. 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.

The static IP it receives is reboot-survivable, so the OSC addresses you write into cues never need updating afterwards. Find it any time under Devices on the buddy, listed beneath the prop’s name.

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.

Which button depends on the board:

BoardHold thisWhat you’ll see
RF Relay (T-Embed CC1101)The encoder knob, pressed straight in — this board has no BOOT buttonA red DELETED confirmation screen, then it reboots into SYNC mode. A successful pair shows a green PAIRED screen.
Universal ControllerBOOT — GPIO 0 on NULLLAB, GPIO 9 on Romeo MiniStatus LED returns to its unprovisioned pattern.
Card Fountain — HandheldBOOTNothing — this board has no status LED. Just hold through the power-up.
Card Fountain — De KoltaBOOTNothing — 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.

qlabbuddy.com QLAB Buddy Paired prop binary, self-update binary, direct download URL + SHA-256 only
The buddy updates itself by downloading its own binary from qlabbuddy.com. Pushing an update to a paired prop works differently: the buddy sends only a URL and a SHA-256 hash — the prop downloads and verifies that binary itself, direct from the internet. The buddy never streams firmware bytes to a prop.

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.

Blocked while the buddy’s own hotspot is on — the tap flashes a HOTSPOT hint instead, since a prop reached only through the hotspot has no route to the internet to pull its binary from. Turn the hotspot off first.

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.

OTA needs real internet, not just a local link: a prop reachable only over a bare USB cable or the buddy’s hotspot can’t update until it also has a working WiFi connection with an actual route out.


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.

No workspaces showing? Tap Enter IP Manually at the bottom of the picker to type the host’s IP directly (e.g. 192.168.1.42). Useful when mDNS is blocked or the device hasn’t announced yet.

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.

Tap a passcode row to open the numeric pad. Tap x to the right of a set passcode to clear it.

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.
There is no per-prop passcode field. The passcode always comes from the buddy’s Go Button passcode setting and flows to the props automatically. If a prop stops firing from a passcode-locked workspace, check that the buddy’s Go Button passcode matches the show — not the prop.

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.

QLab gives you full timer control. Countdown, count-up, pause, resume, end — all set per cue. Go Button only has a single show-elapsed clock with no per-cue control.
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

The duration is set in Go Button’s own show settings, not on the buddy. The buddy just reads it and mirrors the display — there is nothing to configure here. If you need per-cue countdowns or pause/resume control, that’s a QLab feature, not this one.

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.

For per-cue countdowns, count-up stopwatches, pause, and resume — use QLab with #timer/ directives. See timer directives from QLab.

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.

In this demo, the buddy does not fire the relay. QLab addresses it directly, and the buddy’s role here is monitoring: its Devices row flashes when the relay reports back that it fired. That matches real hardware — there’s no Go Button device tag for the RF Relay yet, so QLab is currently the only way to trigger it. See RF Relay OSC reference.

The simulator rebuilds automatically from firmware source on every commit that touches it, so it can’t drift from the boards on your bench.


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 → SETTINGSWiFi), 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.

Auto-install
Watching for a board…

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

Requires QLab 5. The buddy uses QLab 5’s push-update OSC API for live cue names, notes, and #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.

  1. 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.
  2. Allow control Workspace Settings → Network → OSC Access.
    Check Allow OSC connections. In the No Passcode row, tick Control. Save.

    To run in view-only mode (buddy shows cues but GO/PANIC/STOP do nothing), tick View only and leave Control unchecked. You can flip back any time.

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.

USB-C and WiFi are both live all the time. The hotspot joins them once you turn it on (off by default — Settings → Hotspot). A silent host is dropped automatically after 5 s so another path can take over — unless you’ve manually locked to one via the workspace picker, which stays locked through silence. See Workspace Lock.

Buddy hotspot credentials
NetworkQLAB 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.

The one thing that needs real internet, not just the cable, is the status-bar clock — it syncs from an internet time server, and with WiFi off there’s no route to one, so it shows --:-- -- until the buddy also has WiFi or a hotspot-fed connection. Cue firing, QLab detection, and everything else run fine over the bare cable regardless.


Using with Go Button

Go Button is an iPhone and iPad app by Figure 53, the makers of QLab. It’s a standalone cue-advance controller — load a show file, press GO. The buddy connects to Go Button over WiFi and mirrors the show in real time. No Mac needed at all. You can also trigger paired Card Fountain props from a Go Button show — see Device Triggering from Go Button.

Two required settings in Go ButtonSettings → Connections:
  • UDP Reply Port = 53001 — the buddy’s listen port. Without it, Go Button’s replies go to the wrong port and the buddy sees nothing.
  • OSC passcode — if Go Button has an OSC passcode set, enter the same code on the buddy under Settings → OSC Access → Go Button passcode. If the codes don’t match, Go Button silently rejects the buddy’s queries and the show never appears.
These two are the most common setup misses.

Setup

  1. Put both on the same network The buddy and the iPhone / iPad running Go Button must share one network — the buddy uses UDP broadcast for discovery, so it won’t cross subnets or VLANs. Two ways to do that:
    • Shared WiFi — join both to the same router (same SSID). Best when you have a venue or house network.
    • Buddy hotspot — no router? Turn the buddy’s hotspot on at Settings → Hotspot (press and hold the row to enable), then join the iPhone to it. Great for a self-contained rig. The iPhone won’t have internet on the hotspot — that’s fine, Go Button and the buddy only need to reach each other. Same fixed credentials as always — see Three ways to connect.
  2. Set UDP Reply Port to 53001 Open Go Button on your device. Tap Settings → Connections. Set UDP Reply Port to 53001. Do this once — it persists across sessions.
  3. Match the OSC passcode If OSC Access in Go Button has a passcode set, open the buddy’s Settings → OSC Access → Go Button passcode and enter the same numeric code. If no passcode is set in Go Button, leave the buddy’s field blank.
  4. Passcode auto-shares with paired props Set the Go Button passcode once on the buddy and every paired prop receives it in the background — no per-prop setup, no re-pairing. Cue triggering keeps working while OSC is passcode-locked, and a prop that was offline re-syncs itself on reconnect.
  5. Open a show in Go Button Load your show file and start the session. The buddy will detect the broadcast within a few seconds.
  6. Pick the show on the buddy Tap the workspace name in the buddy’s status bar (or go to Settings → Workspace) to open the picker. Go Button shows appear alongside any QLab workspaces. Tap yours. The status dot turns solid blue labeled Go Button.

What you see in Go Button mode

  • Top-bar timer — show elapsed time in HH:MM.SS format, counting up from 00:00 when the show is running. Resets to 00:00 when the show resets. The timer detects motion — it pauses automatically when Go Button stops advancing cues.
  • Stage view — current cue name and cue number, updated each time Go Button advances.
  • Chevron nav — left and right screen-edge arrows appear when a show is live. Hold either edge to step one cue back or forward in Go Button. Chevrons are hidden and navigation is disabled when the buddy is in VIEW mode (Settings → OSC Access → VIEW / CTRL).
  • Status bar — the Go Button show name appears in the center. Status dot is solid blue, labeled Go Button.

Tips

  • If the timer shows 00:00.00 and doesn’t move, fire a cue in Go Button first — it takes one poll cycle (~0.2 s) to detect motion.
  • If your show appears in the picker but the timer is always blank: double-check the UDP Reply Port setting (53001) — that’s almost always the cause.
  • Workspace picker flickering during search is normal — the buddy broadcasts every 400 ms while scanning. It settles within 1–2 s once Go Button replies.

Universal Controller BETA

Current firmware: v… · built checking… · boards: NULLLAB Maker-ESP32  ·  DFRobot Romeo Mini ESP32-C3

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.

⚙️2–4 motor channelsM1–M4 on NULLLAB, M1–M2 on Romeo Mini. Each independently addressable.
🔄4 servo channelsS1–S4, 0–180° timed moves, continuous sweep, or bounce (auto-reversing) until stopped.
📐Cosine rampSmooth motor ease-in / ease-out on every fire.
🔁Static IPReboot-survivable — your cues never need updating.
OTA + rollbackWireless updates, SHA-256 verified. Buddy auto-selects the correct binary for your board variant — NULLLAB or Romeo Mini.
🪄SYNC pairingPairs over the buddy hotspot in seconds.

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.

On this board: factory re-pair holds BOOT — GPIO 0 on NULLLAB, GPIO 9 on Romeo Mini.

Hardware

Board
NULLLAB Maker-ESP32 ESP32-WROOM-32E · TB67H450FNG 3.5 A motor driver · 4 DC + 4 servo channels · NeoPixel LED · USB-C · 80 × 57 mm
NULLLAB GitHub →  ·  Amazon US →
Board (alt)
DFRobot Romeo Mini ESP32-C3 ESP32-C3 · DRV8220 motor driver · 2 DC + 4 servo channels · no onboard LED · smaller form factor
DFRobot →
Battery
9 V USB-C Rechargeable Lithium Battery — 1400 mAh (4-pack) Powers the board + motors. 1000 charge cycles. Recharges via USB-C — same cable as everything else on the rig.
Amazon →
Connector
CHANZON 9 V Battery Snap Connector — I-type pigtail (UL wire) Connects a 9 V battery to the barrel jack (5.5/2.1 mm) power input. 9 V is one option within the 6–16 V range — a 12 V supply or bench power supply works equally well.
Amazon →
Power: the motor driver requires an external 6–16 V supply via the barrel jack (5.5/2.1 mm). USB-C alone powers the ESP32 but won’t drive motors. Use a 9–12 V power supply or battery for anything that moves.
WIRING SCHEMATIC + 6–16V DC barrel jack 5.5 / 2.1 mm MAKER-ESP32 NULLLAB · ESP32-WROOM-32E · TB67H450FNG TB67H450FNG Motor Driver 4 channels (M1–M4) · 3.5 A max · IN1/IN2 mode Servo headers (S1–S4) 50 Hz PWM · 3-pin (Signal / VCC / GND) USB-C powers ESP32 only — motors need barrel jack VIN+ VIN− M+ M− SIG VCC GND M DC Motor × M1–M4 Servo × S1–S4 6–16 V DC barrel jack → board power · PH2.0 motor terminals (M1–M4) · 3-pin servo headers (S1–S4)
6–16 V DC via barrel jack  ·  motor outputs M1–M4 (PH2.0)  ·  servo headers S1–S4 (Signal / VCC / GND)

LED status

Romeo Mini has no onboard LED — everything below is NULLLAB only.

PatternMeaning
Idle
BlueQLab show file open and ready.
GreenWiFi connected, no QLab file open.
Boot
White pulse ×3Boot animation, board is starting up.
YellowConnecting to WiFi.
Magenta blinkOn buddy hotspot, waiting for SYNC or receiving credentials.
Pairing
3× green flashCredentials saved, board rebooting onto your WiFi.
Active
White breathingMotor running.
3× white flash/identify received, confirms which board you’re targeting.
Error
Red blinkWiFi dropped, waiting to reconnect.
YellowActively reconnecting to WiFi.

Blue / green updates instantly when the buddy broadcasts a QLab open / close event.

OSC reference

Same transport as every other prop — port 8000, UDP or TCP. See the RF Relay’s OSC reference for the transport details and when to prefer TCP.

Swap the channel number for another: m1m4 for motors, s1s4 for servos. M3 and M4 are NULLLAB Maker-ESP32 only — the Romeo Mini has two motor channels. Both boards have all four servo channels. Servo angles are 0–180° and every servo boots centred at 90°. Durations accept 00:00:03, 3s, 500ms, or a bare number (3, 1.5) read as seconds.

Custom MessageWhat it does
Motors
/m1/start/80/00:00.03Run M1 forward at 80 % for 3 s
/m1/start/80Run M1 forward at 80 % for the default 5 s
/m1/startRun M1 forward at the defaults (75 % / 5 s)
/m1/reverse/80/00:00.03Run M1 in reverse at 80 % for 3 s
/m1/stopHalt M1 immediately
/m1/testQuick test fire — 75 % / 5 s
Servos
/s1/90Move servo 1 to 90° instantly
/s1/90/00:00.03Move servo 1 to 90° over 3 s
/s1/angle/90Same as /s1/90 — explicit keyword form, with or without a duration
/s1/velocity/30Sweep servo 1 continuously at 30°/s (stops at 0° or 180°)
/s1/velocity/0Stop the sweep, hold at the current position
/s1/bounceBounce servo 1 back and forth until stopped — add a number (/s1/bounce/60) to set °/s
/s1/centerMove servo 1 to 90° (centre)
/s1/stopHalt servo 1 at the current position
/s1/pulse/1500Set servo 1 to raw pulse width 1500 µs (advanced)
Whole board
/stopEmergency stop — halts every motor on the board at once
/pingConnectivity check — the board replies /pong
/identifyFlash the LED 3× white — confirms which board you’re talking to. This is what the buddy’s PING button sends.

Aliases: /start, /reverse, and /test (without a channel prefix) target M1 — so any existing QLab patch that already uses those commands works without changes.

OSC passcode: nothing to set here — the buddy syncs it automatically. See Passcode auto-sync to props.

Flash firmware — NULLLAB Maker-ESP32
Flash firmware — DFRobot Romeo Mini ESP32-C3

Card Fountain — Handheld ALPHA

Current firmware: v… · built checking…

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.

🔥Fires on /startSame OSC as De Kolta.
🔘Press-and-hold test fireHold the button to run the motor at 75 % (25 s safety cap), release to stop. No buddy needed.
📦Pocket-sizedESP32-C3, USB-C, 47 × 38.5 mm.
🪄SYNC pairingHotspot provisioning from the buddy.
🔁Reboot-survivableStatic IP holds across power-cycles.
OTA + rollbackWireless updates, SHA-256 verified.

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.

On this board: factory re-pair holds BOOT. There is no status LED, so there is nothing to watch for — just hold through the power-up.

Hardware

Board
DFRobot Romeo Mini ESP32-C3 (DFR1063) ESP32-C3 · integrated dual-channel PH/EN motor driver · USB-C · 47 × 38.5 mm
DFRobot →
Motor
FA-130 Brushed DC Motor — 3–12 V, 25,000 RPM Standard 130-size hobby motor. Wires directly into the Romeo Mini’s M1 EN / PH screw terminals. 10-pack.
Amazon →
Battery
9 V USB-C Rechargeable Lithium Battery — 1400 mAh (4-pack) Powers the board + motor. 1000 charge cycles. Recharges via USB-C — same cable as everything else on the rig.
Amazon →
Connector
9 V Battery Snap Connector — I-type pigtail (20-pack) Connects the 9 V battery to the Romeo Mini’s power input.
Amazon →
9 V battery → VIN+ / VIN−  ·  DC motor → M1 screw terminals
Flash firmware onto your Handheld

Card Fountain — De Kolta ALPHA

Current firmware: v… · built checking…

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.

🔥Fires on /startDirect from QLab Network cues.
📈Smooth rampCosine ease-in / ease-out.
🪄SYNC pairingHotspot provisioning from the buddy.
🏷️Device-owned nameSet once, survives reboot — every paired prop supports this, not just De Kolta.
🔁Reboot-survivableStatic IP from the buddy’s /24.
OTA + rollbackWireless updates, SHA-256 verified.

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.

On this board: factory re-pair holds BOOT. There is no status LED, so there is nothing to watch for — just hold through the power-up.

Hardware

Board
DFRobot Romeo Mini ESP32-C3 (DFR1063) ESP32-C3 · integrated dual-channel PH/EN motor driver · USB-C · 47 × 38.5 mm. Motor wires into the M1 EN / PH screw terminals; power via VIN+ / VIN−.
DFRobot →
Motor
FA-130 Brushed DC Motor — 3–12 V, 25,000 RPM Standard 130-size hobby motor. Wires directly into the Romeo Mini’s M1 EN / PH screw terminals. 10-pack.
Amazon →
Battery
9 V USB-C Rechargeable Lithium Battery — 1400 mAh (4-pack) Powers the board + motor. 1000 charge cycles. Recharges via USB-C — same cable as everything else on the rig.
Amazon →
Connector
9 V Battery Snap Connector — I-type pigtail (20-pack) Connects the 9 V battery to the Romeo Mini’s power input.
Amazon →
9 V battery → VIN+ / VIN−  ·  DC motor → M1 screw terminals
Flash firmware onto your De Kolta

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

PropCommands
Card Fountain (Handheld / De Kolta) /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 →

/ping and /identify work identically on all three — connectivity check and a physical flash to confirm which board you’re patched to.

Patch it into QLab

  1. 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.
  2. 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.
  3. 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 — or UDP on prop firmware older than 0.12.0), Host: <prop IP>, Port: 8000.
  4. 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.
Want the cue guaranteed to fire? Use TCP. QLab’s default 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

Both Handheld and De Kolta take the same commands. In QLab’s Network cue Custom Message field, type the OSC address directly. Velocity is 0–100; duration uses the s suffix for seconds. For the RF Relay or Universal Controller, see the table above.

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.

Reliability tip: the strongest option is switching the patch to TCP. On UDP (or prop firmware older than 0.12.0), QLab sends one packet per cue and never retries, so one dropped packet on busy venue WiFi is a missed fire. Group 2–3 identical Network cues under one Group cue with no pre-wait, so they fire as a burst — losing all copies is far less likely than losing one. The prop ignores repeat /starts, so the extras are harmless.


Device Triggering from Go Button

Go Button can’t send OSC. Unlike QLab, Go Button has no Network/OSC cue — its OSC support is receive-only (for being remote-controlled). So you can’t patch a prop inside Go Button the way you do in QLab. The buddy bridges the gap instead.

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.

GO BUTTON QLAB BUDDY PROP Big Reveal #IP/80%/3s (tagged) Next cue GO pressed sees playhead leave tagged cue QLAB Buddy fires OSC + passcode Prop responds
Go Button never sends a signal when GO is pressed — it can’t send OSC at all. The buddy is continuously watching the displayed cue name instead, and the instant it sees the playhead leave a tagged cue, it fires the matching prop itself, with the passcode included automatically.
Where to find the IP: pair the prop with the buddy, then open the Devices view. The static IP is shown under each prop’s name (e.g. 192.168.86.62). This address is permanent — it never changes after pairing, so paste it once and it works forever.
  1. Pair the prop to the buddy first Go Button can’t talk to props directly — the buddy handles that. Follow the pairing steps in the Handheld, De Kolta, or RF Relay section. Once paired, the prop’s static IP appears in the buddy’s Devices view.
  2. 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
  3. 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.
Passcodes are handled for you. Pair the prop once and cue triggering keeps working even when OSC is passcode-locked — see Passcode auto-sync to props.

Tag syntax

Format is #<IP>/<command>. Multiple tags in one cue name fire all their props independently.

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.
RF Relay isn’t reachable this way yet. These tags only ever produce a bare /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.
  • Velocity0 to 100. The % sign is optional (75 and 75% are identical).
  • Duration3s (seconds), 500ms, or decimal seconds (2.5s). Always include the unit suffix: a bare number with no s/ms (e.g. 3000) is parsed as seconds, not milliseconds — 3000 means 3000 s, clamped down to the 60 s firing cap, not a quick 3 s burst.

On a Universal Controller, every tag above only ever reaches Motor 1 — there’s no tag syntax for M2–M4 or for any servo channel. Trigger those from QLab directly (Universal Controller OSC reference).

Don’t tag the last cue

The tagged cue can’t be the last cue in the list. The buddy fires by watching Go Button’s displayed cue name: when the playhead leaves a tagged cue and lands on the next one, that transition is the trigger. When the tagged cue is last there is no next cue — the playhead stays on (or leaves to nothing) and the buddy sees no transition, so the prop won’t fire.
  • Fix — keep at least one cue after the tagged cue: a 1-second blackout, a silent memo, or an END marker. Trailing / cleanup cues are normal show-building practice and this is the only reliable solution.
Go Button only. QLab has its own OSC out and reaches a prop directly via a Network cue, so this tag mechanism is Go Button’s cue name only — a QLab cue’s Notes field is never scanned for a device tag.

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.

Where to find it. Swipe or tap to the API tab in the buddy’s top navigation bar. The view shows three labeled sections — WIKITEST, BRIDGE, and ELIPS — each independently configurable.

Setting URLs

Each section is configured with its own URL. URLs are stored on the buddy and survive reboots.

  1. Open the API page on the buddy and navigate to the section you want to configure (WIKITEST, BRIDGE, or ELIPS).
  2. Double-tap the section header (e.g. tap “WIKITEST” twice quickly). The on-screen keyboard appears.
  3. Type or paste your endpoint URL and confirm. Both http:// and https:// are supported. URLs can be up to 127 characters. Certificate errors on HTTPS are bypassed — self-signed certs work fine.
  4. 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.

The blue SYNC button in the header is unrelated to polling — it broadcasts your configured URLs to other buddies on the network. See Multi-Buddy sync below.

To clear a URL, double-tap the header and submit an empty value.

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:

SlotReads from the responseShown as
WIKITESTthe raw body, verbatim — no JSON parsingValue
BRIDGEJSON value or rawValueValue
ELIPSJSON artist, song, and word or lyric or selected or titleArtist, 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 shownWhat it means
Set URL — double-tap headerNo URL configured for this slot yet.
Can't connectNetwork error reaching the server (DNS failure or refused connection).
No responseThe buddy connected but got no reply in time — the request timed out.
Wrong URL or codeServer returned 401 or 403 — check the URL or any access token.
Link not foundServer returned 404 — the path doesn’t exist.
Server busyServer returned 5xx — try again or check the service.
Check the linkResponse 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.
Multi-Buddy sync. When multiple buddies are on the same network, tapping the blue SYNC button on the API page broadcasts this buddy’s configured URLs to every other buddy listening on the same WiFi over local UDP — it flashes green to confirm the broadcast was sent. This is a manual, one-way push, not automatic: set the URLs on one buddy, then tap SYNC to propagate them. A buddy that joins later won’t pick up an existing peer’s URLs on its own — someone has to tap SYNC again after it joins.

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.

Live Log

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.

Test firmware — not for show nights. These builds compile cleanly but are still being verified on real hardware. Flash them only if you’re actively testing. To go back, use the regular Install button in the matching device section above — it always carries the released version.

Loading…

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.

No physical or on-screen go/panic button on this board — this is final, not a placeholder. GPIO0, where the 3.5″ board’s button lives, is permanently wired to the display panel’s own data bus on this hardware (confirmed against Waveshare’s official schematic), and nothing else on the board can substitute for it. /go and /panic still work on this board — just over OSC from elsewhere on the network, never from the buddy itself.

Do not buy this yet if you need a working buddy. Support for the board is written but has never been run on the physical hardware — no one has flashed a panel. Every value in the display and touch drivers was taken from Waveshare’s own published schematics and example code, which is enough to compile but is not proof that it boots. Until this turns into a tested release, treat it as a part for building along, not as a device that works. The 3.5″ board is the only size that is actually shipping.
4.3″
Waveshare ESP32-S3-Touch-LCD-4.3B 4.3″ diagonal · 800 × 480 IPS · capacitive touch (GT911, 5-point) · RGB parallel panel · ESP32-S3 · 16 MB flash + 8 MB PSRAM · USB-C
Amazon →  ·  Waveshare →  ·  Schematics & vendor code →

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 → SETTINGSWiFi.

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 pickerEnter 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 .qlab4 files 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 53001Settings → 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 /ping first 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

The current version’s bullets appear in the callout at the top of this page — auto-fetched from qlab-buddy.version.json. Earlier highlights below.

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/connected and /qlab/disconnected to 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/verb tag 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 /hello reduced 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.