I got a message from someone a few days ago asking for my code to run my 3x3x3 cube, but I accidentally deleted his email when I was clearing out my Outlook folders. I don't want to seem to be a git by ignoring him so I'm going to post it here as I know he reads the forums regularly. Sorry mate, my fingers slipped and I deleted your email.
WARNING - I annotate my sketches heavily so I can track what is going on. If it irritates you, just delete the excess annotations and white space accordingly.
/* 04/11/21 - A sketch for a 3 x 3 x 3 LED cube, the details of which follow:
* The cube has 9 columns, each directly connected to an Arduino pin ( numbers D2 to D10 ) via a 420 ohm resistor for each column. The columns are active positive ( active HIGH ) directly
* through the Arduino digital pins and associated resistors.
* The cube has 3 layers, each connected to a separate Collector of a BC547 ( or equipvalent ) transistor. The Emitters are connected to common GND. The BASE of each transistor
* is connected to one side of a 22k ohm resistor and the other side of the resistor is connected to the relevant Arduino pin. In this case, A4 for the top layer, A2 for the Mid layer
* and A0 for the bottom layer. Making the relevant Base HIGH will turn on the transistor and thus make the relevant plane of the cube connected to GND, which will turn on any activated LEDs on
* that particular plane. If the Base is LOW, the transistor is turned off and the relevant plane is not connected to GND and so no LEDs on that plane will light up.
*
* Diagramatically, the cube columns and pin allocations for my cube are as follows:
*
* 1 8 7 D2 D9 D8
* CUBE COLUMNS: 2 9 6 ASSOCIATED ARDUINO PINS: D3 D10 D7
* 3 4 5 D4 D5 D6
*
* Top Plane is controlled from Analog Pin A4
* Mid Plane is controlled from Analog Pin A2
* Bot Plane is controlled form Analog Pin A0
*
* In the user function displayLayer, I use a PoV delay setting of 10 milliseconds. This is enough to eliminate led flicker for me, but you can change it to conform to your
* own eyesight capability. For instance, my grand daughter does not see any flicker at 15 milliseconds, but I need 10 milliseconds beacuse I'm an old git with crappy vision.
*/
const byte columnArray [ ] = { 2, 3, 4, 5, 6, 7, 8, 9, 10 }; // these are the arduino digital pins controlling the columns
const byte planeArray [ ] = { A4, A2, A0 }; // these are the arduino pins controlling the layers via transistors
const word cubeArray [ ] = {
0b111000000, 0b000000000, 0b000000000, 1000, // A simple demo. It illuminates 3 consecutive leds on the top ( 1, 2 and 3 ), ( 4, 8 and 9 ) on the mid, ( 5, 6 and 7 ) on the bottom...
0b111000000, 0b000100011, 0b000000000, 1000, // .. with a one second delay between each plane illumination
0b111000000, 0b000100011, 0b000011100, 1000, // I've laid it out this way so you can visually determine the columns as Top, Mid, Bot and delayInterval of each frame....
0b000000000, 0b000000000, 0b000000000, 1000 // ... so that each of the lines are one full frame. Binary notation also helps to visualise which LEDs are active
}; // end of cubeArray[]. REMEMBER TO MISS OUT THE LAST COMMA OF THE ARRAY or you'll get a compile error.
word frameStart = 0; // this points to the beginning of the cubeArray, it is initialised to element zero = cubeArray [ 0 ]
word arraySize = sizeof ( cubeArray ) / 2; // i've declared the elements as word, so need to divide the sizeof() by 2 to get just the element total instead of total bytes
unsigned long currentTime = millis(); // used in the loop to initialise the timing to the current time of the start of the loop()....
unsigned long previousTime = 0; // ... along with this. See loop() for it's use
word delayInterval = cubeArray [ frameStart + 3 ]; // zero indexed and points at the delay between frames ( contained in the cubeArray every 4 elements )
void setup()
{
for ( byte incrementColumn = 0; incrementColumn < sizeof ( columnArray ); incrementColumn ++ ) // this 'for' loop just sets the pinMode and state of the COLUMN pins, ie to OUTPUT and LOW
{
pinMode ( columnArray [ incrementColumn ], OUTPUT );
digitalWrite ( columnArray [ incrementColumn ], LOW );
} // end of for incrementColumn
for ( byte incrementPlane = 0; incrementPlane < sizeof ( planeArray ); incrementPlane ++ ) // this 'for' loop just sets the pinMode and state of the PLANE pins, ie to OUTPUT and LOW
{
pinMode ( planeArray [ incrementPlane ], OUTPUT );
digitalWrite ( planeArray [ incrementPlane ], LOW );
} // end of for incrementPlane
} // end of setup
void loop()
{
currentTime = millis(); // get the current time
if ( currentTime - previousTime >= delayInterval ) // if the delayInterval is exceeded ( ie the time in milliseconds indicated in element 4 of the current array frame )
{
frameStart = frameStart + 4; // ... move to the next frame. Remember, a FRAME is 4 elements of the cubeArray - cubeArray [ 0 ], [ 1 ], [ 2 ] and [ 3 ]
if ( frameStart > arraySize - 1 ) // if we are at the end of the cubeArray elements ...
{
frameStart = 0; // reset the cubeArray to the beginning of the very first frame...
} // end of if frameStart
previousTime = currentTime; // ... and update the timer
} // end of if currentTime
displayLayer ( 0, cubeArray [ frameStart ] ); // send the planeArray element for the TOP layer and the content of the cubeArray element of the current frame out to the function displayLayer()
displayLayer ( 1, cubeArray [ frameStart + 1 ] ); // send the planeArray element for the MID layer and the content of the second cubeArray element of the current frame out to the function
displayLayer ( 2, cubeArray [ frameStart + 2 ] ); // send the planeArray element for the BOT layer and the content of the third cubeArray element of the current frame out to the function
} // end of loop ()
void displayLayer ( byte layerNumber, word elementContent ) // get the content of the above
{
digitalWrite ( planeArray [ layerNumber ], HIGH ); // turn on the relevant plane ( talk to it nicely in a sexy voice )
if ( ( elementContent & 0b100000000 ) == 0b100000000 ) { digitalWrite ( columnArray [ 0 ], HIGH ); } // using BIT comparison on the columnArray element that was passed to the function to see if the relevant column is TRUE and turned on
else { digitalWrite ( columnArray [ 0 ], LOW ); } // if not TRUE, then turn it off. Do the same with all 9 bits of the element
if ( ( elementContent & 0b010000000 ) == 0b010000000 ) { digitalWrite ( columnArray [ 1 ], HIGH ); }
else { digitalWrite ( columnArray [ 1 ], LOW ); }
if ( ( elementContent & 0b001000000 ) == 0b001000000 ) { digitalWrite ( columnArray [ 2 ], HIGH ); }
else { digitalWrite ( columnArray [ 2 ], LOW ); }
if ( ( elementContent & 0b000100000 ) == 0b000100000 ) { digitalWrite ( columnArray [ 3 ], HIGH ); }
else { digitalWrite ( columnArray [ 3 ], LOW ); }
if ( ( elementContent & 0b000010000 ) == 0b000010000 ) { digitalWrite ( columnArray [ 4 ], HIGH ); }
else { digitalWrite ( columnArray [ 4 ], LOW ); }
if ( ( elementContent & 0b000001000 ) == 0b000001000 ) { digitalWrite ( columnArray [ 5 ], HIGH ); }
else { digitalWrite ( columnArray [ 5 ], LOW ); }
if ( ( elementContent & 0b000000100 ) == 0b000000100 ) { digitalWrite ( columnArray [ 6 ], HIGH ); }
else { digitalWrite ( columnArray [ 6 ], LOW ); }
if ( ( elementContent & 0b000000010 ) == 0b000000010 ) { digitalWrite ( columnArray [ 7 ], HIGH ); }
else { digitalWrite ( columnArray [ 7 ], LOW ); }
if ( ( elementContent & 0b000000001 ) == 0b000000001 ) { digitalWrite ( columnArray [ 8 ], HIGH ); }
else { digitalWrite ( columnArray [ 8 ], LOW ); }
delay ( 10 ); // pov delay ( see notes at top of the sketch )
digitalWrite ( planeArray [ layerNumber ], LOW ); // turn off the layer or all sorts of funny stuff happens
} // end of displayLayer