Repository navigation
Conversation
The Disco 240 desk has no MIDI input. The DMX planner replaces the desk as the light source. The design keeps the song events of the MIDI mapping, and adds looks, an effect layer and phrase triggers.
A preset of the DMX scenes can now be a look on the DMX output of the box. The events, rows, overlays, steps and gates of the scenes stay the same. The scenes must be on main before the work on the planner starts.
The plan is the first of five plans for the DMX planner. It adds the crate autoblink-core-dmx, the show partition, the DMX thread that renders the looks of the scenes, the show routes and the Lights panel.
The icon is an inline SVG in the page. Thus the host, the emulator and the ESP32 need no icon route.
The A is a magenta light cone below a spot. It stays readable at 16 px.
The light cone of the icon turns yellow for 100 ms, at the same time as the BPM flash of the page.
The scene engine now keeps the presets that its rows sent. The new method drain_fired gives them to the caller in order and clears the list. The list keeps the newest 16 entries.
The new crate autoblink-core-dmx holds the fixtures, the groups and the looks. The sanitize method cuts a show to the limits and removes each wrong reference. The from_json function always returns a valid show.
The engine stacks the scene layers, fades them in, keeps each look for at least 80 ms, and starts the strobe and fog bursts of a phrase event. It shows the idle look when no layer plays. The engine allocates no memory in the frame function.
The idle look is now the bottom layer of each frame, so a layer without a look never blacks out the rig. A scene note on turns a manual layer of the same preset into a held layer.
The partition table gets the 64 KB partition "show" at 0x3F0000. OTA does not change the partition table, so a box needs one flash over USB to get it. The new module show.rs keeps the DMX show as JSON in this partition. Bytes 0 to 3 hold the length of the JSON. The save writes the length last, so a power loss gives "no show". The save counts the bytes first and erases only the sectors that the new data needs. A show that is too large does not erase the stored show. The load reads through a 256-byte buffer and calls sanitize. The DMX output is now a default feature.
espflash 4.6.0 stops with a panic on the data subtype 0x40. The runner and the release workflow give this table to espflash, so each flash failed. The show partition now has the subtype "undefined". The firmware has the DMX output by default. Thus the first firmware step of scripts/check.sh now builds without the DMX output, and the second step builds the default configuration.
The dmx thread runs the Lights engine and sends one DMX frame after the other on IO4. It replaces the beat flash on channel 1. The midi thread queues the scene notes, the fired events and the preset tests. The receive thread queues the master beats. The firmware loads the show from the show partition before the network starts. Thus the maintenance start of a firmware update also sends the idle look.
GET /show sends a copy of the show in the memory as JSON. POST /show reads the body as a stream, cuts the show to the limits, writes it to the show partition and then replaces the show of the dmx thread. The dmx_live command sets the master and the strobe hold. The dmx_fog command queues the fog. The hello message gets the capability dmx, and the status gets the field dmx. ShowStore::write_json is removed, because no route uses it.
A long list in the JSON of a show can use all of the heap of the firmware before sanitize runs, and the box then restarts. The reader now keeps only the first entries of each list up to its limit and skips the other entries. A look has a new limit of 8 rows (MAX_ROWS), and sanitize applies it too. POST /show replies 503 when the body is larger than half of the largest free heap block.
The version is 1.24.0. AGENTS.md describes the DMX crate, the show partition, the show routes and the dmx thread. Three comments use clearer wording.
A row entry can have a hold time in beats. The row stays busy until the hold ends. A trigger during the hold waits, and the next entry fires when the hold ends.
A hold entry of a scene row comes back after an overlay note off. A queued fire of a scene row that is no longer the current scene is dropped. A row without holds leaves out the holds key. A hold below 0.0625 beats rises to 0.0625.
… and add the hold control
AGENTS.md describes the hold of each scene entry, the pending trigger and the restore of a held entry after a flash or a pulse.
The plan is the second of five plans for the DMX planner. The box fetches the fixture data, and the page maps the channels.
A fixture of the show now has an optional ofl field. It holds manufacturer/model#mode name. The sanitize step cuts the field to 96 characters and removes control characters.
A combined dimmer and strobe channel maps to the dimmer. A shutter that is closed at 0 is kept open. Template channels of pixel bars map to the colours and the dimmer. The ofl routes need the feature dmx. A show with an empty ofl key drops the key. The STROBE button waits for a library fetch.
The show model gets the channel kinds pan, pan_fine, tilt and tilt_fine. It also gets up to 8 positions with one aim for each fixture. A look row can point to a position and carry a shape. An old show loads and saves unchanged. The render code does not use the new fields yet.
Render the pan and tilt channels from the position of a row. Add the circle, figure 8 and sweep shapes with a phase shift for each fixture. Add Lights::aim, which holds the pan and tilt of a fixture for 10 s over every layer.
The WebSocket command dmx_aim queues DmxMsg::Aim to the dmx thread. The firmware build without the dmx feature no longer warns about dead code in ofl and query_value.
The page maps the Pan and Tilt channels of a library light, and their fine channels, to the pan and tilt channel types. Lights setup has a Positions section with one pan slider and one tilt slider for each moving head. A look row of a group with heads gets a position and a shape. The pixel keys that OFL makes from a pixel count now resolve the explicit pixel channel names.
The generated pixel keys use the pixel coordinate on each axis with more than one pixel, as OFL does. The aim throttle sends a pending aim of another head at once. The fine channel type uses the same capability as the coarse channel. The idle look row no longer shows the text false.
The DMX show section of AGENTS.md describes the pan and tilt kinds, the positions, the shapes and the aim override. The OFL mapping sentence now says that a pixel channel gets the kind of its template channel.
The shape index counts the beats from a bar line. A period of 4, 8 or 16 beats now starts on beat 1, whatever beat the clock saw first.
A beat with beat_in_bar 0 uses the beat count for the shape index and for the bar effects.
The scene engine takes a guessed bucket in SceneInput. It fires the bucket row at a block start when no phrase is present and the guess differs from the last guess that fired for the track. The status shows the guessed bucket and sets the field guessed.
The firmware builds a DropGuess when the overview of a track without phrases arrives. TrackData keeps it next to the overview. The midi loop calculates the position from the playhead and the track length of the master deck, and gives the guess to the scenes. The host builds a DropGuess in set_overview and keeps it in DeviceState. scene_input gives the guess only for a track without phrases. It also gives phrase16 from the 16-beat counter of the master. The scenes panel shows "(guess)" next to the current bucket while the bucket comes from the guess.
A new master deck starts its first guess without the previous bucket of the old deck. Thus the hysteresis of the old deck does not hold a Drop or a Break on the new deck. The Every::Bar effects use the same no-bar-information fallback as the shapes.
The guess of the phrase buckets from the waveform is in AGENTS.md. The image of the firmware uses 85 % of the slot.
The new `artnet` thread receives ArtDmx on UDP port 6454 on all interfaces. It keeps the last frame of universe 0 and its time in `State::artnet`. The `dmx` thread copies that frame with `try_lock` and merges it into each frame with `Merge::apply`. The status field `dmx.artnet` is true while the last frame is not older than 3 s. The merge state and the frame copy are on the heap. The `dmx` stack increases from 6 KB to 7 KB. This size is an estimate from the stack frames of the build. The firmware starts the `artnet` thread after the network, and the OTA maintenance start does not start it.
The config section [dmx] sets artnet_target (an IP or IP:port, the default port is 6454) and universe (default 0). A bad target logs a warning, and the host starts without the engine. With a target, a supervised task runs the Lights engine and the beat clock like the dmx thread of the firmware. It sends one ArtDmx frame each 25 ms. The MIDI mapper sends the scene notes, the fired presets and the master beats to the task through a bounded channel with try_send. While an AUTOBLINK box is on the Pro DJ Link network, the task sends nothing. The host serves GET /show and POST /show with the limits of the box. It writes the show to show.json next to the config file on a blocking thread. The hello has the capability dmx only with a target. The status field box gives the IP address of the box.
The host handles the WebSocket commands dmx_live, dmx_fog and dmx_aim. The engine reads the live master and the strobe hold for each frame, and it starts the fog and the aims. scene_test also shows the look of the preset on the engine, as on the box. Without an engine, these commands give an error. The status field dmx gives the layers, the live master and the strobe hold of the engine. The engine writes the field only after a change, with try_lock. The mapper stamps each DMX beat with the receive time of the beat. The doc of DmxConfig tells that two hosts with the default device name see each other as a box.
A phone that locks during a strobe hold, or a tab that closes, sends no release. The whole rig then strobes until another page releases the hold. Each WebSocket client now has a guard. The guard records whether the last dmx_live of this page set the strobe hold. When the socket closes, the guard releases the hold. A page that did not set the hold releases nothing. The hello has the capability dmx only when the DMX engine runs.
The host page reads and writes the show of the box when the host finds one. It posts the same show to the host copy after each save. The live strip moves to the page of the box.
The page loads the show once, after the first status. A late load of another endpoint cannot replace the show. A failed load clears the show. A pending save goes to the old endpoint before the switch. The look editor hides Preview when a box is the target.
The look editor closes when the show fails to load, so a click on a stale editor cannot throw. The drop guess documents the length limit of its look-ahead.
The version is 1.29.0. AGENTS.md describes Art-Net in on the box, the DMX engine on the host and the new image size.
The page posts the show that it loads from the box to the host. The host warns when the default device number hides the box. The 413 reply of POST /show carries a CORS header.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This branch adds the first part of the DMX planner (plan 1 of 5) and a new favicon. Version 1.24.0.
A preset of the DMX scenes can now be a look. The ESP32 sends the look on its own DMX output (IO4), in time with the beat. The rows, the steps and the gates of the DMX scenes stay the same.
autoblink-core-dmxholds the show model, the look renderer, a beat clock, the intensity effects and theLightsengine. A late WiFi beat packet does not move the beat clock. The idle look is always under the other looks, so the rig does not go dark.Scenes::drain_fired).show. A box needs one USB flash to get it, because OTA does not change the partition table.dmxthread starts before the network. It gets the scene notes and the beats through a FreeRTOS queue.dmxis now a default feature.GET /showandPOST /showread and write the show. The POST route checks the Origin, refuses a body above 60 000 bytes and a body above half the largest free heap block, and bounds each list while it reads it.dmx_liveanddmx_fogdrive the master, the strobe hold and the fog. The strobe hold ends when the active page closes, when a send to it fails, or when another page takes over.The design is in
docs/superpowers/specs/2026-10-27-dmx-planner-design.md. The plan is indocs/superpowers/plans/2026-10-27-dmx-planner-1-looks.md.Tests
cargo test --locked: 460 tests pass. The new crate has 42 tests.sh scripts/check.sh --esp32passes: format, clippy, tests, and the firmware with and withoutdmx.Update: scene holds and the new Lights layout (1.25.0)
Update: plans 2 to 5 (1.26.0 to 1.29.0)
[dmx] artnet_target).Each task had a code review, and each plan had a whole-plan review with one fix wave. The rulings and the open follow-ups are in the commit history and in AGENTS.md.
Box test (WT32-ETH01, USB flash of 1.24.0)
/memwith the page open:dmx3 836 bytes free of 6 KB,midi3 820,httpd5 432 of 8 KB after all the POST tests. The 8-bit heap has 104 KB minimum and a 110 KB largest block.GET /showon a new partition gives the empty show. A POST with a foreign Origin gives 403. A valid show gives 204, and aGETreturns it, also after a restart.Not tested yet
These steps need a player, a DMX monitor and a phone: