Pages: [1]   Go Down
Author Topic: DMX send and receive with some signal modification  (Read 750 times)
0 Members and 1 Guest are viewing this topic.
0
Offline Offline
Newbie
*
Karma: 0
Posts: 4
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Good morning ladies and gentlemen of the Arduino forum.
So...

My problem is i have a DMX device that is hard adressed to channels 1 through 4.
This can be annoying in some venues (it's going on tour with a theatre show soon) so i'm trying to use an arduino with a modified DMX shield to fix my problem.

Basically I want to receive DMX at an address that i specify (through dip switches) and redirect the information to DMX adresses 1 through 4.
The catch is that i need all other DMX information to also be passed on without modification.

I have no problems building the shield it's the coding side where i fall down *sigh*

I've looked around and found other peoples code that sends DMX and receives it, and have slapped it toghether with some modifications of my own (based on hope and guesswork)

I'm certain it wont work properly (there's too much i don't understand) so i'm now going to take a break, build my shield, try to find my usb AtoB cable in the mess that is my room and post my code in the hopes you wonderful people can help me debug it. smiley
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 4
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

part 1


Code:
/* Cobbled together by Genehed
*----------------------------
this sketch uses borrowed elements from dmxlab_2__fade in and out all

 * (cleft) 2006 by Tomek Ness and D. Cuartielles
 * K3 - School of Arts and Communication
 * fhp - University of Applied Sciences
 * <http://www.arduino.cc>
 * <http://www.mah.se/k3>
 * <http://www.design.fh-potsdam.de>
 *
 * @date: 2006-09-30
 * @idea: Tomek Ness
 * @code: D. Cuartielles and Tomek Ness
 * @acknowledgements: Johny Lowgren for his DMX devices
 *
and
/***********************************************************

* DMX-512 Reception                                        *

* Developed by Max Pierson                                 *

* Version Rev12 12 May 2009                                *

* Released under the WTFPL license, although I would       *

* appreciate Attribution and Share-Alike                   *

* See blog.wingedvictorydesign.com for the latest version. *

************************************************************

the purpose is too recieve dmx from a settable address (8 dip switches) and then to change dmx outputs 1 through 4. while still sending everything else.
hopefully i've properly understood the code i'm borrowing and my changes work :)
using 2 MAX-485 chips (IC1(send) , IC2(receive)) as i have them and i may as well

my Arduino pin lay out
0  rxpin recieving the dmx signal from IC2
1  txpin to driver input on IC2
2  add 1
3  add 2
4  add 4
5  add 8
6  add 16
7  add 32
8  add 64
9  add 128
10
11    dmx out IC1
12    
13    error check LED

analouge
aref
0
1
2
3
4  to reciever enable IC2
5  to driver enable IC2

/* DMX Shift Out for arduino - 004 and 005

 *
 * (cleft) 2006 by Tomek Ness and D. Cuartielles
 * K3 - School of Arts and Communication
 * fhp - University of Applied Sciences
 * <http://www.arduino.cc>
 * <http://www.mah.se/k3>
 * <http://www.design.fh-potsdam.de>
 *
 * @date: 2006-09-30
 * @idea: Tomek Ness
 * @code: D. Cuartielles and Tomek Ness
 * @acknowledgements: Johny Lowgren for his DMX devices
 */
#include "pins_arduino.h"

int sig = 11;           // signal

int count = 0;


/* Sends a DMX byte out on a pin.  Assumes a 16 MHz clock.
 * Disables interrupts, which will disrupt the millis() function if used
 * too frequently. */
void shiftDmxOut(int pin, int theByte)
{
  int port_to_output[] = {
    NOT_A_PORT,
    NOT_A_PORT,
    _SFR_IO_ADDR(PORTB),
    _SFR_IO_ADDR(PORTC),
    _SFR_IO_ADDR(PORTD)
    };

    int portNumber = port_to_output[digitalPinToPort(pin)];
  int pinMask = digitalPinToBitMask(pin);

  // the first thing we do is to write te pin to high
  // it will be the mark between bytes. It may be also
  // high from before
  _SFR_BYTE(_SFR_IO8(portNumber)) |= pinMask;
  delayMicroseconds(10);

  // disable interrupts, otherwise the timer 0 overflow interrupt that
  // tracks milliseconds will make us delay longer than we want.
  cli();

  // DMX starts with a start-bit that must always be zero
  _SFR_BYTE(_SFR_IO8(portNumber)) &= ~pinMask;

  // we need a delay of 4us (then one bit is transfered)
  // this seems more stable then using delayMicroseconds
  asm("nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n");
  asm("nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n");

  asm("nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n");
  asm("nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n");

  asm("nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n");
  asm("nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n");

  for (int i = 0; i < 8; i++)
  {
    if (theByte & 01)
    {
      _SFR_BYTE(_SFR_IO8(portNumber)) |= pinMask;
    }
    else
    {
      _SFR_BYTE(_SFR_IO8(portNumber)) &= ~pinMask;
    }

    asm("nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n");
    asm("nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n");

    asm("nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n");
    asm("nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n");

    asm("nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n");
    asm("nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n");

    theByte >>= 1;
  }

  // the last thing we do is to write the pin to high
  // it will be the mark between bytes. (this break is have to be between 8 us and 1 sec)
  _SFR_BYTE(_SFR_IO8(portNumber)) |= pinMask;

  // reenable interrupts.
  sei();
}
/******************************* Addressing variable declarations *****************************     */
  int add1 = 0;
  int add2 = 0;  
  int add4 = 0;
  int add8 = 0;
  int add16 = 0;
  int add32 = 0;
  int add64 = 0;
  int add128 = 0;

unsigned int dmxaddress = 1;
  int pinadd1 = 2;
  int pinadd2 = 3;
  int pinadd4 = 4;
  int pinadd8 = 5;
  int pinadd16 = 6;
  int pinadd32 = 7;
  int pinadd64 = 8;
  int pinadd128 = 9;

/* The dmx address we will be listening to.  The value of this will be set in the Addressing()*/

Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 4
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

part 2
Code:


/******************************* MAX485 variable declarations *****************************/

int receiveroutputenable = 2;

/* receiver output enable (pin2) on the max485.

*  will be left low to set the max485 to receive data. */

int driveroutputenable = 3;

/* driver output enable (pin3) on the max485.

*  will left low to disable driver output. */

int rxpin = 0;   // serial receive pin, which takes the incoming data from the MAX485.

int txpin = 1;   // serial transmission pin

/******************************* DMX variable declarations ********************************/

volatile byte i = 0;              //dummy variable for dmxvalue[]

volatile byte dmxreceived = 0;    //the latest received value

volatile unsigned int dmxcurrent = 0;     //counter variable that is incremented every time we receive a value.

volatile byte dmxvalue[4] = {1, 0, 0, 0};     //stores the DMX values we're interested in using.
volatile byte dmxstream[512]={  0,0,0,0,0,0,0,0  ,0,0,0,0,0,0,0,0  ,0,0,0,0,0,0,0,0  ,0,0,0,0,0,0,0,0,
                                0,0,0,0,0,0,0,0  ,0,0,0,0,0,0,0,0  ,0,0,0,0,0,0,0,0  ,0,0,0,0,0,0,0,0,
                                0,0,0,0,0,0,0,0  ,0,0,0,0,0,0,0,0  ,0,0,0,0,0,0,0,0  ,0,0,0,0,0,0,0,0,
                                0,0,0,0,0,0,0,0  ,0,0,0,0,0,0,0,0  ,0,0,0,0,0,0,0,0  ,0,0,0,0,0,0,0,0,
                                
                                0,0,0,0,0,0,0,0  ,0,0,0,0,0,0,0,0  ,0,0,0,0,0,0,0,0  ,0,0,0,0,0,0,0,0,
                                0,0,0,0,0,0,0,0  ,0,0,0,0,0,0,0,0  ,0,0,0,0,0,0,0,0  ,0,0,0,0,0,0,0,0,
                                0,0,0,0,0,0,0,0  ,0,0,0,0,0,0,0,0  ,0,0,0,0,0,0,0,0  ,0,0,0,0,0,0,0,0,
                                0,0,0,0,0,0,0,0  ,0,0,0,0,0,0,0,0  ,0,0,0,0,0,0,0,0  ,0,0,0,0,0,0,0,0,
                                
                                0,0,0,0,0,0,0,0  ,0,0,0,0,0,0,0,0  ,0,0,0,0,0,0,0,0  ,0,0,0,0,0,0,0,0,
                                0,0,0,0,0,0,0,0  ,0,0,0,0,0,0,0,0  ,0,0,0,0,0,0,0,0  ,0,0,0,0,0,0,0,0,
                                0,0,0,0,0,0,0,0  ,0,0,0,0,0,0,0,0  ,0,0,0,0,0,0,0,0  ,0,0,0,0,0,0,0,0,
                                0,0,0,0,0,0,0,0  ,0,0,0,0,0,0,0,0  ,0,0,0,0,0,0,0,0  ,0,0,0,0,0,0,0,0,
                                
                                0,0,0,0,0,0,0,0  ,0,0,0,0,0,0,0,0  ,0,0,0,0,0,0,0,0  ,0,0,0,0,0,0,0,0,
                                0,0,0,0,0,0,0,0  ,0,0,0,0,0,0,0,0  ,0,0,0,0,0,0,0,0  ,0,0,0,0,0,0,0,0,
                                0,0,0,0,0,0,0,0  ,0,0,0,0,0,0,0,0  ,0,0,0,0,0,0,0,0  ,0,0,0,0,0,0,0,0,
                                0,0,0,0,0,0,0,0  ,0,0,0,0,0,0,0,0  ,0,0,0,0,0,0,0,0  ,0,0,0,0,0,0,0,0};  /* the dmx stream 32 by 16 rows */
                                
volatile boolean dmxvaluesend = 0; //when dmx is recieved and modified so it's ready to sendready to send

/******************************* Timer2 variable declarations *****************************/

volatile byte zerocounter = 0;

/* a counter to hold the number of zeros received in sequence on the serial receive pin.

*  When we've received a minimum of 11 zeros in a row, we must be in a break.  As written,

*  the timer2 ISR actually checks for 22 zeros in a row, for the full 88uS break.       */

void setup() {
  
  /*****************************  dmx out ******************************* */
    pinMode(sig, OUTPUT);
  digitalWrite(13, HIGH);

  /******************************* Max485 configuration ***********************************/

  pinMode(receiveroutputenable, OUTPUT);

  pinMode(driveroutputenable, OUTPUT);

  digitalWrite(receiveroutputenable, LOW);

  digitalWrite(driveroutputenable, LOW);    //sets pins 3 and 4 to low to enable reciever mode on the MAX485.

  pinMode(rxpin, INPUT);  //sets serial pin to receive data

  /******************************* Addressing subroutine *********************************/

  digitalWrite(pinadd1, HIGH);
  digitalWrite(pinadd2, HIGH);
  digitalWrite(pinadd4, HIGH);
  digitalWrite(pinadd8, HIGH);
  digitalWrite(pinadd16, HIGH);
  digitalWrite(pinadd32, HIGH);
  digitalWrite(pinadd64, HIGH);
  digitalWrite(pinadd128, HIGH);  //turns on the internal pull-up resistor for adress pins 2 through 9
  
  pinMode(pinadd1, INPUT);
  pinMode(pinadd2, INPUT);
  pinMode(pinadd4, INPUT);
  pinMode(pinadd8, INPUT);
  pinMode(pinadd16, INPUT);
  pinMode(pinadd32, INPUT);
  pinMode(pinadd64, INPUT);
  pinMode(pinadd128, INPUT);      //sets address pins to input
  
  add1 = digitalRead(pinadd1);
  add2 = digitalRead(pinadd2) *2;
  add4 = digitalRead(pinadd4) *4;
  add8 = digitalRead(pinadd8) *8;
  add16 = digitalRead(pinadd16) *16;
  add32= digitalRead(pinadd32) *32;
  add64 = digitalRead(pinadd64) *64;
  add128 = digitalRead(pinadd128) *128;

dmxaddress = (add1 + add2 + add4 + add8 + add16 + add32 + add64 + add128);

  dmxaddress = dmxaddress + 3; // not sure about this

  /*  this will allow the USART receive interrupt to fire an additional 3 times for every dmx frame.
  *   Here's why:
  *   Once to account for the fact that DMX addresses run from 0-511, whereas channel numbers
  *        start numbering at 1.
  *   Once for the Mark After Break (MAB), which will be detected by the USART as a valid character
  *        (a zero, eight more zeros, followed by a one)
  *   Once for the START code that precedes the 512 DMX values (used for RDM).  */

  /******************************* USART configuration ************************************/
  Serial.begin(250000);
  /* Each bit is 4uS long, hence 250Kbps baud rate */
  cli(); //disable interrupts while we're setting bits in registers
  bitClear(UCSR0B, RXCIE0);  //disable USART reception interrupt
  /******************************* Timer2 configuration ***********************************/
  //NOTE:  this will disable PWM on pins 3 and 11.
  bitClear(TCCR2A, COM2A1);
  bitClear(TCCR2A, COM2A0); //disable compare match output A mode
  bitClear(TCCR2A, COM2B1);
  bitClear(TCCR2A, COM2B0); //disable compare match output B mode
  bitSet(TCCR2A, WGM21);
  bitClear(TCCR2A, WGM20);  //set mode 2, CTC.  TOP will be set by OCRA.
  bitClear(TCCR2B, FOC2A);
  bitClear(TCCR2B, FOC2B);  //disable Force Output Compare A and B.
  bitClear(TCCR2B, WGM22);  //set mode 2, CTC.  TOP will be set by OCRA.
  bitClear(TCCR2B, CS22);
  bitClear(TCCR2B, CS21);
  bitSet(TCCR2B, CS20);   // no prescaler means the clock will increment every 62.5ns (assuming 16Mhz clock speed).
  OCR2A = 64;
  /* Set output compare register to 64, so that the Output Compare Interrupt will fire
  *  every 4uS.  */
  bitClear(TIMSK2, OCIE2B);  //Disable Timer/Counter2 Output Compare Match B Interrupt
  bitSet(TIMSK2, OCIE2A);    //Enable Timer/Counter2 Output Compare Match A Interrupt
  bitClear(TIMSK2, TOIE2);   //Disable Timer/Counter2 Overflow Interrupt Enable          
  sei();                     //reenable interrupts now that timer2 has been configured.

}  //end setup()



void loop()  {
    // the processor gets parked here while the ISRs are doing their thing.

 
  if (dmxvaluesend == 1) {    //when all dmx is read switch to send

      ///////////////////////////////////////////////////////////sending the dmx signal
 
  
  // sending the break (the break can be between 88us and 1sec)
   digitalWrite(sig, LOW);
   delay(10);
    
   //sending the start byte
   shiftDmxOut(sig,0);
  
   // send out the dmx
   for (count = 1; count<=512; count++){
     if (count <=4)
       {
         shiftDmxOut(sig, dmxvalue[count]);
        
       }                        // send dmx 1 thru 4
     else{
       shiftDmxOut(sig, dmxstream[count]);  //send normal dmx
     }
   }

    dmxvaluesend = 0;

    dmxcurrent = 0;

    zerocounter = 0;      //and then when finished reset variables and enable timer2 interrupt

    i = 0;

    bitSet(TIMSK2, OCIE2A);    //Enable Timer/Counter2 Output Compare Match A Interrupt
  }
  
 
 

} //end loop()



//Timer2 compare match interrupt vector handler

ISR(TIMER2_COMPA_vect) {

  if (bitRead(PIND, PIND0)) {  // if a one is detected, we're not in a break, reset zerocounter.

    zerocounter = 0;

    }

  else {

    zerocounter++;             // increment zerocounter if a zero is received.

    if (zerocounter == 22)

      {

      bitClear(TIMSK2, OCIE2A);    //disable this interrupt and enable reception interrupt now that we're in a break.

      bitSet(UCSR0B, RXCIE0);

      }

  }

} //end Timer2 ISR

ISR(USART_RX_vect){

  dmxreceived = UDR0;

  /* The receive buffer (UDR0) must be read during the reception ISR, or the ISR will just

  *  execute again immediately upon exiting. */

  dmxcurrent++;                        //increment address counter
  dmxstream[dmxcurrent]=dmxreceived;
  
  if(dmxcurrent == dmxaddress) {         //check if the current address is the one we want.

    dmxvalue[i] = dmxreceived;   // stores the levels we want

    i++;

    if(i == 4) {
      bitClear(UCSR0B, RXCIE0);
    }
  }
if (dmxcurrent >= 512) //*****************************  if all values have been read ******//  
   {
   dmxvaluesend = 1;                        //set tells the program to send dmx
   }
  

} // end ISR


any and all advice greatfully acepted.
Logged

0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 75
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hey Gene,
      This might work, or not, depending on your lighting console and device.  Since the output code bitbangs out the serial protocol manually on pin 11, it's not going to fight with the input coming in to the serial pin 0.  I didn't realize the DMX send code example was written this way.  I don't know why they didn't just use the onboard hardware USART for the 512 data bytes and then manually bang the pin for the break and MAB, it would be a lot simpler and allow more flexibility in timing.  Maybe I'll write up some code that does that.  But anyway, luckily for you it's written the way it is.

Mainly, I think you want to make sure the send and receive code go sequentially, rather than trying to run them at the same time.  i.e.:

1.  receive the desired address from the lighting console (output disabled)
2.  send those addresses to the device as address 1-4
3.  finish out your dmx frame (you should only have to send 50 addresses if the device is standards compliant)
4.  park the output pin at LOW for the break
5.  now, while the pin is low and you don't need the processor for output, jump over to your receiver code, and grab the new incoming values
6. jump back to output, bang out your MAB, and go to step 2.

Since the break time is variable from 88uS to 1S, you should be able to go grab your new input values quickly enough that your device doesn't decide that it's lost data connection and shut down.  But, many devices fail to be E1.11 standard compliant in this regard.  Also, if you have a lighting console that puts a lot of padding between addresses, it may take too long to receive the the values that you're interested in.

I don't know, personally it seems awfully prone to error to me, are you sure that you can't just open the device up and replace their DMX reception hardware with your own?

Max
« Last Edit: May 28, 2009, 02:23:03 am by ohnoezitasploded » Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 4
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

cheers Max,

Quote
I don't know, personally it seems awfully prone to error to me, are you sure that you can't just open the device up and replace their DMX reception hardware with your own?
yeah might have to do this. Was hoping not fix it as it wasn't broke.

Quote
3.  finish out your dmx frame (you should only have to send 50 addresses if the device is standards compliant)
it's a moving trolley with a moving light on top of it, so I wanted to send the whole DMX stream in an attempt to make it as flexible as possible.

Glad you think the concept is doable even if it might not work due to desk padding and non compliant devices.
i'll make it anyway and hope for the best.

thanks

-gene
Logged

France
Offline Offline
Newbie
*
Karma: 0
Posts: 7
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi Gene,

I try to do the same thing, but no more informations than this post.
Have you successfully do your projet ?

I can try to find a solution with you ...?

Bye
Goes, French arduino user. smiley-wink

Logged

Montreal
Offline Offline
Newbie
*
Karma: 0
Posts: 9
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

It's an old post but i'm working on a DMX project at the moment. The best solution I see for your project is to use 2 arduino. One will received and the other one transmit.

 8-)
Madkart
Logged

Pages: [1]   Go Up
Jump to: