DMX Serial Receive Inaccuracy

Hi everyone,

I am receiving dmx with the DMXSerial.h library and a max 485 dmx shield on one Arduino and passing it thru via I2C to another Arduino, but every now and then my code misinterprets the signal.

For example: my lighting desk is only sending Channel 10 at Value 255, but sometimes it fails to read that signal propperly and thinks I am sending a Value of 255 on Channel 7, 8 or 9.

Please let me know if you know what I am doing wrong!

Arduino that receives the DMX:

#include <Wire.h>
#include <DMXSerial.h>

bool Setup;

byte delayTime = 1;

int FaderChannel;
byte Universe32[32];
byte FullUniverse[512];

void setup() {
  // put your setup code here, to run once:
  Wire.begin(8);  // join i2c bus with address #8
  //  Serial.begin(9600);
  Wire.setClock(250000);
  Wire.onReceive(receiveEvent);
  Wire.onRequest(requestEvent);
  DMXSerial.init(DMXReceiver);  // initialize DMX receiver in manual mode
}

void receiveEvent() {                   // Checks if Setup Status
  while (Wire.available()) {
    Setup = Wire.read();
    //    Serial.println(Setup);
  }
}

void requestEvent() {                   // Sends requested Data depending on Setup
  if (Setup == 1) {                     // Returns 1 Byte for Fader Channel
    for (int i = 0; i <= 511; i++) {
      if (FullUniverse[i] != DMXSerial.read(i + 1)) { // Checks if different than last time
        FaderChannel = i + 1;
      }
      FullUniverse[i] = DMXSerial.read(i + 1);
    }
    Wire.write(FaderChannel);
  }
  else {                                // Returns 32 Bytes for Universe32
    for (byte i = 0; i <= 31; i++) {
      Wire.write(DMXSerial.read(i + 1));
      //      Serial.println(Universe32[i]);
    }
  }
}

void loop() {
  // put your main code here, to run repeatedly:

  // for (byte i = 0; i <= 31; i++) {
    // Universe32[i] = DMXSerial.read(i + 1);
    //      delay(1);

  }

}

Arduino that receives the dmx data via I2C and Serial.prints the received data:

#include <Wire.h>

byte S9S = 9;// Setup Switch
byte S10S = 10; // Setup Switch
byte S11S = 11; // Rel Abs Switch
byte S12S = 12;// Rel Abs Switch

bool S9state = 0;
bool S10state = 0;
bool S11state = 0;
bool S12state = 0;

bool Setup = 0;

byte delayTime = 1;

int FaderChannel;

byte requBytes;

byte Universe32[32];


void setup() {
  // put your setup code here, to run once:
  Wire.begin(9);  // join i2c bus with address #9
  Serial.begin(250000); // Unit 2 need 57600 not 2000000
  Wire.setClock(250000);

  pinMode(S9S, INPUT_PULLUP);
  pinMode(S10S, INPUT_PULLUP);
  pinMode(S11S, INPUT_PULLUP);
  pinMode(S12S, INPUT_PULLUP);
}


void loop() {
  // put your main code here, to run repeatedly:

  S9state = digitalRead(S9S);
  S10state = digitalRead(S10S);
  S11state = digitalRead(S11S);
  S12state = digitalRead(S12S);

  if (S9state == LOW) {                     // Setup Active
    Setup = 1;
    requBytes = 1;
  }
  else {                                    // Setup Inactive
    Setup = 0;
    requBytes = 32;
  }

  Wire.beginTransmission(8);               // Sends Status to Receiver
  Wire.write(Setup);
  Wire.endTransmission();
  delay(delayTime);
  //  Serial.println(Setup);


  Wire.requestFrom(8, requBytes, true);   // Requests 1 or 32 Bytes
  while (Wire.available()) {
    if (Setup == 1) {                     // Requests 1 Byte for FaderChannel
      FaderChannel = Wire.read();
      if (FaderChannel != 0) {
        Serial.println(FaderChannel);
      }
    }
    else {                                // Requests 32 Bytes for Universe32
      for (byte i = 0; i <= 31; i++) {
        Universe32[i] = Wire.read();
        if (Universe32[i] != 0) {
          Serial.print(i+1);
          Serial.print(": ");
          Serial.println(Universe32[i]);
//          delay(20);
        }
        
      }
    }
  }

}

Thanks for your help.

All the best,
Leon

Nice job with the code, can you post an annotated schematic showing exactly how this is wired? Be sure to show all power, ground, power sources etc.

Well that sounds like a misinterpretation of the break recognition. A DMX frame is sent by sending a break of 88us minimum followed by bunch of channels and if that break is detected but the receiver is enabled in time, the channel numbers shift.

There is a good chance this is caused by the I2C communication. To confirm you should disable the I2C communication and instead turn on the buitlin LED for a small period if any of those channels(7, 8 & 9) are not '0' as they are being transmitted.
To have a device requesting information from an AVR is probably fine if it would be just Serial reception, but not during break-detection where timing is critical.

As a patch solution you can see if you can increase the break-time of the lighting desk, as well as make sure always a complete frame is transmitted. Thing is that if it can miss 3 whole channels, there is a good chance this won't suffice. The Arduino doing the DMX reception can not be interrupted by another device and should be in full control of it's processing.

I would suspect that the Wire interrupt is causing DMXSerial to miss characters. That would be easy to verify I think.

Also, it is not clear to me whether the error is occurring on the DMX side, or the I2C side. Isolating that would be a first step.

Thanks for the repl!

I checked and the issue is definetly on the dmx receive side. Has anyone got an idea how I can manually check for the dmx bread and then read out the values?

Well yes. i did some work on it. This is a newer version of what Max Pierson did But rather than fiddling with hardwareSerial.h the registries are set manually and the RX interrupt is defined in the sketch as normal.

Of course you can't echo them out over 'Serial'
I found a few examples, and i think this is the latest version.
I had some issues with getting it on the right channel with the FiFO and all, but i think like this it's more or less correct although i can test it for you right now.

#define PWM_LED_PIN 5
#define DMX_PING 13
#define MAX_DMX_CHANNELS 512

#define NUMBER_OF_CHANNELS 510

#define BAUDDMX 250000UL

#define RECEIVE_FORMAT 0x06 // 8N1 (could be 8N2 aswell.

#define UBDMX ((F_CPU / (8 * BAUDDMX)) - 1)



volatile uint16_t dmxaddress = 1;
volatile uint16_t i = 0;
volatile uint8_t dmxreceived = 0;
volatile uint16_t dmxcurrent = 0;
volatile uint8_t dmxvalue[NUMBER_OF_CHANNELS];

volatile uint8_t dmxnewvalue = 0;
volatile uint8_t zerocounter = 0;
volatile bool storeSerial = false;

void setup() {

  pinMode(PWM_LED_PIN, OUTPUT); // first dmxchannel LED value
  pinMode(DMX_PING, OUTPUT); //ping LED
  init_uart();
  SetupTimerRegisters();
}

void loop()  {
  ReceiveDMX();
  Processing();

}

void ReceiveDMX() {
  dmxnewvalue = 0;
  dmxcurrent = 0;
  zerocounter = 0;
  i = 0;
  UCSR0C = RECEIVE_FORMAT; //0x06; // 8N1
  bitSet(UCSR0B, RXCIE0);   // enable RX ISR
  bitSet(TIMSK2, OCIE2A);
  while (dmxnewvalue != 1);
}

void Processing() {  // main processing
  static bool pin = true;
  pin = !pin;
  analogWrite(5, dmxvalue[0]);
  digitalWrite(13, pin);
  if (dmxvalue[0] > 200) {
    digitalWrite(13, HIGH);
  }
  else {
    digitalWrite(13, LOW);
  }
}

void init_uart() {
  DDRD |=  (1 << PORTD1); // set TX pin  to output
  DDRD &= ~(1 << PORTD0); // set RX pin to input
  UCSR0A = 0x02; // 1<<U2X | 0<<MPCM;
  UCSR0B = 1 << RXCIE0 | 0 << TXCIE0 | 0 << UDRIE0 | 1 << RXEN0 | 1 << TXEN0 | 0 << UCSZ02; // Enable TX & RX, disable RX interrupt
  UCSR0C = RECEIVE_FORMAT; // 8N1
  UBRR0H = (UBDMX >> 8);  // set baud rate
  UBRR0L = (UBDMX & 0xFF); // HL register
}

void SetupTimerRegisters() {  // Sets up Timer2 to fire every 4us
  cli();
  bitClear(TCCR2A, COM2A1);
  bitClear(TCCR2A, COM2A0);
  bitClear(TCCR2A, COM2B1);
  bitClear(TCCR2A, COM2B0);
  bitSet(TCCR2A, WGM21);
  bitClear(TCCR2A, WGM20);
  bitClear(TCCR2B, FOC2A);
  bitClear(TCCR2B, FOC2B);
  bitClear(TCCR2B, WGM22);
  bitClear(TCCR2B, CS22);
  bitClear(TCCR2B, CS21);
  bitSet(TCCR2B, CS20);
  OCR2A = 64;
  bitClear(TIMSK2, OCIE2B);
  bitSet(TIMSK2, OCIE2A);
  bitClear(TIMSK2, TOIE2);
  sei();
}

ISR(TIMER2_COMPA_vect) {
  if (bitRead(PIND, PIND0)) {  // checks if the pin has gone HIGH
    zerocounter = 0;
  }
  else {
    zerocounter++;
    if (zerocounter == 20)  // if 80us have passed 'Break' has been detected
    {
      bitClear(TIMSK2, OCIE2A);  // disable Timer2 Interrupt
      storeSerial = true;

    }
  }
} //end Timer2 ISR

ISR(USART_RX_vect) {
  dmxreceived = UDR0;
  if (storeSerial) {
    dmxcurrent++;     //increment address counter starts at 0
    // this skips the start code
    if (dmxcurrent > dmxaddress) {        //check if the current address is the one we want.
      dmxvalue[i] = dmxreceived;
      i++;
      if ((i == NUMBER_OF_CHANNELS) || (dmxcurrent > MAX_DMX_CHANNELS - 2)) {
        bitClear(UCSR0B, RXCIE0);                   // disable RX ISR
        storeSerial = false;                      // set the flag to false
        dmxnewvalue = 1;                        //set newvalue, so that the main code can be executed.
      }
    }
  }
} // end ISR

Hope this helps.

Note that in receiving all channels, the chance of actually have completed all processing before the break starts are next to none, effectively reducing the frame-rate to 25Hz or so. If you don't have to receive all channels this greatly improves the frame-rate.

Thanks for the help!

This is just a bit out of my league, sorry. I just want to receive 32 Channel of DMX and then send the Values of those Channels to another Arduino via I2C.

Is it possible to receive uninterrupted DMX ... store that into an array ... and then send it to another Arduino, without it getting the break wrong?

It should be. Have you tried conceptinetics.h as a dmx library yet.

It doesn't seem that complicated to me.
Change this

#define NUMBER_OF_CHANNELS 510

to

#define NUMBER_OF_CHANNELS 32

your channels end up in this array

volatile uint8_t dmxvalue[NUMBER_OF_CHANNELS];

and do whatever you want to do with those values here

void Processing() {  // main processing
  static bool pin = true;
  pin = !pin;
  digitalWrite(13, pin);  // leave this it will indicate reception is working by flashing the builtin LED
// here 

}

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.