Introduction: Interactive Color-wheel RGB Cube 8x8x8

About: PLC, Arduino - Do it yourself project

This is special version of RGB Led Cube 8x8x8, so called "Interactive Color-wheel RGB Cube 8x8x8". Let's see all attached videos to know how it works.

  • Interactive effect: whenever you move led cube in any axis, cube's color will be changed according to color-wheel rule. See DEMO version below.

  • Color-wheel effect: It looks more amazing if we do effects with color-wheel function. For my project, you can see on videos, there are a lot of effects that were collected from many sources, some of them were done by myself.

  • VU meter effect: Read music signal by MSGEQ7 through 3.5mm audio jack.

On the forum, I saw a lot of posts talking about led cube so I just briefly discussed how to do a RGB led cube in the simplest way.

Step 1: Bill of Material

With RGB CUBE 8x8x8, we have 512 LED RED, 512 LED GREEN and 512 LED BLUE, so totally we have to control: 512x3 = 1,536 LEDs.

By 4-bit BAM method, we can create 4,096 separated colors (16 ^ 3) for each RGB LED.

For this project, we'll do a lot of soldering works. Let prepare masks, gloves and exhaust fan for health protection.

Step 2: Cube Template & Led Tester

Print this template drawing out paper, stick by glue to wood, and then drill with diameter 5mm (if using 5mm RGB LED) at the center of each circle. We will have a led cube wood template with 64 holes.

Note: The holes spacing of wood template and LED spacing of cube base printed circuit board is the same. Thus, we can put 3 pins of led into cube base PCB easily.

Make a Led tester with 9V battery, take note that resistor R470 ohm are connected in series between battery and Led.

Step 3: Cube Base PCB

Distance between 2 LEDs in PCB is 20mm, same as wood template holes spacing.

With this distance, I didn't use any cooper wires or extra zinc wires to connect LED together. I just bend the Led pins and then soldered them easily.

Step 4: Shift Register Board

  • Schematic:

Schematic above is only for one color so we will have totally 3 sets of shift register board like that.

  • PCB:

As shown on shift register board PCBs, we have 3 blocks, each controlling 64 LEDs (R, G, B) -> 8 x 74HC595. These blocks are connected in series so that each time 24 bytes data is shifted out in the following order: BLUE --> GREEN --> RED.

Step 5: Layer Board

This layer circuit can be used in two options:

  • Select layers through 74HC595.
  • Select layers directly from 8 pins of Arduino Mega 2560.

I used the second option for faster response.

Step 6: Overall Schematic

RGB Led is common anode type, connected in series R100 for every single R, G, B led. Resistor R100 are included on shift register board PCB, located after ULN2803.

Arduino Mega 2560 control 24 x 74HC595 through SPI interface. And cube layer is collected directly from pin 26 ~ 33 of Arduino.

Step 7: Cube Box

I made a box containing 5V power supply, PCB boards, Arduino Mega 2560. Some switches, push buttons, jack 3.5mm, fan ... were mounted at cube's backside. You can refer to the internet how to make an beautiful acrylic cube.

In my design, I added one small fan & audio jack 3.5mm on the back of box to ventilate power PCB board and make it become VU meter as optional.

For VU meter, we can use MSGEQ7 with PCB design as picture, or, we can use FFT transformation to read audio analog data from 3.5mm jack.

Step 8: Main Program

Main program for RGB LED CUBE 8x8x8

#include
#define latch_pin 4
#define blank_pin 5
#define data_pin 51
#define clock_pin 52
#define layer1 26
#define layer2 27
#define layer3 28
#define layer4 29
#define layer5 30
#define layer6 31
#define layer7 32
#define layer8 33
int layerArray[8] = {layer1, layer2, layer3, layer4, layer5, layer6, layer7, layer8};
int lastAnode;
byte red[4][64];
byte blue[4][64];
byte green[4][64];
#define BAM_RESOLUTION 4
const byte Size_X = 8;
const byte Size_Y = 8;
const byte Size_Z = 8;
int level=0;
int anodeLevel=0;
int BAM_Bit, BAM_Counter=0;
void setup(){
SPI.setBitOrder(MSBFIRST);
SPI.setDataMode(SPI_MODE0);
SPI.setClockDivider(SPI_CLOCK_DIV2);
noInterrupts();
TCCR1A = B00000000;
TCCR1B = B00001011;
TIMSK1 = B00000010;
OCR1A=30;
pinMode (2, OUTPUT);
pinMode (3, OUTPUT);
pinMode(data_pin, OUTPUT);
pinMode(clock_pin, OUTPUT);
pinMode(layer1, OUTPUT);
pinMode(layer2, OUTPUT);
pinMode(layer3, OUTPUT);
pinMode(layer4, OUTPUT);
pinMode(layer5, OUTPUT);
pinMode(layer6, OUTPUT);
pinMode(layer7, OUTPUT);
pinMode(layer8, OUTPUT);
SPI.begin();
interrupts();
}
void loop()
{
clearfast();
LED(4, 5, 6, 3, 6, 13);
delay(5000);
clearfast();
LED(0,0,0,15,0,0);
delay(5000);
LED(7,0,0,0,15,0);
delay(5000);
LED(7,7,0,0,0,15);
delay(5000);
LED(0,7,0,15,15,0);
delay(5000);
LED(0,7,7,15,0,15);
delay(5000);
LED(7,0,7,0,15,15);
delay(5000);
LED(0,0,7,15,15,15);
delay(5000);
LED(7,7,7,15,9,0);
delay(5000);
}
void LED(int Z, int Y, int X, int R, int G, int B)
{
X = constrain(X, 0, Size_X - 1);
Y = constrain(Y, 0, Size_Y - 1);
Z = constrain(Z, 0, Size_Z - 1);
R = constrain(R, 0, (1 << BAM_RESOLUTION) - 1);
G = constrain(G, 0, (1 << BAM_RESOLUTION) - 1);
B = constrain(B, 0, (1 << BAM_RESOLUTION) - 1);
int WhichByte = int(((Z << 6) + (Y << 3) + X) >> 3);
int WhichBit = ((Z << 6) + (Y << 3) + X) - (WhichByte << 3) ;
for (byte BAM = 0; BAM < BAM_RESOLUTION; BAM++)
{
//*** RED ***
bitWrite(red[BAM][WhichByte], WhichBit, bitRead(R, BAM));
//*** GREEN ***
bitWrite(green[BAM][WhichByte], WhichBit, bitRead(G, BAM));
//*** BLUE ***
bitWrite(blue[BAM][WhichByte], WhichBit, bitRead(B, BAM));
}
}
ISR(TIMER1_COMPA_vect)
{
PORTE |= 1<
if(BAM_Counter==8)
BAM_Bit++;
else
if(BAM_Counter==24)
BAM_Bit++;
else
if(BAM_Counter==56)
BAM_Bit++;
BAM_Counter++;
switch (BAM_Bit)
{
case 0:
//Red
mySPI(red[0][level + 0]);
mySPI(red[0][level + 1]);
mySPI(red[0][level + 2]);
mySPI(red[0][level + 3]);
mySPI(red[0][level + 4]);
mySPI(red[0][level + 5]);
mySPI(red[0][level + 6]);
mySPI(red[0][level + 7]);
//Green
mySPI(green[0][level + 0]);
mySPI(green[0][level + 1]);
mySPI(green[0][level + 2]);
mySPI(green[0][level + 3]);
mySPI(green[0][level + 4]);
mySPI(green[0][level + 5]);
mySPI(green[0][level + 6]);
mySPI(green[0][level + 7]);
//Blue
mySPI(blue[0][level + 0]);
mySPI(blue[0][level + 1]);
mySPI(blue[0][level + 2]);
mySPI(blue[0][level + 3]);
mySPI(blue[0][level + 4]);
mySPI(blue[0][level + 5]);
mySPI(blue[0][level + 6]);
mySPI(blue[0][level + 7]);
break;
case 1:
//Red
mySPI(red[1][level + 0]);
mySPI(red[1][level + 1]);
mySPI(red[1][level + 2]);
mySPI(red[1][level + 3]);
mySPI(red[1][level + 4]);
mySPI(red[1][level + 5]);
mySPI(red[1][level + 6]);
mySPI(red[1][level + 7]);
//Green
mySPI(green[1][level + 0]);
mySPI(green[1][level + 1]);
mySPI(green[1][level + 2]);
mySPI(green[1][level + 3]);
mySPI(green[1][level + 4]);
mySPI(green[1][level + 5]);
mySPI(green[1][level + 6]);
mySPI(green[1][level + 7]);
//Blue
mySPI(blue[1][level + 0]);
mySPI(blue[1][level + 1]);
mySPI(blue[1][level + 2]);
mySPI(blue[1][level + 3]);
mySPI(blue[1][level + 4]);
mySPI(blue[1][level + 5]);
mySPI(blue[1][level + 6]);
mySPI(blue[1][level + 7]);
break;
case 2:
//Red
mySPI(red[2][level + 0]);
mySPI(red[2][level + 1]);
mySPI(red[2][level + 2]);
mySPI(red[2][level + 3]);
mySPI(red[2][level + 4]);
mySPI(red[2][level + 5]);
mySPI(red[2][level + 6]);
mySPI(red[2][level + 7]);
//Green
mySPI(green[2][level + 0]);
mySPI(green[2][level + 1]);
mySPI(green[2][level + 2]);
mySPI(green[2][level + 3]);
mySPI(green[2][level + 4]);
mySPI(green[2][level + 5]);
mySPI(green[2][level + 6]);
mySPI(green[2][level + 7]);
//Blue
mySPI(blue[2][level + 0]);
mySPI(blue[2][level + 1]);
mySPI(blue[2][level + 2]);
mySPI(blue[2][level + 3]);
mySPI(blue[2][level + 4]);
mySPI(blue[2][level + 5]);
mySPI(blue[2][level + 6]);
mySPI(blue[2][level + 7]);
break;
case 3:
//Red
mySPI(red[3][level + 0]);
mySPI(red[3][level + 1]);
mySPI(red[3][level + 2]);
mySPI(red[3][level + 3]);
mySPI(red[3][level + 4]);
mySPI(red[3][level + 5]);
mySPI(red[3][level + 6]);
mySPI(red[3][level + 7]);
//Green
mySPI(green[3][level + 0]);
mySPI(green[3][level + 1]);
mySPI(green[3][level + 2]);
mySPI(green[3][level + 3]);
mySPI(green[3][level + 4]);
mySPI(green[3][level + 5]);
mySPI(green[3][level + 6]);
mySPI(green[3][level + 7]);
//Blue
mySPI(blue[3][level + 0]);
mySPI(blue[3][level + 1]);
mySPI(blue[3][level + 2]);
mySPI(blue[3][level + 3]);
mySPI(blue[3][level + 4]);
mySPI(blue[3][level + 5]);
mySPI(blue[3][level + 6]);
mySPI(blue[3][level + 7]);
if(BAM_Counter==120)
{
BAM_Counter=0;
BAM_Bit=0;
}
break;
}
lastAnode = (anodeLevel-1);
if (anodeLevel == 0) { lastAnode = 7; }
digitalWrite(layerArray[lastAnode], LOW);
digitalWrite(layerArray[anodeLevel], HIGH);
PORTE |= 1<
PORTE &= ~(1<
delayMicroseconds(3);
PORTE &= ~(1<
//delayMicroseconds(3);
anodeLevel++;
level = level+8;
if(anodeLevel==8)
anodeLevel=0;
if(level==64)
level=0;
pinMode(blank_pin, OUTPUT);
}
void clearfast ()
{
for (unsigned char j=0; j<64; j++)
{
red[0][j] = 0;
red[1][j] = 0;
red[2][j] = 0;
red[3][j] = 0;
green[0][j] = 0;
green[1][j] = 0;
green[2][j] = 0;
green[3][j] = 0;
blue[0][j] = 0;
blue[1][j] = 0;
blue[2][j] = 0;
blue[3][j] = 0;
}
}
inline static uint8_t mySPI(uint8_t mydata){
SPDR = mydata;
asm volatile("nop");
asm volatile("nop");
}

Step 9: Bit Angle Modulation

About B.A.M, you can check my topic: https://www.instructables.com/id/BI-COLORS-MATRIX-...

Or refer to two amazing websites:

http://www.kevindarrah.com/?cat=99.

http://www.batsocks.co.uk/readme/art_bcm_3.htm.

Example with command LED(4, 5, 6, 3, 6, 13), we can see conversion from actual 3D dimension to program coordinates "Byte" & "Bit ", as well as, the mix colors on pictures.

Step 10: Color-wheel and Interactive Program

Refer from Nick Schulze: http://www.hownottoengineer.com/projects/rgb-led-c..., I did some modifications on his color-wheel program to match with my color template as follows:

  • From:
array[phase] = {red, green, blue};
  • To:
RED[phase] = red;
GREEN[phase] = green;
BLUE[phase] = blue;
  • Get color from color-wheel function
void get_colour(int16_t p, uint8_t *R_colorwheel, uint8_t *G_colorwheel, uint8_t *B_colorwheel){
  if (p >= COLOUR_WHEEL_LENGTH)
    p -= COLOUR_WHEEL_LENGTH;
  *R_colorwheel = RED[p];
  *G_colorwheel = GREEN[p];
  *B_colorwheel = BLUE[p];
}

For MPU-6050, you can refer to my previous project: https://www.instructables.com/id/2-Wheel-Self-Bala...

In my demo video, I took three values yaw, pitch, roll from MPU-6050 and input them to color-wheel function to get red, green & blue color for Led. K1, K2, K3 is converted coefficients from yaw, pitch, roll to color-wheel position. Parameter "COLOUR_WHEEL_LENGTH", in my case, is 256.

get_colour(K1*pitch + K2*roll + K3*yaw, &RED, &GREEN, &BLUE);
fillCube(RED, GREEN, BLUE);
void fillCube(byte R, byte G, byte B){
for (byte z=0; z<8; z++){
    for (byte x=0; x<8; x++){
      for (byte y=0; y<8; y++){
        LED(z, y, x, R, G, B);}}}}

Step 11: RGB Led Cube Pictures

Some pictures for my cube project.

Step 12: More RGB Led Cube Videos

More videos for my led cube project:

Finally, I would like to say a big thanks to Kevin Darrah, Nick Schulze, SuperTech-IT with the best references about RGB led cube.