On firmware v0.8.38 or earlier? OTA updates won’t reach you — reflash once with the installer below to restore automatic wireless updates. v0.8.39+ updates over WiFi as normal.

QLAB Buddy BETA

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 Card Fountain props 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 — auto-failover in <5 s.
🎯Locked workspaceStays on your show through blips & overtakes.
🪄Wireless propsPair Card Fountains 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.

Try it

Interactive simulation of the real firmware UI — 480 × 320 px. Click the tabs, tap cues, test device buttons.

Reading the status dot

Bottom-left of every view. The dot’s color is the connection state and the label beside it names the active path. A small gold center pip appears when the internet is also reachable. Every state the buddy can show:

Blue — connected to a show

  • QLAB + gold pip — QLab connected, internet reachable. Healthy showtime state.
  • QLAB — QLab connected, no internet. Cues still work fine.
  • Go Button + gold pip — Go Button connected, internet reachable.
  • Go Button — Go Button connected, no internet. The show still runs fine.

Gold — on a network, no show yet

  • WiFi — on WiFi with internet, but no QLab or Go Button found yet.
  • LAN — USB-C to a Mac with internet, no QLab or Go Button yet.
  • Hotspot — the buddy’s own hotspot is on and a device has joined it.
  • PairingPair Devices is running and a prop has joined the hotspot.

White — on a network, not ready yet

  • WiFi — on WiFi but no internet yet, and no QLab or Go Button.
  • LAN — USB-C cable plugged in, still getting an address or no internet yet.
  • Hotspot — hotspot on, nothing connected to it yet.
  • PairingPair Devices running, waiting for a prop to join.

Red — no network

  • No WiFi — no network path at all. Plug in USB-C or enter WiFi credentials.

Hardware

Hardware
Waveshare ESP32-S3 Touch LCD 3.5" ESP32-S3 · 480 × 320 capacitive touch · USB-C · 4 MB flash + 8 MB OPI PSRAM · ~$35
Waveshare  ·  Amazon US  ·  UK  ·  EU  ·  Canada
Flash firmware onto your Buddy

Hold BOOT on the board, plug in USB-C, release BOOT, then click Connect and Install (~60 s). After flashing, follow Get Started.

Desktop Chrome, Edge, or Opera — flashing isn’t supported in Firefox or Safari.


Get Started

Four steps from unboxed hardware to a working buddy.

  1. Buy the hardware The Waveshare ESP32-S3 Touch LCD 3.5” — links above in The Buddy. Nothing else to buy.
  2. Flash the firmware In Chrome (or Edge / Opera) on a desktop: hold BOOT, plug in USB-C, release BOOT, then click Connect and Install in the install button above. Takes about 60 seconds. Web Serial isn’t available in Firefox or Safari.
  3. Enter your WiFi credentials On the buddy, tap Devices (top center tab) → SETTINGSWiFi row. Type your network SSID and password. The buddy will connect immediately — no reboot needed. The status dot turns gold when it has a network path.
  4. Choose your mode
    • QLab 5 — plug into your Mac via USB-C or join the same WiFi network. The buddy finds QLab automatically within a few seconds. Status dot turns blue labeled QLAB. See Using with QLab 5 for the full setup.
    • Go Button — make sure your iPhone/iPad is on the same WiFi network and Go Button’s UDP Reply Port is set to 53001. The buddy discovers the show automatically. Status dot turns blue labeled Go Button. See Using with Go Button for the full setup.

The buddy broadcasts itself as qlabbuddy.local and listens on UDP port 53001. All three network paths (USB-C, WiFi, hotspot) are live simultaneously and fail over automatically.


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.

All three are active simultaneously. Failover is automatic — the buddy always routes through whatever path has QLab on it.

Buddy hotspot credentials
NetworkQLAB Buddy Passwordqlabbuddy

Cue-note timer 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.

Using with Go Button NEW in 0.9.0

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.
      Buddy hotspot credentials
      NetworkQLAB Buddy Passwordqlabbuddy
  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 NEW in 0.9.5 If your Go Button workspace has an OSC passcode, the buddy shares it with every paired Card Fountain automatically — no per-prop setup or re-pairing needed. Set or change the Go Button passcode once on the buddy and each prop receives it in the background, so cue triggering keeps working even when OSC is passcode-locked. If a prop was offline or missed a push, it re-syncs on its own the next time it reconnects.
  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.

Show timer

Go Button’s timer is simple and automatic. No setup on the buddy — it just mirrors whatever Go Button is doing. If you need per-cue countdowns or pause/resume control, that’s a QLab feature.

Go Button handles timing differently from QLab — there are no cue notes, so there are no #timer/ directives. Instead, the buddy mirrors Go Button’s own show timer directly. How it displays depends on how your Go Button show is configured:

  • No show duration set — Go Button counts up. The buddy shows elapsed time in green, counting up from 00:00:00.
  • Show duration set — Go Button counts down. The buddy shows time remaining in amber while time is left. When the show runs over, it flips to red and shows how far over you are.

You set the show duration in Go Button’s show settings — the buddy reads it automatically and adjusts its display accordingly. You don’t configure anything on the buddy.

The timer resets to 00:00:00 when the show resets in Go Button. The buddy detects motion from Go Button’s elapsed time — fire the first cue to kick it off.

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

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.5 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 500 ms while scanning. It settles within 1–2 s once Go Button replies.

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, a grey ghost row stays at the top so you can see and unlock it without losing the lock inadvertently. The buddy waits quietly for that host to reappear and ignores 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 Card Fountain 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 NEW in 0.9.5

When OSC is passcode-locked, a Card Fountain only fires from a matching passcode — so a prop on a shared network won’t fire from an unauthorized sender. You never type that passcode into the prop. Enter the Go Button passcode once on the buddy (Settings → OSC Access → Go Button passcode) and the buddy pushes it to every paired prop for you.

  • Set once, shared everywhere — the buddy sends the stored Go Button passcode to each paired Card Fountain 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 — whenever you change the passcode on the buddy — from the numeric pad or by tapping x to clear it — the new value is pushed to 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.

RF Relay BETA

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

A networked sub-GHz capture and replay node. Point a 315, 433, 868, or 915 MHz fixed-code remote at it, learn the code via OSC or the onboard encoder, then replay it from QLab or Go Button on every subsequent show. Once provisioned the RF Relay fires independently — the buddy is only needed for initial WiFi SYNC and monitoring.

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 MHzHardware antenna switch covers 315, 433, 868, and 915 MHz. Auto-scans all bands during capture; switches path 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 “CHG” 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 45 s idle.

Pairing with the buddy

  1. Start pairing mode. On the buddy, tap Devices → Pair Devices. This turns on the hotspot and begins scanning.
  2. Power on the RF Relay. It scans for the “QLAB Buddy” hotspot on every boot and joins automatically.
  3. Tap SYNC on the buddy when the device appears. Pushes your main WiFi credentials and a static IP.

Factory re-pair: hold the encoder button while powering on, keep it held for 3 s after the boot sequence. Wipes NVS credentials and reboots into SYNC mode.

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 for 315 / 433 / 868 / 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.
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 45 s idle. Any input exits.

OSC reference

Port 8000. Send Network cues from QLab (Custom Message) or Go Button cue-name tags directly to the board’s static IP. Passcode-gated when a passcode is set on the device.

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. 30 s window — hold the remote button to capture. Auto-scans all bands when no frequency is stored yet.
/rf/learn/N/MHZArm capture for slot N at an explicit frequency — 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. Sent by the buddy’s TEST button.

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.

RF Relay — v0.10.3

