maker

icStation 4x4x4 LED Cube Shield for Arduino – Software

As described in my previous post, I now have a working (I’ve run the demo code!) icStation 4x4x4 LED cube shield connected up to an Arduino Uno.  Unfortunately as I started to dig around in the sample library, I just couldn’t quite work out how it was meant to address each plane of the cube.  Eventually I decided it wasn’t possible, due to a number of bugs in the code.

The demo code worked as the code just showed the same pattern on each of the four planes.  This is largely because the initialisation routines set the four Ardunio data pins to low, and then never did anything else with them!  There is some mention of pins 16 and 17, but no mention of pins 18 and 19, and actually in the main display routines, there is nothing done to actually set any of these pins once initialisation is complete.  So at this point I decided to read a bit more about the 74HC595 and just go it alone.  Attached is the result.  Feel free to use as you see fit.

Basic initialisation

The two shift registers seem fairly simple to setup.  The Arduino needs some control pins setting up as outputs alongside the four digital outs used for the LEDs.  The initialisation is quite straight forward as follows:

#include <Arduino.h>

int HC595_clockPin=0;   // SH_CP of 74HC595 
int HC595_latchPin=1;   // ST_CP of 74HC595 
int HC595_dataPin=3;    // DS of 74HC595 
int HC595_enablePin=2;  // Not OE of 74HC595
int LED_Pin16= 4;
int LED_Pin17= 5;
int LED_Pin18= 6;
int LED_Pin19= 7;

void setup() {
  // put your setup code here, to run once:
  pinMode( HC595_latchPin,  OUTPUT );
  pinMode( HC595_clockPin,  OUTPUT );
  pinMode( HC595_dataPin,   OUTPUT );
  pinMode( HC595_enablePin, OUTPUT );
  
  pinMode( LED_Pin16, OUTPUT );
  pinMode( LED_Pin17, OUTPUT );
  pinMode( LED_Pin18, OUTPUT );
  pinMode( LED_Pin19, OUTPUT );
  
  digitalWrite(LED_Pin16,HIGH);
  digitalWrite(LED_Pin17,HIGH);
  digitalWrite(LED_Pin18,HIGH);
  digitalWrite(LED_Pin19,HIGH);
//  digitalWrite(HC595_enablePin, LOW);  // Enable Not OE (negative logic)
}

I never did quite work out if I needed to do anything with the enable pin, but as it is active low, I assumed that by not doing anything, everything would be enabled by default anyway.

In terms of actually writing a value to the 74HC595, again that is fairly straight forward.

/*
  Protocol for sending the data to the hc595 is as follows:
   (see: http://www.arduino.cc/en/Tutorial/ShiftOut)
   
   "when the clock pin goes from low to high, the shift register
    reads the state of the data pin ... when the latch pin goes
    from low to high the sent data gets moved from the shift
    registers ... to the output pins"
   
   As we have two HC595s chained together, we use a 16 bit input value
*/
void write_74HC595 (unsigned int hc595value) { 
   digitalWrite(HC595_latchPin, LOW);   // ensures LEDs don't light whilst changing values
   
   // Shift each 8 bit value in sequence - the two chained HC595s automatically grab
   // the right bits - the first 8 to the first chip, second 8 to the second chip
   shiftOut(HC595_dataPin, HC595_clockPin, LSBFIRST, hc595value);  
   shiftOut(HC595_dataPin, HC595_clockPin, LSBFIRST, (hc595value >> 8));
   
   digitalWrite(HC595_latchPin, HIGH);  // data transferred from shift register to outputs when latch goes LOW->HIGH
}

So 16 bits of the hc595value variable are sent in two 8-bit chunks over the serial port to the shift registers and with the appropriate signalling via the latch pin, that is basically that.  This sets the outputs of the 74HC595 to high, but in order to make the LEDs come on, the Arduino data pins corresponding to the horizontal planes must be set low.  If a different pattern is required for each plane, then some kind of simple ‘scanning’ is required as illustrated below.

int LED_Plane[] = {LED_Pin16, LED_Pin17, LED_Pin18, LED_Pin19};

