A while back I completed an 8x8x8 LED cube build following Hari Wiguna's Hackaday project. If you ever intend to build a cube for yourself I highly recommend checking out his documentation of the build process.
Anyway, I've recently had some time to reexamine my cube (school and work got in the way for a good while). Before I start, here is a schematic and the code for my setup (taken from Hari Wiguna's project and slightly adapted). There are also some pictures of my project and videos demonstrating the issues that I am referring to:
Video Demonstrations:
- LEDs on layers 2 and 7 lit with FETs disconnected entirely from the anode layers
- 1 by 1 LED test failure; LEDs on layers 2 and 7 consistently misfire
- The best demonstration of the problem with layers 2 and 7
Schematic (updated 12/6):
Code:
// 8x8x8x Blue LED Cube
// by Hari Wiguna 2014
//-- Shift Register pins -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
int latchPin = 13; // Arduino D13 to IC pin 12 (ST_CP) -- Green
int clockPin = 12; // Arduino D12 to IC pin 11 (SH_CP) -- Yellow
int dataPin = 11; // Arduino D11 to IC pin 14 (DS) -- Blue
//-- Preferences -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
int baudRate = 9600;
byte slomo = 0;
//-- Globals -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
volatile int8_t cube[8][8]; // byte bits = X, 1st index=Y, 2nd index = Z
volatile int8_t gZ = 0;
int pot0; // Brightness Potentiometer (A6, currently not implemented)
int brightSet; // Brightness controlled by pot 0
int pot1; // Speed Potentiometer (A5)
int animSpeed; // Speed controlled by pot1
int buttonState; // Animation select button (A4)
int buttonCount = -1;
float pi = 3.14;
float pi2 = 6.28;
int8_t prevD = 0; // last depth value
int8_t drawD = 0; // Last drawn depth
int8_t dCount = 0;// How many times has the same depth been reported by distance sensor?
const int8_t sineMaxIndex = 32;
int8_t sineArray[sineMaxIndex];
//-- USB Serial commands -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
String inputString = ""; // a string to hold incoming data
boolean stringComplete = false; // whether the string is complete
//-- Paths -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
int8_t quarterArc[][2] = {{7,0},{7,1},{7,2}, {6,4},{5,5},{4,6}, {2,7},{1,7},{0,7}};
int8_t circle[][2] = {
{2,7},{3,7},{4,7},{5,7}, {6,6},
{7,5},{7,4},{7,3},{7,2}, {6,1},
{5,0},{4,0},{3,0},{2,0}, {1,1},
{0,2},{0,3},{0,4},{0,5}, {1,6}
};
int8_t arrow[] = {
B00011000,
B00011000,
B00011000,
B00011000,
B00011000,
B01111110,
B00111100,
B00011000,
};
int8_t pathSquare[][2] = {
{0,0},{1,0},{2,0},{3,0},{4,0},{5,0},{6,0},{7,0}, // Front
{7,1},{7,2},{7,3},{7,4},{7,5},{7,6},{7,7}, // Right
{6,7},{5,7},{4,7},{3,7},{2,7},{1,7},{0,7}, // Back
{0,7},{0,6},{0,5},{0,4},{0,3},{0,2},{0,1}, // Right
};
int8_t pathSmallSquare[][2] = {
{2,2},{3,2},{4,2},{5,2}, // Front
{5,3},{5,4},{5,5}, // Right
{4,5},{3,5},{2,5}, // Back
{2,4},{2,3},{2,2}, // Right
};
int8_t pathSmallCircle[][2] = {
{3,2},{4,2}, // Front
{5,3},{5,4}, // Right
{4,5},{3,5}, // Back
{2,4},{2,3}, // Right
};
int8_t funnelPath[] = {0,1,2,2,2,3,3,3};
//-- SETUP -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
void SetupPins()
{
//Analog IO
pinMode(A0, INPUT_PULLUP); //UNUSED
pinMode(A1, INPUT_PULLUP); //UNUSED
pinMode(A2, INPUT_PULLUP); //UNUSED
pinMode(A3, INPUT_PULLUP); //UNUSED
pinMode(A4,INPUT);
pinMode(A5,INPUT);
pinMode(A6,INPUT);
pinMode(A7, INPUT_PULLUP); //UNUSED
// Digital IO
pinMode(latchPin, OUTPUT);
pinMode(clockPin, OUTPUT);
pinMode(dataPin, OUTPUT);
pinMode(0, INPUT_PULLUP); //UNUSED
pinMode(1, INPUT_PULLUP); //UNUSED
pinMode(10, INPUT_PULLUP); //UNUSED
for (int i=0; i<8; i++) {
pinMode(2+i, OUTPUT);
}
}
void SetupTimer()
{
cli();
// Reset any PWM that arduino may have setup automatically
TCCR2A = 0;
TCCR2B = 0;
TCCR2A |= (1 << WGM21); // CTC mode. Reset counter when OCR2 is reached
TCCR2B |= (1 << CS21) | (1 << CS22); // Prescaler = 256
//TCCR2B |= (1 << CS20) | (1 << CS22); // Prescaler = 128
OCR2A = 70; // Fire interrupt when timer2 has looped 80 times
TCNT2 = 0; // initial counter value = 0;
TIMSK2 |= (1<<OCIE2A); // Enable CTC interrupt
sei();
}
ISR (TIMER2_COMPA_vect)
{
Refresh();
}
void Refresh(void) // WITHOUT the added delayMicroseconds, this routine takes 8052 microseconds
{
noInterrupts();
//-- Compute new layer --
int8_t prevLayer = gZ;
gZ++;
if (gZ>=8) gZ=0;
// Prepare for data. Shift data to shift registers but do not reflect it on the outputs yet.
digitalWrite(latchPin, LOW);
//-- Spit out the bits --
DrawLayer(gZ);
//-- Turn off previous layer --
digitalWrite(2+prevLayer,LOW); // Turn off prev layer
//-- Turn on this layer --
digitalWrite(2+gZ,HIGH); // Turn on this layer
// All data ready. Instantly reflect all 64 bits on all 8 shift registers to the led layer.
digitalWrite(latchPin, HIGH);
interrupts();
}
void PreComputes()
{
for (int8_t index=0; index < sineMaxIndex; index++)
{
float a = pi*2 * index / sineMaxIndex;
sineArray[ index ] = 4 + (sin(a) * 3.5);
}
}
void setup(void) {
SetupPins();
Serial.begin(baudRate);
inputString.reserve(600);
PreComputes();
CubeAllOff();
SetupTimer();
}
void loop(void) {
pot0 = analogRead(A6);
pot1 = analogRead(A5);
buttonState = analogRead(A4);
animSpeed = map(pot1, 0,1023, 16,256);
brightSet = map(pot0, 0,1023, 16,256);
if (buttonState > 512) {
buttonCount++;
/*delay(animSpeed); Unsure about the delay here, push button input switches animation so long as button held when the animation ends, so without this line the code works in that regard.*/
if (buttonCount == 4) {
buttonCount = -1;
}
}
switch (buttonCount){
case -1: CubeAllOn(); break;
case 0: Rain(); CubeAllOff(); break;
case 1: Scan_all_layers(); break;
case 2: RippleToRight(); break;
case 3: CubeAllOff(); break;
/*delay(animSpeed);
default: CubeAllOn();*/
}
}
//-- Drawing Routines -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
void Bresenham3D(int8_t x1, int8_t y1, int8_t z1, const int8_t x2, const int8_t y2, const int8_t z2, const byte mode)
{
// This routine is from:
//https://gist.github.com/yamamushi/5823518
// I only adapted it for my sketch.
int8_t i, dx, dy, dz, l, m, n, x_inc, y_inc, z_inc, err_1, err_2, dx2, dy2, dz2;
int8_t point[3];
point[0] = x1;
point[1] = y1;
point[2] = z1;
dx = x2 - x1;
dy = y2 - y1;
dz = z2 - z1;
x_inc = (dx < 0) ? -1 : 1;
l = abs(dx);
y_inc = (dy < 0) ? -1 : 1;
m = abs(dy);
z_inc = (dz < 0) ? -1 : 1;
n = abs(dz);
dx2 = l << 1;
dy2 = m << 1;
dz2 = n << 1;
if ((l >= m) && (l >= n)) {
err_1 = dy2 - l;
err_2 = dz2 - l;
for (i = 0; i < l; i++) {
//output->getTileAt(point[0], point[1], point[2])->setSymbol(symbol);
if (mode) SetDot(point[0], point[1], point[2]); else ClearDot(point[0], point[1], point[2]);
if (err_1 > 0) {
point[1] += y_inc;
err_1 -= dx2;
}
if (err_2 > 0) {
point[2] += z_inc;
err_2 -= dx2;
}
err_1 += dy2;
err_2 += dz2;
point[0] += x_inc;
}
} else if ((m >= l) && (m >= n)) {
err_1 = dx2 - m;
err_2 = dz2 - m;
for (i = 0; i < m; i++) {
//output->getTileAt(point[0], point[1], point[2])->setSymbol(symbol);
if (mode) SetDot(point[0], point[1], point[2]); else ClearDot(point[0], point[1], point[2]);
if (err_1 > 0) {
point[0] += x_inc;
err_1 -= dy2;
}
if (err_2 > 0) {
point[2] += z_inc;
err_2 -= dy2;
}
err_1 += dx2;
err_2 += dz2;
point[1] += y_inc;
}
} else {
err_1 = dy2 - n;
err_2 = dx2 - n;
for (i = 0; i < n; i++) {
//output->getTileAt(point[0], point[1], point[2])->setSymbol(symbol);
if (mode) SetDot(point[0], point[1], point[2]); else ClearDot(point[0], point[1], point[2]);
if (err_1 > 0) {
point[1] += y_inc;
err_1 -= dz2;
}
if (err_2 > 0) {
point[0] += x_inc;
err_2 -= dz2;
}
err_1 += dy2;
err_2 += dx2;
point[2] += z_inc;
}
}
//output->getTileAt(point[0], point[1], point[2])->setSymbol(symbol);
if (mode) SetDot(point[0], point[1], point[2]); else ClearDot(point[0], point[1], point[2]);
}
void DrawLine3(int8_t x0,int8_t y0, int8_t x1,int8_t y1, int8_t z)
{
boolean steep = abs(y1 - y0) > abs(x1 - x0);
if (steep) { Swap(&x0, &y0); Swap(&x1, &y1); }
if (x0 > x1) { Swap(&x0, &x1); Swap(&y0,&y1); }
int dX = (x1 - x0);
int dY = abs(y1 - y0);
int err = (dX / 2);
int ystep = (y0 < y1 ? 1 : -1);
int y = y0;
for (int x = x0; x <= x1; ++x)
{
if (steep) SetDot(y, x, z); else SetDot(x, y, z);
err = err - dY;
if (err < 0) { y += ystep; err += dX; }
}
}
void EraseLine3(int8_t x0,int8_t y0, int8_t x1,int8_t y1, int8_t z)
{
boolean steep = abs(y1 - y0) > abs(x1 - x0);
if (steep) { Swap(&x0, &y0); Swap(&x1, &y1); }
if (x0 > x1) { Swap(&x0, &x1); Swap(&y0,&y1); }
int dX = (x1 - x0);
int dY = abs(y1 - y0);
int err = (dX / 2);
int ystep = (y0 < y1 ? 1 : -1);
int y = y0;
for (int x = x0; x <= x1; ++x)
{
if (steep) ClearDot(y, x, z); else ClearDot(x, y, z);
err = err - dY;
if (err < 0) { y += ystep; err += dX; }
}
}
void CalcLine3D(int8_t x1, int8_t y1, int8_t z1, const int8_t x2, const int8_t y2, const int8_t z2, byte mode)
{
Bresenham3D(x1,y1,z1, x2,y2,z2, mode);
}
void DrawLine3D(int8_t x0, int8_t y0, int8_t z0, const int8_t x1, const int8_t y1, const int8_t z1)
{
CalcLine3D(x0,y0,z0, x1,y1,z1, 1);
}
void EraseLine3D(int8_t x0, int8_t y0, int8_t z0, const int8_t x1, const int8_t y1, const int8_t z1)
{
CalcLine3D(x0,y0,z0, x1,y1,z1, 0);
}
void DrawLine2(int8_t x0,int8_t y0, int8_t x1,int8_t y1, int8_t z)
{
int dx = abs(x1-x0);
int dy = abs(y1-y0);
int sx = (x0 < x1) ? 1 : -1;
int sy = (y0 < y1) ? 1 : -1;
int err = dx-dy;
Serial.print("dx, dy, sx, sy, err =");
Serial.print(dx); Serial.print(", ");
Serial.print(dy); Serial.print(", ");
Serial.print(sx); Serial.print(", ");
Serial.print(sy); Serial.print(", ");
Serial.println(err);
while (true)
{
// Serial.print("x0,y0 = ");
// Serial.print(x0); Serial.print(", ");
// Serial.println(y0);
SetDot(x0,y0, z);
if ((x0 == x1) && (y0 == y1)) exit;
int e2 = 2*err;
if (e2 > -dy)
{
err = err - dy;
x0 = x0 + sx;
}
if (e2 < dx)
{
err = err + dx;
y0 = y0 + sy;
}
}
}
void SetDot(int8_t x,int8_t y, int8_t z)
{
//noInterrupts();
bitSet(cube[y][z], x);
// Serial.print("X,Y,Z = ");
// Serial.print(x); Serial.print(",");
// Serial.print(y); Serial.print(",");
// Serial.println(z);
//interrupts();
if (slomo) delay(64);
}
void ClearDot(int8_t x,int8_t y,int8_t z)
{
bitClear(cube[y][z], x);
}
void SetLayer(int8_t z, int8_t xByte)
{
//z = Wrap(z);
for (int8_t y=0; y<8; y++) {
cube[y][z] = xByte;
//for (int8_t x=0; x<8; x++) {
// if (xByte==0) ClearDot(x,y,z); else SetDot(x,y,z);
//}
}
}
void CalcLine(int8_t x1, int8_t y1, int8_t z1, int8_t x2, int8_t y2, int8_t z2, byte mode)
{
byte parallelAxis = 0; // 0=x, 1=y, 2=z
int8_t a1, a2, b1, b2;
if (x1!=x2) {
parallelAxis = 0;
a1=x1;
a2=x2;
} // parallel to X
if (y1!=y2) {
parallelAxis = 1;
a1=y1;
a2=y2;
} // parallel to Y
if (z1!=z2) {
parallelAxis = 2;
a1=z1;
a2=z2;
} // parallel to Z
for (int8_t p=a1; p<=a2; p++) {
int8_t x,y,z;
switch (parallelAxis) {
case 0:
x=p;
y=y1;
z=z1;
break; // parallel to X
case 1:
x=x1;
y=p;
z=z1;
break; // parallel to Y
case 2:
x=x1;
y=y1;
z=p;
break; // parallel to Z
}
if (mode==1) SetDot(x,y,z);
else ClearDot(x,y,z);
}
}
void DrawLine(int8_t x1, int8_t y1, int8_t z1, int8_t x2, int8_t y2, int8_t z2)
{
CalcLine(x1,y1,z1, x2,y2,z2, 1);
}
void EraseLine(int8_t x1, int8_t y1, int8_t z1, int8_t x2, int8_t y2, int8_t z2)
{
CalcLine(x1,y1,z1, x2,y2,z2, 0);
}
void CalcRect(int8_t x1, int8_t y1, int8_t z1, int8_t x2, int8_t y2, int8_t z2, byte mode)
{
byte tangentAxis = 0; // 0=x, 1=y, 2=z
int8_t a1, a2, b1, b2;
if (y1!=y2 && z1!=z2) {
tangentAxis = 0;
a1=y1;
a2=y2;
b1=z1;
b2=z2;
} // YZ Plane
if (x1!=x2 && z1!=z2) {
tangentAxis = 1;
a1=x1;
a2=x2;
b1=z1;
b2=z2;
} // XZ plane
if (x1!=x2 && y1!=y2) {
tangentAxis = 2;
a1=x1;
a2=x2;
b1=y1;
b2=y2;
} // XY Plane
// Serial.print("a1,a2 b1,b2 t = ");
// Serial.print(a1); Serial.print(",");
// Serial.print(a2); Serial.print(" ");
// Serial.print(b1); Serial.print(",");
// Serial.print(b2); Serial.print(" ");
// Serial.println(tangentAxis);
for (int8_t p=a1; p<=a2; p++) {
for (int8_t q=b1; q<=b2; q++) {
int8_t x,y,z;
switch (tangentAxis) {
case 0:
x=x1;
y=p;
z=q;
break; // YZ Plane
case 1:
x=p;
y=y1;
z=q;
break; // XZ plane
case 2:
x=p;
y=q;
z=z1;
break; // XY Plane
}
// Serial.print("x,y,z = ");
// Serial.print(x); Serial.print(",");
// Serial.print(y); Serial.print(",");
// Serial.print(z); Serial.print(" mode=");
// Serial.println(mode);
if (mode==1) SetDot(x,y,z);
else ClearDot(x,y,z);
}
}
}
void DrawRect(int8_t x1, int8_t y1, int8_t z1, int8_t x2, int8_t y2, int8_t z2)
{
CalcRect(x1,y1,z1, x2,y2,z2, 1);
}
void EraseRect(int8_t x1, int8_t y1, int8_t z1, int8_t x2, int8_t y2, int8_t z2)
{
CalcRect(x1,y1,z1, x2,y2,z2, 0);
}
void SetCubeByString(int8_t cubeFromPC[8][8])
{
// byte bits = X, 1st index=Y, 2nd index = Z
for (int8_t y=0; y<1; y++) {
for (int8_t z=0; z<8; z++) {
cube[y][z] = cubeFromPC[y][z];
}
}
// int yy = y*8;
// for (int8_t z=0; z<8; z++) {
// int idx = yy + z;
// if (bitRead(cubeStr[idx],x) SetDot(x,y,z); else ClearDot(x,y,z);
}
//-- Animations -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//=== BASIC OPERATIONS ===
void TurnOnLayer(int8_t z)
{
int8_t prev = z==0 ? 7 : z-1;
// Prepare for data. Shift data to shift registers but do not reflect it on the outputs yet.
digitalWrite(latchPin, LOW);
//-- Spit out the bits --
DrawLayer(z);
//-- Turn off previous layer --
digitalWrite(2+prev,LOW); // Turn off prev layer
}
void TurnOffLayer(int8_t z)
{
// All data ready. Instantly reflect all 64 bits on all 8 shift registers to the led layer.
digitalWrite(latchPin, HIGH);
//-- Turn on this layer --
digitalWrite(2+z,HIGH); // Turn on this layer
}
void DrawLayer(int8_t z)
{
// Spit out all 64 bits for the layer.
for (int8_t y=0; y<8; y++) {
shiftOut(dataPin, clockPin, MSBFIRST, ~cube[y][z]); // Push Most significant BYTE first
}
}
void LayerOn(int8_t z)
{
for (int8_t y=0; y<8; y++) {
for (int8_t x=0; x<8; x++) {
SetDot(x,y,z);
}
}
}
void SetXPlane(int8_t x)
{
x = Wrap(x);
int8_t xPattern = 1 << x;
for (int8_t z=0; z<8; z++) {
for (int8_t y=0; y<8; y++) {
cube[y][z] = xPattern;
}
}
}
void One_Pixel_Up_a_wall(int8_t y)
{
for (int8_t z=0; z<8; z++) {
for (int8_t x=7; x>=0; x--) {
SetDot(x,y,z);
delay(64);
ClearDot(x,y,z);
}
}
}
void Line_Up_a_wall(int8_t y)
{
for (int8_t z=0; z<8; z++) {
for (int8_t x=7; x>=0; x--) {
SetDot(x,y,z);
}
delay(64);
CubeAllOff();
}
}
void FillLayerLeftToRight(int8_t z)
{
for (int8_t x=0; x<8; x++) {
for (int8_t y=0; y<8; y++) {
SetDot(x,y,z);
}
delay(64);
}
}
void FillWallDownUp(int8_t x)
{
for (int8_t z=0; z<8; z++) {
for (int8_t y=0; y<8; y++) {
SetDot(x,y,z);
}
delay(64);
}
}
void FillLayerRightLeft(int8_t z)
{
for (int8_t x=7; x>=0; x--) {
for (int8_t y=0; y<8; y++) {
SetDot(x,y,z);
}
delay(64);
}
}
void DropOneCenterLine(int8_t x)
{
for (int8_t z=7; z>=0; z--) {
SetDot(x,3,z);
SetDot(x,4,z);
delay(64);
}
}
void FillWallFromCenter(int8_t x)
{
for (int8_t y=1; y<4; y++) {
for (int8_t z=0; z<8; z++) {
SetDot(x,3-y,z);
SetDot(x,4+y,z);
}
delay(64);
}
}
void PinWheel(int8_t y)
{
for (int8_t n=0; n<8; n++)
{
DrawLine3D(0,y,n, 7,y,7-n);
delay(64);
}
for (int8_t n=0; n<8; n++)
{
DrawLine3D(n,y,7, 7-n,y,0);
delay(64);
}
}
void DrawCircle(int8_t z)
{
for (int8_t n=0; n<20; n++)
{
SetDot(circle[n][0], circle[n][1], z);
delay(128);
}
}
void FillFrontAndBackRightLeft()
{
for (int8_t x=0; x<8; x++) {
for (int8_t z=0; z<8; z++) {
SetDot(x,0,z);
SetDot(x,7,z);
}
delay(64);
}
}
void CubeShrink()
{
int8_t cubeSize = 5;
for (int8_t n=1; n<4; n++) {
CubeAllOff();
DrawRect(n,n,n, n, n+cubeSize, n+cubeSize); // YZ plane
DrawRect(7-n,n,n, 7-n, n+cubeSize, n+cubeSize); // YZ plane
DrawRect(n,n,n, n+cubeSize, n+cubeSize, n); // XY plane
DrawRect(n,n,7-n, n+cubeSize, n+cubeSize, 7-n); // XY plane
cubeSize -= 2;
delay(128);
}
}
void CubeAllOn()
{
//noInterrupts();
for (int8_t z=0; z<8; z++) {
for (int8_t y=0; y<8; y++) {
for (int8_t x=0; x<8; x++) {
SetDot(x,y,z);
}
}
}
//interrupts();
}
void CubeAllOff()
{
for (int8_t z=0; z<8; z++) {
SetLayer(z, 0x00);
}
}
void DrawSine(int8_t offset, int8_t y)
{
for (int8_t x=0; x<8; x++)
{
SetDot(x, y, sineArray[ (offset+x) % sineMaxIndex ]);
}
}
//-- Animations -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//==SPECIFIC ANIMATIONS==
void Scan_all_layers()
{
for (int8_t z=0; z<8; z++) {
for (int8_t y=0; y<8; y++) {
pot1 = analogRead(A5);
animSpeed = map(pot1, 0,1023, 16,256);
for (int8_t x=0; x<8; x++) {
SetDot(x,y,z);
delay(animSpeed);
ClearDot(x,y,z);
}
}
}
}
void Rain()
{
//-- Draw plane --
DrawRect(0,0,7, 7,7,7); // Draw top plane
//-- Create an array of those pixels --
int8_t plane[8][8]; // z position of each dot on the plane
for (int8_t x=0; x<8; x++)
for (int8_t y=0; y<8; y++) {
plane[x][y] = 7;
}
int8_t floorCount = 0;
while (floorCount<64)
{
//-- Pick a random dot on the plane to start moving --
boolean found = false;
byte tryCount = 0;
while (!found && tryCount++<20) {
int8_t x = random(8);
int8_t y = random(8);
int8_t z = plane[x][y];
if (z==7) {
ClearDot(x,y,7);
plane[x][y]--;
found = true;
}
}
//-- Fly pixels \that is no longer on plane to opposite side
floorCount = 0;
for (int8_t x=0; x<8; x++)
{
for (int8_t y=0; y<8; y++) {
int8_t z = plane[x][y];
if (z<7 && z>0) {
ClearDot(x,y,z);
z--;
SetDot(x,y,z);
plane[x][y] = z;
}
if (plane[x][y]==0) floorCount++;
}
}
delay(64);
}
delay(1000);
EraseRect(0,0,0, 7,7,0);
}
void RippleToRight()
{
// for (int8_t index=0; index < sineMaxIndex ; index++)
// {
// Serial.print("index="); Serial.print(index);
// Serial.print(" sine="); Serial.println(sineArray[ index ]);
// }
for (byte n=0; n<2; n++) // repeat animation n times
{
for (byte offset=0; offset<sineMaxIndex; offset++)
{
CubeAllOff();
for (int8_t y=0; y<8; y++)
{
DrawSine(offset, y);
}
delay(32);
}
}
}
//-- Utilities -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
int8_t Wrap(int8_t val)
{
if (val>7)
return 0;
else if (val<0)
return 7;
else
return val;
}
int8_t Crop(int8_t val)
{
if (val>7)
return 7;
else if (val<0)
return 0;
else
return val;
}
void Swap(int8_t *x,int8_t *y)
{
int8_t t;
t=*x;
*x = *y;
*y = t;
}
/*NOTES: Here's where we're at with troubleshooting
*
* The cube seems to work correctly and animations run properly with one key exception:
*
* It seems that certain layers are grounded/powered at the incorrect times, or grounded/powered all the time no matter what.
* For example, when any animation is run, you can see a faint flicker in all of the bottom row LEDs.
*
* When the column ribbon cable is entirely unplugged and all of the shift register/rows are left plugged in,
* it is observed that layers 2 and 7 are significantly lit up. This is confusing the heck out of me; something somewhere is somehow always supplying a few of my layers.
*
* In response to this "phantom powering" I have tried unplugging specific shift register ribbon cables. I found that when a certain 4 shift registers are unplugged, that the
* layer lighting issue subsides with the exception of the very faint lit LEDs in the first row.
*
* I have checked my board many, many times looking for connection errors and shorts. I have meticulously cleaned out between each and every connection on my perfboard to ensure there are no shorts,
* I have added 47mic and 100nanofarad bypass caps to each shift register AND the arduino.
*
* I have tried replacing all of the shift registers since I used DIP sockets on my board.
* I have tried with numerous arduino's.
* The BJT's were sourced from Adafruit and the FETs from HiLetGo on Amazon, and the Shift Registers from one of those two sources as well.
*
* I was about to replace some transistors but happened upon the oddity that the cube would light up certain layers when the column header is entirely unplugged
*
*/
Pictures:
- Board top (added wire colors showing ribbon cable orientation)
- Board Bottom
- Fully Assembled
Problem Description:
I noticed at first that there was persistent flickering on some layers, especially on layers 2 and 7. I scratched my head over this for a while, and ended up adding bypass caps on my perf board for each 74HC595 and the Arduino as well, hoping that this may clear up this issue to no avail.
I inspected the board for a long while clearing any of what seemed to be shorts between pads,
and after about 5 rounds of this tried replacing all the shift registers. This also didn't fix the issue. I switched Arduinos; same problem.
During this debug process, minutes before I began replacing transistors, I unplugged the connector providing 5V to each layer through the MOSFET-BJT pairs, and was surprised to see that layers 2 and 7 were mostly illuminated. I tried in various scenarios unplugging this header to see whether or not the LEDs were consistently on in those layers with this connector unplugged, and they were. After this, leaving the layer connector plugged in, I began unplugging shift register rows 1 by 1, noting that the animations appear perfect when a select 4 shift registers are unplugged. This led me back to inspecting the board for shorts on these 4 suspect 595's, which again led nowhere. I checked with my multimeter for shorts throughout the board and the cube and found nothing.
So I am at the point of thinking that (a) these two problems (the flicker and persistently powered layers) are related. I simply cannot find anything wrong with my board, so this leads me to believe (b) it is a code/software related issue.
I have trouble believing that there is something in the code that is causing this problem on my end since it has been used by anyone who has replicated Hari's project, but my understanding of C is very shaky. My research about this issue has led to mostly dead ends, with the exception of one forum post, which seems to describe a similar problem but with my lack of understanding of C it is hard for me to abstract from this discussion and apply it toward my project.
If it is a problem with the transistors, then replacing the appropriate transistors corresponding to layers 2 and 7 would maybe solve the issue. But when the LED anode layers' voltage supply (controlled via the transistors) is entirely disconnected from the LED anodes, layers 2 and 7 still light up (and flash a bit with the intended animation pattern). So why would the transistors be faulty? If it were an electrical connection problem with the shift registers, for it to only exist on two layers would require a consistent miswiring for one specific pin on each of the eight shift registers, which is also unlikely. If it were a problem with the overarching choice of hardware, the issues would exist on all layers instead of only on layers 2 and 7.
I am really proud of this project but the issues on layers 2 and 7 distract from the animations significantly, and am hoping to solve the issue. If anyone may be able to provide advice; software, hardware, or otherwise about what I should try going forward I am willing to try anything. I have an oscilloscope, multimeter, plenty of jelly-bean parts, etc.
For reference:
The parts were sourced from Adafruit/HiLetGo on Amazon. Transistors tested before soldering.
The sections of the code that I do not understand all too well are Paths, Drawing Routines, Utilities, and some various functions all throughout.
A quick summary:
Observing persistent power and flickering during animation on two layers of my cube in particular; layers 2/7. The cube consists of eight 595 ICs and eight FETs to drive the LED layers. I think the issue has to do with the code and my shift registers, but I am looking for outside help since I'm a bit stuck and have observed some definitively consistent errors while debugging.