SparkFun ESP32 DMX to LED Shield
Introduction
The SparkFun ESP32 DMX to LED Shield is the perfect way to send and receive DMX data whether it be coming in over the onboard XLR-3 jack or ArtNet, or outputting over the XLR-3 Jack/ArtNet, this shield has you covered. It's the perfect way to get started developing your own custom DMX fixtures, or even adding ArtNet capabilities to a current fixture. It also holds up to the DMX standard which requires electrical isolation between the controller and communication side to avoid ground loops.
In this hookup guide we'll go over the several different ways that the DMX to LED Shield can be configured, go through simple DMX output and input, look at how to use ArtNet input to control arrays of LEDs and even servos using the available poke-home connectors and finally check out how to turn ArtNet input into DMX output to enable ArtNet control on an existing DMX fixture.
Required Materials
To do all the examples, users will need:
- +2 DMX to LED Shields (Controller/Peripheral) with ESP32 Thing Plus -OR- Separate DMX Controller/Peripheral
- XLR-3 Cable
- Addressable LEDs
- Pan Tilt servo kit
- Headers and Soldering Tools
Headers and Soldering Tools
The DMX to LED shield is designed with a Feather footprint. Make sure to choose the appropriate header combo to suit your microcontroller and project.
Suggested Reading
We'd recommend checking out the following tutorials and hookup guides before getting started if you're not familiar with the topics. At the very least check out the DMX related tutorials if you haven't used the protocol before.
How to Solder: Through-Hole Soldering
Using Artnet DMX and the ESP32 to Drive Pixels
Introduction to DMX
LuMini Ring Hookup Guide
ESP32 Thing Plus Hookup Guide
LuMini 8x8 Matrix Hookup Guide
Hardware Overview
Power
You should always power the DMX shield with 5V to provide proper power for RS485. The DMX to LED shield can be powered through either the screw terminals or the USB connection on your microcontroller that is plugged into the shield.
Power Screw Terminal
For applications with high current draw be sure to use the available screw terminals to provide power. If you are powering a large LED array or many servos, you may start seeing power dips due to your USB power supply's inability to give enough current. The screw terminal is a 2-pin 3.5 mm screw terminal that should be able to accept 26-16 AWG wire with a strip length of ~5-6 mm. The screws are M2 screws, so you will probably need a smaller jewelry size screw driver.
Logic Level Converter
Since many Feather boards are 3.3V logic, there is a logic level converter between the microcontroller and LED data pins to bring the poke-home connectors up to 5V logic.
Power and Data Isolation
As per DMX spec, microcontroller power should be electrically isolated from XLR communication. This has been accomplished with a DC-DC converter and optoisolators. The DC-DC converter has an isolation voltage of 1 kVDC (rated to 1 min.) and isolation resistance of 1 GΩ. The DC-DC converter outputs 200 mA at 5V and can take a surge voltage of 9 VDC (for 100 μs).
XLR Connectors
XLR cables are commonly used in the stage lighting and sound industry. Each DMX to LED Shield has a 3-pin (aka XLR-3) male and female XLR connector. When using these, be very careful NOT to plug into any audio equipment as you will likely damage your DMX to LED Shield. To remove a cable, just press down on the button on the cable or connector and pull it out. Typically, the male XLR connector on the peripheral is the input while the female is the output.
Poke Home Connectors
The outputs for the poke home connectors are laid out to match up with APA102 LED strips and LuMini devices.
Due to this, all of the clock lines have been connected to the same output on the ESP32 (or whatever microcontroller you happen to be using). The pins on the shield have been labeled with the corresponding poke home connectors in the image below, so you know which pins to connect to if you want to use them for other purposes.
ESP32 Thing Plus | DMX Shield |
SCK |
CO C1 C2 |
COPI | D0 |
CIPO | D1 |
Pin 27 | D2 |
To use the poke-home connector, simply press down on the tab with a ballpoint pen (a key or screwdriver also works well) while pushing in the wire. The tabs to depress are highlighted in the image below, be aware that it'll take some extra finesse to get stranded core wire into these connectors. These connections should be able to accept **24-18 AWG** wire.
Termination Resistor Pad
A pad for a 120 Ω termination resistor is broken out on the shield. This is used to help increase the signal reflection, if the shield is at the end of a chain of DMX devices. If you don't have a 120 Ω resistor, a 100 Ω resistor can be used.
Hardware Assembly
The DMX to LED shield is designed with a Feather footprint; we recommend using the SparkFun ESP32 Thing Plus for it's WiFi capabilities. Make sure to choose the appropriate header combo to suit your needs when soldering headers into your microcontroller board. In the examples below, we will be using an ESP32 Thing Plus with soldered male headers. Check out the assembly section of the ESP32 Thing Plus Hookup Guide for tips on soldering the ESP32 Thing Plus.
This shield is easy to plug in and start playing around with. When creating a high current installation, make sure to power your system through the screw terminals. If you know the board will be at the end of a chain of DMX devices, solder in a 120 Ohm resistor in the term spot (highlighted below). If you don't have a 120 Ω reisitor, a 100 Ω resistor can be used.
Software Overview
For some of these examples, we'll be using a software that will be new to most Arduino users, Resolume Arena 6, which is a popular video jockey software. If you plan on following along with some of the ArtNet examples, you'll need to download the demo here in order to output some ArtNet data to your network. If you have another device generating ArtNet signals on your network, you can just stick with that.
First, you'll need the SparkFunDMX Arduino library, ArtnetWifi Arduino Library, ESP32Servo Arduino Library, and Resolume Arena 6 (click button above). You can obtain these libraries through the Arduino Library Manager by searching for them to install the latest version. If you prefer downloading the library from the GitHub repository and manually installing it, you can grab them here:
Note: This tutorial assumes you are using the latest version of the Arduino IDE on your desktop. If this is your first time using Arduino, please review our tutorial on installing the Arduino IDE. If you have not previously installed an Arduino library, please check out our installation guide.
SparkFunDMX Library Functions
Below is a list of the available SparkFunDMX library functions.
.initRead(int maxChan)
- Enables XLR input.
Integer for total number of channels being used.
.initWrite(int maxChan)
- Enables XLR output.
Integer for total number of channels being used.
.read(int Channel)
- Reads data in buffer from Channel.
Unsigned 8-bit integer to do with what you please.
.write(int Channel, uint8_t value)
- Writes data (value) in buffer for Channel.
uint8_t value
Unsigned 8-bit integer to be written to channel.
.update()
- Writes data buffer to Serial output if we are in write mode, reads data into buffer if we are in read mode.
Examples
Example 1 - Controller
Materials
- DMX Cable
- DMX Fixture/Additional DMX to LED Shield
To start, we'll output DMX over the XLR Jack to control a DMX capable peripheral device. We'll map out our channels so they control the peripheral device we'll create in the next example. For the sake of simplicity in this first example, we'll control the color of the entire 8x8 matrix as well as the values of each servo. Go ahead and open File->Examples->SparkFun DMX-.Example1-DMXOutput. Looking at the beginning of the example, we include the SparkFunDMX library and create a SparkFunDMX
object called dmx
We'll use channels 1, 2, and 3 for hue, saturaion and value, the 4th channel will contain pan, and the 5th will be tilt. These are defined in the preamble of the example to help keep track of which channels are where. If we are using two DMX to LED shields (one as output and one as input) we'll want the channel definitions sections to be identical on both output and input sides
language:c
#include <SparkFunDMX.h>
SparkFunDMX dmx;
//Channel Definitions
#define TOTAL_CHANNELS 5
#define HUE_CHANNEL 1
#define SATURATION_CHANNEL 2
#define VALUE_CHANNEL 3
#define PAN_CHANNEL 4
#define TILT_CHANNEL 5
In our setup()
loop we begin our DMX shield in write mode by calling dmx.initWrite(TOTAL_CHANNELS);
, which enables XLR output. We can then write to our buffer using dmx.write(int channel, uint8_t value);
and then send that out over the XLR jack using dmx.update();
. In our void loop()
we use a for
loop and several if
statements to decide what to put in each channel, then send that data out. This is shown below.
language:c
void loop() {
for (int channel = 1; channel <= TOTAL_CHANNELS; channel++) //We don't (and can't) write to channel 0 as it contains 0x00, the DMX start code
{
if (channel == SATURATION_CHANNEL || channel == VALUE_CHANNEL) //Write the same value (190/255) = 74.5% to both saturation and value.
{
dmx.write(channel, 190);
}
else if (channel == HUE_CHANNEL || channel == PAN_CHANNEL || channel == TILT_CHANNEL) //Sweep across our servos as well as a rainbow cycle.
{
dmx.write(channel, x);
}
}
Serial.print(x++);//Print and increment x. Since x is an unsigned 8 bit integer it will loop back to 0 if we try to increment it past 255
dmx.update(); // update the DMX bus witht he values that we have written
Serial.println(": updated!");
delay(100);
}
You can open the Serial Monitor to 115200 baud if you'd like to see the current value being written to hue and the servos, but we won't truly be able to see the output until we connect to a peripheral device.
Example 2 - Peripheral
Materials
- DMX Cable
- DMX Controller/additional DMX to LED Shield
- LED's (Optional)
- Pan/tilt Servo (Optional)
In this example, we'll be creating the world's most adorable moving head light using a LuMini 8x8 Matrix and our Pan/Tilt Servo Kit, although you're totally allowed to use different servos with our other servo mounting kit if you want some nicer servos. What we'll do is set our ESP32 to send our first 3 channels of DMX data to our LED matrix to color it, the 4th and 5th channels will be used to control the servos.
Optional Hardware Assembly
If you're deciding to create a tiny version of a moving head, assemble your pan and tilt servos as well as soldering some wire onto the LuMini Matrix if you haven't already. Attach your LED's to your moving head how you see fit, I've used poster putty to attach mine for this simple demo. Go ahead and connect the data pin (orange on the Pan/Tilt Servo Kit) on your pan servo (left to right) to the D1 poke-home connector and the tilt servo to the D2 poke-home. Connect D0 and C0 to DI and CI on whatever APA102/LuMini LED's you've decided to use. If you're using WS2812 type LED's, you'll only need to connect your DATA line to D0. My setup is shown below, with the LED lines on the left set of poke-home connectors and the servos on the two right sets.
Peripheral Code
If you haven't already, go ahead and open up File->Examples->SparkFun DMX-.Example2-DMXInput. On our peripheral side, we'll want to make sure that our channel definitions in our preamble match those in our output example, as we'll need to know how many channels we'll be receiving and what to do with each part of the data. We'll then need to declare our hardware for our custom DMX fixture, in this case two servos and a matrix of LED's. All of the definitions are shown below.
language:c
//Channel Defintions
#define TOTAL_CHANNELS 5
#define HUE_CHANNEL 1
#define SATURATION_CHANNEL 2
#define VALUE_CHANNEL 3
#define PAN_CHANNEL 4
#define TILT_CHANNEL 5
//Fixture Hardware Definitinos
#define NUM_LEDS 64
CRGB matrix[NUM_LEDS];
Servo pan;
Servo tilt;
In our setup()
loop we initialize our LEDs and servos as well as put our shield in read mode by calling dmx.initRead(totalChannels);
.
language:c
void setup()
{
Serial.begin(115200);
dmx.initRead(TOTAL_CHANNELS); // initialization for complete bus
Serial.println("initialized...");
FastLED.addLeds<APA102, DATA0, CLOCK, BGR>(matrix, NUM_LEDS);
FastLED.setBrightness(16);
pan.attach(DATA1);
tilt.attach(DATA2);
}
We can then call dmx.update();
to update our data buffer, which we can then access by calling dmx.read(int channel);
. We use this process to read our data from the corresponding channels into the proper hardware peripherals in the void loop()
.
language:c
void loop()
{
dmx.update();
for (int led = 0; led < NUM_LEDS; led++)
{
matrix[led] = CHSV(dmx.read(HUE_CHANNEL), dmx.read(SATURATION_CHANNEL), dmx.read(VALUE_CHANNEL));
}
pan.write(map(dmx.read(PAN_CHANNEL), 0, 255, 0, 160));
tilt.write(map(dmx.read(TILT_CHANNEL), 0, 255, 0, 160));
FastLED.show();
}
Loading example 1 up to one shield and example 2 up to another and connecting them via XLR should yield an output similar to the below GIF.
Example 3 - ArtNet Input
Materials
- LED's (Optional)
- Pan/tilt Servo (Optional)
Arduino Code
This example is quite similar to the previous peripheral example, except this time we will be receiving ArtNet data over WiFi from Resolume Arena 6, a popular video jockey software. Let's begin by setting up our ArtNet Input, we'll be taking our hardware setup from the previous example and leveraging it here, so all of the definitions for the hardware peripherals will remain the same. However, in this example, we'll be controlling each LED on our matrix individually, so we'll use 192 (64 LED's multiplied by 3 channels per LED) channels for LED's instead of 3. This makes our Channel definition section look like the below.
language:c
//Channel and Peripheral Definitions
#define NUM_LEDS 64
#define NUM_LED_CHANNELS NUM_LEDS * 3 //Ends up being 192 channels for our 8x8 LED matrix
#define PAN_CHANNEL 193
#define TILT_CHANNEL 194
CRGB matrix[NUM_LEDS];
Servo pan;
Servo tilt;
We then need to begin WiFi and ArtNet. The example defaults to using the ESP32 as a standalone access point but you can change lines 23, 24, 54, 55 and 57 if you'd like to connect to an existing network. If not however, the standalone access point will have an SSID of myDMX and the password will be artnetnode
. We then initialize our artnet
object (which we created in the preamble with ArtnetWifi artnet;
) with artnet.begin();
which will begin scouring the network for Artnet packets. We then use artnet.setArtDMXCallback(onDmxFrame);
so that onDmxFrame
is called each time we see an Artnet packet on the network. onDmxframe
is the function that we will be changing based on our setup to parse data into the proper hardware attachments. We loop through all of our channels and place them into the proper segments using if statements. This code is outlined below.
language:c
void onDmxFrame(uint16_t universe, uint16_t length, uint8_t sequence, uint8_t* data)
{
// read universe and put into the right part of the display buffer
//DMX data should be sent with the first LED in the string on channel 0 of Universe 0
for (int channel = 0; channel < length; channel++)
{
if (channel < NUM_LED_CHANNELS && channel % 3 == 0) //Only write on every 3rd piece of data so we correctly parse things into our RGB array
{
matrix[channel / 3] = CRGB(data[channel], data[channel + 1], data[channel + 2]);
}
else if (channel == PAN_CHANNEL - 1) //Subtract 1 due to the fact that we index at 0 and ignore the startcode
{
pan.write(map(data[channel], 0, 255, 0, 160));
}
else if (channel == TILT_CHANNEL - 1)
{
tilt.write(map(data[channel], 0, 255, 0, 160));
}
}
previousDataLength = length;
if (universe == endUniverse) //Display our data if we have received all of our universes, prevents incomplete frames when more universes are concerned.
{
FastLED.show();
}
}
Resolume Setup
If you haven't yet, go check out the ArtNet DMX and ESP32 Pixel Pushing Guide for some help setting up custom fixtures in Resolume. You'll need to create two fixtures, one for your 8x8 display, and a single channel fixture to control pan and tilt. Then, create a Lumiverse with your 8x8 matrix in the center and the two fixtures; one for pan and one for tilt in each corner. Also ensure that you change TargetIP to IPAddress and change the address to 192.168.4.1
to send data directly to your ESP32.
We'll then downsize our main video source so it isn't taking up the corners of the display and affecting the inputs for the pan and tilt channels. We'll then add two solid color blocks in the corners so we can affect the pan and tilt channels of our fixture. This is a very hacky way to do things, but Resolume is meant for video screens and not full fledged fixtures. We will then modulate the brightness of our solid color blocks in order to send data to the corresponding DMX channels. You can set all this up yourself, or download the composition below.
Connecting to the same network as the ESP32 will send data to your screens and servos. Check out the below gif to see what should be going on if you've set everything up correctly.
Example 4 - ArtNet to XLR
Materials
- DMX Cable
- DMX Fixture
This final example will combine everything we've learned so far to control a non ArtNet capable peripheral over ArtNet. Our Controller board will be turning Artnet data into DMX output. Our main change here will be that we change our onDmxFrame
function to write our incoming Artnet data to our XLR buffer. This is accomplished with a simple for loop, shown below.
language:c
void onDmxFrame(uint16_t universe, uint16_t length, uint8_t sequence, uint8_t* data)
{
sendFrame = 1;
//Read universe and put into the right part of the display buffer
//DMX data should be sent with the first LED in the string on channel 0 of Universe 0
for (int channel = 0; channel < length; channel++)
{
dmx.write(channel, data[channel]);
}
previousDataLength = length;
if (universe == endUniverse) //Display our data if we have received all of our universes, prevents incomplete frames when more universes are concerned.
{
dmx.update();
}
}
I have a non Artnet capable fixture laying around; an old Casa laser. I did some digging round on the internet to find this slot map. I then mapped out channels in Resolume to match up with the 7 channels available on the laser. To do this, I simply created three separate solid colors and placed an LED on each one. This makes my red value on my first led channel one, green channel 2, and so on. The second LED is channels 4-6 and sits on the second color. This is shown in the below image.
With all of these and my fixture map, I am able to send the data I want over to the laser over WiFi. The below clip consists of simply moving some channels around at random and watching the output.
Resources and Going Further
For more information, check out the resources below:
- GitHub Product Repo
- Schematic (PDF)
- Eagle Files (ZIP)
- SparkFun DMX Arduino Library (ZIP)
- SparkFun DMX Arduino Library GitHub Repository
- SFE Product Showcase
Need some inspiration for your next project? Check out some of these related tutorials: