Sources
- Firmware: source code
- Reference: DMX description of Theater Technisch Lab
- Reference: Color conversion
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(&amp;amp;amp;amp;amp;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;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; 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; (dmxtxstate == DMXINIT)) DmxSend();//starts sending data } if(((mstimer-dmxdelay)>=0) &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; (1<<FE)){ //stop bit is 0; break condition? rxcounter = 0; // dummy read to clear frame error while ( UCSRA &amp; (1<<RXC) ) dummy = UDR; dmxrxcomplete = 0; } else{ if((rxcounter >= dmxrxoffset) &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; (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;= ~((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.
[…] DMX Projects […]