This article appeared in Make: Vol. 91. Subscribe for more maker projects and articles!

If you’ve ever seen giant, bright lighting systems on stage, at concerts, or on big outdoor art installations, you’ve been looking at DMX lighting. DMX512 is a control protocol used by the pros but it’s easy to use in your own projects with just an Arduino!

DMX stands for Digital Multiplex. It was invented in 1986 as an easy way to control lots of theater lights. DMX is really useful because it’s a very simple protocol that lets you command a large number of elements with a single controller. Lots of off-the-shelf DMX lights are really made to light big things: like a stage, building, or sculpture. Using devices that are controlled through DMX can really make your work feel more professional.

Why DMX?

DMX fixtures are often available in waterproof enclosures and can be mounted easily onto basically anything. With these tools, you can create cool distanced lighting effects and make some truly immersive stuff! Personally I’ve used DMX a lot on outdoor or large-scale installations. You can just get way more power out of a standard DMX fixture than a standard LED strip. They are super bright — I’ve used them to light sculptures even in full daylight. After all, they’re made for this kind of thing!

DMX is a one-way communication1, so devices cannot talk back to the controller. While it’s most commonly used in stage work, I’ve seen a ton of really cool projects that use DMX to control a big array of other smaller things. It’s not just for the pros — it works with familiar maker-friendly boards, too. In this guide I’m going to show you how to control these big light fixtures using your Arduino, and your favorite NeoPixel or FastLED patterns.

Channels

DMX uses channels to transfer information along a chain of elements. Think of a channel as a numbered slot for information. Channels are arranged in a DMX universe, which contains 512 channels. You can have multiple universes if you want to control a lot of channels. Channels can also have a personality, which is the attribute they are controlling. For example, a channel might control light color, but it could also control the rotation or strobe of a lighting fixture. In another context, this could be really any parameter you want. In a standard DMX stage lighting situation your channels might be Red, Green, Blue, and White, making it a four-channel light. It takes 23 milliseconds to transmit a DMX packet, which means we have a maximum frame rate of 44Hz.

Controllers, fixtures, and scenes

DMX lights use a single controller to control a large number of channels. Controllers can be either a normal DMX light that all other lights mimic, or a more complicated setup that drives patterns or cues on a stage. Commonly, you’ll see controllers referred to as master and each light referred to as slave. I only reference this here so that you’re aware of the language when researching; from here out I will be referring to them as controller and peripherals or fixtures. This is in reference to the Open Source Hardware resolution to replace master/slave words in SPI protocols, but I think it applies here too.

Each DMX fixture groups a series of channels. Below, you can see how the channels are sequential in groups defined by the fixtures. In a typical DMX controller, settings can be saved in scenes. Each fixture is basically a lamp.

DMX channel addresses are configurable, so you can set each fixture to its own address. If you’re using your own code or a controller, you’ll need to be aware of how many channels each fixture uses, and the order they’re in, so that they don’t overlap.

If you’re using off-the-shelf controllers and lights, it’s important to read the documentation to understand how the channels are working in that particular unit. It’s really something that’s impossible to guess, so don’t be like me and waste hours playing with channel addresses — read the manual. Every single DMX light or model is different, but commonly there are buttons on the side or back of the fixture itself that can be used to set both the channel number and the controller or peripheral modes.

DMX connectors

DMX controllers originally used a five-pin XLR5 connector, but most entry-level DMX controllers use XLR5-size connectors that have only three pins. In most cases, DMX and three-pin XLR5 are interchangeable for hobby work or small installations. They’re the same dimensions except two pins are missing, which is why they’re able to mesh.

There are also mini XLR and mini DMX connectors, as well as a variety of intermediate sizes that are hard to assess without looking at the specs. One thing I ran into is that there are a variety of three-pin XLR sockets on mid-range, weatherproof DMX lights that are in between mini and standard size!

Two sizes of XLR connectors. Photo by Lee Wilkins

Weatherproof or stage-quality DMX lights can be really expensive, so you’ll find a wide range of qualities and with them a wide range of connectors. It’s really worth double checking the specs on each connector while you’re sourcing lights. I’ve been using DMX lights for outdoor installations, so it’s important to me that they be IP65 rated which have specific connectors and are very well sealed. You can find affordable non-waterproof DMX lights on Amazon or AliExpress, but be aware they might be extremely not waterproof, with open enclosures! This is fine for indoor installations. I’ve seen people manufacture boxes to put them in with acrylic covers to keep them safe too.