T-Embed uses native USB: plug in USB-C, click Connect — the tool sends a 1200-baud touch to enter download mode automatically. No BOOT button needed. Subsequent updates are wireless OTA from the buddy.


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

  1. Start pairing mode On the buddy, tap Devices → Pair Devices. This turns on the hotspot and begins scanning.
  2. Power on the Universal Controller. It scans for the “QLAB Buddy” hotspot on every boot and joins automatically. The buddy’s hotspot just needs to be active — there is no fixed time window.
  3. Tap SYNC on the buddy when the device appears. Pushes your main WiFi credentials + static IP assignment.

Factory re-pair: hold BOOT (GPIO 0) while powering on, keep it held for 3 s after power-up. The button is read during boot — holding it after the boot sequence completes has no effect. Wipes NVS and reboots into SYNC mode.

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

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.

Blue / green updates instantly when the buddy broadcasts a QLab open / close event. Romeo Mini has no onboard LED — all LED states above apply to the NULLLAB board only.

OSC reference

Port 8000. Send Network cues from QLab (Custom Message) or Go Button cue-name tags directly to the board’s IP.

Motors (M1–M4)

Replace m1 with m2, m3, or m4 for other channels. M3 and M4 are NULLLAB Maker-ESP32 only — the Romeo Mini has two motor channels (M1–M2).

Custom MessageWhat it does
/m1/start/80/00:00.03Run M1 forward at 80 % for 3 s
/m1/start/80Run M1 forward at 80 % for default duration (5 s)
/m1/startRun M1 forward at default (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
/stopEmergency stop — halts all four motors immediately
/pingConnectivity check — board replies /pong
/identifyFlash LED 3× white — confirms which board you’re talking to. Sent automatically by the buddy’s TEST button.

Servos (S1–S4)

Replace s1 with s2, s3, or s4 for other channels. Angles are 0–180°. All servos boot at 90° (center).

Custom MessageWhat it does
/s1/90Move servo 1 to 90° instantly
/s1/90/00:00.03Move servo 1 to 90° over 3 s
/s1/angle/90Move servo 1 to 90° instantly (explicit keyword form)
/s1/angle/90/00:00.03Move servo 1 to 90° over 3 s (explicit keyword form)
/s1/velocity/30Sweep servo 1 continuously at 30°/s (stops at 0° or 180°)
/s1/velocity/0Stop continuous sweep, hold at current position
/s1/bounceBounce servo 1 back and forth at 30°/s until stopped
/s1/bounce/60Bounce servo 1 back and forth at 60°/s until stopped
/s1/centerMove servo 1 to 90° (center)
/s1/stopHalt servo 1 at current position
/s1/pulse/1500Set servo 1 to raw pulse width 1500 µs (advanced)

Duration formats

FormatExampleResult
HH:MM:SS00:00:033 s
Seconds suffix3s3 s
Milliseconds suffix500ms0.5 s
Bare decimal1.51.5 s

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: if your Go Button workspace has an OSC passcode, the buddy pushes it to the Universal Controller automatically after pairing — no per-device setup needed.

Flash firmware — NULLLAB Maker-ESP32

Hold BOOT on the board, plug in USB-C, release BOOT, then click Connect and Install.

Desktop Chrome, Edge, or Opera only — Web Serial is not supported in Firefox or Safari.

Flash firmware — DFRobot Romeo Mini ESP32-C3

Hold BOOT on the board, plug in USB-C, release BOOT, then click Connect and Install.

Desktop Chrome, Edge, or Opera only — Web Serial is not supported in Firefox or Safari.


Card Fountain — Handheld ALPHA

Current firmware: v… · built checking…

Pocket-sized Card Fountain. Fires on the same OSC interface as the Dekolta. 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 Dekolta.
🔘Offline test button5 s countdown-fire, no buddy needed.
📦Pocket-sizedESP32-C3, USB-C, 47 × 38 mm.
🪄SYNC pairingHotspot provisioning from the buddy.
🔁Reboot-survivableStatic IP holds across power-cycles.
OTA + rollbackWireless updates, SHA-256 verified.

Pairing with the buddy

  1. Start pairing mode On the buddy, tap Devices → Pair Devices. This turns on the hotspot and begins scanning.
  2. Power on the Handheld within ~5 seconds. It scans for the “QLAB Buddy” hotspot on every boot and joins automatically.
  3. Tap SYNC on the buddy when the Handheld appears in the list. This pushes your main WiFi credentials to the prop, gives it a static IP, and completes the pairing.

Factory re-pair: power on, wait for 3 boot pulses, then hold BOOT for 3 s. Wipes NVS and reboots into SYNC mode.

OSC passcode: there’s nothing to set on the Handheld. If your Go Button workspace has an OSC passcode, the buddy shares it with each paired prop automatically — and re-syncs it whenever you change it. No per-prop setup or re-pairing is needed.

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 →
WIRING SCHEMATIC + 9V snap connector USB-C rechargeable ROMEO MINI DFRobot ESP32-C3 · DFR1063 Integrated Motor Driver M1 channel · PH/EN VIN+ VIN− M1+ M1− M DC Motor 3–12V DC Motor Battery attaches via snap connector · All connections use screw terminals on the Romeo Mini
9 V battery → VIN+ / VIN−  ·  DC motor → M1 screw terminals
Flash firmware onto your Handheld

C3 boards auto-enter bootloader — no button hold needed. Just plug in USB-C and click Connect and Install.

Desktop Chrome, Edge, or Opera — flashing isn’t supported in Firefox or Safari.


Card Fountain — Dekolta 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.
🔁Reboot-survivableStatic IP from the buddy’s /24.
OTA + rollbackWireless updates, SHA-256 verified.

Pairing with the buddy

  1. Start pairing mode On the buddy, tap Devices → Pair Devices.
  2. Power on the Dekolta. It joins the “QLAB Buddy” hotspot automatically on first boot or after a factory reset.
  3. Tap SYNC on the buddy row. Pushes WiFi credentials + static IP assignment.

OSC passcode is automatic: if your Go Button workspace has an OSC passcode, the buddy shares it with the Dekolta on its own — no per-prop setup or re-pairing. Change it once on the buddy under Settings → OSC Access → Go Button passcode and every paired prop re-syncs automatically, so cue triggering keeps working when OSC is passcode-locked.

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 →
WIRING SCHEMATIC + 9V snap connector USB-C rechargeable ROMEO MINI DFRobot ESP32-C3 · DFR1063 Integrated Motor Driver M1 channel · PH/EN VIN+ VIN− M1+ M1− M DC Motor 3–12V DC Motor Battery attaches via snap connector · All connections use screw terminals on the Romeo Mini
9 V battery → VIN+ / VIN−  ·  DC motor → M1 screw terminals
Flash firmware onto your Dekolta

Hold BOOT on the board, plug in USB-C, release BOOT, then click Connect and Install.

Desktop Chrome, Edge, or Opera — flashing isn’t supported in Firefox or Safari.


Device Triggering from QLab

Both Card Fountain models use the same OSC interface. Set each one up as a Network Patch in QLab and fire it with Network cues.

  1. Pair the prop to the buddy first Before you can trigger a prop from QLab, it needs to be paired to the buddy. Follow the pairing steps in the Handheld or Dekolta section. 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: UDP, 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.

OSC 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.

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 for live shows: QLab sends one UDP packet per cue by default. On a busy venue WiFi a single dropped packet is a missed fire. Duplicate the Network cue twice with a 20 ms pre-wait on each copy — three packets per fire, and the prop is idempotent on repeated /start so extras are harmless.


Device Triggering from Go Button NEW in 0.9.5

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 Card Fountain 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.

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 or Dekolta 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. If the Go Button workspace has an OSC passcode, the buddy shares it with each paired prop automatically — no per-prop setup or re-pairing needed. Pair the prop once and cue triggering keeps working even when OSC is passcode-locked.

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).
#192.168.86.62/80%/3s Fire at 80 % for 3 s. (Velocity first, then duration — implicit start.)
#192.168.86.62/reverse/80%/3s Run motor in reverse at 80 % for 3 s.
#192.168.86.62/stop Stop the prop immediately.
#192.168.86.62/test Quick test fire — 75 % for 5 s. Handy for a soundcheck cue.
Big Reveal  #192.168.1.2/80%/3s  #192.168.1.3/80%/3s Two tags in one cue name — fires both props simultaneously.

The pieces

  • IP address — the prop’s static IP from the buddy’s Devices view. Works with any device type (Handheld, Dekolta, or future props) 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, a plain number of milliseconds (3000), or decimal seconds (2.5s).

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.
Tip: the same #IP/ tag syntax works in QLab too — put it in the cue’s Notes field (Go Button has no notes field, so it reads the cue name instead). Same syntax, different field.

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 128 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. Tap the blue SYNC button in the header to force a manual refresh at any time — it flashes green to confirm the request was sent.

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.

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.

RequestExpected response
GET <your-url>Plain text string — displayed as Value

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.

RequestJSON field readDisplayed as
GET <your-url>value or rawValueValue

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.

RequestJSON field readDisplayed as
GET <your-url>artistArtist
songSong
word or lyric or selected or titleWord

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, refused connection, timeout).
No responseServer accepted the connection but returned no data.
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, configured API URLs broadcast automatically to other Buddies over local UDP. If one Buddy already has URLs set up, a newly joined Buddy on the same subnet will adopt them within seconds — no need to configure each unit individually.

Troubleshooting

Flashing issues

  • “Browser not supported” — switch to Chrome, Edge, or Opera on desktop. Web Serial doesn’t work in Firefox or Safari.
  • Port doesn’t appear in the list — try a different cable (many are charge-only). Quit any app holding the port (Arduino IDE, screen, etc.). On Mac, check ls /dev/cu.usbmodem*.
  • Flash fails partway — unplug, hold BOOT, plug back in, release BOOT, retry.
  • Flashed successfully but buddy doesn’t show anything — power-cycle the board. On first boot after a clean flash, the buddy goes straight to the Devices view and waits for WiFi credentials.

QLab not connecting (dot stays white or gold)

  1. Confirm the buddy and QLab Mac are on the same network, or connected via USB-C.
  2. Workspace Settings → Network → OSC Controls: enable Network OSC input, port 53000. Save.
  3. Tap GO on the buddy — status dot should turn blue within seconds.
  4. If you’re on a different subnet or the venue has AP isolation, try Settings → QLab IP to enter the Mac’s IP directly.

Stage view blank with dot blue (QLab 4)

The buddy needs QLab 5’s push-update OSC API. QLab 4 doesn’t have it. Upgrade free at qlab.app — QLab 5 imports .qlab4 files via File → Import.

Buddy connects but GO / PANIC / STOP do nothing

First check the buddy’s own mode: Settings → OSC Access → VIEW / CTRL. If it’s set to VIEW, all control buttons and chevron nav are intentionally silenced — tap CTRL to restore full control.

If the toggle is already on CTRL, check QLab’s OSC permissions: Workspace Settings → Network → OSC Access → No Passcode row → tick Control. Save. Also make sure no QLab passcode is set, or enter the matching code under Settings → OSC Access → QLab passcode.

Go Button show not appearing in the workspace picker

  1. Confirm both the buddy and the iPhone/iPad are on the same WiFi network. Go Button discovery uses UDP broadcast — it doesn’t cross subnets or VLANs.
  2. In Go Button: Settings → Connections → UDP Reply Port = 53001. This is the single most common miss. Go Button hears the buddy’s query but replies to the wrong port and the buddy sees nothing.
  3. If OSC Access in Go Button has a passcode, enter the same numeric code on the buddy under Settings → OSC Access → Go Button passcode. A mismatched or missing passcode makes Go Button silently reject the buddy’s queries.
  4. Confirm Go Button has a show loaded and the session is active. The buddy only discovers active shows.
  5. On the buddy, tap the workspace name in the status bar to force a fresh scan.

