Reading serial bytes and packet from DMX software

I am wondering if it is possible for an Arduino to read and interpret the data that a DMX control program produces. My thinking is to have the Arduino read the first 8 bytes that correspond to the first 8 channels and based on those values (0-255) set a digital pin to high or low. DMX signals are sent in packets and each packet contains a start byte and then 512 bytes followed by a break (if I understand it correctly). Each of the 512 bytes contains a value between 0-255. So, in the software on the computer if I set DMX channel 1 to a value of 255 I want the Arduino to interpret that and set digital pin 2 (for example) to high. If I then change the value of channel 1 to 0 then digital pin 2 will switch to low.

I'm not trying to have the Arduino generate DMX signals because the computer software does that already, I'm simply trying to have the Arduino interpret the signals sent to it over the serial connection.

Thanks for your help!

Here is some code I started working on. I am very new at this and have no idea if this even makes any sense. I also don't have my Arduino with me currently so I can't test any of this but I want to see if I understand the concept.

int relay2 = 2;  //digital pin 2
int relay3 = 3;  //digital pin 3
int relay4 = 4;  //digital pin 4
int relay5 = 5;  //digital pin 5
int relay6 = 6;  //digital pin 6
int relay7 = 7;  //digital pin 7
int relay8 = 8;  //digital pin 8
int relay9 = 9;  //digital pin 9
byte channel0;    //variable for byte 1
byte channel1;    //variable for byte 2
byte channel2;    //variable for byte 3
void setup()
{
  Serial.begin(9600);  
  pinMode(relay2, OUTPUT);  
  pinMode(relay3, OUTPUT);
  pinMode(relay4, OUTPUT);
  pinMode(relay5, OUTPUT);
  pinMode(relay6, OUTPUT);
  pinMode(relay7, OUTPUT);
  pinMode(relay8, OUTPUT);
  pinMode(relay9, OUTPUT);
}


void loop()
{

  while(Serial.available() <= 513 )  //read 513 bytes
  {
    
     channel0 = Serial.read();
     channel1 = Serial.read();
     channel2 = Serial.read();
     //etc. for all 513 bytes (need to create a loop)
  }
  if(channel1 == '255')  
  { 
    digitalWrite(relay2, HIGH);  //if channel1 value = 255 set pin 2 to high
  }
  else if(channel1 == '0')  //if channel1 value = 0 set pin 2 to low
  {
    digitalWrite(relay2, LOW);
  }

}

I know I need to create some kind of loop for all the byte variables. I also know that this will only read the first 513 bytes (the first byte being the start byte and the next 512 bytes correspond to each channel). DMX signals are a continuous stream and they also contain a break between packets which I do not know how to handle.

Am I on the right track? Any help would be greatly appreciated.

Hi,

take a look here

http://www.arduino.cc/playground/Learning/DMX

I apologize for sounding stupid but I have spent a lot of time reading the DMX information found on the Arduino playground site and it does not seem helpful to me. I am unable to find example code for receiving a DMX signal via the USB connection. All the code is for either generating a DMX signal or receiving it from a shield.

You should have the while loop reading data as long as there is any data available to read.

while(Serial.available() > 0)
{
   // Read and store the byte
}

You appear to think that you need to store all 512 bytes, but, if you are only interested in the first 8, then you could just read and ignore the other 504 bytes.

byte interestingBytes[8];
int numBytesRead = 0;
int byteRead;
while(Serial.available() > 0)
{
   byteRead = Serial.read();
   if(byteRead >= 0)
  {
     if(numBytesRead < 8)
       interestingBytes[numBytesRead] = byteRead;
     numBytesRead++;
  }
}

// Now do something with the interesting bytes, 
// if there were more than 8 bytes read

OK I think I'm starting to understand. Will the 8 bytes that are read be reread each time the 512 byte packet restarts?

Not the same 8 values, but the same first 8 bytes of each packet, presuming that you make sure to discard 504 bytes of un-interesting data.

hello

arduino can easily read DMX signals.

Make sure you have a transceiver (MAX485 or similar) to receive the signal then connect it to the RX pin on the Arduino.

setup the serial port for a speed of 250000 bits per second
( Serial.begin(250000) )
then wait for the "break" signal that is a pulse around 800uSec
then read a byte from the serial port... if the byte is 0 then the signal is a valid dmx stream...

start reading and throw away bytes until you reach the right address

sorry for the short message but it should be enough :slight_smile:

m

OK I see now. Does the break between packets matter? What about the start byte for each packet? I will just have to offset everything by one then.

I'm not trying to read DMX data from another DMX device. I'm trying to read DMX data that a program called DMXControl produces and then outputs over USB to the Arduino board. I'm basically trying to create an open USBDMX device but with out the shield.

I wired 8 relays into my Arduino via the digital pins and my hope is to use it control Christmas lights. DMXControl allows you to load a song and create cues based on timing within the song. So my goal is to use DMX channel 1 to control relay 1 on or off with the DMX channel value and so on for all 8 relays.

http://blog.wingedvictorydesign.com/2009/03/20/receive-dmx-512-with-an-arduino/

This should get you going in the right direction

OK thanks! I will take a look at that and see what I can figure out.

OK now that finals are over I can spend some more time on this. I'm still trying to read the serial stream data. Here is what I have come up with and so far it's not working.

int relay2 = 2;  //digital pin 2
int relay3 = 3;  //digital pin 3
int relay4 = 4;  //digital pin 4
int relay5 = 5;  //digital pin 5
int relay6 = 6;  //digital pin 6
int relay7 = 7;  //digital pin 7
int relay8 = 8;  //digital pin 8
int relay9 = 9;  //digital pin 9
int numBytesRead = 0;      //byte number variable
volatile byte zerocounter = 0;   //zero counter for break
void setup()
{
  Serial.begin(115200);  
  pinMode(relay2, OUTPUT);  
  pinMode(relay3, OUTPUT);
  pinMode(relay4, OUTPUT);
  pinMode(relay5, OUTPUT);
  pinMode(relay6, OUTPUT);
  pinMode(relay7, OUTPUT);
  pinMode(relay8, OUTPUT);
  pinMode(relay9, OUTPUT);
}


void loop()
{
byte interestingBytes[9];
int byteRead;
while(Serial.available() > 0)
{
   byteRead = Serial.read();
   numBytesRead++;
   if(byteRead >= 0)
  {
     if(numBytesRead <= 9)
     {
       interestingBytes[numBytesRead] = byteRead;
     } 
  }
}
     /* detecting break between packets */
     if (bitRead(byteRead,1))  // if a one is detected, we're not in a break, reset zerocounter.
     {  
        zerocounter = 0;
    }
    if(bitRead(byteRead,0))  //if the bit is a 0 then we are in a break
    {
        zerocounter++;             // increment zerocounter 
    }
    if (zerocounter == 22)        // if 22 0's are received in a row (88uS break)
      {   
          numBytesRead = 0;      //Set number of bytes read back to 0 so we can read the first 9 bytes of the new packet
      }

  /* Reading Byte data to control pins */
  if(interestingBytes[2] == '255')  
  { 
    digitalWrite(relay2, HIGH);  //if byte2 value = 255 set pin 2 to high
  }
  else if(interestingBytes[2] == '0')  //if byte2 value = 0 set pin 2 to low
  {
    digitalWrite(relay2, LOW);
  }
 if(interestingBytes[3] == '255')  
  { 
    digitalWrite(relay3, HIGH);  //if byte3 value = 255 set pin 3 to high
  }
  else if(interestingBytes[3] == '0')  //if byte2 value = 0 set pin 3 to low
  {
    digitalWrite(relay3, LOW);
  }
   if(interestingBytes[4] == '255')  
  { 
    digitalWrite(relay4, HIGH);  //if byte4 value = 255 set pin 4 to high
  }
  else if(interestingBytes[4] == '0')  //if byte4 value = 0 set pin 4 to low
  {
    digitalWrite(relay4, LOW);
  }
   if(interestingBytes[5] == '255')  
  { 
    digitalWrite(relay5, HIGH);  //if byte5 value = 255 set pin 5 to high
  }
  else if(interestingBytes[5] == '0')  //if byte5 value = 0 set pin 5 to low
  {
    digitalWrite(relay5, LOW);
  }
   if(interestingBytes[6] == '255')  
  { 
    digitalWrite(relay6, HIGH);  //if byte6 value = 255 set pin 6 to high
  }
  else if(interestingBytes[6] == '0')  //if byte6 value = 0 set pin 6 to low
  {
    digitalWrite(relay6, LOW);
  }
   if(interestingBytes[7] == '255')  
  { 
    digitalWrite(relay7, HIGH);  //if byte7 value = 255 set pin 7 to high
  }
  else if(interestingBytes[7] == '0')  //if byte7 value = 0 set pin 7 to low
  {
    digitalWrite(relay7, LOW);
  }
   if(interestingBytes[8] == '255')  
  { 
    digitalWrite(relay8, HIGH);  //if byte8 value = 255 set pin 8 to high
  }
  else if(interestingBytes[8] == '0')  //if byte8 value = 0 set pin 8 to low
  {
    digitalWrite(relay8, LOW);
  }
   if(interestingBytes[9] == '255')  
  { 
    digitalWrite(relay9, HIGH);  //if byte9 value = 255 set pin 9 to high
  }
  else if(interestingBytes[9] == '0')  //if byte9 value = 0 set pin 9 to low
  {
    digitalWrite(relay9, LOW);
  }
}

I'm not sure if I'm reading the bytes properly and if you can simply check and see if a byte is equal to some value as I am doing.

   numBytesRead++;
   if(byteRead >= 0)
  {
     if(numBytesRead <= 9)
     {
       interestingBytes[numBytesRead] = byteRead;
     }
  }

This code will increment the position in the array where the character is to be stored, even if the character should not be stored. You need to move the numBytesRead increment statement AFTER the test for a valid character.

if (bitRead(byteRead,1))  // if a one is detected, we're not in a break, reset zerocounter.
     {  
        zerocounter = 0;
    }

This code gets executed after the last byte was read, regardless of how many that was. Is that the intent? What does the bitRead function do? You didn't post the code for that function.

  if(interestingBytes[2] == '255')  
  {
    digitalWrite(relay2, HIGH);  //if byte2 value = 255 set pin 2 to high
  }

What is the result of this block of code if you only read one (or two) valid characters?

Here is what I have come up with and so far it's not working.

What a shame. Can you be just a little bit more precise as to HOW it's not working, and how you know it's not working?

 if(interestingBytes[5] == '255')

That's a very odd constant there.
Is that what you meant?

Here is a better explanation. I am basing everything off of the information here: Arduino Playground - Protokoll

I'm trying to read the first 9 bytes of the stream with this code. I'm only going to use bytes 2-9 because the first byte of every DMX stream is a start byte and it contains a value of 0.

while(Serial.available() > 0)
{
   byteRead = Serial.read();
   
   if(byteRead >= 0)
  {
    numBytesRead++;
     if(numBytesRead <= 9)
     {
       interestingBytes[numBytesRead] = byteRead;
     } 
  }
}

The code is saying that while serial data is being received, record the data to byteRead. If byteRead is greater then or equal to 0 then increase numBytesRead by one. If the numBytesRead is less then 9 then record the serial data from byteRead to interestingBytes[1-9]. Each interestingByte should contain a value for a channel.
I'm not sure on how to handle the remaining 500 or so bytes in the stream. My thought is just to ignore them but I'm not sure if that works.

The next section of code is designed to detect the break between the stream. It's a bunch of bits with the value of 0 for approximately 88 milliseconds.

 /* detecting break between packets */
     if (bitRead(byteRead,1))  // if a one is detected, we're not in a break, reset zerocounter.
     {  
        zerocounter = 0;
    }
    if(bitRead(byteRead,0))  //if the bit is a 0 then we are in a break
    {
        zerocounter++;             // increment zerocounter 
    }
    if (zerocounter >= 22)        // if 22 0's are received in a row (88uS break)
      {   
          numBytesRead = 0;      //Set number of bytes read back to 0 so we can read the first 9 bytes of the new packet
      }

This code is reading each bit to see if it is a 0 or a 1. Each time a 1 is received it resets zerocounter but if a 0 is received it increments zerocounter by 1. When we receive 22 0's in a row then there is a break and we reset numBytesRead back to 0 to start reading the first 9 bytes from the stream again.

The final section is attempting to read the value of the byte. In the DMX program I can set the value of a channel which corresponds to a byte to any number between 0 and 255. I'm not sure if you can simply check to see if a variable is equal to a number like I am doing.

 /* Reading Byte data to control pins */
  if(interestingBytes[2] == '255')  
  { 
    digitalWrite(relay2, HIGH);  //if byte2 value = 255 set pin 2 to high
  }
  else if(interestingBytes[2] == '0')  //if byte2 value = 0 set pin 2 to low
  {
    digitalWrite(relay2, LOW);
  }

When I have the DMX program running I set channel 1 to a value of 255 which should correspond to the second byte of each packet/stream since byte 1 is the start byte. However, when I change the value nothing happens.

I hope that is a better explanation and any help would be great.

The code after this comment:

/* detecting break between packets */

is not reading any more data. It is doing something (we don't know what since you still haven't posted the code for bitRead) with the last byte read (that you stored in the array).

I'm not sure if you can simply check to see if a variable is equal to a number like I am doing.

You can.

The comments about the "if(interestingBytes[2] == '255')" statements concern the [2] part.

Suppose that there were only 2 bytes available to read, instead of 512. If both bytes are good, they would be stuffed into interestingBytes[0] and interestingBytes[1]. Then, when you compare the values in interestingBytes[2] and interestingBytes[5], you are comparing data from a previous pass through loop. That is probably not what you want to be doing.

Also, the 2 in [2] (as well as the 5 in [5]) and the 255 are magic numbers. They mean something to you today, but they don't mean anything to anyone else.

Defining some names to use in place of the magic numbers would be a good idea. Something like:

#define Channel2 2
#define Channel5 5
#define Whatever255Means '255'

if(interestingBytes[Channel2] == Whatever255Means)
{
}

Some time in the future, you will come back to look at this code, and [Channel2] will mean a lot more than [2].

Add some Serial.print statements to the code, especially in the while loop that's reading the data, to help see what is going on.

I have defined some variables like you said.

#define channel1 2
#define channel2 3
#define channel3 4
#define channel4 5
#define channel5 6
#define channel6 7
#define channel7 8
#define channel8 9
#define on 255
#define off 0

And then for the byte checking:

  /* Reading Byte data to control pins */
  if(interestingBytes[channel1] == on)  
  { 
    digitalWrite(relay2, HIGH);  //if byte2 value = 255 set pin 2 to high
  }
  else if(interestingBytes[channel1] == off)  //if byte2 value = 0 set pin 2 to low
  {
    digitalWrite(relay2, LOW);
  }

Is that the correct way to check the value of a byte? What I'm trying to say is: "if the value of byte2 is equal to 255 then set pin 2 to high."

I'm still confused on how to properly read the bytes from the stream. My thinking is this: "while serial data is available, read and store the first 9 bytes of the packet/stream and disregard the rest. When a break is detected, reread the first 9 bytes of the stream/packet and overwrite the previous data."

The changes you make look good.

If you are sending 512 bytes of data, and only care about the first 9 bytes, you have to make those other 503 bytes go away.

There are 3 ways to do that.

You can use a while loop to read 503 bytes, and do nothing with them:

int bytesToIgnore = 503;
int bytesIgnored = 0;
while(bytesIgnored < bytesToIgnore)
{
    while(Serial.available())
    {
        byte boringByte = Serial.read();
        bytesIgnored++;
    }
}

The problem with this approach is that you don't account for dropped bytes in the transmission process.

You could simply flush the buffer:

Serial.flush();

The 1st problem with this approach is that there may be more than 504 bytes in the buffer, and flushing it would remove too much data. The 2nd problem is that there may not be 504 bytes in the buffer, and you won't flush enough data.

You could read, and discard an arbitrary number of bytes, until the end-of-buffer marker is found.

It's a bunch of bits with the value of 0 for approximately 88 milliseconds.

You'll need to determine more accurately what constitutes an end-of-buffer marker. The time it takes to transmit 512 bytes depends on the baud rate and the speed with which those bytes are processed on the receiving end. Just discarding bytes for 88 milliseconds is a sure way to get out of sync.

Once thing to keep in mind. The Arduino is a microprocessor. It does not have a 512+ byte serial communications buffer. If it did, you could wait for all 512 bytes to be available before you did any processing, which would make your job easier.

Do you have control over what is sent to the Arduino? Can you add start and stop markers to the data stream?

"if(interestingBytes[2] == '255')" statements concern the [2] part.

The comments also concern the '255' part.
'255' is a multicharacter constant (0x323535), so comparing it to a byte-wide variable like "interestingBytes[2]" will always return false.

I have got it sort of working. Some of the pins turn on and off when the values are changed but there is a loss of sync issue that I can't figure out how to fix and I still can't figure out how to properly detect the break between packets. Also, some of the pins just turn on and off in quick succession. I have discovered that there are 513 bytes per packet, the first being the start byte which has a value of 0 and bytes 2-513 are the channel bytes.

Here is the code I am using:

volatile byte interestingBytes[1];  //actual DMX byte variable
volatile byte byteRead;              //reading bytes

  while(Serial.available() > 0)  //read serial data
  {
    byteRead = Serial.read();
    numBytesRead++;          //Increse number of bytes read
    
     if(numBytesRead <= 9)  //number of bytes read is less then or equal to 
     {
       interestingBytes[numBytesRead] = byteRead;  //write byte data to variable
        
     } 
  }
     
     if(numBytesRead >= 513)
     {
      Serial.flush();
     numBytesRead = 0;
     }

Serial.flush() does appear to work but I think it is dumping too much data as mentioned above.

Also,

if(interestingBytes[channel1] == on)  
  { 
    digitalWrite(relay1, HIGH);  
  }
  else if(interestingBytes[channel1] == off)  
  {
    digitalWrite(relay1, LOW);
  }

This code does work because when I set the value of a channel in the dmx software to 255 the pin turns on. When I set it to 0 it turns off.

Any ideas on how to properly read the break between packets?