/*
  Inputs: Array of 4 integers - one for each plane
*/
void display (unsigned int *pPattern)
{
  int i;
  for (i=0; i<4; i++)
  {
    int j;
    for (j=0; j<1000; j++)
    {
      // Slow this down so that there is time for the LEDS to light
      // Experimentation shows that 200+ gives brighter LEDs
      // NB: Do it this way so an empty loop isn't optimised out
      if (j==0)
      {
        digitalWrite(LED_Plane[0], HIGH);
        digitalWrite(LED_Plane[1], HIGH);
        digitalWrite(LED_Plane[2], HIGH);
        digitalWrite(LED_Plane[3], HIGH);
        write_74HC595 (pPattern[i]);
        digitalWrite(LED_Plane[i], LOW);
      }
    }
  }
}

I guess the only thing to really note here is that without the loop involving j then the ‘scanning’ was happening too fast to allow the LEDs to have any significant brightness at all.  I initially had a simple for (j=0; j<1000; j++) {}; statement at the end, but this seemed to be optimised out – at least it didn’t seem to have the delay effect I wanted, so instead I made the code only act on one pass through the loop.

So, with these basics, it is now possible to get a 64 bit pattern, in the form of four 16 bit values, each representing one plane of 16 LEDs, onto the cube.

I have the full demo code file below – it isn’t massively pretty, and its not cpp (sorry – I’ve always been more of a C person I’m afraid, and my C is a little rusty), but it works for me.  Your proverbial mileage, as they say, may vary.

Kevin

#include <Arduino.h>

int HC595_clockPin=0;   // SH_CP of 74HC595 
int HC595_latchPin=1;   // ST_CP of 74HC595 
int HC595_dataPin=3;    // DS of 74HC595 
int HC595_enablePin=2;  // Not OE of 74HC595
int LED_Pin16= 4;
int LED_Pin17= 5;
int LED_Pin18= 6;
int LED_Pin19= 7;
int LED_Plane[] = {LED_Pin16, LED_Pin17, LED_Pin18, LED_Pin19};

// Each line (8 bytes) is an entire cube, with two consecutive bytes per plane of LEDS,
// and 16 LEDS per plane. LEDs are encoded in the following order:
//    Lowest plane byte 1, lowest plane byte 2, second lowerst plane 1, then 2,
//    second from top plane 1, then 2, highest plane 1, highest plane 2.
//
//    Each plane is encoded looking at the Arduino oriented with the USB/power
//    designated by 'south' by started 'north west' as follows:
//        D0    D1    D2    D3
//        D4    D5    D6    D7
//        D8    D9    D10   D11
//        D12   D13   D14   D15
//
//        D16   D17   D18   D19
//          (USB)      (Power)
//    With D16 being the lowest plane, through to D19 being the highest plane
//    Of course, if you wire the planes up differently, that is up to you!
//
//    Each two bytes of the pattern are therefore:
//        B00000000, B00000000 -> D0-7, D8-15
//    with D0 = msb of the first value, D7 being the lsb of the first value,
//    and  D8 = msb of the second value, D15 being the lsb of the second value.
//
//    So the entire pattern is:
//    B10010000,B00001001,B00000000,B00000000,B00000000,B00000000,B10010000,B00001001,
//     |      |  |     ||                                          |      |  |     ||
//     |      |  |     |\ D15 bottom plane                         |      |  |     |\ D15 top plane
//     |      |  |     \ D14 bottom plane                          |      |  |     \ D14 top plane
//     |      |  \ D8 bottom plane                                 |      |  \ D8 top plane
//     |      \ D7 bottom plane                                    |      \ D7 top plane
//     \ D0 bottom plane                                           \ D0 top plane
//
// Comment following in or out to switch patterns in or out
#define SWAP   1
#define SNAKE  1
#define BURST  1
#define SPIRAL 1
#define ALT    1
unsigned char pattern[] = {
#ifdef SWAP
  B10010000,B00001001,B00000000,B00000000,B00000000,B00000000,B10010000,B00001001,
  B00000000,B00000000,B10010000,B00001001,B10010000,B00001001,B00000000,B00000000,
  B00000000,B00000000,B01100000,B00000110,B01100000,B00000110,B00000000,B00000000,
  B01100000,B00000110,B00000000,B00000000,B00000000,B00000000,B01100000,B00000110,
  B00001001,B10010000,B00000000,B00000000,B00000000,B00000000,B00001001,B10010000,
  B00000000,B00000000,B00001001,B10010000,B00001001,B10010000,B00000000,B00000000,
  B00000000,B00000000,B00000110,B01100000,B00000110,B01100000,B00000000,B00000000,
  B00000110,B01100000,B00000000,B00000000,B00000000,B00000000,B00000110,B01100000,
#endif
#ifdef SNAKE
  B11001100,B00000000,B11001100,B00000000,B00000000,B00000000,B00000000,B00000000,
  B00000000,B00000000,B11001100,B00000000,B11001100,B00000000,B00000000,B00000000,
  B00000000,B00000000,B00000000,B00000000,B11001100,B00000000,B11001100,B00000000,
  B00000000,B00000000,B00000000,B00000000,B00001100,B11000000,B00001100,B11000000,
  B00000000,B00000000,B00000000,B00000000,B00000000,B11001100,B00000000,B11001100,
  B00000000,B00000000,B00000000,B00000000,B00000000,B01100110,B00000000,B01100110,
  B00000000,B00000000,B00000000,B00000000,B00000000,B00110011,B00000000,B00110011,
  B00000000,B00000000,B00000000,B00110011,B00000000,B00110011,B00000000,B00000000,
  B00000000,B00110011,B00000000,B00110011,B00000000,B00000000,B00000000,B00000000,
  B00000011,B00110000,B00000011,B00110000,B00000000,B00000000,B00000000,B00000000,
  B00110011,B00000000,B00110011,B00000000,B00000000,B00000000,B00000000,B00000000,
  B01100110,B00000000,B01100110,B00000000,B00000000,B00000000,B00000000,B00000000,
#endif
#ifdef BURST
  B00000000,B00000000,B00000110,B01100000,B00000110,B01100000,B00000000,B00000000,
  B00000110,B01100000,B01101001,B10010110,B01101001,B10010110,B00000110,B01100000,
  B01101001,B10010110,B10010000,B00001001,B10010000,B00001001,B01101001,B10010110,
  B10010000,B00001001,B00000000,B00000000,B00000000,B00000000,B10010000,B00001001,
  B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,
#endif
#ifdef SPIRAL
  B11001100,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,
  B01100110,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,
  B00110011,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,
  B00000011,B00110000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,
  B00000000,B00110011,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,
  B00000000,B01100110,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,
  B00000000,B11001100,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,
  B00001100,B11000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,
  B11001100,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,
  B00000000,B00000000,B11001100,B00000000,B00000000,B00000000,B00000000,B00000000,
  B00000000,B00000000,B01100110,B00000000,B00000000,B00000000,B00000000,B00000000,
  B00000000,B00000000,B00110011,B00000000,B00000000,B00000000,B00000000,B00000000,
  B00000000,B00000000,B00000011,B00110000,B00000000,B00000000,B00000000,B00000000,
  B00000000,B00000000,B00000000,B00110011,B00000000,B00000000,B00000000,B00000000,
  B00000000,B00000000,B00000000,B01100110,B00000000,B00000000,B00000000,B00000000,
  B00000000,B00000000,B00000000,B11001100,B00000000,B00000000,B00000000,B00000000,
  B00000000,B00000000,B00001100,B11000000,B00000000,B00000000,B00000000,B00000000,
  B00000000,B00000000,B11001100,B00000000,B00000000,B00000000,B00000000,B00000000,
  B00000000,B00000000,B00000000,B00000000,B11001100,B00000000,B00000000,B00000000,
  B00000000,B00000000,B00000000,B00000000,B01100110,B00000000,B00000000,B00000000,
  B00000000,B00000000,B00000000,B00000000,B00110011,B00000000,B00000000,B00000000,
  B00000000,B00000000,B00000000,B00000000,B00000011,B00110000,B00000000,B00000000,
  B00000000,B00000000,B00000000,B00000000,B00000000,B00110011,B00000000,B00000000,
  B00000000,B00000000,B00000000,B00000000,B00000000,B01100110,B00000000,B00000000,
  B00000000,B00000000,B00000000,B00000000,B00000000,B11001100,B00000000,B00000000,
  B00000000,B00000000,B00000000,B00000000,B00001100,B11000000,B00000000,B00000000,
  B00000000,B00000000,B00000000,B00000000,B11001100,B00000000,B00000000,B00000000,
  B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B11001100,B00000000,
  B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B01100110,B00000000,
  B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00110011,B00000000,
  B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000011,B00110000,
  B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00110011,
  B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B01100110,
  B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B11001100,
  B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00001100,B11000000,
  B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B11001100,B00000000,
#endif
#ifdef ALT
  B11111001,B10011111,B10010000,B00001001,B10010000,B00001001,B11111001,B10011111,
  B00000110,B01100000,B01101001,B10010110,B01101001,B10010110,B00000110,B01100000,
  B00000000,B00000000,B00000110,B01100000,B00000110,B01100000,B00000000,B00000000,
  B00000110,B01100000,B01101001,B10010110,B01101001,B10010110,B00000110,B01100000,
#endif
};
int patternNumber=0;
int numPatterns=sizeof(pattern)/8;
int tickCount=0;
int tickCountMax=50;      // How many times to loop before changing the pattern
unsigned int currentPattern[4];