Go Button timer stuck at 00:00:00

  • Double-check UDP Reply Port = 53001 in Go Button (above).
  • Fire a cue in Go Button. The timer starts after Go Button reports elapsed time advancing — takes one poll cycle (~0.5 s).
  • If the show was reset, the timer resets to 00:00 automatically and resumes when the show advances.

Card Fountain not appearing in Devices

  • Tap Pair Devices on the buddy to enable the hotspot, then power-cycle the prop. It scans for “QLAB Buddy” on every boot and joins within ~10 s.
  • Tap the prop’s row SYNC button to push WiFi credentials and complete pairing.
  • If the row shows WAIT for more than 30 s, power-cycle the prop closer to the buddy and try again.
  • Paired devices vanished after a reboot? FIXED in 0.9.5 — on buddy firmware before 0.9.5, a quirk in flash storage could let a stale “deleted” list survive a power-cycle and prune a freshly-paired prop on the next boot. This is fixed: paired devices now reappear automatically on reboot, and re-pairing a device you previously deleted now sticks across reboots too. Update the buddy to 0.9.5+ if a prop keeps disappearing after power-cycling.

Card Fountain doesn’t fire from QLab

  • Verify the Network Patch is set to UDP, the prop’s IP (found on the Devices view), port 8000.
  • Tap TEST on the Devices row — if the prop fires, the issue is in the QLab Network cue patch, not the prop or buddy.
  • Use /ping from a QLab Network cue to confirm you can reach the prop before relying on /start.

Card Fountain doesn’t fire from a Go Button cue name

  • Is the tagged cue the last cue in the list? This is the most common cause. The buddy detects a GO by the playhead advancing to the next cue, so GO’ing a tagged last cue moves the playhead to nothing and the prop won’t fire. Reliable fix: keep at least one cue after the tagged cue — a 1-second blackout, a silent memo, or an “END” marker. Note: giving the tagged cue a longer duration does not help if the cue contains an internal GoTo or other action that completes the group immediately — the trailing-cue approach is the only reliable fix in that case.
  • Check the tag uses the prop’s IP address (shown in the buddy’s Devices view), not a name. Format: #192.168.86.62/80%/3s. The IP is permanent after pairing.
  • Confirm the verb is valid: /start, /80%/3s (implicit start), /reverse/80%/3s, /stop, or /test.
  • The prop must be paired with the buddy, and the buddy must be powered on and connected to the show — it’s the bridge between Go Button and the prop (USB power is enough, no computer needed). The trigger fires the moment Go Button advances the playhead past the tagged cue, even right after a buddy reboot before the prop has reported in.
  • Tap TEST on the prop’s Devices row to confirm the prop itself works — if TEST fires but the cue tag doesn’t, the issue is the tag text or the last-cue gotcha above.
  • OSC passcode is handled for you. NEW in 0.9.5 If the Go Button workspace has an OSC passcode, the buddy shares it with each paired prop automatically — no per-prop setup or re-pairing needed. Change the passcode once on the buddy and every prop re-syncs on its own, even one that was offline when you changed it.

Card Fountain fires when you open the Go Button show FIXED in 0.9.5

On buddy firmware before 0.9.5, simply opening or reopening a Go Button show file could fire a tagged cue that the buddy saw as already “running.” This is fixed: a prop now fires only when the playhead actually advances past the tagged cue (i.e. when GO is pressed and Go Button moves on), never on file open, reopen, or while you scrub through cues with the on-screen arrows. Update the buddy to 0.9.5+ if a prop triggers unexpectedly on show open.


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. UC 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’s hotspot has a single DHCP slot. Pair them one-by-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.