DMX Projects

Sources

Block diagram for two modes of firmware

DMX reception / translation based on #define in code. Both options work on same hardware

General

In this post I will try to describe two projects I made that use the DMX protocol. Both projects were built for Atak, a pop music stage in the Netherlands. Disclaimer for both projects: these were built in my own spare time and on my own budget; quality was not the end objective, although I’ve tried to make a decent design. Possible improvements are described later on….

Description

What was the purpose of these projects?

  • Learn to interface DMX
  • Make an interface that can transmit and receive DMX with one microcontroller
  • Create a DMX interface for a Clay-Paky AstroDisco which had already been fitted with solid state relays
  • Create a possibility to use the RGB LED-panels in the wall of the venue as a color wash of the same type that is already in use, i.e. creating a ‘virtual’ Robe Washlight that could be patched after the existing washes. Details: see later on

Environment

I’m using Linux Mint on my laptop, for hardware design I used Eagle, for firmware design I used AVR-GCC, avr-libc and avrdude as programmer. I’m a big fan of Code::Blocks as IDE. PCB’s were made with laser toner transfer with hot iron.

Option one: DMX Receiver

The simplest version of this firmware is just receiving the DMX packages. This design was used to give DMX control to a ClayPaky AstroDisco. Originally, these lamps were controlled by swithchin mains to one of its 3 motors and /or the lamp. The AstroDisco in Atak was already hacked and had 4 solid state relays fitted to control its functions using a 9 volt battery and an audio multicable, but still DMX was a very often requested feature.
The circuit should be able to be set to different DMX addresses, and control 4 IO’s. The relays’ input threshold lies around 1VDC, so I could use the microcontrollers output directly.

Functional description

The address set on dipswitches is read out (bit 8 directly from a pin, bit 7..0 with an SPI shift register). This channel controls the first output, the second, third and fourth output are controlled by subsequent channels. The output is switched at 50%, so if a channel fader on a connected lighting console is below 50% the output is off, otherwise it is on.

Option two: DMX transceiver, CMYK converter

This is the functionality that made me want to make this…. In Atak a series of LED strips was mounted behind some panels to make very nice architectural lighting. These panels were DMX controlled, and the DMX receiver was set to ‘channel 1’ for all panels, and was made unreachable. Two problems arose:

  • The LED strips had the following addressing: RED=1, GREEN=2, BLUE=3. However, all other lighting was using CMYK (color washes and spots). To make the colors on the wall match the colors used on stage the light operator would have to match a couple of preset colors and save these in memory; time consuming and error prone. Live adjustment of colors on stage and on the wall was impossible.
  • It is common to start with dimmers on channel 1. No big issue, but slightly unpractical.

Thus, a plan was born:

Functional Description

Make something that receives DMX data, and mimics the DMX addresses of one of the color washes. The device translates the CMYK values from the incoming DMX messages to RGB values, and then retransmits these starting from DMX address ‘1’ from the DMX output line.

Hardware Design

Hardware Description

Darn! I still haven’t found the original hardware drawings I made. Configuration of the electronics is as follows:

  • 16 MHz Xtal
  • ATTINY2313
  • UART is used for DMX
  • For power supply a transformer is used with two separate output windings, one is used as supply for the receiver electronics, the other for the microcontroller and transmitter
  • An optocoupler (4N37?) is used to shift data from reception site to microcontroller
  • Address [7..0] is read out over SPI, uses same pins as programmer
  • Four pins drive the solid state relays when programmed as ‘Receiver’.

The design is setup around an Atmel AT90S2313. This controller was chosen because of its low price and my experience with Atmel controllers. I used the UART to interface with DMX, the receiving side is isolated electrically, a link is provided to daisy chain the DMX connection.

Firmware Design

What makes this project different from other DMX projects is that incoming and outgoing DMX messages are running asynchronously and interrupt based. I started implementing the CMYK-to-RGB converter, as this would require the separated functionality for reception / transmission of DMX data. To reduce load on the Atmel controller, all messages are received, but only those that are of interest for the required functionality are stored in RAM, all other DMX bytes are lost.
A 1ms timer is used as source for tasks that should be run at regular intervals.

 //dmx.h 
#define DMXRXLENGTH 12  //size of rxbuffer from 1st valid data 
#define DMXTXOFFSET 0   //1st channel to tx data 
#define DMXTXLENGTH 12  //size of txbuffer from 1st valid address 
#define DMXBREAKMS 180  //number of milliseconds to count for break 

The first reception address is set by dipswitches, and read out in the main while loop

 //main.c 
       if((mstimer-spitimer >= 0)){
            SpiUpdate(&spidata);                            //Run SPI function; shift register update, retrieve data when new byte has arrived
            spitimer = mstimer + 1;                         //SPI frequency
            DmxSetRxOffset(spidata|(((~PINB)&amp;amp;amp;amp;amp;amp;0x08)<<5));    //Offset is data from shift register + from last bit from PINB3
        }

By means of a #define the user can choose to let the board act as a receiver (for AstroDisco) or as transceiver (CMYK to RGB).

        #if defined(TRANSCEIVER)
        if((dmxrxcomplete &amp;amp;amp;amp;amp;&amp;amp;amp;amp;amp; dmxtxcomplete)){// || (mstimer > secondtimer)){
            PORTB ^= 0x02; //toggle LED, only when valid DMX range is received
            //secondtimer = mstimer + 1000;
            HeadToWall(dmxrxbuffer,dmxtxbuffer);
            if(dmxtxcomplete &amp;amp;amp;amp;amp;&amp;amp;amp;amp;amp; (dmxtxstate == DMXINIT))
                DmxSend();//starts sending data
        }
        if(((mstimer-dmxdelay)>=0) &amp;amp;amp;amp;amp;&amp;amp;amp;amp;amp; dmxtxdelayactive){
            DmxTxMachine();
        }
        #endif
        #if defined(RECEIVER)
        if((dmxrxcomplete)){
            PORTB ^= 0x02; //toggle LED, only when valid DMX range is received
            SetOutputs(dmxrxbuffer);
            dmxrxcomplete = 0;
        }
        #endif

The dmxcomplete and dmxtxcomplete flags are set from the interrupt routines, where the start and length of the message is used to determine if a message was completely received / transmitted or not.

//dmx.c
ISR(USART_RX_vect , ISR_BLOCK){
    static uint16_t rxcounter; //16-bit for 512 channels
    volatile uint8_t dummy; //used for catching dummy bytes
    if (UCSRA &amp;amp; (1<<FE)){ //stop bit is 0; break condition?
        rxcounter = 0;
        // dummy read to clear frame error
        while ( UCSRA &amp;amp; (1<<RXC) ) dummy = UDR;
        dmxrxcomplete = 0;
    }
    else{
        if((rxcounter >= dmxrxoffset) &amp;amp;&amp;amp; (rxcounter <= (dmxrxoffset + DMXRXLENGTH))){
            dmxrxbuffer[rxcounter - dmxrxoffset] = UDR;
            if(rxcounter == ((DMXRXLENGTH + dmxrxoffset)-1)){ //complete message received
                dmxrxcomplete = 1; //start processing in main()
            }
        }
        else{
            dummy = UDR; //catch byte
        }
        rxcounter++;
    }
}

ISR(USART_TX_vect , ISR_BLOCK){
    static uint16_t txcounter;
    if(dmxtxstate == DMXTXSC){
        dmxtxstate = DMXTX;
        txcounter = 0;
    }
    if((txcounter >= DMXTXOFFSET) &amp;amp;&amp;amp; (txcounter < (DMXTXOFFSET + DMXTXLENGTH))){ //if within useful information region
        UDR = dmxtxbuffer[txcounter - DMXTXOFFSET]; //send useful info
        txcounter++;
    }
    else{
        if(txcounter >= (DMXTXOFFSET + DMXTXLENGTH)){ //if last byte*/
            UCSRB &amp;amp;= ~((1<<TXCIE)|(1<<TXEN)); //disable new interrupt
            dmxtxstate = DMXINIT;
            dmxtxcomplete = 1; // tell main application to send new info if available
            txcounter = 0; //reset counter just in case
            PORTD |= (1<<PD1);
        }
        else {
            UDR = 0x00; //send byte
            txcounter++;
        }
    }
}

Again, sending and receiving of messages is implemented independently, the only link is made in the main loop in main.c

Please take a look around in the sources before any of this is used, and update me if anything interesting is found 😉

Possible Improvements

  • Retrieve hardware schematics!
  • For transmission the ‘Mark after break’ is now 1millisecond as this is the minimum time length with the 1ms timer. The DMX specification specifies several microseconds as minimum delay, thus timing could be improved.

One thought on “DMX Projects

Leave a Reply

Your email address will not be published. Required fields are marked *