5x5x5 LED Cube w/ Animation Software Need Help

Hello Everybody! As all new users who are overly fascinated with blinking lights I have built a 5x5x5 blue led cube. It was my first complete circuit design and build. See pictures below for the finished hardware portion of the project. I am using 4 shift register chips with a 32 digit shift to control the cube off 3 wires. Yes I am driving the ground wires for the rows off transistors controlled by the shift registers, make for nice neat code.

Pics:
http://twilightsavant.com/forumPics/cube1.jpg
http://twilightsavant.com/forumPics/cube2.jpg
http://twilightsavant.com/forumPics/cube3.jpg
http://twilightsavant.com/forumPics/cubeSoftware.jpg

I just got the thing turned on and can display static test patterns, however when I turn the delay off in my code to get it multiplexing it is working and not flickering, however the lights are only about 75% as bright as when not multiplexing. I know that the 74HC595 was a little weak on the power side in driving this so I kinda expected it. I am okay with the brightness for this cube but am hoping answers to my question below might turn the speed up and solve my issue.

The second part of this project, which I am working on now, is an open source c# app that lets you animate and control the cube from the desktop. I will be posting up full source code and finished project for other people to use since i havent been able to find one myself that wasn't in german and was generic enough to modify. The software is at v1 right in the UI without the animation piece working but it is coming along.

So my questions are as follows:

  1. I heard that the digitalWrite function has a bunch of logic behind it that slows it down, I dug in the core files to see if I could figure out the direct command but couldn't find the specific code to turn on and off a pin without the logic. Is there a faster way to control a pin if we don't need the logic?

  2. I have my c# app working and spitting out serial commands to the Cube, but can't move on to the animation portion of it until I get unstuck on the cube. Problem I am having is I can't seem to find out how to do an interrupt off a serial input to deliver the new patterns from the c# app to the cube? Anybody have example code of how to drive an interrupt off a serial line?

Below is my code:

int latchPin = 2; //(Latch)
int clockPin = 3; //(Clock)
int dataPin = 4; //(Data)

long int shiftOutValues[5];

void setup() {
  //Serial.begin(9600);

  //set pins to output
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);

  //these are the actual numbers that get shifted out on each cycle (can't seem to store 32 digit binary in any type of var)
  shiftOutValues[0] = 2231369727;
  shiftOutValues[1] = 1107296255;
  shiftOutValues[2] = 536870943;
  shiftOutValues[3] = 268435487;
  shiftOutValues[4] = 134217759;
}

void loop() {
  for(int rowCount=0; rowCount<5; rowCount++) {
    dataToRegisters(rowCount);
    delay(delayCount);  //this is just here for debugging so we can see what is going on
  }
}

void dataToRegisters(int rowCount) {
  unsigned long shiftdata = shiftOutValues[rowCount]; //grab the values for this row
  unsigned long shiftmask = 1;

  //set latchPin low to allow data flow
  digitalWrite(latchPin, LOW);

  //clear shift register ready for sending data (not sure why we have to do this but just do it! (maybe we can remove it later????)
  digitalWrite(dataPin, LOW);
  digitalWrite(clockPin, LOW);

  for (byte i = 0; i < 32; i++) { //send all 32 bits out to the registers
    digitalWrite(clockPin, LOW);
    
    if (shiftdata & shiftmask) {
      digitalWrite(dataPin, HIGH);
      //Serial.print('1');  //send data to serial as well so we can see whats going on
    }
    else {
      digitalWrite(dataPin, LOW);
     //Serial.print('0');  //send data to serial as well so we can see whats going on
    }

    digitalWrite(clockPin, HIGH);  //clock each bit into shift register

    shiftmask = shiftmask * 2;       //right shift bit mask one bit
  }

  digitalWrite(clockPin, LOW);  //reset our clock pin just because
  digitalWrite(latchPin, HIGH); //latch so the LEDs reflect the new patter
  //Serial.println('B');  //ya just so we get a better debug
}

