DMX Dimmer
DMX Dimmer
DMX Dimmer
Sunday, January 6, 2013
The concept is to design and create a portable dimmer.
Requirements:
•DMX512 Controllable
•4 Channels
•Portable
•Easy to use
A few hurdles and lessons learned along the way
•If you are not a register expert, stick with a ATMEGA328P
•Wrong optocouplers. You do not want Zero Cross
•High channels were unstable. Switching from 16MHz to 20MHz solved this issue
•Unable to have a DMX status light because the interrupt call had to be very fast
•DC power has to be extremely stable, any ripple will cause the DMX signal to become very noisy
Please send any comments to DanFredell@Gmail.com
I proposed this idea to my professor at WSU because I wanted to combine my passions for theatre and computers. This project acted a little like my senior project in the theatre department. If you have any comments or question please send me an email, I would love to help.
Future development could include more channels, 5 pin DMX connector, DMX passthrough, 8 dip switches to change the channel, printed circuit board.
Hardware
Hardware Used: (Most of it was ordered from Tayda Electronics)
•ATMEGA328, Micro-controller
•MOC3020, TRIAC Optocoupler. Not ZeroCross.
•MAX458, DMX Receiver
•ISP814, AC Optocoupler
•7805, 5v Regulator
•BTA24-600, 600V 25A TRIAC
•20MHz Crystal
•9V Power supply
Circuit Design:
Finished product:
My first and biggest mistake was thinking that because the ZeroCross optocouplers were better because they cost more.
Getting a stable DC voltage into the Arduino and MAX485 is very important. If there is any ripple in the DC it will cause the Arduino to misread some of the DMX data and the output will flicker.
In a future production of this project I would get a PCB printed, instead of putting it on a prototype board.
The TRIAC design came from MRedmon, thank you.
Finished product in a case.
The case got a theatre c-clamp attached to the top for hanging purposes. Status lights for every input and output to help diagnose if there is ever a problem. A label maker was used to explain the different ports on the device. The numbers next to each plug represents the DMX channel number.
Software
Summery:When the Arduino first boots the setup() method is called. In there I set up a few of the variables and output locations to be used later. zeroCrossInterupt() is called/ ran every time the AC crosses from positive to negative voltage. It will set the zeroCross flag for every channel and start the timer. The loop() method is called continually forever. To turn on the output, the TRIAC only has to be triggered for 10 microseconds. If it is time to trigger he TRIAC and zeroCross has happened the output will turn on until the end of the AC phase.
There were a few examples online that I used to get this project started. The main thing that I could not find was having multiple TRIAC outputs. Others used the delay function to PWM the output, but that would not work in my case because the ATMEGA has to be listening to DMX all the time. I solved this by pulsing the TRIAC at so many ms after zero-cross. By pulsing the TRIAC closer to zero-cross the more of the sin wave is output.
Here is what the half 120VAC sin wave looks like on an oscilloscope.
The ISP814 is connected to interrupt 1. So when it receives signal that the AC transitions from positive to negative or vise versa it sets the zeroCross for each channel to true and starts the stopwatch.
In the loop() method, it checks every channel if zeroCross is true and the time for it to activate has passed it will pulse the TRIAC for 10 microseconds. This is enough to turn the TRIAC on. Once a TRIAC is turned on it will stay on until zeroCross. The light would flicker when the DMX was around 3% so I added the truncating in there to prevent it. This was caused the Arduino being too slow, and the pulse would some times trigger the next sin wave instead of the last 4% of the wave.
Also in the loop() I set the PWM value of the status LEDs. These LEDs can use the internal PWM generated by the Arduino because we do not have to worry about the zeroCross of AC. Once the PWM is set the Arduino will continue at that brightness until told other wise.
As noted in the top comments in order to use a DMX interrupt on pin 2 and run at 20MHz you will have to edit some of the Arduino application files. In HardwareSerial.cpp a chunk of code must be deleted, this allows us to write our own interrupt call. This ISR method is at the bottom of the code to handle the DMX interrupt. If you are going to use an Arduino as an ISP programmer, be sure to revert your changes to HardwareSerial.cpp otherwise the ATMEGA328 on the bread board will be unreachable. The second change is an easer one. The boards.txt file has to be changed to the new 20MHz clock speed.
//**************************************************************//
// Name : DMX 4 ch Dimmer //
// Author : Dan Fredell //
// Date : 16 Apr, 2013 //
// Version : 1.1 //
// Website : //
//**********************************z ******************************
/*File modifacations:
* hardware/arduino/boards.txt uno.build.f_cpu=20000000L
* It tells the software that the hardware is running at 20MHz
*
* In cores/arduino/HardwareSerial.cpp remove 42 lines,
* this allows us to make our own intrupt for DMX signal in
* From
* #if !defined(USART0_RX_vect) && defined(USART1_RX_vect)
* to
* #if defined(USART1_RX_vect)
*/
/*
* V1.1 - fixed issue of flashing output caused by AC interrupting the DMX data
*/
//double dim = 0; // Dimming level (0-128) 0 = on, 128 = 0ff
int AC_pin[4] = {
6,7,8,12}; // Output to Opto Triac
int LED_pin[4] = {
5,9,10,11}; // Output to Opto Triac
int brightness[4];
int LEDBrightness[4];
boolean zeroCross[]={
false,false,false,false};
int PrevLEDValue[]={
0,0,0,0};
unsigned long startZeroTime=0;
unsigned long timeElapsed=0;//Time until end of cycle
volatile uint8_t DmxRxField[4]; //array of DMX vals (raw) DMX Max:255 Min:0
volatile uint16_t DmxAddress; //start address
enum {
IDLE, BREAK, STARTB, STARTADR}; //DMX states
volatile uint8_t gDmxState;
void setup() {
delay(500);//Wait for the system to charge
//Setup DMX
gDmxState= IDLE; // initial state
DmxAddress = 1; // The desired DMX Start Adress. Starts at 1
Serial.begin(250000);//Read DMX signal via RX
//Setup Outputs
attachInterrupt(1,zeroCrossInterupt,FALLING);
for(int ch=0;ch<4;ch++){
pinMode(AC_pin[ch],OUTPUT);
pinMode(LED_pin[ch],OUTPUT);
digitalWrite(AC_pin[ch],HIGH);
analogWrite(LED_pin[ch],255);
delay(1000);
digitalWrite(AC_pin[ch],LOW);
analogWrite(LED_pin[ch],0);
}
}
void zeroCrossInterupt(){
startZeroTime=micros();
gDmxState= IDLE;
for(int ch=0;ch<4;ch++){
brightness[ch]=map(DmxRxField[ch],0,265,8000,0);
zeroCross[ch]=true;
}
}
void loop() {
timeElapsed=micros()-startZeroTime;
/**
* This is the main loop. I use timeElapsed instead of delay to allow for more then 1 output.
* The +50 is to elimnate random noisy high and low values
* DmxRxField[ch] > 10 because at DMX val8 it flickers
*/
for(int ch=0;ch<4;ch++){
if(zeroCross[ch] && timeElapsed > brightness[ch] && timeElapsed < brightness[ch]+50 && DmxRxField[ch]>10){
digitalWrite(AC_pin[ch], HIGH); // fire the Triac
delayMicroseconds(10); //pause briefly to ensure the triac turned on
digitalWrite(AC_pin[ch], LOW); // turn off the Triac gate (triac will not turn off until next zero cross)
zeroCross[ch]=false;
}
if(PrevLEDValue[ch]!=DmxRxField[ch]){//Change the LED value only if it changed
analogWrite(LED_pin[ch],DmxRxField[ch]);
PrevLEDValue[ch]=DmxRxField[ch];
}
}
}
//ISR method from http://playground.arduino.cc/DMX/Ardmx
ISR(USART_RX_vect) {
static uint16_t DmxCount;
uint8_t USARTstate= UCSR0A; //get state before data!
uint8_t DmxByte = UDR0; //get data
uint8_t DmxState = gDmxState; //just load once from SRAM to increase speed
if (DmxState == STARTADR)
{
DmxRxField[DmxCount++]= DmxByte; //get channel
if (DmxCount >= sizeof(DmxRxField)) //all ch received?
{
gDmxState= IDLE; //wait for next break
}
} else if (USARTstate &(1<<FE0)) //check for break
{
DmxCount = DmxAddress; //reset channel counter (count channels before start address)
gDmxState= BREAK;
}
else if (DmxState == BREAK)
{
if (DmxByte == 0) gDmxState= STARTB; //normal start code detected
else gDmxState= IDLE;
}
else if (DmxState == STARTB)
{
if (--DmxCount == 0) //start address reached?
{
DmxCount= 1; //set up counter for required channels
DmxRxField[0]= DmxByte; //get 1st DMX channel of device
gDmxState= STARTADR;
}
}
}
This page was created to document my design and development of a 4 Channel DMX Dimmer. It will be donated to Winona State University to be used in the Theatre Department. Two good locations for this could be in a set piece or in the lobby.