# DMX Troubleshooting — Real Rig, 2026-09-24

**Status: active investigation.** Zach back on-site with Wi-Fi access to
Understudy from his phone. Real symptoms reported: many cheap fixtures on
the venue rig (Understudy → Art-Net node → DMX splitter → multiple home-run
lines) not responding to DMX, some apparently resetting settings, many
flashing in ways that don't look DMX-driven. This doc is the real research
base + a working checklist + a findings log to fill in as he investigates.

## Real, sourced research

### 1. DMX splitter/opto-isolator failure modes
- Intermittent flashing is a **documented symptom of a failing opto-isolator**
  on a splitter port, not folklore. ([ETC Community](https://community.etcconnect.com/service_technician_forums/f/service-technician-forum/13138/response-opto-splitter-intermittent-dmx))
- Real named root causes: failed DMX chip, failed opto-isolator (often from
  a surge/external event), or failed power-supply capacitors on the
  splitter itself — "if all outputs are affected except pass-through, the
  capacitor for the input power supply is certainly bad." ([ETC support](https://support.etcconnect.com/ETC/Networking/Response_Classic/DMX_Port_Not_Working_on_Response_Opto_or_I3M))
- **Ports fail independently, not always the whole unit** — real hardware
  (ETC's own splitter) sockets each port's chip separately for exactly this
  reason.
- A failed fixture can corrupt **the entire daisy-chain on that one port**,
  not just itself. ([forum.thelightingcontroller.com](https://forum.thelightingcontroller.com/viewtopic.php?t=3210))

### 2. EMI / cable routing / AC power proximity
- DMX's shield must be **grounded at both ends** (RS-485 standard) — an
  unconnected or single-grounded shield acts as an antenna instead of a
  Faraday cage. ([Hackaday](https://hackaday.com/2021/10/15/learn-dmx512-basics/), [erg.abdn.ac.uk](https://www.erg.abdn.ac.uk/users/gorry/eg3576/DMX-phy.html))
- Differential (RS-485) signaling is why DMX tolerates *some* EMI — noise
  hits both conductors roughly equally and cancels — but this degrades
  badly with bad shielding/grounding.
- Real separation-distance data (general low-voltage-signal-near-AC-power
  guidance, not DMX-specific but directly applicable): **12–16" minimum**
  recommended parallel-run separation from AC power; field measurements
  show 60–80VAC induced on a DC pair run parallel to 240VAC for 75–150ft.
  ([engineerfix.com](https://engineerfix.com/minimum-separation-distances-between-power-and-data-cables/), [industrialmonitordirect.com](https://industrialmonitordirect.com/blogs/knowledgebase/nec-conduit-wiring-ac-and-dc-conductor-separation-requirements))
- **"Nearly all problems encountered by DMX512 users and installers are due
  to simple cable faults, poor cable layout or interference."** ([Hackaday](https://hackaday.com/2021/10/15/learn-dmx512-basics/))
- Coiling cable around metal compounding EMI pickup (loop-antenna effect)
  is sound engineering inference from the above, **not independently
  sourced as its own DMX-specific citation** — flagged, not presented as
  equally solid as the rest.

### 3. A faulty fixture corrupting the whole line — real, not just theoretical
- **Bus contention**: a fixture with a failed RS-485 transceiver that
  doesn't release the bus cleanly can hold/corrupt the line for every
  fixture sharing that run, exactly matching Zach's "garbage going the
  wrong way down the line" instinct. ([Analog Devices](https://www.analog.com/en/resources/analog-dialogue/articles/isolated-rs-485-in-dmx512-lighting.html))
- **Termination**: the last fixture in every daisy-chain run needs a real
  120Ω terminator, or reflections corrupt data for the whole run, not just
  the end. ([Analog Devices](https://www.analog.com/en/resources/analog-dialogue/articles/isolated-rs-485-in-dmx512-lighting.html), [On Stage Lighting](https://www.onstagelighting.co.uk/lighting-equipment/stage-lighting-control/dmx-fault-finding-and-fixing-dmx-problems/))

### 4. Real professional troubleshooting methodology
- **Sequential isolation**: unplug the run at the first fixture, confirm
  clean behavior upstream, then walk down the chain fixture-by-fixture to
  find where the fault reappears — exactly Zach's own stinger-cable plan.
  ([On Stage Lighting](https://www.onstagelighting.co.uk/lighting-equipment/stage-lighting-control/dmx-fault-finding-and-fixing-dmx-problems/))
- **Bisection** for long runs: split the run in half with a long cable,
  test each half, keep subdividing the bad half — faster than one fixture
  at a time.
- **Real, important caveat for Zach's splitter concern**: a splitter's own
  re-driven output can mask a marginal/faulty cable that would otherwise
  visibly fail. To actually test a suspect run, **bypass the splitter** —
  connect a source directly to the run under test; an unbuffered signal
  "just seems to stop at the point of the fault," making it obvious.

### 5. Real bonus finding — may reframe the whole diagnosis
Cheap fixtures very commonly **fall back to a built-in auto-chase/
sound-reactive program the instant they lose valid DMX signal**, then
return to normal DMX control once signal resumes — documented, normal
firmware behavior, not damage. ([ADJ Forums](https://forums.adj.com/topic/dmx-failure), [thelightingcontroller.com](https://forum.thelightingcontroller.com/viewtopic.php?t=611))

This matches Zach's own description ("randomly resetting," "flashing...
not really DMX-controlled") closely enough to be worth testing directly
before assuming fixture damage: feed a suspect unit a clean, direct,
unbuffered signal (stinger straight from the node). If it snaps to
steady, correct behavior → the fixture was never broken, it was starved
of signal (splitter/cable/contention problem upstream). If it still
misbehaves on a known-clean direct feed → that's real evidence the unit
itself is at fault.

### 6. Two-way Art-Net node diagnostics — real, not a pipe dream
- **ArtPoll/ArtPollReply is real and standard** — a controller broadcasts
  ArtPoll every 2.5–3s, each node answers with ArtPollReply reporting its
  IP and handled universes.
- **ArtPollReply carries real per-port status**, confirmed from a working
  Art-Net library's actual source: `GoodInput[4]`/`GoodOutput[4]` byte
  arrays (per-port input/output status, up to 4 ports) plus `PortTypes[4]`
  and `SwIn`/`SwOut` (per-port address config). ([go-artnet source](https://github.com/jsimonetti/go-artnet/blob/master/packet/artpollreply.go))
- **Real gap, not yet confirmed**: the exact bit-level meaning of
  `GoodOutput` (does any bit report a real line/short fault, or only
  "node is configured and sending"?) needs checking against the actual
  Art-Net 4 spec PDF before promising more than basic node liveness/config.
- **RDM is a separate, real, fixture-level two-way protocol** (ANSI E1.20,
  over DMX pins 4/5 of a true 5-pin XLR) — this project's prior research
  already found cheap Chinese fixtures essentially never implement it;
  not re-verified with new sourcing this pass, still stands from before.
- **Real, concrete implementation gap, RESOLVED 2026-09-24**: `pyartnet`
  (the library Understudy's `core/artnet.py` already wraps) has **no
  ArtPoll/ArtPollReply support at all** — confirmed by checking the
  installed package directly. Checked for alternatives before deciding
  to hand-roll it: `stupidArtnet` (ArtDMX only, no ArtPoll),
  `python-artnet` (archived/unmaintained since 2019, not usable). Real
  match found: **`aioartnet`** (PyPI/GitHub, TeaEngineering) implements
  ArtPoll/ArtPollReply send+receive AND ArtDMX, pure stdlib `asyncio`, no
  extra dependencies, maintained (commits as recent as June 2025),
  asyncio-native (matches `core/artnet.py`'s own stated reason for
  picking pyartnet in the first place). Also checked OLA (Open Lighting
  Architecture, the established open-source lighting control project) —
  it does support ArtPoll, but it's a whole separate C++ daemon to run
  and manage; not worth it when aioartnet covers the need in pure Python
  with zero extra services. **Plan: keep pyartnet for DMX output
  (proven, already doing real work this session), add `aioartnet` as a
  second, narrowly-scoped dependency for node discovery/liveness only.**

  **Zach pushed back, live 2026-09-24** — asked for a real second look
  at (a) whether OLA offers enough more to be worth the integration
  cost, and (b) whether to consolidate onto aioartnet alone instead of
  two libraries. Re-investigated with real evidence, not a repeat:
  - **OLA**: confirmed it is NOT an importable library — `olad` is a
    standalone C++ daemon; Python only gets an RPC client
    (`ola.ClientWrapper`) talking to that running daemon. Real cost:
    a second service to deploy and keep running on showcomputer. Real
    gain: RDM gateway + multi-protocol bridging, neither of which this
    project currently needs (fixtures don't support RDM, no sACN use).
    Commits are recent, but the last tagged RELEASE was Feb 2023 — real,
    nuanced signal (active dev, stale releases). License unresolved
    (GitHub reports `NOASSERTION`, needs manual check). **Not adopted.**
  - **aioartnet-only**: pulled its real feature table directly. Two real
    problems: **no fade support at the library level** (only raw whole-
    universe byte writes — its bundled demo CLI hacks fading on top,
    the core library doesn't have it), and **RDM is marked
    unimplemented in its own feature matrix** despite the repo's
    tagline claiming RDM support. Initially read this as a worse trade
    than keeping two libraries.

  **Honest walk-back on the fade point, same conversation**: Zach asked
  point-blank what a "fade" actually costs to rebuild, and the real
  answer is: not much. A fade is just stepping a channel's value in
  small increments over a duration instead of jumping instantly —
  aioartnet's own bundled demo CLI already proves the pattern (a loop
  ticking N times/sec, computing the interpolated value, writing it),
  and Understudy's own animation engine (built this session for chase/
  rainbow/strobe/pulse) is the SAME shape of code already, just simpler
  math. Real estimate to add fade support on top of aioartnet directly:
  ~20-30 minutes, low risk, reusing an already-proven pattern — this was
  overweighted as a blocker in the first pass. **The real remaining
  concern is narrower and different in kind**: aioartnet's README
  claims RDM support that its own feature table shows as not
  implemented — a docs-vs-code trust signal, not a missing-feature-we'd-
  have-to-rebuild problem, and separate from whether RDM is actually
  needed (it isn't, for these fixtures). Net position: a full
  consolidation onto aioartnet alone is now a reasonable call if Zach
  wants it — the fade gap is cheap to close ourselves; the open
  question is just how much to trust a library whose docs overstate one
  feature, which is a judgment call, not a hard blocker either way.

  **Zach then asked Orson to verify directly against the real GitHub
  data (commits/PRs/issues), not secondhand summary — done, real
  primary-source findings:**
  - **RDM confirmed genuinely absent from shipped code, not just docs
    lagging.** Found the exact open, unmerged PR: [#9 "Add rdm"](https://github.com/TeaEngineering/aioartnet/pull/9),
    opened 2025-06-23, still open 15 months later, own description
    "Adds skeleton RDM implementation, fetches table-of-devices" (~150
    lines incl. a dedicated `rdm.py`, never merged). Confirmed directly
    on the live `main` branch: no `rdm.py` exists there; `client.py`'s
    only "rdm" references are a commented-out line and code that reads
    a raw 6-byte RDM field out of a packet without doing anything with
    it. A separate open issue, ["Implement RDM fixture
    enumeration"](https://github.com/TeaEngineering/aioartnet/issues/5),
    is still tracked as not done. This is a real, known,
    abandoned-in-progress feature — not docs trailing code.
  - **Real activity numbers, aioartnet vs. pyartnet vs. OLA:**
    aioartnet — 1 real contributor (`shuckc`, 61 commits) + dependabot,
    **last real code commit 2025-06-29 — 15 months stale as of this
    writing**, not "recent" as first characterized. pyartnet (our
    current library, confirmed via PyPI's own listed repo,
    `spacemanspiff2007/PyArtNet`) — 80 stars (10× aioartnet's 8),
    published docs site (readthedocs), pushed 2025-11-04 — smaller
    project too but more established and fresher. OLA
    (`OpenLightingProject/ola`) — 752 stars, top two contributors alone
    at 4920/3931 commits, pushed 3 days ago — genuinely different scale,
    a real long-running team-maintained project, not comparable to
    either Python library.
  - **"Vibecoded" verdict**: neither Python library reads as vibecoded —
    both have real tests, CI, structured commit history — but both are
    small, essentially single-author hobby-scale projects. OLA is the
    one that's a genuine "traditional product" by these numbers.
  - **"Only expensive fixtures have RDM" — pushed on, honestly
    unresolved with fresh sourcing.** No independently-sourced figure
    found for RDM prevalence by fixture price tier; this claim still
    rests on this project's earlier, not-freshly-reverified research.
    Real, available evidence instead: none of this project's own
    fixtures' real manuals (already read directly for their profiles)
    mention RDM anywhere, and RDM is normally advertised prominently
    when present — indirect but real evidence none of what we currently
    own supports it. Zach's practical framing holds regardless of the
    general rate: we don't have an RDM-capable fixture today, so it
    doesn't matter here either way.
  - **Updated recommendation, round 2**: keep the two-library split
    (pyartnet for output, aioartnet added narrowly for ArtPoll) — not
    because of RDM (confirmed irrelevant to us), but because aioartnet
    itself is less actively maintained than first characterized once
    directly verified (15 months since last real commit, one author).

  **Zach's real follow-up, same conversation**: does Understudy even
  need aioartnet as a dependency, or is the ArtPoll/ArtPollReply piece
  small enough to just own directly (or fork pyartnet and upstream)?
  Counted the real, exact scope by reading aioartnet's actual functions
  line-by-line: **~200-210 lines total** dedicated specifically to
  ArtPoll send/receive/parse (`on_art_poll` ~8 lines, `on_art_poll_reply`
  ~110 lines — the meaty one, unpacking the full packed-binary
  ArtPollReply struct and diffing node/port changes — plus
  `_send_art_poll`/`send_art_poll_reply`/`_send_art_poll_reply_bindindex`
  ~93 lines combined), plus ~130 lines of network-interface-enumeration
  helper code (`network.py`, needed to find local IPs to broadcast
  from) and a handful of shared node/port model classes — **~350-400
  lines total including supporting plumbing.** Read the real parsing
  code directly: genuine, correct, working implementation, matches the
  real packet layout precisely, not sloppy.

  **Final recommendation, given the real scope**: don't add aioartnet
  as a runtime dependency at all, and don't try to fork pyartnet and
  wait on an upstream PR to land either — the fact that aioartnet's OWN
  "Add rdm" PR has sat unmerged 15 months is real, direct evidence that
  landing something in a small solo-maintained project isn't fast or
  guaranteed. Instead: **write a small, self-contained ArtPoll module
  directly in Understudy** (~200-300 lines), using aioartnet's real
  working code as a direct reference for the packet parsing (MIT
  licensed, explicitly permits this) rather than depending on it at
  runtime. Zero new dependencies, nothing riding on someone else's
  stale project, genuinely small and bounded now that the real scope is
  known.

  Not yet built — waiting on Zach's go-ahead (physical troubleshooting
  is the current priority).
- **Bottom line: not a pipe dream.** ArtPoll/ArtPollReply is real,
  standard, and gives real per-port node-side status with zero fixture
  cooperation needed — just not built into Understudy yet.

### 7. Multi-port Art-Net node (Brett's spare node)
- Port count and per-port opto-isolation are **correlated in real
  products but not automatic** — no source claims more ports inherently
  means better isolation, but reputable multi-port nodes (DMXKing,
  Artistic Licence) explicitly advertise independently-isolated ports as
  a named feature. ([DMXKing](https://dmxprosales.com/products/dmxking-edmx1-max), [ENTTEC](https://support.enttec.com/dmx/edmx-art-net-sacn-networking/art-net-to-dmx-converter-node-setup))
- Real installer-confirmed reliable brands: ETC, Enttec, Elation, MA,
  DMXKing. ([ControlBooth](https://www.controlbooth.com/threads/which-artnet-to-dmx-node-do-you-use.43678/))
- **Real practical benefit here specifically**: a 2nd, independently-
  isolated port is a genuine diagnostic tool — move a suspect run onto
  it and see whether the fault follows the run (cable/fixture problem)
  or stops reproducing (points back at the original port/splitter).

### 8. Node-to-splitter connection specifics
- **DMX512 spec calls for 5-pin XLR (XLR5)**; 3-pin (XLR3) is explicitly
  non-compliant "but this has not prevented many low-end makers from
  using them widely" — a real, common mismatch point mixing gear from
  different vendors/eras. ([uking-online](https://www.uking-online.com/blogs/stage-lighting-dmx-knowledge-hub/3-pin-vs-5-pin-dmx))
- **Gender convention**: DMX OUT = female, DMX IN = male — a real,
  checkable point if an adapter/jumper was wired backwards.
- **Every splitter output is its own fresh DMX source** and needs its
  OWN 120Ω termination at the end of whatever run it feeds, independent
  of the node→splitter hop itself. ([Reitalight](https://reitalight.com/dmx-wiring-guide/))
- No sourced figure found for node-to-splitter cable length limits
  specifically beyond general DMX-512 practice (~1000ft/segment in the
  standard) — flagged as a real gap, not asserted as fact.

## Concrete Understudy features — buildable, grounded in the real codebase

Checked the actual code (`core/artnet.py`, `api/routers/artnet.py`,
`api/routers/lighting.py`, and `pyartnet`'s installed source directly)
before proposing anything. Real, confirmed fact this all rests on:
**`pyartnet`'s UDP sends never raise on failure** — even a totally
unreachable node produces no exception, matching `core/artnet.py`'s own
documented "fire and forget" design. There is currently no error signal
anywhere in the send path to surface — this is exactly why C below is
real, non-decorative value, not a nice-to-have.

1. **DONE, shipped 2026-09-24 (0.77.2)** — per-fixture "Identify" button
   on `/lighting`: one tap flashes just that physical fixture (4x solid
   white/off) so a table row maps obviously to a real light on his
   phone. Pure client-side, reuses the existing `/lighting/trigger`
   endpoint, zero backend change, works for every fixture kind.
2. **Already built, use as-is**: the `dmx_test` scene (distinct color
   per fixture) and the `rainbow`/`chase`/`strobe`/`pulse` animations
   (0.76-0.77) — `chase` in particular already walks one fixture at a
   time, hands-free, from the phone.
3. **Not built — "Walk/Split Test" bisection tool.** Real gap: nothing
   today implements "divide and decide" (item 4 in the methodology
   above) as an actual tool — `dmx_test` colors everything at once,
   which proves addressing but doesn't bisect. Would split a room/all
   fixtures into two groups by address order, light one half bright and
   kill the other, with a flip/bisect-further control. Built entirely on
   existing primitives (partition registered `Fixture`s, call the
   existing trigger per fixture) — an afternoon-sized build.
4. **Not built — real ArtPoll/ArtPollReply node liveness.** `pyartnet`
   has zero ArtPoll support (confirmed by grepping its installed
   source directly) and Understudy's own code has none either — `GET
   /artnet/nodes` is explicitly config-only today, "not a live network
   check." A real implementation means hand-rolling raw ArtPoll UDP
   broadcast + ArtPollReply parse alongside pyartnet, not a config flip.
   Real, valuable, because it's the ONLY way to get true node liveness
   given UDP's silent-failure behavior — but bigger scope than 1/3, a
   distinct follow-up task. Would report node liveness/config for
   certain; per-port electrical fault detail is NOT confirmed (the
   `GoodOutput` bit-table needs checking against the real Art-Net 4 PDF
   spec before promising that).
5. **Not built — per-port/run fixture tag.** If Brett's 2-port node gets
   used, register a 2nd Art-Net universe + give fixtures a `port`/`run`
   tag (beyond just `room`) so a diagnostic action can target
   "everything on port 2" specifically, matching the port-swap
   isolation test in the checklist.

Say which of 3-5 (if any) to prioritize next.

## On-site checklist

1. **Baseline at the source** — confirm Understudy's own Art-Net output is
   clean (`/artnet/{universe}` readback / the `dmx_test` rainbow scene) —
   already established this session. This only proves the signal leaving
   Understudy, not what a physical fixture receives.
2. **Bypass the splitter to diagnose** — a raw, unbuffered signal dies
   visibly right at the real fault; through the splitter, a marginal
   fault can hide.
3. **Per-run bisection** — jump the stinger to the far end of a suspect
   run first. Works on the stinger but not its own home run → the fault
   is in that run's cable/termination/interference, not the fixture.
4. **Progressive walk-down** within a bad run — move fixture-by-fixture
   (or bisect) until behavior flips from good to bad — that boundary is
   the real fault (bad cable segment, bad connector, or a specific
   fixture whose failed transceiver is corrupting the bus downstream).
5. **Termination check — DEPRIORITIZED, 2026-09-24.** Real Zach report:
   he's tried terminators before ("2 terminators somewhere") and it made
   no observable difference last time he debugged this rig. Real,
   consistent explanation, not him doing it wrong: reflection problems
   from missing termination only become visibly significant when a run
   is long relative to DMX's signal edge speed — on typical venue-length
   runs (tens to a couple hundred feet), a missing terminator often
   produces no perceptible symptom at all. His null result is actual
   evidence AGAINST termination being the current cause here — still
   worth a quick real check (confirm the real last fixture in each run
   has one, in case a genuinely long run is the exception), but not
   worth spending real troubleshooting time chasing as the lead suspect.
   Priority shifted to steps 2/6/7/8 (splitter/opto-isolator, EMI, and
   bus contention) instead.
6. **Cable routing audit** — photograph every place DMX runs parallel to
   or crosses Edison/AC line, and every place it's coiled around metal.
   Cross AC at 90°, not parallel; uncoil/re-route where physically
   possible as a real fix, not a guess.
7. **"Fell into auto mode" test** — feed a misbehaving fixture a clean
   direct signal (stinger straight from the node). Snaps to steady
   correct behavior → signal/cable/contention problem, not the fixture.
   Still wrong on a known-clean feed → that unit is the real fault.
8. **Splitter-port isolation**, if still unexplained — swap which
   physical port feeds a known-bad run. Bad behavior follows the PORT →
   splitter. Follows the RUN/cable → downstream of the splitter.

## Zone/group control for multi-zone bar modes (separate design thread, 2026-09-24)

Real Zach ask: use the 252 Wash Light Bar's higher-channel-count modes
(4-zone/14ch or 8-zone/26ch, real numbers from `platform/fixtures/dmx/
252-wash-light-bar.json`, sourced from the actual manual) for real per-
zone animation, but with a "macro"/"phantom channel" convenience so one
action still sets the whole bar to one color when that's all that's
wanted.

- **Real established term for this**: a **Group** with a **Palette**
  (or **Preset**) applied to it — standard terminology across every
  major real lighting console (grandMA, ETC Eos, Hog). Select the
  group, hit one palette, every zone snaps to that state; individual
  zones can still be grabbed out of the group for a chase/rainbow.
  Nothing new needed here, this is the real industry pattern.
- **Real channel budget, computed against our actual 7-bar roster**:
  even at the biggest mode (26ch/8-zone) on all 7 rooms' bars, total
  universe usage is 26+6+4=36ch/room × 7 = 252 of 512 — comfortably
  under the limit, 260 channels of headroom left. Not a concern.
- **Buildable today with zero Understudy code changes**: register each
  zone of a bar as its OWN `Fixture` (e.g. `library-bar-zone1`..`zone8`,
  each pointing at that zone's 3 real channels within the bar's already-
  allocated address block) instead of one "whole bar" fixture. A
  **Scene** that sets all 8 zone-fixtures to the same color IS the
  macro/phantom-channel Zach described — fires in one action, uses the
  existing Scene primitive exactly as designed. The existing
  chase/rainbow **Animations** can then run across just those zone-
  fixtures for real per-zone movement. Only real cost: physically
  re-address each bar to mode 6 and re-register it as 8 fixtures
  instead of 1 — not yet done, offered as a test-one-room build once
  Zach is back from the physical troubleshooting.

### Decided + built, 2026-09-24 (bench-tested live)

Bench-tested mode H (26ch/8-zone) on a real bar on showcomputer: registered
8 zone fixtures, confirmed solid color + a chase animation + a live
alternating-color theater-chase script all work correctly through the
real Fixture/Scene/Animation API, zero backend changes needed (confirms
the design above). Zach decided to commit to mode H for all 7 rooms'
bars.

**Real migration gap found**: the 7-room fixture roster (Library, The
Cellar, Drawing Room, The Lab, Graveyard, Music Room, Phantom's Flask --
each with a 252 Wash Light Bar + LED Par + China RGB Bar 4ch) only
existed on the ThinkPad (100.99.184.103); showcomputer (the new primary
instance) had zero fixtures registered. Migrated the full roster to
showcomputer as part of this same update, since every bar's address was
changing anyway for the mode switch.

**New DMX address plan** (universe 0, 36 channels/room: 26 for the bar in
8-zone mode + 6 for the LED Par + 4 for the China RGB Bar -- matches the
252-of-512 budget already computed above):

| Room | Block | Bar start address (set this on the bar's onboard menu, mode H) | Par start | Bar2 (China 4ch) start |
|---|---|---|---|---|
| Library | 1-36 | 1 | 27 | 33 |
| The Cellar | 37-72 | 37 | 63 | 69 |
| Drawing Room | 73-108 | 73 | 99 | 105 |
| The Lab | 109-144 | 109 | 135 | 141 |
| Graveyard | 145-180 | 145 | 171 | 177 |
| Music Room | 181-216 | 181 | 207 | 213 |
| Phantom's Flask | 217-252 | 217 | 243 | 249 |

Each bar's 8 zones are 3 channels each starting at its block's start
address (zone1 = start..start+2, zone2 = start+3..start+5, ... zone8 =
start+21..start+23), then flash at start+24, dimmer at start+25 (matches
`platform/fixtures/dmx/252-wash-light-bar.json`'s real Mode 6/26ch
layout). Understudy fixture ids follow `<room-slug>-bar-zone<1-8>`,
`<room-slug>-par`, `<room-slug>-bar2` (e.g. `library-bar-zone1`
.. `library-bar-zone8`, `library-par`, `library-bar2`). All 7 bars'
dimmer channels were set to 255 (full) and flash to 0 (off) as sane
defaults on registration.

**Not yet done**: the Par and China RGB Bar 4ch fixtures physically don't
need re-addressing (their channel COUNT didn't change, only their
position in the universe shifted) -- but they still need their DMX start
address changed on-site to match the new block layout above, same as the
main bar.

### SUPERSEDED same day -- real second bar model confirmed, address plan revised

The "China RGB Bar 4ch" above turned out to be an outdated assumption --
Zach live-tested the room's actual second bar (no model number, no
manual) on-site and confirmed a real working mode: 24ch, 8 zones of
plain RGB (channels 1-24, r/g/b x8, no separate flash/dimmer channel --
simpler than the main bar's 26ch mode). See
`platform/fixtures/dmx/unlabeled-china-rgb-bar.json` for the full real
probe results, including 12ch and 48ch modes that were tried and NOT
confirmed (both showed anomalous macro/auto-cycle behavior rather than
clean zone control -- left unconfirmed rather than guessed at further).

**Current real per-room block is 56 channels, not 36** (26 main bar +
6 par + 24 second bar): 56 x 7 = 392 of 512, still comfortably under
budget.

| Room | Block | Main bar start (mode H/26ch) | Par start | Second bar start (24ch mode) |
|---|---|---|---|---|
| Library | 1-56 | 1 | 27 | 33 |
| The Cellar | 57-112 | 57 | 83 | 89 |
| Drawing Room | 113-168 | 113 | 139 | 145 |
| The Lab | 169-224 | 169 | 195 | 201 |
| Graveyard | 225-280 | 225 | 251 | 257 |
| Music Room | 281-336 | 281 | 307 | 313 |
| Phantom's Flask | 337-392 | 337 | 363 | 369 |

Fixture ids: `<room-slug>-bar-zone<1-8>` (main bar), `<room-slug>-par`,
`<room-slug>-bar2-zone<1-8>` (second bar, renamed from the old singular
`<room-slug>-bar2` id now that it's 8 zones not 1). All 119 fixtures
(17/room x 7) registered live on showcomputer 2026-09-24, dimmer/flash
defaults set on the main bars.

## Findings log

*Fill in as Zach reports from the shop/venue — date, what was tested, what
was found. Keep entries factual (what was observed), not conclusions
until confirmed.*

---
