4x4x4 LED Cube with ATtiny85 and MAX7219

//4x4x4 LED Cube using ATtiny85 and MAX7219
//P.Beard
//Dec 2013

//Pins for serial communications with the MAX7219 IC
#define M7219DATA 0
#define M7219CLK 2
#define M7219LOAD 1
#define LDR 3

//MAX7219 Control Registers
#define INTENSITY 0x0A
#define ON_OFF 0x0C
#define DECODE 0x09
#define SCAN_DIGITS 0x0B
#define TEST_MODE 0x0F

word layer[4]; //Holds the current bit pattern for each layer of the cube

int animSpeed;

void setup() {

  pinMode(M7219DATA, OUTPUT);
  pinMode(M7219CLK, OUTPUT);
  pinMode(M7219LOAD, OUTPUT);
  pinMode(LDR, INPUT);

  send7219(ON_OFF, 1);//Switch on LED Driver
  send7219(DECODE, 0);//Set no decoding on any digits
  send7219(SCAN_DIGITS, 7);//Scan all 8 digits
  
  for (int l=0; l<4; l++) {
    layer[l] = 0;
  }
  updateCube();

  send7219(TEST_MODE, 1);//Display test mode on
  delay (1000);
  send7219(TEST_MODE, 0);//Display test mode off
  delay (1000);
  
  randomiseCube();
  updateCube();

}

void loop() {
  rotatingSquare();
  rotation();
  randomRiseAndFall();
  rain();
  bubbles();
}

void rotatingSquare() {

  //Rotating square

  //  07        03	15	11
  //  02	04	10	12
  //  00	05	08	13
  //  01	06	09	14

  layer[0] = B00000000 << 8 | B10000111;

  animSpeed = 75;

  for (int i = 0; i<100; i++) {

    layer[0] = rotateClockwise(layer[0]);
    layer[1] = layer[0];
    layer[2] = layer[0];
    layer[3] = layer[0];
    updateCube();

  }

  layer[1] = rotateClockwise(layer[0]);
  layer[2] = rotateClockwise(layer[1]);
  layer[3] = rotateClockwise(layer[2]);

  for (int i = 0; i<100; i++) {

    for (int l = 0; l<4; l++) {
      layer[l] = rotateClockwise(layer[l]);
    }

    updateCube();

  }
}

void rotation() {

  //Rotation

  animSpeed = 151;

  for (int l=0; l<4; l++) {
    layer[l] = 1 << random(16) | 1 << random(16);
  }

  for (int i=0; i<150; i++) {

    for (int l=0; l<4; l++) {
      layer[l] = rotateClockwise(layer[l]);
    }
    animSpeed--;

    updateCube();

  }

  for (int i=0; i<150; i++) {

    for (int l=0; l<4; l++) {
      layer[l] = rotateAnticlockwise(layer[l]);
    }
    animSpeed++;

    updateCube();

  }

  animSpeed = 25;

  for (int i=0; i<100; i++) {

    layer[0] = rotateClockwise(layer[0]);
    layer[1] = rotateAnticlockwise(layer[1]);
    layer[2] = rotateClockwise(layer[2]);
    layer[3] = rotateAnticlockwise(layer[3]);
    updateCube();

  }
}

void randomRiseAndFall() {

  //Random rise & fall

  layer[1] = 0;
  layer[2] = 0;
  layer[3] = layer[0] ^ 0xFFFF; // top layer opposite of bottom layer

  word w;
  int l = 0;

  animSpeed = 100;

  for (int i=0; i<33; i++) {

    do {
      //Pick an led on the bottom row that is lit
      w = 1 << random(16);
    } 
    while ((layer[0] & w) == 0);

    //Move that led up to the top layer

    do {
      layer[l] = layer[l++] ^ w;
      layer[l] = layer[l] ^ w;
      updateCube();
    }
    while (l < 3);

    do {
      //Pick an led on the top row that is lit
      w = 1 << random(16);
    } 
    while ((layer[3] & w) == 0);

    //Move that led down to the bottom layer

    do {
      layer[l] = layer[l--] ^ w;
      layer[l] = layer[l] ^ w;
      updateCube();
    }
    while (l > 0);

  }
}

void bubbles() {

  //Random Bubbles

  animSpeed = 100;

  for (int i=0; i<100; i++) {

    for (int l=3; l>=0; l--) {

      layer[l] = layer[l-1];

    }
    layer[0] = 1<<random(16) | 1<<random(16) | 1<<random(16);

    updateCube();

  }
}

void rain() {

  //Random Rain

  animSpeed = 50;

  for (int i=0; i<100; i++) {

    for (int l=0; l<3; l++) {

      layer[l] = layer[l+1];

    }
    layer[3] = 1<<random(16) | 1<<random(16) | 1<<random(16);

    updateCube();

  }
}

void send7219(byte reg, byte val) { //Send 2 bytes to the MAX7219 Driver IC

  shiftOut(M7219DATA, M7219CLK, MSBFIRST, reg); //Send the register we want to set
  shiftOut(M7219DATA, M7219CLK, MSBFIRST, val); //Send the value 

  digitalWrite(M7219LOAD, HIGH); //Tell MAX7219 to load the data
  digitalWrite(M7219LOAD, LOW);

}

void updateCube() { //Update the cube LEDs

  static int LDRavg;

  send7219(2, highByte(layer[0]));
  send7219(6, lowByte(layer[0]));
  send7219(8, highByte(layer[1]));
  send7219(4, lowByte(layer[1]));
  send7219(3, highByte(layer[2]));
  send7219(7, lowByte(layer[2]));
  send7219(5, highByte(layer[3]));
  send7219(1, lowByte(layer[3]));

  LDRavg = (analogRead(LDR) + LDRavg * 15) / 16;
  send7219 (INTENSITY, (LDRavg >> 6)); //Set intensity
  delay (animSpeed);

}

void randomiseCube() {//Set entire cube to random

  for (int l=0; l<4; l++) {
    layer[l] = random(65536);
  }

}


word rotateClockwise(word w) { //Rotate w0 clockwise
  
  //  07        03	15	11
  //  02	04	10	12
  //  00	05	08	13
  //  01	06	09	14

  byte bit1 = bitRead(w, 1);
  bitWrite(w,  1, bitRead(w,  6));
  bitWrite(w,  6, bitRead(w,  9));
  bitWrite(w,  9, bitRead(w, 14));
  bitWrite(w, 14, bitRead(w, 13));
  bitWrite(w, 13, bitRead(w, 12));
  bitWrite(w, 12, bitRead(w, 11));
  bitWrite(w, 11, bitRead(w, 15));
  bitWrite(w, 15, bitRead(w,  3));
  bitWrite(w,  3, bitRead(w,  7));
  bitWrite(w,  7, bitRead(w,  2));
  bitWrite(w,  2, bitRead(w,  0));
  bitWrite(w,  0, bit1);

  byte bit5 = bitRead(w, 5);
  bitWrite(w,  5, bitRead(w,  8));
  bitWrite(w,  8, bitRead(w, 10));
  bitWrite(w, 10, bitRead(w,  4));
  bitWrite(w,  4, bit5);

  return w;

}

word rotateAnticlockwise(word w) { //Rotate w0 anticlockwise

  //  07        03	15	11
  //  02	04	10	12
  //  00	05	08	13
  //  01	06	09	14

  byte bit1 = bitRead(w, 1);
  bitWrite(w,  1, bitRead(w,  0));
  bitWrite(w,  0, bitRead(w,  2));
  bitWrite(w,  2, bitRead(w,  7));
  bitWrite(w,  7, bitRead(w,  3));
  bitWrite(w,  3, bitRead(w, 15));
  bitWrite(w, 15, bitRead(w, 11));
  bitWrite(w, 11, bitRead(w, 12));
  bitWrite(w, 12, bitRead(w, 13));
  bitWrite(w, 13, bitRead(w, 14));
  bitWrite(w, 14, bitRead(w,  9));
  bitWrite(w,  9, bitRead(w,  6));
  bitWrite(w,  6, bit1);

  byte bit5 = bitRead(w, 5);
  bitWrite(w,  5, bitRead(w,  4));
  bitWrite(w, 04, bitRead(w, 10));
  bitWrite(w, 10, bitRead(w,  8));
  bitWrite(w,  8, bit5);

  return w;

}