Build a DIY ESP32 DMX512 Lighting Controller
This site's WS2812B addressable LED guide covers pixel strips in depth — individually addressable RGB LEDs driven by a single-wire, self-clocked protocol that's perfect for cosplay props, ambient lighting, and art installations. But WS2812B and DMX512 solve different problems. The moment you need to control a real PAR can, a moving-head fixture, an RGB flood, a fog machine, or anything else sold to the stage-lighting industry, you're in DMX512 territory, and none of what applies to NeoPixel strips carries over. DMX512 is the standard nearly every professional and prosumer lighting fixture speaks: it's what runs concert rigs, theater grids, church sanctuaries, and increasingly, ambitious home theaters and maker spaces with secondhand stage lights picked up from a liquidator. This project builds a standalone ESP32-based DMX512 controller that can push channel data to a fixture chain directly, or act as an ArtNet/sACN-to-DMX bridge so software like QLC+, MagicQ, or a DAW-based lighting plugin can control real hardware over WiFi. It's also the natural next build after this site's RS-485 and Modbus RTU for makers guide, since DMX512 rides on the exact same RS-485 differential electrical layer — you're just changing the protocol running on top of it.
What DMX512 Is (and Why It's Not Just Another LED Protocol)
DMX512 (officially ANSI E1.11 / USITT DMX512-A) is an asynchronous serial protocol built on RS-485 differential signaling running at a fixed 250 kbps, using 8 data bits, no parity, and 2 stop bits (8N2). A controller repeatedly transmits a "universe" of up to 512 one-byte channels, preceded by a break signal (a minimum 92 µs low pulse) and a mark-after-break (at least 12 µs high), followed by a start code byte (0x00 for standard dimmer data) and then up to 512 channel slots, each 0–255. A moving head fixture might occupy 16 consecutive channels for pan, tilt, color wheel, gobo, dimmer, and strobe; a simple RGB PAR can might use just 3 or 4. Refresh rate tops out around 44 Hz for a full 512-channel frame, though most controllers send faster with partial frames.
The differences from WS2812B are structural, not incidental. WS2812B uses a proprietary ~800 kHz single-ended timing protocol with no error checking, addresses LEDs purely by position in the data chain, and is only reliable over a few meters without signal reshaping. DMX512 uses true differential RS-485 signaling (a twisted pair carrying inverted copies of the same signal, read by comparing the voltage difference between the two conductors) specifically because it needs to survive 100+ meter cable runs across a venue, running alongside AC power cables and stage machinery, without corrupting the signal. It's addressed by channel number within a universe rather than physical position, uses a standardized 5-pin XLR connector, and depends on proper daisy-chain topology with a termination resistor at the far end of the chain to prevent signal reflections — all fundamentals covered in this site's RS-485 and Modbus RTU for makers guide, which apply here without modification.
Hardware Needed
- ESP32 dev board — any ESP32-WROOM-32 based DevKitC-style board works; dual-core and WiFi are what make the ArtNet/sACN bridging role possible.
- RS-485 transceiver IC — a MAX485 (or SN75176, or the fail-safe MAX3485) converts the ESP32's 3.3V UART TX/RX lines into a proper RS-485 differential pair capable of driving a DMX line.
- 5-pin XLR connector(s) — per the DMX512-A spec, the DMX OUT jack on a source device is a 5-pin male panel-mount XLR; if you also want a DMX IN/through port for RDM or pass-through testing, add a 5-pin female XLR as well.
- 120Ω termination resistor — for terminating the far end of a fixture chain during bench testing.
- Project enclosure, terminal blocks, power supply, and wiring — covered in the parts list below.
Wiring: ESP32 to MAX485 to XLR
The ESP32 has three hardware UARTs; UART0 is tied to the USB-serial bridge used for flashing and the serial monitor, so use UART2 for DMX to avoid conflicts. A typical wiring is GPIO17 (TX2) to the MAX485's DI (driver input) pin, and GPIO16 (RX2) to the MAX485's RO (receiver output) pin if you want bidirectional capability for RDM (Remote Device Management) queries later. Since this project is primarily a DMX transmitter, tie the MAX485's DE (driver enable) and RĒ (receiver enable, active low) pins together and drive them from a spare GPIO (e.g., GPIO21) set HIGH during transmission — the esp_dmx library handles this switching automatically if you pass it the enable pin during initialization.
MAX485 PinConnects ToVCC3.3V or 5V (check your specific breakout's logic level tolerance)GNDESP32 GND, and DMX cable shield/pin 1DIESP32 GPIO17 (UART2 TX)ROESP32 GPIO16 (UART2 RX)DE / RĒ (tied together)ESP32 GPIO21 (direction control)A (non-inverting)XLR pin 3 (Data+)B (inverting)XLR pin 2 (Data−)On the 5-pin XLR jack itself: pin 1 is signal ground/shield, pin 2 is Data 1−, pin 3 is Data 1+, and pins 4/5 are reserved for a rarely-used second data pair. Many consumer fixtures actually ship with 3-pin XLR connectors (electrically identical to standard microphone XLR, using pins 1/2/3 only) — if your fixtures use 3-pin, you can wire a 3-pin jack directly with the same pin 1/2/3 assignments, or use 5-to-3-pin adapter cables. Keep the DMX cable a genuine twisted-pair, 120Ω-nominal-impedance cable rated for RS-485/DMX — standard microphone cable is not spec'd for this and will cause reflections and dropouts on longer runs.
Firmware and Library Options
The library to use is esp_dmx (by @somewhatlurker/rickeyww, maintained under the Rob Faludi DMX King ecosystem naming as "esp_dmx"), available for both Arduino-ESP32 and native ESP-IDF. It manages the precise break/mark-after-break timing using the ESP32's UART hardware and RMT peripheral, exposes a simple dmx_write()/dmx_send() API for setting channel values and pushing a frame, and includes optional RDM support for querying connected fixture addresses and personalities. Initialize it against UART2 with your chosen TX, RX, and enable-direction pins, set the output to 512 channels (or fewer if your fixture chain doesn't need the full universe, which speeds up the refresh rate), and call the send function in a loop or on a timer.
Older alternatives exist — the original ESPDMX library and various Arduino "DMXSerial"-derived ports — but esp_dmx is the actively maintained option with correct ESP32 timing and is the one to reach for on a new build.
Software Control: Standalone Sequences vs ArtNet/sACN Bridging
There are two ways to run this controller, and the ESP32's WiFi radio is what makes the second one worth building:
- Standalone sequence mode — the ESP32 runs a hardcoded or EEPROM-stored chase/fade sequence and pushes it to the DMX line continuously, with no network dependency. Good for a fixed installation or a simple test rig, and it's how you'll validate the hardware first.
- ArtNet/sACN bridge mode — the ESP32 joins WiFi, listens for ArtNet (UDP port 6454) or sACN/E1.31 (multicast UDP port 5568) packets carrying DMX universe data from lighting control software, and re-transmits that data out the RS-485 line as real DMX. This turns the ESP32 into a wireless DMX node, letting a laptop running QLC+ (the open-source Q Light Controller Plus), MagicQ onPC, FreeStyler, or a DAW's lighting-automation plugin control physical fixtures without running a DMX cable to the computer. Libraries like ArtnetWifi (hideakitai/rstephan) or ESPAsyncE131 (forkineye) handle the network side; you decode the incoming universe packet and copy its channel bytes directly into the buffer esp_dmx sends out.
A single ESP32 with 4MB flash has plenty of headroom to run both: fall back to a stored standalone sequence if no ArtNet/sACN traffic arrives within a timeout, and switch to network-driven data whenever packets are present. This is effectively how commercial WiFi DMX nodes (LumenRadio, W-DMX bridges, etc.) behave, just built from parts you already understand.
Assembly Steps
- Flash a minimal esp_dmx test sketch to the bare ESP32 first and confirm UART2 output on an oscilloscope or logic analyzer before wiring anything else — you're checking for the break/MAB pattern followed by 250 kbps serial data.
- Solder the MAX485 module's DI, RO, DE/RĒ, VCC, and GND to the ESP32 per the pinout table above, either dead-bug style on perfboard or using a small breakout/prototype shield.
- Wire the MAX485's A and B differential outputs to XLR pins 3 and 2 respectively, and tie XLR pin 1 to common ground/cable shield.
- Mount the XLR jack, a power switch, and (if using a barrel jack rather than USB power) a DC input jack into the enclosure; drill and file mounting holes as needed.
- Install the ESP32 and MAX485 board inside the enclosure on standoffs or a perfboard tray, keeping the RS-485 wiring runs short.
- Power up, load the esp_dmx standalone sequence sketch, connect a DMX cable from the XLR output to a test fixture, and confirm channel response (a cheap RGB PAR can with a visible LCD channel display is ideal for this because you can read back the channel/DMX-address it's listening on).
- Add the WiFi ArtNet/sACN bridge firmware, connect QLC+ (or your software of choice) on the same network, configure it to output an ArtNet or sACN universe, and confirm the fixture responds to software-driven changes.
- Add the 120Ω termination resistor across A/B at the last fixture in your test chain (not at the controller end) and confirm this eliminates any flicker on longer cable runs.
Testing and Troubleshooting
SymptomLikely CauseFixFixture doesn't respond at allWrong DMX start address set on fixture, or A/B swappedCheck fixture's DIP switches/menu for its start address; try swapping A and B leadsFlickering or random channel jumps on long runsMissing termination resistor, non-twisted-pair cable, or ground loopAdd 120Ω terminator at the last fixture, use genuine DMX/RS-485 rated cable, tie all shields to a single ground pointFixture works standalone but not through ArtNet bridgeUniverse/subnet mismatch between software and ESP32 firmwareConfirm ArtNet universe number and IP subnet in QLC+ match what the ESP32 firmware is listening forSome channels work, others don'tWrong channel count sent, or fixture in wrong personality/modeRecheck fixture's channel mode (3-channel vs 16-channel personality) and match your sequence/patch accordinglySignal degrades only when other equipment is powered on nearbyEMI coupling, unshielded cable run near AC power/dimmer packsRoute DMX cable away from AC power runs, use shielded twisted-pair cable with shield grounded at one end onlySafety Notes
Most DMX fixtures are mains-powered and draw meaningful current — treat the fixture's AC wiring with the same caution as any mains-powered device, and never modify a fixture's internal AC wiring unless you're qualified to do so. Keep the DMX signal wiring (low-voltage, RS-485) physically separated from AC power runs to avoid both EMI issues and shock hazards. Always terminate the far end of a DMX chain with a proper 120Ω resistor across the data pair when running long cable lengths or multiple fixtures — an unterminated line causes signal reflections that can produce erratic, flickering, or unresponsive fixture behavior, and while it won't damage the ESP32 or MAX485, it will make troubleshooting the rest of the system miserable. Finally, confirm your enclosure's ground pin/shield connection is solid before connecting to a shared lighting rig; a floating or poorly grounded DMX node is one of the most common sources of intermittent faults on multi-fixture chains.
Once this controller is working, it becomes a genuinely useful bridge between the maker-electronics world and real stage lighting gear — you can drive secondhand PAR cans and moving heads picked up cheap from theater surplus, sync them to music through a DAW-linked ArtNet output, or extend a WiFi DMX node to a hard-to-reach spot in a rig without running a physical cable the whole way. The same esp_dmx and RS-485 fundamentals scale up directly if you later want to build a multi-universe node, add RDM device discovery, or combine this with the addressable-LED side of the site for a hybrid WS2812B/DMX installation.
Related Guides
- RS-485 and Modbus RTU for Makers: Wiring, Termination, and Reading Industrial Sensors
- Build a Wired ESP32 IoT Sensor Node with W5500 Ethernet for Reliable Uptime
- How to Program Addressable LED Strips: WS2812B Patterns, Effects, and Power Design
- I2C vs SPI vs UART: How to Choose and Use Serial Communication Protocols
- How to Use Sensors with Arduino and ESP32: Temperature, Distance, Load, Current, and Hall Effect
- How to Control Motors with Arduino and ESP32: Stepper, DC, and Servo Drivers
- ESP32: Setting Up for Arduino IDE
- Arduino vs ESP32: Which Should You Use? A Practical Comparison