DMX mapping to LED strips
How to map RGB data from an sACN or Art-Net input to one or more physical LED strips in firmware 0.12.11 using a native Berry helper.
What map() is for
Section titled “What map() is for”DMX.map() is a native C++ helper available from Berry Script. It maps a concrete DMX byte buffer to one or more physical LED ports.
Use it when a controller receives RGB channels from an sACN or Art-Net universe and needs to render them quickly to a physical LED strip or several strips. The conversion runs in the C++ firmware layer, so Berry Script does not need to run a per-pixel for loop.
It is typically called from the render callback of an input SACN or Art-Net IO. This callback receives the latest DMX buffer and can map it during the render phase into PIX1, PIX2 or other port buffers. Physical ports are then displayed through the standard firmware path.
Be careful not to confuse IO label and IO type. The label in io[...] is label_t, so it can have at most 5 characters. For Art-Net, use a short label such as ARTN and set "type": "ARTNET" in the configuration.
You do not need your own ready flag or render callbacks on the physical ports. The important part is that mapping is explicitly connected to the input DMX buffer:
io["SACN"].render = def(channels, offset, frames_received, bytes_received) DMX.map(channels, 120, 255, io["PIX1"], 340, true, io["PIX2"], 340, false)endTypical use:
- one sACN or Art-Net universe is stretched to one physical LED strip
- one sACN or Art-Net universe is split into two luminaire branches
- one sACN or Art-Net universe is split into four separate LED outputs
- a smaller number of logical RGB colours is smoothly interpolated to more physical pixels
Call shape
Section titled “Call shape”DMX.map(channels, [logical_pixels], [fade], port, [physical_pixels], [reverse], ...)The same signature written with named arguments:
DMX.map( channels, # bytes, received DMX RGB buffer logical_pixels?, # int, default = all RGB pixels in the buffer fade?, # int 0..255, default = 255 port, physical_pixels?, # int, default = available port size reverse?, # bool, default = false ...) -> boolMental model for one call:
map( channels: bytes, logical_pixels?: number, fade?: number, ...targets: { port: PORT, physical_pixels?: number, reverse?: boolean }[]): booleanThe parameters are intentionally positional:
channelsis thebytesbuffer passed toio["SACN"].renderorio["ARTN"].render.logical_pixelsis the optional number of RGB pixels in the input. If omitted, the full available RGB buffer is used. If provided, the input buffer must contain exactlylogical_pixels * 3RGB bytes.fadeis the optional interpolation strength between neighbouring logical pixels.255means smooth transition;0keeps the nearest logical colour without blending.portis the physical output PORT, for exampleio["PIX1"].physical_pixelsis the optional number of physical pixels to write to that port. If missing or0, the available port size is used.reverseis an optional boolean.truewrites physical pixels in the opposite direction.
Each additional port starts another output branch. The source RGB range is split evenly between the listed ports.
One universe to one strip
Section titled “One universe to one strip”Example: 120 RGB values from sACN are smoothly stretched to 680 physical pixels on PIX1.
io["SACN"].render = def(channels, offset, frames_received, bytes_received) DMX.map(channels, 120, 255, io["PIX1"], 680)endFor Art-Net, the shape is the same; only the callback is attached to the Art-Net IO. This example uses the short label ARTN, which fits the maximum 5-character limit:
io["ARTN"].render = def(channels, offset, frames_received, bytes_received) DMX.map(channels, 120, 255, io["PIX1"], 680)endOne universe to two branches
Section titled “One universe to two branches”Example: the first half of the RGB input goes to PIX1, the second half goes to PIX2. PIX1 is physically wired in reverse, so it uses reverse = true.
io["ARTN"].render = def(channels, offset, frames_received, bytes_received) DMX.map( channels, 120, 255, io["PIX1"], 340, true, io["PIX2"], 340, false )endOne universe to four strips
Section titled “One universe to four strips”Example: 120 logical RGB pixels are split into four physical outputs, 30 logical pixels each. Each output can define its own physical length and direction.
io["SACN"].render = def(channels, offset, frames_received, bytes_received) DMX.map( channels, 120, 255, io["PIX1"], 170, false, io["PIX2"], 170, false, io["PIX3"], 170, true, io["PIX4"], 170, true )endReceive telemetry
Section titled “Receive telemetry”For performance smoke tests, measure the actual received DMX frames and bytes, not the number of Berry render callbacks. In firmware 0.12.11, the callback also receives the number of frames and bytes received since the previous call.
io["SACN"].cb = def(channels, offset, frames_received, bytes_received) print("received frames", frames_received, "bytes", bytes_received)endThe Art-Net callback receives the same four arguments:
io["ARTN"].cb = def(channels, offset, frames_received, bytes_received) print("received frames", frames_received, "bytes", bytes_received)endNamed callback signature:
io["SACN"].cb = def( channels, # byte buffer with received DMX slots offset, # first DMX channel represented by the buffer frames_received, # number of received sACN frames since the previous callback bytes_received # number of received DMX data bytes since the previous callback)endPractical rules
Section titled “Practical rules”DMX.map() only writes colours into the pixel buffers of the individual ports. The physical send to the LED strip is still handled by the standard firmware port render/show step.
io["SACN"].render and io["ARTN"].render are called before the implicit render of physical ports. Use them to fill the port buffers and let the ports display through their normal path.
The callback is called even without a TNGL segment. This is useful for installations where animation frames are produced by an external DMX console and the controller only receives, maps and displays them.
The current implementation is intended for RGB input data. If you receive a DMX order other than RGB, set the input IO to RGB or convert the data to a packed RGB buffer before calling DMX.map().
One call can use up to eight output ports.