Introduction: Inexpensive DMX Tester
Hello All,
I work part-time (more of a hobby) in the lighting industry and use DMX since it is the industry standard for communicating or controlling devices (lighting fixtures, controllers, consoles, etc..) I have seen commercial DMX testers on the market but I wanted to create my own.
I have been working on an idea to create a low cost (<$50), Arduino based DMX tester.
The tester would provide the following functionality:
- Simple input protocol for entering commands using 4 X 4 - 16 button keypad matrix.
- Support LCD display - 4 line x 20 character
- Output DMX for single channel or a range of channels at a set intensity level.
The Hardware:
I started with the following:
- Arduino UNO board
- A 4 x 4 (16) button key pad (button matrix)
- A 4 x 20 Character LCD display w I2C (Serial) Interface
- low cost DMX / RDM Shield purchased from EBay (model: CTC-DRA-10-1, low cost, non-isolated)
The Input Commands:
I wanted to use a 4 x 4 (16) button key pad to input all the commands with a simple / easy to remember protocol (format).
Here is the basic command format:
Channel@Intensity
Start Channel-End Channel@ Intensity
Here is the actual input protocol using only a 4 x 4 (16) button key pad:
XXX@III# (Single Channel at a Specified Intensity)
XXX-XXX@III# (Range of Channels at a Specified Intensity)
*@*# (All Channels at Full Intensity)
XXX@*# (Single Channel at Full Intensity)
XXX-*@III# (Start Channel to Max Channel at a Specified Intensity)
XXX-*@*# (Start Channel to Max Channel at Full Intensity)
Key Mappings:
A = @ (at sign)
B = Bump (not implemented)
D = - (dash)
C = Clear
# = Execute
* = Wildcard value: 512 for channel and 256 for Full intensity
XXX = 1 to 512 Channel Number
III = 1 to 256 Intensity Level
Code Development / Testing:
I developed / tested the code in several stages:
- Keypad input - 4 x 4 (16) button key pad (or switch array)
- LCD display - 4 x 20 Character LCD w I2C interface
- Verify/test the input commands (protocol) were working correctly
- Add DMX master (sending) code
Step 1: The Keypad
The Keypad
Hardware:
I had 3 types of key pads that I played with (switches on a PC board, membrane switch, soft-touch)
See the pictures of keypads.
Software:
I started with the Keypad library for easy matrix style keypad mapping. See http://playground.arduino.cc/code/Keypad for more information.
I had to play around with the Row and Column mapping to get my Key Pad switch matrix to work. The pin-out in the documentation wasn't correct on any of the keypads. So once I determined the correct pin-out, the code worked perfectly.
Here is code snippet showing how to use the Keypad library:
(This is for the 16 switches on PC board)
#include <Keypad.h> const byte ROWS = 4; // define four rows const byte COLS = 4; // define four columns char keys [ROWS] [COLS] = { {'1', '2', '3','@'}, {'4', '5', '6','B'}, {'7', '8', '9','C'}, {'*', '0', '#','-'} }; // Pin R/C // 8 C4 // 7 C3 // 6 C2 // 5 C1 // 4 R1 // 3 R2 // 2 R3 // 1 R4 // Connect 4 * 4 buttons faithfully port, the corresponding digital IO ports panel byte colPins [COLS] = {10,11,12,13}; // Call the function library function Keypad Keypad keypad = Keypad (makeKeymap (keys), rowPins, colPins, ROWS, COLS); void loop () { char key = keypad.getKey (); if (key != NO_KEY) { // Clear if(key == 'C') { state = CLEAR; } }
Step 2: LCD Display
Hardware:
I used a standard 4x20 character display with I2C interface which can be purchased from Ebay for about $10.00. See picture of 4x20 character display.
Software:
I started with a I2C LCD display library. See LiquidCrystal_I2C Library for more information.
I really didn't have any problems getting the display to work. The 2x16 character display included in my Arduino kit didn't work so I ordered a 4x20 character display and it worked the first time I tried it.
Here is a code snippet showing how to use the LiquidCrystal_I2C library:
#include <LiquidCrystal_I2C.h> LiquidCrystal_I2C lcd(0x3F,20,4); void setup () { lcd.init(); lcd.backlight(); lcd.setCursor(0,0); lcd.print("DMX Tester "); lcd.print(VERSION); lcd.setCursor(0,1); lcd.print("Enter Cmd:"); }
Step 3: The Input Protocol (Commands)
At first I was going to use a state machine logic to handle the input
from the keypad but I decided to try to just code it with normal logic. After writing the initial version, I spent about 4 or 5 hours debugging the code. I soon realized I should have created a state diagram and used state driven logic. to parse the input properly.
So I deleted most of the code and wrote a test plan and state diagram to match the input protocol.
Here is the test plan with all the input protocols defined: DMX Tester Test Plan
and here is the state diagram: DMX Tester State Diagram
Once I have the state diagram completed, I coded the input part of the projects in about 1.5 hours while I was on a sitting on plane. After I tested and was satisfied the input logic was working I attached the DMX shield and added the DMX library.
Step 4: Introduction to State Machine Logic
If you aren't familiar with or haven't used state machine logic in programming, it is the easiest way to to break complex problems into manageable states and state transitions. One of the easiest ways to implement a state machine is to use a switch statement.
Example of a state machine using a switch statement:
switch(state) {case INITIAL: // process INITIAL state break; case STATE1: // process STATE1 state break; case CLEAR: clearAll(); state = INITIAL; break; default: break; }
Depending on the state, there are only a few valid keys. Lets take a look at the first state called INITIAL. As you can see from the state diagram the INITIAL state can only have the following keys: 0-9 and '*' (asterisk), else it is a format error.
StateKeysNext StateCommentINITIAL 0-9 START1 X
* WILDCARD1 *@
Anything Else INITIAL Format Error
Here is a code snippet showing how I implemented the INITIAL state:
char key = keypad.getKey ();<br> if (key != NO_KEY) { switch(state) { case INITIAL: // X if(validKeys0to9(key)) { pos = displayKey(key, pos); storeKey(key); state = START1; } else if (validWildCard(key)) { // *@ pos = displayKey(key, pos); state = WILDCARD1; } else { invalidFormat(); clearAll(); } break;
As you can see I created some helper functions to make the code easy to read.
// Validate key *<br>// return true if valid, else false int validWildCard(int key) { int valid=0; switch(key) { case '*': valid=1; break; } return valid; } <br>// Validate key 0-9 // return true if valid, else false int validKeys0to9(int key) { int valid=0; switch(key) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': valid=1; break; } return valid; }
Step 5: The DMX Shield
Hardware:
I used a DMX Shield - Model: CTC-DRA-10-1, low cost, non-isolated which can be purchased from Ebay for about $15.00
See picture for DMX shield.
Software:
I used the Conceptinetics DMX Library to handle the DMX master (sending). See Conceptinetics DMX Library and this DMX Shield Blog more information. The library is very simple to use. Initialize and use 2 different commands to send either a single channel or a range of channels.
Here is a code snippet showing how to initial and use the DMX Library:
#define DMX_MASTER_CHANNELS 512<br> // Pin number to change read or write mode on the shield #define RXEN_PIN 2<br> // Configure a DMX master controller, the master controller // will use the RXEN_PIN to control its write operation // on the bus DMX_Master dmx_master ( DMX_MASTER_CHANNELS, RXEN_PIN ); void setup () { dmx_master.enable (); } void sendDMX(int start, int end, unsigned char intensity) { if(start == end) { dmx_master.setChannelValue(start, intensity); } else { dmx_master.setChannelRange(start, end, intensity ); } }
Step 6: Development Issues
During the development of this project I ran into a few issues. I will describe the issues and how I resolved them.
- The serial port and the DMX library have a conflict with the interrupt handler. This means you can't debug using the serial terminal and have the DMX shield working at the same time.
Here what I did to resolve this issue:- I defined SERIAL_DEBUG_ENABLED
- I used #ifdef to conditionally compile in/out the serial/DMX functions.
- For some unknown reason I couldn't #ifdef out the #include header file so I have to comment it in/out to make it work.
See examples below.
FOR DMX ENABLED
// Comment out for Serial but not DMX //#define SERIAL_DEBUG_ENABLED // Comment out for Serial - include for DMX #include <conceptinetics.h> // Serial or DMX but not both #ifdef SERIAL_DEBUG_ENABLED Serial.begin (9600); #else dmx_master.enable (); #endif
FOR SERIAL ENABLED
// Comment out for Serial but not DMX #define SERIAL_DEBUG_ENABLED // Comment out for Serial - include for DMX //#include <conceptinetics.h> // Serial or DMX but not both #ifdef SERIAL_DEBUG_ENABLED Serial.begin (9600); #else dmx_master.enable (); #endif
Step 7: Wrap Up
Packaging:
Here is the complete project without a case and no battery pack.
Source Code:
The complete DMX Tester source code is available here.
Parts List:
- Arduino Uno
- LCD display 4X20 character with I2C interface
- Keypay - 4X4 (16 button)
- DMX shield
- Plastic enclosure case
- Battery case
- XLR 5 pin connectors - chassis mount (I use 5 pin instead of 3 pin on the DMX shield)
Future Enhancements / Ideas:
- Allow selectable percent (1 to 100) or value (1 to 256) for intensity
- Support channel bump operation.
- Allow channels to be selected in a range by pressing the 'B'ump button.
- Add menu for additional features.
- Option to receive a channel and display the value.
- Support multifunction keys (keys can have multiple meaning depending on mode)
Other Projects:
- I'm hoping to use this project to spin off into several other projects. Here is a brief description of the other projects: A proximity detector that sends a DMX trigger signal (message). I want to use this for a Halloween project. When a person walks by an area, the proximity detector will trigger and send a DMX message to a lighting console configured to accept this channel. The message will trigger a pre-programmed lighting scene.
Questions?
If you have any questions please feel free to ask.
I hope this article is helpful and useful to someone interested in DMX in the lighting field.