Pages: [1]   Go Down
Author Topic: 4x4x4 LED Cube with ATtiny85 and MAX7219  (Read 2563 times)
0 Members and 1 Guest are viewing this topic.
West Yorkshire, UK
Offline Offline
Edison Member
*
Karma: 44
Posts: 1401
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi everyone, here's my take on that old classic, the 4x4x4 LED cube.

I believe my design must be one of the simplest around. There are very few components:
  • 64 x 5mm LEDs (preferably high brightness), any colour you like
  • 1 x ATtiny85, plus PCB socket. ATtiny45 would probably be OK too. Or you can leave off the ATtiny and drive the cube directly with almost any Arduino.
  • 1 x MAX7219 LED driver IC, plus PCB socket
  • 3 x 10K resistors
  • 2 x 0.1uF capacitors
  • 1 x 10uF capacitor
  • 1 x light-dependant resistor (LDR)
  • 1 x PCB. Stripboard or Tripad board is a possibility, but very fiddly. I will give single-sided Eagle PCB design files below. Making or getting a PCB made makes the cube far easier and far faster to construct.
  • 1 x battery box for 3 or 4 x AA cells, plus connector if needed. If using 4 cells, they must be rechargeables. 4 non-rechargeables would be too high a voltage and could damage the IC. Alternatively power with 5V from Arduino or other regulated source.

« Last Edit: March 16, 2014, 10:33:02 am by PaulRB » Logged

West Yorkshire, UK
Offline Offline
Edison Member
*
Karma: 44
Posts: 1401
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Here are images of the PCB - schematic and board layout, and the Eagle files:


* Cube.png (21.88 KB, 1278x626 - viewed 97 times.)
* Cube.brd (98.35 KB - downloaded 19 times.)
* Cube.sch (711.35 KB - downloaded 19 times.)

* CubeBrd.png (50.69 KB, 952x994 - viewed 50 times.)
« Last Edit: March 16, 2014, 10:54:25 am by PaulRB » Logged

West Yorkshire, UK
Offline Offline
Edison Member
*
Karma: 44
Posts: 1401
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

This involves first making a "jig" by drilling 8 x 5mm holes in an off-cut of wood. I recommend using a piece of stripboard or perfboard as a template. The hole centres should be 0.8" apart, in a 2 x 4 pattern. Use a fine marker pen or propelling pencil to mark the wood through the stripboard. Then remove the stripboard and use a bradawl to mark the wood in the same spots. Then drill holes through with a 5mm bit. Finally, mark the surface of the wood with pencil to remind you where to bend the LED legs and which which direction to orient the flat on the side of each LED. See attached picture.


* 20131209_172700.jpg (221.28 KB, 1280x720 - viewed 29 times.)

* 20131209_172758.jpg (274.79 KB, 1280x720 - viewed 25 times.)
« Last Edit: March 16, 2014, 11:18:57 am by PaulRB » Logged

West Yorkshire, UK
Offline Offline
Edison Member
*
Karma: 44
Posts: 1401
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

First test 8 LEDs using breadboard and a series resistor (e.g. 200R).

Place 8 LEDs in the holes, taking care to orient the flat spot on the LED correctly. These flat spots indicate which of the 2 legs is the cathode. In this design, the anode legs form the vertical pillars and the cathodes form the horizontal "floors" in the structure. The flat spot should be in a diagonal, North-West orientation.

Then bend the cathode legs down flat in a circuit as in the 3rd picture. You will need to use small electronics pliers to bend the legs at the corners as shown in the 4th picture. Before soldering, double-check to ensure the LEDs are oriented correctly and there is no chance of a short-circuit between anodes and cathodes.


* 20131209_173722.jpg (237.79 KB, 1280x720 - viewed 29 times.)

* 20131209_173046.jpg (231.83 KB, 1280x720 - viewed 25 times.)

* 20131209_174153.jpg (242.53 KB, 1280x720 - viewed 28 times.)

* 20131209_174333.jpg (199.87 KB, 1280x720 - viewed 31 times.)
« Last Edit: March 17, 2014, 05:07:39 am by PaulRB » Logged

West Yorkshire, UK
Offline Offline
Edison Member
*
Karma: 44
Posts: 1401
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Solder the cathodes. Then fold the anode legs flat in a south-east direction as in the second picture. Use the pliers to bend the anode legs 90 degrees upwards again. There are bulges in the legs of many LEDs and they will naturally bend at this point, giving a uniform shape to all 8 legs.

Finally, carefully push the LEDs out of the wood using a small screwdriver from the other side of the wood. Ease all 8 LEDs out a little way first, then ease all 8 out a little more and so on until they are all free. This avoids bending the whole structure by accident.


* 20131209_174733.jpg (220.37 KB, 1280x720 - viewed 27 times.)

* 20131209_174814(0).jpg (229.11 KB, 1280x720 - viewed 28 times.)

* 20131209_175008.jpg (228.5 KB, 1280x720 - viewed 28 times.)

* 20131209_175127.jpg (231.47 KB, 1280x720 - viewed 34 times.)
« Last Edit: March 16, 2014, 11:58:45 am by PaulRB » Logged

West Yorkshire, UK
Offline Offline
Edison Member
*
Karma: 44
Posts: 1401
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Now make 7 more of those structures!

Then solder up the two halves of the cube. You will need some object to place between the layers to get the shape consistent. I found this kitchen clip used to seal food bags. A small piece of wood, planed to the right depth and width, could also be used.

