8x8x8 LED Cube - Flickering/Phantom/Persistent Power Issue

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:

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.

Hi,
Can you please post some images of the PCB pattern?
Are the bypass capacitors as close as possible the the 595 IC's.

Are the gnd and LED power tracks of sufficient width go carry the current?

Thanks.. Tom. :smiley: :+1: :coffee: :australia:

Hi Tom,

Thanks for your reply. The board is actually a perf board with 2.54mm spaced copper plated through holes. It is a bit hard to see what is going on on the board, so I'll attempt to trace it out in ExpeditionPCB and get back to you/update the post. I will say that I routed the clock and latch pins oddly on my board. I soldered the Arduino pins D12 and D13 for clock/latch respectively directly to the correct pins of the first shift register. I then jumped these pins to the next shift register and so forth for the remaining 6 instead of soldering 8 wires directly to D12,D13 on the Arduino.

I used 20AWG stranded core for the power interconnections and 24AWG for everything else. I also used DIP sockets for the ICs and properly sized pin sockets for the Arduino.

I used tinned 20AWG copper bus bar for the two power/GND busses on my perf board. In reflection this isn't ideal but it's what I had on hand.

The cube itself is made of 24AWG copper bus bar and all ribbon cable is 24AWG stranded core for the one layer cable and 8 shift register cables. So LED current is primarily in 24 gauge wires

The caps were added after I observed the problem and are not ideally fitted; I soldered 100nF ceramics to the radial leads of a 47mic electrolytic and tacked them across power/GND for each IC. The 595's are 16pin DIPs so I left the electrolytic leads at full length to reach across from pin 1 to 16. But as I said, adding the caps didn't seem to help at all. In addition I think my PSU is a bit overkill and most of the projects I've seen neglected to mention bypass caps.

I want to emphasize that the problem is definitely related to two layers specifically, and that weird things happen on these layers even when the layers are disconnected from the FET drains.

Oh, and my power connector is one of those fudgy screw terminal ones :smiling_face_with_tear:

Hi,
Thanks for the update.
Hint; When you add information, please do it in a new post, rather than update an old post.
I helps to follow the flow of the thread to new readers.

Tom.. :smiley: :+1: :coffee: :australia:

Thanks for bearing with me as I get used to posting on the forum. Since my shift register wiring is messy and impossible to workout over the internet I created a representative layout in KiCad. What's the best way for me to share these files to the topic?

Here are some screen captures for the time being:

  • An overview of the entire shift register section of the board

where Clock, Latch, and Data come from the appropriate Arduino pins D11, D12, or D13.

  • Zoomed on two shift register IC's, because the first image lacks detail

  • Reference 595 pinout

595pinout

I'm sorry for the lack of detail in the PCB; I'm unfamiliar with the KiCad PCB editor (and PCB routing in general) and am working without a complete schematic/netlist. The via's off of the 595 MR' and VCC pins are tied to +5V while OE' and GND are tied to GND (as in the schematic)

As I noted in my original reply to your post, the latch and clock pins are not wired as shown, and instead sort of daisy chained along the shift registers with the data line. You can observe what I mean via the picture of the underside of my perf board (I wanted to repost this image since I added it in an edit to the original topic):

Showing this would be messy and contradict the reason I made the drawing, so it is omitted. I should redo the wiring on my board regardless.

Also not shown in the PCB drawings are the bypass caps. I will work on adding the remaining components and traces so that readers can visualize the board better.

Ghosting is almost always caused by software. In those cases, there is insufficient blanking during state changes. Have a look at your scan timing and logic.

Hi aarg,

So I've read. Can you link me something relevant to blanking so I can read up on the topic? Further, I'm not sure ghosting is the right term to describe what I'm observing. Could you have a look at the videos I posted and let me know if it looks like ghosting to you? Especially this one.

Thank you for your contribution!

Slow the timing down so you can sit and observe what is going on. To me, it looks like faulty pattern logic, not ghosting.

The pattern logic is done with nested iterations. It's tiresome to plow through someone's code to find issues, so you should slow it down first. Even, arrange to single step through the sequence via a key press or something. Believe me, I've done that. At the same time, go through your logic with a fine toothed comb.

Also, you should construct some test suites that obey a very simple pattern, such that the mode of deviation from it is crystal clear, obvious.

You do not want to tune your violin in the middle of Beethoven's 5th symphony. :slight_smile:

I’ll do the work of course :grin:

I’m relieved to hear that you’re pretty convinced its a logic issue. Thanks for pointing me in a direction.

I’m eager to hear what others think

Hi,
Write some code that just turns one line or one layer on and see what you have.
Don't modify your existing code, start from fresm.

Tom.... :smiley: :+1: :coffee: :australia:

Hi Tom,

Haven't had the time to sit down and write any code yet but noticed just now that my wiring per the shift register output pins is a bit off. The project I was following along with sensibly uses a 1:1 relationship with the numbering of the shift register outputs (ie. Q0-->Q7) and the numbering of the LED cube columns. So essentially pin Q0 on shift register 0 (SR0 as shown) should correspond to column 0, which is effectively the origin of the 512 LED matrix.

BUT,
On my board, I tied pin Q0 for every single shift register to the rightmost socket of my pin header. At the same time, Q1-->Q7 belong to the first 7 sockets from left to right. No matter which way I orient the pin header with the socket soldered in place as is I will not properly see the animations. Hopefully I described that well enough. I'm not sure if it will fix the issues with layers 2 and 7 but I will make the change and report back, and at the same time work on writing some code.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.