This PAR can fixture is built to take some abuse. Photo by Lee Wilkins

In the end, it is just a three-pin connector that you need to plug into the standard three-pin DMX socket on your Arduino breakout, so there are a few simple solutions if your connectors don’t match. I bought a few spare DMX cables and a variety of XLR sizes and created a series of adapters. These are pretty easy to make because there are only three wires. Cut your standard DMX cable and use a multimeter to find the corresponding wires and solder them. Make sure to use heat-shrink tubing over each connection and the whole wire, then you’re ready for any budget DMX lamps.

If you are chaining together many lights, you only need to create one adapter to connect the Arduino breakout to the first fixture. Then, each subsequent fixture can use whichever style connector they came with.

Choosing and using fixtures

For fixtures, there are two main shapes I use: a PAR can or a light bar. A PAR can is what you might think of as a spotlight, although they do have a wide range of angles. Originally parabolic aluminized reflector lamps, there are now LED replacements in the same form factor. Some are tightly focused to light up a single narrow area, and others are more like floodlights that can light up a wide area. I used LED PAR cans in the Earth Resist sculpture in the conclusion to light both the exterior and interior of the piece.

Light bars. Photo by Dave Cox

Light bars are wide, good for lighting up a wall or big area. We also used light bars to edge-light large acrylic panels. They’re great for covering a large area with lights rather than highlighting a particular element.

Light bars illuminating a large acrylic panel. Photo by Lee Wilkins

You’ll find that some of the lower-quality lights have individual red, green, and blue lamps on the fixture. While this might seem at first like you won’t get the clean light colors you want to make, you’d be surprised at how well they do mix with a bit of distance. In this large acrylic panel, we used a cheaper lamp to create an edge-lit effect by putting it on the edge of a large piece of acrylic. At the base of the acrylic the three colors aren’t mixed, but after a few inches they fully mix to create really nice colors. We covered the first few inches of the acrylic so that only the full effect was visible. If you want lamps with RGB integrated together, it will cost you. But I really don’t think it’s a big deal!

Finished edge lighting result. Photo by Lee Wilkins

If you give the lamps a lot of space, the lights will mix in really interesting ways. I placed the lights at the bottom of these large metal sculptures and it created a great backlight effect while showing off the textures of the metal framing.

Photo by Lee Wilkins

Breakout board for Arduino

For controllers, I have tried a variety of DMX breakout boards and settled on DFRobot’s DMX Shield for Arduino (DFR0260) as my favorite. It fits neatly onto an Arduino Uno, which is my preference for installation work, and it has a few jump switches that let you quickly program it and set different modes.

Photo by Lee Wilkins
  1. RDM was added to the DMX protocol in 2006, but not every device supports it. ↩︎

What will the next generation of Make: look like? We’re inviting you to shape the future by investing in Make:. By becoming an investor, you help decide what’s next. The future of Make: is in your hands. Learn More.

Project Steps

Understanding the Code

Photo by Lee Wilkins

After testing a range of DMX libraries, I have been using the Conceptinetics DMX library for Arduino. It provides a series of really clear functions for using fixtures. You’ll start by defining a series of parameters for your sketch. I’ve created an example:

#define channelSpacing 10 // This is the channels

Here we are setting the number of channels each fixture has. I chose to set channelSpacing at 10 because I was using a variety of different fixtures, some of which had four channels and some of which had eight. By setting it at 10, we are sure the channels won’t run into each other. If you really want to fit in as many fixtures as possible, you’ll need to fine-tune this.

#define DMX_MASTER_CHANNELS 512

This is the number of channels in one universe, but you may have bigger projects.

#define RXEN_PIN 2

This is connected via the DFRobot board. It is a jumper used to denote when you are in programming or running mode.

#define lamps 16

This is the total number of fixtures we will use. It’s useful for running a loop in your sketch that iterates through each lamp. In this example, we want to loop through 10 channels for 16 lamps, sending data to each channel. So the loop will increase by the 10 (channel spacing number) each time, for the amount of lamps we have. I also created a set of modifiers for each channel. For example, red is the first channel in each fixture, so it needs no adjustment. But white is the fourth, so for each loop of the lamp we need to increment by 4 to get to the white value.

typedef enum DmxCh {
  RED   = 0,
  GREEN = 1,
  BLUE  = 2,
  WHITE = 3
} DmxCh;

Prepping Your Lights

Photo by Lee Wilkins

You also need to prepare your fixtures. This will be different for each DMX fixture you have, so be sure to read the instructions.

You’ll want to set your controller to its peripheral mode and manually input the starting channel address. You’ll also need to make sure it connects to your Arduino, so that may involve either creating a new cable or purchasing an adapter.

It’s worth making a map of all your lights and channels so you can fully understand how they work. You can also test each light by making the first light in the chain a controller, and seeing if the peripheral lights follow its sequence. DMX and XLR cables do not carry power, so it’s important to check how your power will be connected throughout your installation.

Making Patterns

There are a series of functions I’ve modified to help mimic how NeoPixel libraries are constructed, so that you can use your favorite NeoPixel patterns on big giant DMX lights. First, let’s look at how to set a DMX light manually:

void set_rgb_value(int lampNumber, int
red, int green, int blue, int white) {
  int channelNumber = lampNumber * channelSpacing;
  // Set each of the colors
  dmx_master.setChannelValue(channelNumber + RED, red);
  dmx_master.setChannelValue(channelNumber + GREEN, green);
  dmx_master.setChannelValue(channelNumber + BLUE, blue);
  dmx_master.setChannelValue(channelNumber + WHITE, white);
}

This manually takes the lamp number and determines the channel spacing based on the offsets, then applies the RGB-W value you assigned. You can use something like this to manually set each pixel, but it’s not ideal for mimicking your favorite patterns if you don’t want to write new ones.

Fortunately there are two functions, setPixel and showPixels, that parallel those in the Adafruit NeoPixel library. NeoPixels use a buffer-style system that sets lights, then shows them. So I’ve had to replicate that with those two functions:

void setPixel(int index, int lampNumber,
int red, int green, int blue, int white) {
  pBuff[index].lamp = lampNumber;
  pBuff[index].red = red;
  pBuff[index].green = green;
  pBuff[index].blue = blue;
  pBuff[index].white = white;
}

The setPixel function uses a buffer to prepare the pixel at each index with a red, green, and blue value. index here indicates the position we are at in the array of lamps.

void showPixels() {
  for (int i = 0; i < index; i++) {
    int channelNumber = pBuff[i].lamp * channelSpacing;
    // Set each of the colors
    dmx_master.setChannelValue(channelNumber + RED, pBuff[i].red);
    dmx_master.setChannelValue(channelNumber + GREEN, pBuff[i].green);
    dmx_master.setChannelValue(channelNumber + BLUE, pBuff[i].blue);
    dmx_master.setChannelValue(channelNumber + WHITE, pBuff[i].white);
  }
  clearBuffer();
}

The showPixels function is similar to set_rgb_value but it shows every single pixel available in the buffer. You can use this buffer style to immediately deploy your pre-existing patterns, like in this fade example:

for (int i = 0; i < 255; i++) {
  for (int j = 0; j < lamps; j++) {
    setPixel(j, i, 255, 0, 0);
  }
  showPixels();
  delay(5);
}
for (int i = 255; i > 0; i--) {
  for (int j = 0; j < lamps; j++) {
    setPixel(j, i, 255, 0, 0);
  }
  showPixels();
  delay(5);
}

Here the pixels are set after each loop, and then shown after the white strip has been set. These functions have the same name as standard NeoPixel functions, so you should be able to swap out your patterns easily. You can do this by replacing strip.setPixelColor with setPixel, and strip.show() with showPixels(), and strip.numPixels() with lamps. Keep in mind that in your code it may not be strip but whatever you have named your NeoPixel object.

Conclusion

DMX me!

Earth Resist by Ryan Longo, Shan Honoridez, and Lee Wilkins in Ontario Place Winter Light Exhibition, 2018. Photo by Maxwell Lander.

I’d love to see you take your projects to the next level and make something BIG! Share your DMX projects with me at hello@leecyb.org, or tag me on Bluesky or Instagram.

 

Featured image is FiddleHex by Lee Wilkins, Hillary Predko, Alex Leitch, Sagan Yee, and Dave Cox in Lumière: The Art of Light, Ontario Place, Toronto, 2023. Photo by Sagan Yee.

This article appeared in Make: Vol. 91. Subscribe for more maker projects and articles!