Go Down

Topic: MegaBrite & DMX video ceiling (Read 3661 times) previous topic - next topic


Each MegaBrite has its own voltage regulator for the PWM logic and oscillator, and each LED channel has its own current regulator. I think the last time I ran 80 MegaBrites together, I was using a power supply capable of 50 amps.

It's still a good idea to use 6-pin cables between each MegaBrite. First it provides a good ground reference for all the data cables...second it allows you to go several modules before needing to tap in power using the screw terminals. You can connect power and ground to the screw terminals every 5 to 8 modules, in my experience. Definitely cuts down on the amount of wiring needed, since the screw terminals are not as fast to wire as the 6-pin connectors you can just plug together in seconds.

Savings between four and six conductors is probably less than 10 cents.

Definitely inspect every connection before turning it on. It's easy to wire these wrong when you have more than 50 things to get right. I would suggest just adding a few to the chain every time, power it up, make sure it works, power it down. That way you don't risk killing an entire chain of modules.
Unique RGB LED Modules and Arduino shields: http://www.macetech.com/store



is DMX really suitable for passing this much information ?
I don't know how many frames per second you intend to have but controlling individual pixels by DMX sounds to me a lot like swatting flies with a sledgehammer !!


DMX is just a neverending stream of raw 8-bit values. As long as you have the software to send out the correct values, it's as minimal and efficient as you can get without some kind of compression scheme.
Unique RGB LED Modules and Arduino shields: http://www.macetech.com/store


Jul 20, 2010, 02:08 pm Last Edit: Jul 20, 2010, 04:41 pm by headfull Reason: 1
Here's a video of the first prototype.


It just uses 9 MegaBrites at the moment and no DMX control yet. I've just made a very simple controller which allows me to set colours for testing out different diffuser materials and spacing of the MB. Also tried separating the MB into their own cell, which gives clearer colours, but not sure if it's the look we'll go for.


Hi Shodan,
DMX seems to be the standard solution to this kind of LED video installation. Check out software like Madrix, http://www.madrix.com/
It has some decent effects and video in via a video card. I plan to use my normal VJ setup on a separate computer and just feed the video out into the PC with Madrix running.
The resolution of even a massive video wall is very low (compared to normal video). Our planned installation is about 27 x 99 px (5 x 16 meters). So that's 2673 pixels = 8019 DMX channels. Spread that over 16 DMX universes (2 arduino megabrite chains per universe) and (fingers crossed) it will be ok. One of the guys from Madrix said they had run over 80 DMX512 universes no problems.
Because DMX512 is a standard for lighting and other devices, it's a good choice for future compatibility of the video wall.
But if you think there is a better way, please let me know. Buying 16 DMX USB interfaces is expensive. I'm not committed to using DMX yet, but will be soon once I buy those interfaces.


Hi Macegr
Thanks again for the tips.


Aug 13, 2010, 07:26 pm Last Edit: Aug 13, 2010, 07:33 pm by headfull Reason: 1
Here's some documentation of the second prototype using 81 MegaBrites and now working with Madrix software via DMX. There are a few mistakes in the code still so the channel addressing is a bit weird, but it works enough to show you this test. It was really easy to get this going thanks to the sample code on the Macetech site
and the excellent work of Max Pierson

I didn't have the diffuser material we are going to use in the production version so have just used what I had at hand, which in this case was shoji window screens (I live in Japan). But they work pretty good as a diffuser

I invested a lot of time trying to find a cheap and easy way to attach MegaBrites to the aluminium frame. I'll be attaching about 3200 of them, so it's gotta be super easy and quick to do, but also durable enough to last. The solution I think we will use is this 15mm PVC pipe cut to 10mm lengths and cable ties. A bit of 15mm pipe fits around the pins on the underside of the MegaBrite perfectly and a cable tie goes under the red LED and around the unit. It's actually pretty strong and only allows a little twist, but no other rotation.

Here's a video of it hooked up to Madrix...


This is going to be the best thing ever, in the entire world. I might have to fly to Japan.
Unique RGB LED Modules and Arduino shields: http://www.macetech.com/store


there are a lot of beers with your name on them waiting for you when this club opens in December.
X-mas in Japan?
Thanks for all the help!



below is the code I've got running at the moment. There are a few problems I haven't been able to solve so I would appreciate someone taking a look. I've basically just taken Max Pierson's DMX reception code and edited out the EEPROM addressing bits as I want the addressing to be done in the code as it'll never change. And I've added in MaceTech's sample ShiftBrite code and the conversion from DMX to serial in the action() function.

My main problem is probably to do with a mistake in the addressing. The green LED in first MegaBrite in the chain doesn't work. I'm guessing this is the very first DMX address. Madrix software is sending channels 1 thru 243. Any ideas?



Code: [Select]
* MegaBrite_DMX - DMX controlled MegaBrites video panel
* v01 2010.08.14
* by VJ Headfull (AKA Jamie Goodenough) http://www.headspace.jp
* Original DMX-512 Reception code by Max Pierson http://blog.wingedvictorydesign.com
* Original MegaBrite code by MaceTech http://docs.macetech.com
* Released under the WTFPL license, but please mention above contributers and share alike.          

/******************************* MegaBrite declarations *****************************/
#define clockpin 13 // CI
#define enablepin 10 // EI
#define latchpin 9 // LI
#define datapin 11 // DI

#define NumLEDs 81

int LEDChannels[NumLEDs][3] = {0};
int SB_CommandMode;
int SB_RedCommand;
int SB_GreenCommand;
int SB_BlueCommand;

/******************************* Addressing variable declarations *****************************/

//the number of channels we want to receive.
// 81 megabrites * 3 LEDs = 243 channels
// 243 + 1 = 244
// NOTE: not sure why we need to add one here, but it works.

unsigned int dmxaddress = 4;
// The dmx address we will be listening to.
// NOTE: Madrix software is sending channels 1 thru 243
// Start channel = 1, plus 3 because...
/*  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).  */
/******************************* MAX485 variable declarations *****************************/

/* receiver output enable (pin2) on the max485.  
*  will be left low to set the max485 to receive data. */

/* driver output enable (pin3) on the max485.  
*  will left low to disable driver output. */

#define RX_PIN 0   // serial receive pin, which takes the incoming data from the MAX485.
#define TX_PIN 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[NUMBER_OF_CHANNELS];    
/*  stores the DMX values we're interested in using--
*  keep in mind that this is 0-indexed. */
volatile boolean dmxnewvalue = 0;
/*  set to 1 when updated dmx values are received
*  (even if they are the same values as the last time). */

/******************************* 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.       */

/******************************* setup() ***********************************/

void setup() {
 /******************************* MegaBrite configuration ***********************************/
  pinMode(datapin, OUTPUT);
  pinMode(latchpin, OUTPUT);
  pinMode(enablepin, OUTPUT);
  pinMode(clockpin, OUTPUT);
  SPCR = (1<<SPE)|(1<<MSTR)|(0<<SPR1)|(0<<SPR0);
  digitalWrite(latchpin, LOW);
  digitalWrite(enablepin, LOW);
 /******************************* Max485 configuration ***********************************/
 digitalWrite(DRIVER_OUTPUT_ENABLE, LOW);    //sets pins 3 and 4 to low to enable reciever mode on the MAX485.

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

 /******************************* USART configuration ************************************/
 /* 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()

/******************************* loop() ***********************************/

void loop()  {
 // the processor gets parked here while the ISRs are doing their thing.
 if (dmxnewvalue == 1) {    //when a new set of values are received, jump to action loop...
   dmxnewvalue = 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()

/******************************* action() ***********************************/

void action() {

int counter = 0;

for(int j = 0; j < NumLEDs; j++)
  LEDChannels[j][0] = map(dmxvalue[counter], 0, 255, 0, 1023);
  LEDChannels[j][1] = map(dmxvalue[counter], 0, 255, 0, 1023);
  LEDChannels[j][2] = map(dmxvalue[counter], 0, 255, 0, 1023);

 return;  //go back to loop()
} //end action()

/******************************* ISR() ***********************************/

//Timer2 compare match interrupt vector handler
 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)     // if 22 0's are received in a row (88uS break)
     bitClear(TIMSK2, OCIE2A);    //disable this interrupt and enable reception interrupt now that we're in a break.
     bitSet(UCSR0B, RXCIE0);
} //end Timer2 ISR

 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

 if(dmxcurrent > dmxaddress) {         //check if the current address is the one we want.
   dmxvalue[i] = dmxreceived;
   if(i == NUMBER_OF_CHANNELS) {
     bitClear(UCSR0B, RXCIE0);
     dmxnewvalue = 1;                        //set newvalue, so that the main code can be executed.
} // end ISR

/******************************* MegaBrite Code ***********************************/

void SB_SendPacket() {

   if (SB_CommandMode == B01) {
    SB_RedCommand = 120;
    SB_GreenCommand = 100;
    SB_BlueCommand = 100;

   SPDR = SB_CommandMode << 6 | SB_BlueCommand>>4;
   while(!(SPSR & (1<<SPIF)));
   SPDR = SB_BlueCommand<<4 | SB_RedCommand>>6;
   while(!(SPSR & (1<<SPIF)));
   SPDR = SB_RedCommand << 2 | SB_GreenCommand>>8;
   while(!(SPSR & (1<<SPIF)));
   SPDR = SB_GreenCommand;
   while(!(SPSR & (1<<SPIF)));


void WriteLEDArray() {

   SB_CommandMode = B00; // Write to PWM control registers
   for (int h = 0;h<NumLEDs;h++) {
       SB_RedCommand = LEDChannels[h][0];
       SB_GreenCommand = LEDChannels[h][1];
       SB_BlueCommand = LEDChannels[h][2];

   digitalWrite(latchpin,HIGH); // latch data into registers

   SB_CommandMode = B01; // Write to current control registers
   for (int z = 0; z < NumLEDs; z++) SB_SendPacket();
   digitalWrite(latchpin,HIGH); // latch data into registers



Not really sure...could it have something to do with the DMX address of 4 at the beginning? And then an offset due to the zero index arrays...

How about if you try to make the software address just that one LED. You might have to send starting on channel 2 maybe?

Or that LED might just be bad, unless you can make it work at another location.
Unique RGB LED Modules and Arduino shields: http://www.macetech.com/store


this is amazing.  i do visual design for a band and we have a ton of DIY stuff i've done already... we were considering moving to element labs gear for the next phase but this whole thread has me seriously reconsidering.  this will probably look incredible when finished.

offtopic maybe but:  which version of madrix did need to get to support the pixelmapping?  


Any update on this incredible project?? I'd love to see some more photo's or video of the progress! Pretty please?


Seconded on that. Such an inspiration.

Go Up