Instructables
Picture of Raspberry Pi as a DMX light controller

Turn your Raspberry Pi into a DMX light controller




This instructable shows you how to display a color wheel editor window on the screen of a Raspberry Pi computer, which you can manipulate with your mouse. As you change the color on the screen, the color on the DMX light fixture changes in realtime to match. This can serve as a starting point for a variety of simple lighting projects using the Raspberry Pi ( i.e. haunted houses, onscreen light control panel, TV backlights, living room DJ setup, etc. ).

 
Remove these adsRemove these ads by Signing Up

Step 1: Nuts and bolts

A standard $35 Raspberry Pi computer runs a program that generates the onscreen interface, and sends commands via USB to a DMX controller continuously. The DMX controller then sends DMX commands to the light fixture to change the color. The hardware was chosen for cost and durability, and the software was developed on the platform and is available for free in source and binary format so you can begin hacking right away. Here is what you'll need ( as pictured ):



1. Raspberry Pi

The Raspberry Pi is an amazing $35  credit card sized computer that contains all the basic features of a "real" computer, including a free pre-built Linux operating system ( via SD card ), USB for keyboard and mouse, and most impressively a full HDMI video output. If you are new to the Raspberry Pi ( or raspi ) we strongly suggest you get your mitts on one as soon as possible - its a great platform for anyone interested in knowing more about how computers work, and you can actually write programs on it!

2. USB | DMX Controller

The raspi has a built-in I/O connector for doing all sorts of cool things, but for this project we chose to use one of the USB ports as our output interface because its simpler and more rugged than using breadboards and ribbon cables. To get things into DMX (digital light control) format, we will be using a Velleman USB to DMX interface. This can be bought in either kit or pre-built form, and is a really a great introduction to controlling DMX devices from a computer. Once you have this controller you'll find it a great tool for any lighting control project you may cook up in the future.

3. DMX light fixture

Pretty much any DMX controlled light fixture that has red, green, or blue channels will work, and in fact you can chain together several if you want t control a whole bank of lights from your raspi. In this example we are using a Chauvet LEDSplash 200B spotlight because we found one cheap online for about $60 and its very bright and runs cool. If you have a DMX dimmer and standard PAR cans that's fine too, its only important that you have a device that can receive red, green, and blue intensity channels.

4. HDMI ( or NTSC monitor )

Perhaps the best feature of the raspi is its HDM interface ( compare with Arduino video output ), which provides a full 1920x1080 graphics resolution to any TV screen that has an HDMI input. In this example we used a cheap Vizio monitor that we had in our kitchen, and functions nicely for a video monitor. It might be interesting to to use this kind of system as a starting point for a TV back light project or similar living room light effects when you move it into your living room since you have the video interface right there.
acastellano-e2 months ago

Hi!!

First of all, this is an amazing project.

I'm playing with the code and i can't control more than 6 channels at the same time, when i set values for 6 channels if i set a value to 7th it doesn't changue. If i start to set values from the 7th chanel it works but only for the first 6 channels that i set, any idea?

flashular (author) 10 months ago
Hi djMaxM. The Velleman is a USB to DMX controller, and is powered by the USB connection from the Raspberry Pi. The output of the Velleman is a low voltage DMX signal that is fed to a DMX compatible light fixture that itself has mains power.
djMaxM10 months ago
I just bought one of these: http://www.wiedamark.com/icolorflexslx4-2.aspx (Color Kinetics iColor Flex) - other than the Pi and the Velleman, do I need anything else? Like a power source, or does that come from the Velleman (don't see how given the load)?
omeganut1 year ago
Hi flashular, this is a great project, nice work! Just wondering how to access/download the dmxd.c program? thanks
flashular (author)  omeganut1 year ago
Hi omeguanut. The source can be downloaded here:

http://www.engeldinger.com/services/latest-project/dmxwheel

Let me know if you run into any problems getting it working.
flashular (author)  flashular1 year ago
dmx.c:

// ==========================================================================
// Velleman K8062 DMX controller library for VM116/K8062
// ==========================================================================

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <errno.h>

#include "dmx.h"


int   * maxChanAddr;        // control register for # of channels to update
ubyte * exitAddr;           // control register to exit deamon
ubyte * chanData;           // 512 byte array of channel data

ubyte * shm;                // shared memory segment containing data & ctrl regs
int     shmid = -1;         // handel to shared memory segment


// ==========================================================================
// open the DMX connection
// ==========================================================================

int dmxOpen()
{

  // get the shared memory created by the deamon

    shmid = shmget ( 0x56444D58 , sizeof ( ubyte ) * 515 , 0666 );

    if ( shmid == -1 ) {
      printf ( "error[%d] - is dmxd running?\n" , errno );
      return ( errno );
    }

    // set up control and data registers

    shm = ( ubyte *) shmat ( shmid, NULL, 0 );

    maxChanAddr  = ( int * ) shm;
    exitAddr     = ( ubyte * ) maxChanAddr + 2;
    chanData     = ( ubyte * ) maxChanAddr + 3;
}

// ==========================================================================
// close the DMX connection
// ==========================================================================

void dmxClose()
{
  if ( shmid != -1 ) shmdt ( shm );
}

// ==========================================================================
// dmxSetMaxChannels -- set the maximum # of channels to send
// ==========================================================================

void dmxSetMaxChannels ( int maxChannels )
{
  *maxChanAddr = maxChannels;
}

// ==========================================================================
// dmxSetValue -- set the value for a DMX channel
// ==========================================================================

void dmxSetValue ( ubyte channel , ubyte data )
{
  chanData[channel] = data;
}
flashular (author)  flashular1 year ago
dmx.h:

// ==========================================================================
// Velleman K8062 DMX controller library for VM116/K8062
// ==========================================================================

typedef unsigned char ubyte;

int  dmxOpen           ();
void dmxClose          ();
void dmxSetMaxChannels ( int maxChannels );
void dmxSetValue       ( ubyte channel , ubyte value );
flashular (author)  flashular1 year ago
dmxd.c:

// ==========================================================================
// Velleman K8062 DMX controller Deamon for VM116/K8062
// ==========================================================================
//
// Modified from code from Denis Moreaux 2008 ( <vapula@endor.be> )
//
//
// This program should be run as a background process and continously updates
// a shared memory segment created by the application to control DMX channels
// sent through the DMX controller. The DMX channels can be accessed through
// a shared memory block that is allocated as:
//
// 0     = max # of channels to send  ( 0 - 512 )
// 1     = exit deamon control flag   ( 0 = run, 1 = exit )
// 2-514 = dmx channel data
//
// ==========================================================================
//
// Prerequisites ( USB lib ):
//   sudo apt-get install libusb-dev
//
//
// ==========================================================================
//

#include <usb.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
#include <errno.h>


// dmx data and control registers

typedef unsigned char ubyte;

int   * maxChanAddr;      // control register for # of channels to update
ubyte * exitAddr;         // control register to exit deamon
ubyte * chanData;         // 512 byte array of channel data


ubyte *shm;              // shared memory segment containing data & ctrl regs
int shmid;               // handel to shared memory segment


// constants and defs

#define ProgName "dmxd"  // name of this program
#define VendorID 0x10cf  // K8062 USB vendor ID
#define ProdID   0x8062  // K8062 USB product ID

#define UpdateInt 100000 // update interval ( microseconds )
#define DefMaxChans   16 // default number of maximum channels

// internal structures

struct usb_bus *bus;    // pointer to the USB bus
struct usb_device *dev; // pointer to the K8062 USB device
usb_dev_handle *udev;   // access handle to the K8062 device


// function delcarations

int  main();

int sendDMX();


int  initUSB();
int  writeUSB ( ubyte *data , int numBytes );
void exitUSB();

int  initSHM();
void exitSHM();

void timediff ( struct timeval *res, struct timeval *a, struct timeval *b );
void timeadd  ( struct timeval *res, struct timeval *a, struct timeval *b );


// ==========================================================================
// main -- dmx deamon
// ==========================================================================

int main() {

    struct timeval now,next,diff,delay;
    int success;

    printf ( "%s: starting dmx deamon\n" , ProgName );


    // intialize USB device

    success = initUSB();

    if ( !success ) {
      printf ( "%s: error initializing USB interface\n" , ProgName );
      return ( -1 );
    }

    // initialize shared memory segment

    success = initSHM();

    if ( !success  ) {
      printf ( "%s: error initializing shared memory\n" , ProgName );
      return ( -2 );
    }


    // start timer

    delay.tv_sec = 0;
    delay.tv_usec= UpdateInt;

    gettimeofday ( &next , NULL );



    // loop until commanded to shutdown

    while( !*exitAddr ) {


      // send DMX data

      success = sendDMX();

      if ( !success ) {

printf  ( "%s: DMX send error\n" , ProgName );
*exitAddr++;
      }
     

      // wait for update interval

      timeadd ( &next , &next , &delay );
      gettimeofday ( &now , NULL );
      timediff ( &diff, &next , &now );

      while (diff.tv_sec || diff.tv_usec) {

select ( 0, NULL, NULL, NULL, &diff );
gettimeofday ( &now, NULL );
timediff ( &diff, &next, &now );
      };     

    }

    printf ( "%s: dmx deamon is shutting down\n" , ProgName );


    // on shutdown reset all DMX channels

    memset ( chanData , 0, 512 * sizeof (ubyte) );

    sendDMX();


    // exit the system

    exitUSB();
    exitSHM();

    return ( 0 );
}


// ==========================================================================
// sendDMX -- send current DMX data
// ==========================================================================

int sendDMX ()
{
  ubyte data[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
  int numChans = *maxChanAddr;
 

#if 1

  // find out how many consecutive zeroes are in the data - the start
  // packet can indicate this to avoid sending a bunch of leading
  // zeroes

  int curChanIdx = 0;
 
  for ( curChanIdx = 0; curChanIdx < numChans; curChanIdx++ ) {
    if ( chanData[ curChanIdx ] != 0 ) break;
  }

  // build starting packet. this packet specifies how many channels have
  // zero data from the start and then contains the next 6 channels of
  // data

  data[0] = 4;                          // start packet header (4)
  data[1] = curChanIdx;                 // number of zeroes ( not sent )
 
  data[2] = chanData [ curChanIdx++ ];  // first ( non-zero ) chan data
  data[3] = chanData [ curChanIdx++ ];  // next chan data
  data[4] = chanData [ curChanIdx++ ];  // next chan data
  data[5] = chanData [ curChanIdx++ ];  // next chan data
  data[6] = chanData [ curChanIdx++ ];  // next chan data
  data[7] = chanData [ curChanIdx++ ];  // next chan data
 
  int success = writeUSB ( data , 8 );

  if ( !success ) {
    printf ( "%s: error sending DMX start packet\n" , ProgName );
    return ( 0 );
  }

  if ( curChanIdx >= numChans ) return ( 1 );

 

  // after the first packet additional packets are sent that contain seven
  // channels each up to 512.

  while ( curChanIdx < ( numChans - 7 ) ) {
     
    data[0] = 2;                          // start packet header (2)
    data[1] = chanData [ curChanIdx++ ];  // next chan data
    data[2] = chanData [ curChanIdx++ ];  // next chan data
    data[3] = chanData [ curChanIdx++ ];  // next chan data
    data[4] = chanData [ curChanIdx++ ];  // next chan data
    data[5] = chanData [ curChanIdx++ ];  // next chan data
    data[6] = chanData [ curChanIdx++ ];  // next chan data

  }

  success = writeUSB ( data , 8 );

  if ( !success ) {
    printf ( "%s: error sending DMX bulk packet\n" , ProgName );
    return ( 0 );
  }

  if ( curChanIdx >= numChans ) return ( 0 );

#else

  data[0] = 5;   // packet header for single channeld data

  printf ( "sending %d channels\n" , numChans );
 

  for ( int chIdx = 0; chIdx < numChans; chIdx++ )
    {
      data[1] = chanData [ chIdx ];

      int success = writeUSB ( data , 8 );

      if ( !success ) {
printf ( "%s: error sending DMX data packet\n" , ProgName );
return ( 0 );
      }
    }

#endif 

 
  return ( 1 );

}

// ==========================================================================
// initUSB -- intialize the USB interface for the device
// ==========================================================================

int initUSB()
{
  int success;
 

  // open the usb library

  usb_init();


  // find the usb device for DMX controller

  usb_find_busses();
  usb_find_devices();

  usb_device_descriptor *descr = 0x0;

  for ( bus = usb_busses; bus; bus = bus -> next ) {

    for ( dev = bus->devices; dev; dev = dev -> next ) {

      printf ( "%s: checking device [%s]\n" , ProgName , dev -> filename );

      descr = & dev->descriptor;

      if (      ( descr -> idVendor == VendorID )
             && ( descr -> idProduct == ProdID  ) ) break;
    }
  }

  if ( !dev ) {
printf ( "%s: DMX device not found on USB\n" , ProgName );     
return ( 0 );
  }
 

  // open the device

  printf ( "%s: opening device [%s] ... " , ProgName , dev -> filename );

  udev = usb_open ( dev );

  if ( udev == 0x0 ) {
    printf ( "%s: error opening device\n" , ProgName );
    return ( 0 );
  }
  else {
     printf ( "ok\n" );
  }


  // claim the interface


#if     defined(LIBUSB_HAS_GET_DRIVER_NP) \
     && defined(LIBUSB_HAS_DETACH_KERNEL_DRIVER_NP)

  usb_detach_kernel_driver_np( udev, 0);

#endif


  // set configuration


  usb_set_debug(4);

  success = usb_set_configuration ( udev, 1 );

  if ( success != 0 ) {
    printf ( "%s: configuration error [%d]\n" , ProgName , success );
    return ( 0 );
   
  }


  // claim the interface
     
  success = usb_claim_interface ( udev, 0 );

  if ( success != 0 ) {
     
    printf ( "%s: error claiming interface [%d]\n" , success );
    return ( 0 );
  }

  return ( 1 );
}

// ==========================================================================
// writeUSB -- write a command to the USB interface
// ==========================================================================

int writeUSB ( ubyte *data , int numBytes )
{
  int nSent;

  //  printf ( "%s: writing [%d] bytes " , ProgName , numBytes );
  //  for ( int b = 0; b < numBytes; b++ ) printf ( "[%d]" , data[b] );
  //  printf ( "\n" );

  // write the data

  nSent = usb_interrupt_write ( udev ,
                                1,
                                (char *) data,
                                numBytes,
                                200 );

  if ( nSent != numBytes ) {
     
    printf ( "%s: error writing [%d] bytes [%d]\n" , numBytes , nSent );
    return ( 0 );
  }

  return ( 1 );
 
}


// ==========================================================================
// exitUSB -- terminate USB connection
// ==========================================================================

void exitUSB()
{
    usb_close(udev);
}

// ==========================================================================
// initSHM -- initialize shared memory segment
// ==========================================================================

int initSHM()
{

  printf ( "%s: creating shared memory segment ... " , ProgName );


  // create the shared memory segment

  shmid = shmget ( 0x56444D58 , sizeof ( ubyte ) * 515 , IPC_CREAT | 0666 );

  if ( shmid == -1 ) {
    printf ( "error creating shared memory segment [%d]\n" , errno );
    return ( 0 );
  }
  else
    printf ( "ok\n" );


  // attach to segment and initialize


  printf ( "%s: intitalizing segment [0x%x] ... " , ProgName , shmid );

  shm = ( ubyte * ) shmat ( shmid , NULL , 0 );

  if ( shm == 0x0 ) {
    printf ( "error connecting to segment [%d]\n" , errno );
    return ( 0 );
  }
  else
    printf ( "ok\n" );


  memset ( shm , 0 , sizeof ( ubyte ) * 515 );


  // set up command & data registers

  maxChanAddr  = ( int * ) shm;
  *maxChanAddr = DefMaxChans;

  exitAddr     = ( ubyte * ) maxChanAddr + 2;
  chanData     = ( ubyte * ) maxChanAddr + 3;

  return ( 1 );
}

// ==========================================================================
// exitSHM -- terminate shared memory segment
// ==========================================================================

void exitSHM()
{
    shmdt(shm);
    shmctl(shmid,IPC_RMID,NULL);
}

// ==========================================================================
// timediff | timeadd -- timing functions
// ==========================================================================

void timediff ( struct timeval *res, struct timeval *a, struct timeval *b)
{
    long sec,usec;
    sec=a->tv_sec-b->tv_sec;
    usec=a->tv_usec-b->tv_usec;

    while (usec<0) {
        usec+=1000000;
        sec--;
    }
    if (sec<0) {
res->tv_sec=0;
res->tv_usec=0;
    } else {
res->tv_sec=sec;
res->tv_usec=usec;
    }
}

void timeadd(struct timeval *res, struct timeval *a, struct timeval *b)
{
    res->tv_usec=a->tv_usec+b->tv_usec;
    res->tv_sec=a->tv_sec+b->tv_sec;
    while (res->tv_usec >= 1000000) {
res->tv_usec-=1000000;
res->tv_sec++;
    }
}
flashular (author)  flashular1 year ago
Makefile:

CC=g++
DEAMONOBJS=dmxd.o
LIBOJBS=dmx.o
OBJS=$(LIBOBJS) $(DEAMONOBJS)
DEAMONBIN=dmxd.bin
LIB=libdmx.a
CFLAGS+=
LDFLAGS+=-lusb -lm
INCLUDES+=-I./

all: $(LIB) $(DEAMONBIN) $(TESTBIN)

%.o: %.c
@rm -f $@
$(CC) $(CFLAGS) $(INCLUDES) -g -c $< -o $@ -Wno-deprecated-declarations

dmxd.bin: $(DEAMONOBJS)
$(CC) -o $@ -Wl,--whole-archive $(DEAMONOBJS) $(LDFLAGS) -Wl,--no-whole-archive -rdynamic
mv dmxd.bin ../deamon


%.a: $(LIBOJBS)
$(AR) r $@ $^
mv $(LIB) ../lib
cp dmx.h ../include

clean:
for i in $(OBJS); do (if test -e "$$i"; then ( rm $$i ); fi ); done
@rm -f dmxd.bin $(LIB)
omeganut1 year ago
Hey, thanks for the swift reply. I've actually got the project running great as is. I have it set up on my pi controlling a light and i have set up a vnc to control the pi remotely via my work mac which has been real fun. I'm just having trouble opening up the dmxd.c that you mention checking out at the end of your instructions and wondered if you had any tips/issue advice. My end goal is to be able to monitor twitter for a specific hashtag which will be interfaced with the vcn to change the LED lights colour via twitter!
flashular (author) 1 year ago
I got it on a blowout sale at Musicians Friend. I see you get them normally priced for about $120.
may I ask where you got the color splash so cheap?
nerd74731 year ago
awesome!!!
SoundPon31 year ago
As a lighting/sound guy, this looks like a neat little project. I already use that interface and I just got a pi so I'll look into this
as a lighting/ sound guy I should probably invest in something DMX controlled for when my PI comes in
Would this work with the Enttec USB-to-DMX adapter too?
imarvin1 year ago
This is great, we are in the lighting business and will probably rig something like this up.
What would make it even more useful would be able to use a smartphone to be able to control it. My preference is for Android but I believe there is another popular mobile OS out there too.
Ayy imarvin1 year ago
Combining this project with a web interface would not be too tough, check out (http://www.instructables.com/id/Raspberry-Pi-GPIO-home-automation/) for an idea how to do it. The Raspberry Pi is perfect for this type of application - and instead of writing an application for Android and iOS, you could just use the web browser.
rlerm1 year ago
Sorry, can't reply to posts because of bug on the website...

Certainly would be interesting, but the interface side would take a great deal of work and thinking to be useful (perhaps something using a touchscreen could be done, not the cheapest but it would be less complicated in hardware terms). I had talked about a much simpler converter, but of course that does not need all the power of a Pi.

Sorry, I could not help with this project (don't own a Pi), but it really is surprising that this has not been done before.
rlerm1 year ago
DMX is a really, really simple protocol, provided you can generate the BREAK signal it requires. Other than that, it is just a one-way, 250kbps, 8N2 serial interface. The additional circuit would be a 3.3V RS485 transceiver. It could be used as a Art-net to DMX converter (or other DMX-over-network protocol), allowing any other computer to use it from the network.
flashular (author)  rlerm1 year ago
Yeah, I'm surprised that nobody has offered a project that uses an RS485 circuit. I would be interested in helping develop the software side if someone has a schematic that could eventually be developed into a shield for the pi. I see the opportunity for a full featured lighting control rig with HDMI user interface that could fit into the palm of your hand ( and be under $60! )
I got all excited there looking for a cheap way to make a small self contained controller, think some of the things from this would work but been avoiding USB to DMX controllers because for some reason £100 seems to be awfully expensive...
flashular (author)  killerjackalope1 year ago
I agree. Even the $60 for the kit is pretty expensive, but when I considered the complexity of using the GPIO and the development time and cost of additional circuitry this was the path of least resistance. The other thing that swayed my decision was that the controller can also be used by any other PC and in my case allows me to use it for other purposes.

Thanks for your comment.
Hmm maybe a better way to look at it...