Problem I am having is I can't seem to find out how to do an interrupt off a serial input to deliver the new patterns from the c# app to the cube? Anybody have example code of how to drive an interrupt off a serial line?

You don't need interrupts to read serial data. Just add a call to Serial.available() in loop. If Serial.available() returns a number of bytes greater than 0, read the available data. When it is enough, use the new data instead of the old data.

Hmm... that makes sense. I will try to add that when I get off work later... Thanks for the help!

Nice work, and very clean construction. I also built a blue 5x5x5 led cube, ( http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1271300719) but I used different shift register chips that included constant current drivers built-in, instead of series resistors which maintains even brightness. In your set-up brightness will always be somewhat dependent on your multiplexing scan timing and duty cycle, it's just a nature of the beast.

My design kept the patterns on board either in the avr's eeprom memory or on a larger I2C eeprom chip. I sure wish I could write a pattern generator GUI application for my windows machine, but it's beyond my programming skills. I had to write standalone arduino sketches that would load a specific pattern into my eeprom memories. That is pretty time consuming and boring after awhile, so I have only made about 5 different patterns and kind of left the project at the stage. It works great and is constantly blinking away on my self.

My code uses the MStimer2 library to generate constant 2 millsecond interrupts where I shift out the 25 bits of a given level, resulting in a 10ms refresh time for the complete cube.

Lefty

Also yes, directly writing data to the output ports is much faster then using the arduion digitalWrite() commands. Arduino Reference - Arduino Reference

Keep in mind that this then loses portablity between some of the various arduino compatible avr controller chips as they have different pin mappings between port/pin to arduino pin numbering.

Lefty

Lefty, I took a look at your post up while I was trying to get the firmware working, I borrowed your shiftmask code and analyzed how you did the shift out to get where I am now.

Once I get this project working I want to start schematics on a larger cube I know there are alot of considerations with scaling, but would the "Allegro A6276" be the best for say an 8x8x8 or a 10x10x10 if they are switching transistors instead of the actual LEDs?

Thanks for the heads up on the port manipulation I think I will stick with digitalWrite for now, maybe switch it later after I have released all my code and the GUI, to boost the brightness for the finished project.

Once I get this project working I want to start schematics on a larger cube I know there are alot of considerations with scaling, but would the "Allegro A6276" be the best for say an 8x8x8 or a 10x10x10 if they are switching transistors instead of the actual LEDs?

Humm, I would probably have to see a schematic mock-up first to be sure, but my first impression is no. The shift registers A6276 are active level low constant current outputs, so having them wired to transistor switches doesn't seem easy/practical, but again I would need to understand more. The same driving scheam (constant current low outputs) and +5vdc transistor switched for the common anode levels should scale up OK for even 10x10x10.

After all the soldering and mechanical aligning needed for a 5x5x5 cube, I don't envy your challenge for a 8x8x8 or 10x10x10 cube. I still think having odd numbered cubes has better 'artistic' capablities as you have a true middle row and column to work with.

Lefty

Alright so after much banging my head against the wall with Serial comms I am asking for help...

Firstly my end goal is to be able to send a message like A2230321152,B1073741824,C53687091, etc.... A being the first row of values, B being the second and so on. The letter dictates the row and the comma is the end of value char. I have been working with the serial tutorials and some past projects but can not for the life of me get it to resolve the variable correctly. My code is below, but what I am getting from the serial.print is not the value I send instead it sends back a different number. Can anybody help me get the numerical (long int) value I am sending via serial into a variable that will work for what I am a doing below, the end result has to be a long int because I am shifting out and can't hold a 32bit binary in any var I can find. When I send the first row of instructions to the code below that looks like: A2230321152, I have to break it up into seperate lines (haven't addressed this yet), but it only stores the number 147? Why?

I will be moving the serial stuff to its own function once I get it to properly resolve.

int latchPin = 2; //(Latch)
int clockPin = 3; //(Clock)
int dataPin = 4; //(Data)

long int shiftOutValues[5];
int currentRow = 0;
long int rcvdMessage[5];
int messageRow = 0;
long int readString;

void setup() {
  Serial.begin(9600);

  //set pins to output
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);

  //these are the actual numbers that get shifted out on each cycle (can't seem to store 32 digit binary in any type of var)
  shiftOutValues[0] = 2231369727;
  shiftOutValues[1] = 1107296255;
  shiftOutValues[2] = 536870943;
  shiftOutValues[3] = 268435487;
  shiftOutValues[4] = 134217759;
}

void loop() {
  for(int rowCount=0; rowCount<5; rowCount++) {
    dataToRegisters(rowCount);
   // delay(delayCount);  //this is just here for debugging so we can see what is going on
  }
  
  while(Serial.available() > 0) {
    //store what it gets
    char incomingByte = Serial.read();

    //this will get converted into a while depending on speed tests once we add the other rows to it
    if(incomingByte == 'A') {
        messageRow=0;
        //reset the incoming buffer because we just started a new row
        readString=NULL;
        Serial.println("Row A");
    } else if(incomingByte == ',') { //look for the ending number sequence
        rcvdMessage[messageRow] = readString;  //save down the read string into the corresponding message row in our holding array
        shiftOutValues[messageRow] = rcvdMessage[messageRow];  //for now kick this out, but we will be waiting for a full set of row instructions before this pushes in the final
        Serial.println("End of Sequence");  //debug msgs
        Serial.println(rcvdMessage[messageRow]);  //debug msgs
    } else { 
      long int c = Serial.read();  //grab the incoming bytes as an int
      readString += c;  //add the new bytes to the buffer we have going
    }
  }
}

You read the first character from the serial port. If it is not A and is not comma, you throw it away, and read another character from the serial port to store.

Stop that. Store the character that you read in the first place.

What do you (intend to) do with 'B', 'C', etc.?

LOL, well if it is throwing it away I guess that would explain why it isn't working :-/

Umm what I am trying to do is issue from a GUI on my desktop over the serial line five numbers that will ultimately drop into the array that is being shifted out to the LED cube. So what I want to send is:

2147483664
1073741824
536870912
268435456
134217728

Each number translates into a 32bit binary number that drives the shiftRegisters. I figured that since Serial sometimes drops numbers and it seems to be cleanest to have a start and end character I could use A, B, C, D, E to signify which row the number corresponds to in the shift out array. So the letters are for the rows, then the comma is the closing character to tell it to stop listening for that row. Like below:

while(Serial.available() > 0) {
    //store what it gets
    char incomingByte = Serial.read();

    //this will get converted into a while depending on speed tests once we add the other rows to it
    if(incomingByte == 'A') {
        messageRow=0;
        //reset the incoming buffer because we just started a new row
        readString=NULL;
        Serial.println("Row A");
    } else if(incomingByte == 'B') {
        messageRow=1;
        //reset the incoming buffer because we just started a new row
        readString=NULL;
        Serial.println("Row B");
    } else if(incomingByte == 'C') {
        messageRow=2;
        //reset the incoming buffer because we just started a new row
        readString=NULL;
        Serial.println("Row C");
    } else if(incomingByte == 'D') {
        messageRow=3;
        //reset the incoming buffer because we just started a new row
        readString=NULL;
        Serial.println("Row D");
    } else if(incomingByte == 'E') {
        messageRow=4;
        //reset the incoming buffer because we just started a new row
        readString=NULL;
        Serial.println("Row E");
    } else if(incomingByte == ',') { //look for the ending number sequence
        rcvdMessage[messageRow] = readString;  //save down the read string into the corresponding message row in our holding array
        shiftOutValues[messageRow] = rcvdMessage[messageRow];  //for now kick this out, but we will be waiting for a full set of row instructions before this pushes in the final
        Serial.println("End of Sequence");  //debug msgs
        Serial.println(rcvdMessage[messageRow]);  //debug msgs
    } else { 
      long int c = Serial.read();  //grab the incoming bytes as an int
      readString += c;  //add the new bytes to the buffer we have going
    }
  }

I thought since the serial logic was, if (a) elseif(b) else gather up the number and I could have it store the message as it comes in into a var that was defined outside the loop. The when each new row starts signified by the letter clear out the message var so we have a clean var to start recording into. Is there a better way to do this???

The idea is correct. The implementation is wrong.

while(Serial.available() > 0) {
    char incomingByte = Serial.read();
    if(incomingByte == 'A') {
.
.
.
    } else if(incomingByte == ',') {
        rcvdMessage[messageRow] = readString; 
        shiftOutValues[messageRow] = rcvdMessage[messageRow];
        Serial.println("End of Sequence");  //debug msgs
        Serial.println(rcvdMessage[messageRow]);  //debug msgs
    } else {
      long int c = Serial.read();  //grab the incoming bytes as an int
      readString += c;  //add the new bytes to the buffer we have going
    }

In the else clause, you throw away incomingByte, if it was not a letter or a comma, and read and store another byte.

You should be storing incomingByte in the array, not reading another byte.

The readString object is a String? Is the rcvdMessage array also of type String? Does shiftOutValues expect a String? Does it correctly convert the String to a long?

Ahh... I had a sneaking suspicion that with every Serial.read() it was pulling the next byte. The complete code is below, its the Casting of the vars that get me.

The var types are below:
long int shiftOutValues[5];
int currentRow = 0;
long int rcvdMessage[5];
int messageRow = 0;
long int readString;

I modified the code (see below) so it doesn't throw away the incomingByte but stores it and when I send A2147483664, over the serial line the debug message comes back as:

Row A
End of Sequence
525

So is it the casting that is screwing this up or I am not decoding the values coming over the serial line correctly?

Full Code Below:

int latchPin = 2; //(Latch)
int clockPin = 3; //(Clock)
int dataPin = 4; //(Data)

long int shiftOutValues[5];
int currentRow = 0;
long int rcvdMessage[5];
int messageRow = 0;
long int readString;

void setup() {
  Serial.begin(9600);

  //set pins to output
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);

  //these are the actual numbers that get shifted out on each cycle (can't seem to store 32 digit binary in any type of var)
  shiftOutValues[0] = 2231369727;
  shiftOutValues[1] = 1107296255;
  shiftOutValues[2] = 536870943;
  shiftOutValues[3] = 268435487;
  shiftOutValues[4] = 134217759;
}

void loop() {
  for(int rowCount=0; rowCount<5; rowCount++) {
    dataToRegisters(rowCount);
   // delay(delayCount);  //this is just here for debugging so we can see what is going on
  }
  
  while(Serial.available() > 0) {
    //store what it gets
    char incomingByte = Serial.read();

    //this will get converted into a while depending on speed tests once we add the other rows to it
    if(incomingByte == 'A') {
        messageRow=0;
        //reset the incoming buffer because we just started a new row
        readString=NULL;
        Serial.println("Row A");
    } else if(incomingByte == 'B') {
        messageRow=1;
        //reset the incoming buffer because we just started a new row
        readString=NULL;
        Serial.println("Row B");
    } else if(incomingByte == 'C') {
        messageRow=2;
        //reset the incoming buffer because we just started a new row
        readString=NULL;
        Serial.println("Row C");
    } else if(incomingByte == 'D') {
        messageRow=3;
        //reset the incoming buffer because we just started a new row
        readString=NULL;
        Serial.println("Row D");
    } else if(incomingByte == 'E') {
        messageRow=4;
        //reset the incoming buffer because we just started a new row
        readString=NULL;
        Serial.println("Row E");
    } else if(incomingByte == ',') { //look for the ending number sequence
        rcvdMessage[messageRow] = readString;  //save down the read string into the corresponding message row in our holding array
        shiftOutValues[messageRow] = rcvdMessage[messageRow];  //for now kick this out, but we will be waiting for a full set of row instructions before this pushes in the final
        Serial.println("End of Sequence");  //debug msgs
        Serial.println(rcvdMessage[messageRow]);  //debug msgs
    } else {
      long int c = incomingByte;  //grab the incoming bytes as an int
      readString += c;  //add the new bytes to the buffer we have going
    }
  }
}

void dataToRegisters(int rowCount) {
  unsigned long shiftdata = shiftOutValues[rowCount]; //grab the values for this row
  unsigned long shiftmask = 1;

  //set latchPin low to allow data flow
  digitalWrite(latchPin, LOW);

  //clear shift register ready for sending data (not sure why we have to do this but just do it! (maybe we can remove it later????)
  digitalWrite(dataPin, LOW);
  digitalWrite(clockPin, LOW);

  for (byte i = 0; i < 32; i++) { //send all 32 bits out to the registers
    digitalWrite(clockPin, LOW);
    
    if (shiftdata & shiftmask) {
      digitalWrite(dataPin, HIGH);
      //Serial.print('1');  //send data to serial as well so we can see whats going on
    }
    else {
      digitalWrite(dataPin, LOW);
     //Serial.print('0');  //send data to serial as well so we can see whats going on
    }

    digitalWrite(clockPin, HIGH);  //clock each bit into shift register

    shiftmask = shiftmask * 2;       //right shift bit mask one bit
  }

  digitalWrite(clockPin, LOW);  //reset our clock pin just because
  digitalWrite(latchPin, HIGH); //latch so the LEDs reflect the new patter
  //Serial.println('B');  //ya just so we get a better debug
}

I thought since the serial logic was, if (a) elseif(b) else gather up the number and I could have it store the message as it comes in into a var that was defined outside the loop.

I just read this part, and I think I see what the problem is. The else clause reads a single byte, not the whole number.

Perhaps you should post the sender code, so we can see how the number part of the string is sent - as a collection of characters, as 4 bytes, etc.

Then, we could help you write the else clause so that it does read the whole number, converting what was read to a number, if needed.

The sender app isn't sending yet, just because I am waiting to see what the arduino needs to get to make it happen. So we can literally have it send anything out. In the pic below this is what the app looks like right now:

http://twilightsavant.com/forumPics/cubeSoftware.jpg

Its the five text boxes that hold the numbers that will be getting pushed over the line (I can have them as binary or as Int32 doesn't matter). If there is a good way to transmit them or a format that I should be aiming for let me know and I can do the research on it. I figured for speed since this is happening during the refresh time of the cube it would be faster to push the whole command and have the arduino feed it in one byte per cycle so as not to kill the refresh on the cube.

My goal with the GUI and the code is that it could easily be modified to other size cubes or different sequences (order of the LEDs) for other people to use on their own cubes when I post the c# source.

That being said something like:
A2230321152,B1073741824,C53687091,D2230321152,E1073741824
would be my preference or even something like
A111010101100001111,B111010101100001111,A111010101100001111, etc..

would work for me but one burst would be preferred I guess, as the GUI will also act as an animation program that will send a new pattern over the serial line every second, or whatever the user types in as the pattern hold time in the UI.

The sender app isn't sending yet,

So what is sending the data you are trying to read? The Serial Monitor? Nothing?

It's hard to develop a receiver without having a working transmitter.

That makes sense. I just modified the GUI to send data out over the serial line, this is what it is sending, all on one line:

A2147749888,B1073741824,C537133056,D268435456,E134217728,

Hit me up with a personal message and your email and I can send you the source files and the executable if that would help. From looking at the serial port monitor I am running on the open port the comms look the same if you send them through the serial monitor in arduino IDE as when you send them through the GUI.

I sent you a PM. We can continue this off-line, tomorrow.

WOOT!!! I got it working this is what I had to do to be able to push a each byte (or number) onto the end of the unsigned long int:

readString *= 10;
      readString += incomingByte - '0';
readString *= 10;

This is an integer operation on a variable whose name implies that it is a string. You might want to give some thought to using names that don't cause incorrect assumptions to be made about their purpose.

As in, I_am_wealthy = house+wife+kids+bills; ?