Place one group of LEDs back in the holes, place the separator between the vertical legs and then place a second group of LEDs on top of that. Take care with the orientation! Then solder the two groups together. I find it easiest to solder two opposite corners first.

Solder the remaining legs together, then slide out the separator and repeat the process until you have completed both towers.


* 20131209_185338.jpg (275.59 KB, 1280x720 - viewed 43 times.)

* 20131209_185914.jpg (186.83 KB, 1280x720 - viewed 37 times.)

* 20131209_191045.jpg (267.39 KB, 1280x720 - viewed 43 times.)

* 20131209_192004.jpg (274.15 KB, 1280x720 - viewed 32 times.)
« Last Edit: March 16, 2014, 12:15:21 pm by PaulRB » Logged

West Yorkshire, UK
Offline Offline
Edison Member
*
Karma: 44
Posts: 1401
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

You could skip this step, but I tested the towers by setting up the circuit on breadboard. Wire the anodes and cathodes up in the easiest way. Its not necessary wire them up exactly as per the PCB version for the purposes of testing.  I also uploaded the sketch to the ATtiny at this point (although that could be done on the final PCB).

To upload the sketch to the ATtiny, I first loaded ArduinoISP sketch to a Nano 3, then added the "anti-master-reset" capacitor and wired up the Slave-Reset, MISO, MOSI and SCLK pins as described in the ArduinoISP sketch. Procedure should be exactly the same for Uno and most other Arduinos. During programming of the ATtiny, the led cube will blink randomly, because those same pins are connected to the MAX7219 IC.


* 20131227_155122.jpg (308.62 KB, 1280x720 - viewed 48 times.)
« Last Edit: March 17, 2014, 05:13:36 am by PaulRB » Logged

West Yorkshire, UK
Offline Offline
Edison Member
*
Karma: 44
Posts: 1401
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Solder the IC sockets, resistors, capacitors and header sockets to the PCB, then insert the two ICs. Note that, unlike the other resistors and capacitors, the 10uF cap must be oriented correctly, with its -ve terminal away from the IC sockets.

Then line up and insert the two towers of LEDs and solder in place.

Finally, strip lengths of solid wire and solder the 8 remaining pads to the common anode "floors" on each of the 4 levels on each of the 2 halves of the cube. The pads on the ATtiny side of the PCB should be soldered to the "first floor" anodes and the pads on the MAX7219 side of the PCB should be soldered to the "top floor" anodes. Take care to ensure these wires will not short-circuit against any of the other "floors" of common anodes. If you can find solid wire with clear plastic insulation, you need not strip the entire length. This would avoid the potential shorting issue. If you can find wire like this, please let me know, as I have been unable  to find any! The nearest thing I could find was clear heat-shrink tubing, so that may be an option.


* 20140228_214527.jpg (267.99 KB, 1280x720 - viewed 51 times.)

* 20140228_214506.jpg (255.09 KB, 1280x720 - viewed 54 times.)
« Last Edit: March 17, 2014, 05:24:48 am by PaulRB » Logged

Norfolk UK
Offline Offline
Faraday Member
**
Karma: 69
Posts: 2557
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Nicely done.
Logged


West Yorkshire, UK
Offline Offline
Edison Member
*
Karma: 44
Posts: 1401
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks Riva. I will post the sketch this evening.
Logged

Sydney, Australia
Offline Offline
Edison Member
*
Karma: 33
Posts: 1273
Big things come in large packages
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Nice!
Logged

Arduino libraries http://arduinocode.codeplex.com
Parola hardware & library http://parola.codeplex.com

West Yorkshire, UK
Offline Offline
Edison Member
*
Karma: 44
Posts: 1401
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Code:
//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;

}

Logged

Canberra Australia
Offline Offline
God Member
*****
Karma: 11
Posts: 518
Hardcore Arduino
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Great job Paul. There has been some discussion here before on using a max7219 to control an LED cude and now you have done it   smiley-cool I know what my next project is going to be when I can tear myself away from Wordpress and css long enough to pick up a multimeter and soldering iron again. Before anyone else asks you, do you happen to have any spare pcb's that you would be willing to sell me? I realise that you have kindly provided the Eagle files but eleven boards is always overkill and my stockpile of boards from my last two projects is building up. Thanks for taking the time to document the project so well it is always great to be provided with enough information to actually build something rather than just be teased by some sketchy info that beginners haven't got a snowflakes chance in hell of following.

Pedro
Logged


West Yorkshire, UK
Offline Offline
Edison Member
*
Karma: 44
Posts: 1401
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Before anyone else asks you, do you happen to have any spare pcb's that you would be willing to sell me? I realise that you have kindly provided the Eagle files but eleven boards is always overkill and my stockpile of boards from my last two projects is building up.

No I don't, sorry. I had originally wanted to have a go at home etching the PCB, but decided it was too much hassle, what with all the preparation, unpleasant chemicals and drilling. I found a vendor, on eBay, based in the UK, who makes one-off or very small runs of PCBs using a Router. A single PCB of this size costs around £10, for which you could get several made in China. But he speaks perfect English, will take the raw Eagle files and turn them into the correct Gerber format for his router, and is very helpful, especially with beginners like me, spotting errors and giving advice and suggestions.

Thanks for taking the time to document the project so well it is always great to be provided with enough information to actually build something rather than just be teased by some sketchy info that beginners haven't got a snowflakes chance in hell of following.

Thanks Pedro, that is what I was aiming for, but wasn't sure if I truly achieved it. I will use feedback from anyone trying to follow these steps to improve the detail where needed, if I can.

Paul
Logged

Pages: [1]   Go Up
Jump to: