🎚️ Desk controller

A palm-sized M5Stack unit with a DMX (or Art-Net) input and a 433 MHz transmitter. It learns the props' remote codes from their own config files, shows them as tap-to-fire tiles, and fires them from DMX so a lighting desk can cue the props like any other fixture.

controller firmware v1.1

What it does

The props only ever hear remote-button codes over radio. The controller sends those same codes, so to a lantern it is indistinguishable from the hobby remote β€” nothing on the props changes, show-mode WiFi-off still applies, and the hand remote keeps working alongside. Three things can make it transmit:

TriggerHow
TouchTap a cue tile on the screen. Manual firing at rehearsal, or the stage manager's backup during a show.
DMXA channel in the incoming DMX or Art-Net universe moves into a value band mapped to a cue. This is the desk path β€” see Desk setup.
WebNothing fires from the web page. The web UI is for configuration only (book, map, discovery, settings, updates).
WiFi on the controller is a desk-side convenience for configuration. The RF and DMX paths do not depend on it, and both keep running with WiFi off. A configured controller boots with WiFi on; untick Bring WiFi up on boot in Settings for a dark desk.

Hardware and cabling

Three parts stack together: the M5Stack CoreS3 (the screen), the M5Stack DMX Base underneath it (RS-485 with XLR adapters), and the RF433T transmitter on a Grove lead from Port A with its little antenna.

  1. DMX Base DIP switches. Switch 3 = input termination β€” ON if the controller is the last device on the line. Switch 1 = bias, per your line position. Switch 2 selects whether the Base feeds the host (normal) or bypasses it.
  2. DMX in. Desk (or Art-Net / sACN node) β†’ XLR β†’ the Base's IN. The controller listens to the whole universe; which channel matters is set in the map, not by an address switch.
  3. DMX through. The Base's OUT is a hard-wired pass-through of IN (it parallels the pair, the controller keeps listening either way), so the controller can sit mid-line in front of other fixtures.
  4. Antenna placement. Keep the RF433T and its antenna clear of the DMX Base β€” stacking them detunes the link, and the lanterns stop hearing it reliably. Sitting it a hand's width away on its Grove lead is enough.
  5. Power. USB-C. The unit has a small internal battery for carrying between positions, but run it from mains at the desk.

The Art-Net variant (CoreS3 Lite + node)

Where the desk speaks Art-Net rather than XLR, a second build of the same firmware (controller-artnet.bin) runs on a CoreS3 Lite with three Grove units instead of the DMX Base: the RF433T on Port A as before, an M5Stack Unit PoESP32 on Port C (the Ethernet node, running its own small firmware) and an M5Stack Unit DMX on Port B giving an XLR OUT. The Lite itself only has Port A, so it sits on a Base DIN or a Bus Module to get Ports B and C. Two Art-Net universes are subscribed: the prop universe feeds the same DMX map and fires cues exactly as XLR does, and the out universe is retransmitted as plain DMX512 on the Unit DMX — so the controller doubles as a one-universe Art-Net node for anything else at the desk.

  1. Settings → Art-Net sets both universes (net / subnet / universe) and the node's name; the node shows up in the desk's node list under that name with two output ports.
  2. The status strip reads NO NODE (nothing on the Grove link), NO NET (node up, no Ethernet link), NO IP (waiting for DHCP; after 10 s the node falls back to a 2.x.y.z address), then the usual dmx:live / dmx:idle.
  3. On the MON screen, tap anywhere except the X to flip the grid between the prop and out universes.
  4. The node is updated from Settings → Node firmware over the same Grove link; only its very first flash needs a USB serial adapter on the unit's internal header.
  5. Power: the node runs on PoE if the switch provides it, otherwise from the Lite's Grove 5 V; with both present a small diode mod inside the node keeps the two supplies apart (bench notes in the repo).

First boot and WiFi

  1. A controller with no configuration boots into its own hotspot, vivo-ctrl-xxxxxx (ask your production tech for the password β€” it's the same one the props use). Join it from a phone or laptop; the setup page opens by itself.
  2. Under Settings, give it a name and add your venue WiFi (up to five networks, tried in order β€” Scan networks fills the fields). Press Save; the page asks Join & save and the controller leaves the hotspot and joins the venue network straight away.
  3. From then on it boots WiFi-on and reachable at http://vivo-ctrl-xxxxxx.local (or the IP in the screen's header). Untick Bring WiFi up on boot if you'd rather it started dark.

Whatever the boot setting, the left segment of the on-screen header is a WiFi toggle: tap it to bring WiFi up or take it down without touching a browser. The header shows the IP once it has one.

The screen

The touchscreen is the controller's show-time face and works with no network at all.

AreaWhat it is
Header, leftWiFi state and IP. Tap = toggle WiFi.
Header, rightMAP opens the on-screen map stepper; MON opens the DMX monitor. Both below.
Cue tilesTwo columns of tiles, grouped under a heading per source (one source per prop config in the book). Tap a tile and the controller transmits that cue's code; the tile highlights for the half-second the send takes. Long books scroll.
Status stripTwo lines at the bottom: the last cue fired from DMX (source and cue name), then steady stats β€” DMX live/idle, WiFi, and any map file error.

The book: where the codes come from

The controller never invents codes. It keeps a book of sources, each one a prop's exported configuration; every scene (slot) in that file becomes a cue with that scene's captured codes. Set the props up first β€” names, buttons, looks β€” then fill the book. Three ways, all on the web UI:

RouteWhen
Book β†’ Upload book…You have a prop's Export file. The file name becomes the source name (lantern-config.json reads as lantern).
Discover β†’ Scan β†’ ImportThe props are on the venue WiFi with the controller. Scan lists every prop that answers; Import fetches its config directly; Import All does the lot.
Discover β†’ Enumerate β†’ Crawl selected Fresh props still in hotspot mode. Enumerate lists their hotspots by name, type and signal; tick the ones you want and crawl. The controller leaves the venue WiFi, visits each hotspot in turn and fetches its config, then comes back β€” your browser drops off for the duration and the page picks the result up on its own. The screen shows live progress throughout.

One fleet, one source

Twenty identical lanterns would make twenty identical sources, so imports are de-duplicated by content: a config whose scenes and codes match an existing source is reported as identical to "lantern" β€” skipped, and a config that differs only in name replaces the matching source rather than sitting beside it. A synced fleet therefore converges on one source, which is what you want β€” one heading of tiles on the screen and one fixture on the desk.

Source names are what the DMX map points at, and they are truncated to 16 characters on import (a prop named lantern-prototype becomes lantern-prototyp). If you re-import under a different name after mapping, the map's rows go unresolved and DMX quietly stops firing that source. The map page flags it in red; fix by renaming the source to match, or re-adding the source to the map.

Each source card also offers Download (the stored file, byte-identical) and Delete.

The DMX map

The map is the bridge from desk to book: this channel, in this value band, fires this cue. The natural shape is one DMX channel per source, with the source's cues sharing the channel as even value bands. That gives the desk one fixture per prop type with a single "scene" parameter β€” the Desk setup page covers the desk side of that.

  1. Open DMX Map, pick a source in the drop-down and press Add source to map. The controller auto-maps it: next free channel, bands evenly spread over 0–255, any cue whose name contains "off" placed first (lowest band) so a fader at zero means dark.
  2. Adjust if you like: type a different channel, or edit any band's lo/hi. Rows on one channel must not overlap β€” the page flags an overlap and won't save it. Re-band evenly resets the bands.
  3. Save. The change is live immediately.
  4. Download GDTF exports the mapped source as a fixture file for the desk (see Desk setup). Remove takes the source off the map.

The map holds up to 64 rows in total. A card marked entries span more than one channel was hand-edited or imported that way β€” Re-band it onto one channel before exporting a fixture for it.

The on-screen stepper (MAP)

For quick fixes at the desk without a browser, MAP in the header opens a stepper over the same map. Up/Down move a cursor over the rows (source headings and cues); tap a row to jump the cursor there. The bottom row of buttons depends on what's selected:

Cursor onButtons
a source headingβˆ’10 / βˆ’1 / +1 / +10 on that source's channel (1–512), moving every one of its cues together
a cue rowLoβˆ’ / Lo+ / Hiβˆ’ / Hi+ on that cue's band, Β±5 a tap, kept within 0–255 and lo ≀ hi

Save writes it; Cancel discards. Cue assignment (which cue a row points at) stays web-only. A source showing MIXED spans channels and can't be stepped on screen β€” re-band it from the web page.

The DMX monitor (MON)

MON shows all 512 incoming channels as a 32Γ—16 grid of bars coloured by level β€” the fastest way to confirm the desk is actually sending on the channel you think. Cue firing carries on underneath it; the monitor yields to RF, so a burst is never delayed by the display. The bottom line reads RF n/n Β· def Β· Hz Β· loop ms (sends attempted/completed, paints deferred for RF, repaint rate, worst loop time) and is prefixed NO SIGNAL when no DMX is arriving. Gridlines and their accent spacing are a Settings option. Tap X to leave.

Settings and updates

Troubleshooting

SymptomLikely cause and fix
Screen taps fire the props but DMX doesn'tLook at the status strip: dmx:idle means no frames β€” cable, the node's output, or DIP switch 2 in bypass. dmx:live but nothing fires: open MON and check the channel actually moves; then check the map page for a red unresolved warning (source renamed on re-import β€” see the book).
Desk sends, controller shows the fire, prop doesn't changeRadio. Move the antenna away from the DMX Base, get closer, check the prop responds to its hand remote from the same spot. A lantern sitting in a candle/fire scene sometimes needs a second send to leave it β€” a second cue step or a re-fire.
Three changes in quick succession, middle one lostSends are sequential (~0.5 s each) with room for one held follow-on; a third trigger inside that window replaces the held one. Keep to two prop channels per cue, and a second between follow-on cues.
Fader moves through several scenes on the way upEach band fires as the value enters it. Give the scene parameter zero time / snap on the desk β€” firing rules.
Web page gone mid-crawlExpected: the controller leaves the venue WiFi to visit prop hotspots. Watch the screen; the page reconnects when it returns (a few minutes at most).
Controller boots without WiFi after an updateTap the left header segment to bring it up, then tick Bring WiFi up on boot in Settings if it was unticked.
Import says "identical β€” skipped"Not an error: that prop's config already exists as a source under another name. One source per identical fleet is the intended result.