void setup() {
  // put your setup code here, to run once:
  pinMode( HC595_latchPin,  OUTPUT );
  pinMode( HC595_clockPin,  OUTPUT );
  pinMode( HC595_dataPin,   OUTPUT );
  pinMode( HC595_enablePin, OUTPUT );
  
  pinMode( LED_Pin16, OUTPUT );
  pinMode( LED_Pin17, OUTPUT );
  pinMode( LED_Pin18, OUTPUT );
  pinMode( LED_Pin19, OUTPUT );
  
  digitalWrite(LED_Pin16,HIGH);
  digitalWrite(LED_Pin17,HIGH);
  digitalWrite(LED_Pin18,HIGH);
  digitalWrite(LED_Pin19,HIGH);
//  digitalWrite(HC595_enablePin, LOW);  // Enable Not OE (negative logic)

  patternNumber=0;
  tickCount = tickCountMax;
}

/*
  Protocol for sending the data to the hc595 is as follows:
   (see: http://www.arduino.cc/en/Tutorial/ShiftOut)
   
   "when the clock pin goes from low to high, the shift register
    reads the state of the data pin ... when the latch pin goes
    from low to high the sent data gets moved from the shift
    registers ... to the output pins"
   
   As we have two HC595s chained together, we use a 16 bit input value
*/
void write_74HC595 (unsigned int hc595value) { 
   digitalWrite(HC595_latchPin, LOW);   // ensures LEDs don't light whilst changing values
//   digitalWrite(HC595_enablePin, HIGH); // OE is negative logic
   
   // Shift each 8 bit value in sequence - the two chained HC595s automatically grab
   // the right bits - the first 8 to the first chip, second 8 to the second chip
   shiftOut(HC595_dataPin, HC595_clockPin, LSBFIRST, hc595value);  
   shiftOut(HC595_dataPin, HC595_clockPin, LSBFIRST, (hc595value >> 8));
   
   digitalWrite(HC595_latchPin, HIGH);  // data transferred from shift register to outputs when latch goes LOW->HIGH
//   digitalWrite(HC595_enablePin, LOW);  // re-enable (negative logic again)
}

/*
  Inputs: Array of 4 integers - one for each plane
*/
void display (unsigned int *pPattern)
{
  int i;
  for (i=0; i<4; i++)
  {
    int j;
    for (j=0; j<1000; j++)
    {
      // Slow this down so that there is time for the LEDS to light
      // Experimentation shows that 200+ gives brighter LEDs
      // NB: Do it this way so an empty loop isn't optimised out
      if (j==0)
      {
        digitalWrite(LED_Plane[0], HIGH);
        digitalWrite(LED_Plane[1], HIGH);
        digitalWrite(LED_Plane[2], HIGH);
        digitalWrite(LED_Plane[3], HIGH);
        write_74HC595 (pPattern[i]);
        digitalWrite(LED_Plane[i], LOW);
      }
    }
  }
}

void displayPattern ()
{
  int i;
  // only update it every tick otherwise just display as is
  tickCount--;
  if (tickCount <= 0)
  {
    tickCount = tickCountMax;
    for (i=0; i<4; i++)
    {
      currentPattern[i] = pattern[i*2 + patternNumber*8] * 256 + pattern[i*2 + 1 + patternNumber*8];
    }
    patternNumber++;
    if (patternNumber >= numPatterns)
    {
      patternNumber = 0;
    }
  }
  display(&currentPattern[0]);
}

void loop() {
  // put your main code here, to run repeatedly:
  displayPattern();
}

18 thoughts on “icStation 4x4x4 LED Cube Shield for Arduino – Software

  1. That ICStation shield is frustrating. I wrote generic code that allows patterns to be defined in terms of cartesian coordinates. If you are interested, let me know and I can make the code available.

  2. I’m sorry I just can’t get my icstation cube to work, I have a lot of trouble trying lo load your code all I get are error, I just would like to see it running. any help please. the LED’s were all tested and work fine. everyone talks about how they used icstations code but i can’t find it anywhere.
    Regards
    Ralph

    1. The previous post I mention at the top of this post has a link to where I got my cube. That page has a download link to the provided code, but as I say whilst that is ok for a demo, it only works with symmetrical patterns, hence writing my own code as described here!

      Good luck. Sorry for the delay in replying, I hadn’t noticed your comment waiting!

      Kevin

    1. It should be enough to uncommented one or more of the #ifdef lines near the top. Then it will include all the lines necessary to include that pattern in the code.

      If you are having problems maybe define your own simple static pattern first, maybe just one line on? To check the hardware is wired up ok?

      Kevin

  3. Thanks Kevin, I found the problem:

    ics444_demo ();

    was missing from the loop.

    On another note, I’ve added two patterns:

    #ifdef CUT
    B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000, //columb
    B10000000,B00000000,B10000000,B00000000,B10000000,B00000000,B10000000,B00000000, //columb 1a
    B01000000,B00000000,B01000000,B00000000,B01000000,B00000000,B01000000,B00000000, //columb 1b
    B00100000,B00000000,B00100000,B00000000,B00100000,B00000000,B00100000,B00000000, //columb 1c
    B00010000,B00000000,B00010000,B00000000,B00010000,B00000000,B00010000,B00000000, //columb 1d
    B00000001,B00000000,B00000001,B00000000,B00000001,B00000000,B00000001,B00000000, //columb 2d
    B00000010,B00000000,B00000010,B00000000,B00000010,B00000000,B00000010,B00000000, //columb 2c
    B00000100,B00000000,B00000100,B00000000,B00000100,B00000000,B00000100,B00000000, //columb 2b
    B00001000,B00000000,B00001000,B00000000,B00001000,B00000000,B00001000,B00000000, //columb 2a
    B00000000,B10000000,B00000000,B10000000,B00000000,B10000000,B00000000,B10000000, //columb 3a
    B00000000,B01000000,B00000000,B01000000,B00000000,B01000000,B00000000,B01000000, //columb 3b
    B00000000,B00100000,B00000000,B00100000,B00000000,B00100000,B00000000,B00100000, //columb 3c
    B00000000,B00010000,B00000000,B00010000,B00000000,B00010000,B00000000,B00010000, //columb 3d
    B00000000,B00000001,B00000000,B00000001,B00000000,B00000001,B00000000,B00000001, //columb 4d
    B00000000,B00000010,B00000000,B00000010,B00000000,B00000010,B00000000,B00000010, //columb 4c
    B00000000,B00000100,B00000000,B00000100,B00000000,B00000100,B00000000,B00000100, //columb 4b
    B00000000,B00001000,B00000000,B00001000,B00000000,B00001000,B00000000,B00001000, //columb 4a
    B00000000,B10000100,B00000000,B10000100,B00000000,B10000100,B00000000,B10000100, //columb 4b,3a
    B00001000,B00000010,B00001000,B00000010,B00001000,B00000010,B00001000,B00000010, //columb 4c,2a
    B10000000,B00000001,B10000000,B00000001,B10000000,B00000001,B10000000,B00000001, //columb 4d,1a
    B01000000,B00010000,B01000000,B00010000,B01000000,B00010000,B01000000,B00010000, //columb 3d,1b
    B00100001,B00000000,B00100001,B00000000,B00100001,B00000000,B00100001,B00000000, //columb 2d,1c
    B00010000,B00000000,B00010000,B00000000,B00010000,B00000000,B00010000,B00000000, //columb 1d
    B00000010,B00000000,B00000010,B00000000,B00000010,B00000000,B00000010,B00000000, //columb 2c
    B00000100,B00100000,B00000100,B00100000,B00000100,B00100000,B00000100,B00100000, //columb 2b,3c
    B00000000,B01000000,B00000000,B01000000,B00000000,B01000000,B00000000,B01000000, //columb 3b
    #endif
    #ifdef TWIRL
    B10000100,B00100001,B10000100,B00100001,B10000100,B00100001,B10000100,B00100001, //columb 1a,2b,3c,4d
    B01000100,B00100010,B01000100,B00100010,B01000100,B00100010,B01000100,B00100010, //columb 1b,2b,3c,4c
    B00100010,B01000100,B00100010,B01000100,B00100010,B01000100,B00100010,B01000100, //columb 1c,2c,3b,4b
    B00010010,B01001000,B00010010,B01001000,B00010010,B01001000,B00010010,B01001000, //columb 2c,1d,3b,4a
    B00000011,B11000000,B00000011,B11000000,B00000011,B11000000,B00000011,B11000000, //columb 2c,2d,3b,3a
    B00001100,B00110000,B00001100,B00110000,B00001100,B00110000,B00001100,B00110000, //columb 2a,2b,3c,3d
    B10000100,B00100001,B10000100,B00100001,B10000100,B00100001,B10000100,B00100001, //columb 1a,2b,3c,4d
    B00001100,B00110000,B00001100,B00110000,B00001100,B00110000,B00001100,B00110000, //columb 2a,2b,3c,3d
    B00000011,B11000000,B00000011,B11000000,B00000011,B11000000,B00000011,B11000000, //columb 2c,2d,3b,3a
    B00010010,B01001000,B00010010,B01001000,B00010010,B01001000,B00010010,B01001000, //columb 2c,1d,3b,4a
    B00100010,B01000100,B00100010,B01000100,B00100010,B01000100,B00100010,B01000100, //columb 1c,2c,3b,4b
    B01000100,B00100010,B01000100,B00100010,B01000100,B00100010,B01000100,B00100010, //columb 1b,2b,3c,4c
    B10000100,B00100001,B10000100,B00100001,B10000100,B00100001,B10000100,B00100001, //columb 1a,2b,3c,4d
    #endif

    Thanks again!

    Russell

    1. Excellent. I guess I didn’t help by switching from a single file in the first case to separate files in the second! Glad you found the issue. Thanks for those patterns will give it a go!

      I have seen some standalone tools for generating patterns to save doing it by hand, but not for this cube. The tool I found was for a 8x8x8 cube kit that I’ve just built, but it isn’t based on arduino, so not really applicable here. Might be able to tweak it, but it is a background task to think about for now.

      If I do get anything going (or find something someone else has done) I might do another update.

      Good luck!

      Kevin

  4. Eduardo Furtado
    Dear friend, I also built a cube of this, mine worked perfectly with your code as soon as you have more post please continue with the good work. And any news would be grateful if you could send me.

  5. Kevin, thanks mate! … you really made easy my experience with this cube. I was having a hard time on my own.
    Everything worked fine!, excellent post.

  6. Great post. Bought a kit to build it with my son. Thanks to your code and explanation, it worked first time right. If I had to do it with only the original documentation, it would have spoiled the experience…
    Thanks.

Leave a comment