Very simple 3x3x3 cube sketch

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


Best way to build a 4 by 4 by 4 LED cube (which would therefore include a 3 x 3 x 3) is to use a MAX7219 module. :sunglasses:

Vastly simpler and more reliable code.

Is there a good reason this isn't part of a for loop?

For Paul-B - I appreciate that, but the guy just wanted the absolute basics as a 3x3x3 is his first 'major' project since taking up the Arduino experience, and he was getting confused with shift registers, bit - shifting and other stuff that he'd seen from other sketches. I kept it as simple as I could without the need for shift registers, MAX7219 or other similar ICs. The cube from this sketch just takes the 27 LEDs, 3 Transitors, 3 x 22k ohm Resistors and 9 x 420 ohm Resistors and an Arduino Nano / Uno. The sketch is as simple as I can make it, based on the knowledge he already has accumulated.

For TheMemberFormerlyKnownAsAWOL - As I said, this is a simple sketch just to get the guy started on a 3x3x3 cube using the language knowledge he already has and is comfortable with so that he's not scratching his head trying to figure out a relatively complex 'for' loop. He can easily follow what's happening with the function as it's written.

That is essentially an oxymoron!

A MAX7219 would take the 27 LEDs, one resistor and two capacitors, all bar the LEDs are available ready mounted on a cheap module. The code is massively simplified.

Similarly, you need to comprehend that such things as "for" loops make code much simpler and more comprehensible.

If - of course - properly written. :grin: