Board::DMX (DMX Module Control)
Everything on this page is new in Harucom OS 2.0.
The DMX module sends DMX512, the protocol used for stage lighting. Moving heads and par cans can be driven from Harucom.
A background engine takes care of transmission. It sends all 512 channels 40 times a second, so Ruby only updates values.
Contents
- Wiring
- Getting Started
- Controlling the Lights
- The Dead-Man Switch
- The DMX Module
- Fixture Definitions
Wiring
DMX runs over RS-485, so a transceiver is needed. Connect an isolated transceiver such as the M5Stack DMX Unit to the Grove connector (J5).
| Item | Value |
|---|---|
| UART | UART1, 250000 baud, 8 data bits, no parity, 2 stop bits |
| TX pin | GPIO 20 (Grove connector J5) |
| Frame rate | About 40 Hz |
Harucom only transmits, so no receive pin is used.
Getting Started
Starting Transmission
require "board/dmx"
dmx = Board::DMX.new
dmx.start # clear every channel and begin transmitting
dmx[6] = 255 # set channel 6 to full
loop do
dmx.keepalive # say that the program is still running
sleep_ms 10
end
dmx.stop # black out, then stop transmitting
Board::DMX.new initializes DMX and start begins transmission.
After that you only change channel values, and the sending continues in the background.
Call keepalive from the main loop.
Stopping those calls turns the rig off, which
the dead-man switch explains.
Writing Channel Values
Channels are numbered 1 to 512 and values run from 0 to 255. What each channel means depends on the fixture you connected.
dmx[6] = 255 # one channel
dmx.set_range(1, [pan, tilt, 0, 0, 0, 128]) # a block of channels at once
set_range suits fixtures such as moving heads, where one unit occupies
several channels.
A value you write reaches the fixture on the next frame, at most 25 ms later.
Controlling the Lights
Load it with require "board/dmx".
Board::DMX.new
dmx = Board::DMX.new
Initializes DMX on the board’s default wiring, the Grove connector. Nothing is transmitted yet. To use other wiring, see The DMX Module.
Board::DMX#start
Clears every channel to zero, then begins transmitting.
startclears every channel, so set your values after calling it.
Board::DMX#stop
Clears every channel, waits for that to reach the fixtures, then stops transmitting.
A fixture holds its last values when the signal stops, so simply cutting transmission
would leave the rig lit. stop makes sure the rig is dark first.
Board::DMX#set(channel, value)
dmx.set(6, 255)
dmx[6] = 255 # the same thing
Writes one channel. channel runs from 1 to 512 and value from 0 to 255.
Values outside those ranges do nothing.
Board::DMX#set_range(channel, values)
dmx.set_range(1, [pan, tilt, 0, 0, 0, dimmer])
Writes the array into consecutive channels, starting at channel.
Handy for a fixture that occupies a block of channels.
Board::DMX#get(channel)
dmx.get(6) #=> 255
dmx[6] # the same thing
Returns the value currently written to a channel.
Board::DMX#blackout
Sets every channel to zero, so the rig goes dark on the next frame. Transmission continues, and writing a value lights it again.
Board::DMX#keepalive
Tells the dead-man switch that the program is still running. Call it on every pass of the main loop. See The Dead-Man Switch for the details.
Board::DMX#deadman_ms=(ms)
dmx.deadman_ms = 1000
dmx.deadman_ms = 0 # disable
Sets how long, in milliseconds, the engine waits after keepalive stops
before it darkens the rig. It defaults to 500 ms, and 0 disables it.
Board::DMX#active_slots=(count)
Sets how many channels are transmitted, from 1 to 512. Shortening the frame to the channels you actually use leaves more idle time between frames.
Board::DMX#frame_count
Returns the number of frames sent since start.
It grows by about 40 per second, which makes it useful for checking the real frame rate.
The Dead-Man Switch
A fixture holds its last values when the signal stops. If your program dies, the rig stays lit.
To prevent that, the engine has a dead-man switch.
When keepalive stops being called, the engine forces every channel to zero and the rig goes dark.
loop do
dmx.keepalive
# update values here
sleep_ms 10
end
The grace period is set with deadman_ms= and defaults to 500 ms.
Once keepalive resumes, the values set from Ruby take effect again.
The switch runs inside the engine, so it works even when Ruby has stopped.
The DMX Module
Board::DMX wraps the DMX module.
Methods such as set and keepalive exist on DMX under the same names and arguments
(DMX.set(6, 255), for instance).
Reach for DMX directly when the wiring differs.
DMX.init(unit:, txd_pin:)
DMX.init # use the board default wiring
DMX.init(unit: :RP2040_UART1, txd_pin: 20)
Initializes a UART for DMX512. Omitting the arguments selects the board default.
txd_pin has to be a pin that can carry TX for the chosen unit.
It returns the DMA channel it claimed.
The line parameters (250000 baud, 8 data bits, no parity, 2 stop bits) are fixed by the standard and cannot be changed.
Board::DMX.new calls this method with no arguments.
DMX.stop and DMX.shutdown
DMX.stop only stops transmission. Fixtures hold their last values, so a lit rig stays lit.
DMX.shutdown darkens the rig first, then stops. This is what Board::DMX#stop calls.
Fixture Definitions
Channel order differs from fixture to fixture.
Put Open Fixture Library JSON definitions in
/data/dmx/fixtures to work with named channels.
require "dmx/fixture"
paths = DMX::Fixture.list("/data/dmx/fixtures")
fixture = DMX::Fixture.read(paths[0])
fixture[:name] #=> "SHEHDS 80W LED Spot Light"
mode = fixture[:modes][0]
mode[:label] #=> "13ch"
mode[:channels][5][:name] #=> "Dimmer"
mode[:channels] is ordered as the channels appear on the wire,
so entry i sits at the fixture’s base address plus i.
dmx_demo is an app built on these definitions. To drive sound and light together from patterns, see Johakyu.