Introduction: 3X3 LED Cube Programming Tips (Arduino Based)
How to make that cube do what you want it to!
I followed the instructable here and built a 3x3 LED cube run by an Arduino. I was delighted! But it could only do one routine. Again and again and again and again and again.... So I decided to try and program a different routine. I needed a change. Do you?
If you study the code in the cube's Arduino sketch you can see all the instructions right in front of you. To me, it looks like the Matrix, streaming by in all its digital confusability. I needed a graphic interface to help me code the routines. I FOUND ONE!!! But it wrote code that contained extra information that I did not want. How to rewrite all those instructions? Read on!
Be prepared, this is a twisted solution, combining an (apparently) anonymous website with a macro in my word processor, but in the end I have come up with a routine that is very powerful indeed!
Step 1: Graphic Pattern Generator
I searched and searched for "LED Cube Programmers" on the web. I found some, but they were for PIC based cubes. I changed the title of my search to "LED Cube Pattern Generator" and BINGO! I finally found this one, and it allowed my simple mind to grasp the relationship between little flashing lights and gobs of 1's and 0's. I could punch little buttons on the screen and see a simulation of what I was doing! Just what I wanted. This generator allows one to use a cube of any size! Press the "new" button and you will be prompted to set up your cube.
'Insert' creates a new frame, just like a simple animation program. We are going to make a 'flip book,' in effect. Each page is programmed and then you hit the insert button to add another frame. Notice the speed parameter near the bottom of the window marked 'frames.' F and E mean 'Fill' and 'Erase.' Program a few frames and hit the 'Play' button. Your animation will be shown in the 'preview' window. Simple! When you are done and want to save your new light show, hit the 'File' button. You will see the buttons marked 'New',' to Code',' Load' and 'Save' appear. Press 'To Code,'and a window will open, asking you to choose in which order you wish to display your data. You can go back and change this later if you get it wrong. Just choose one for now.
You will then see a window with all your animation's coded bytes.
Step 2: Clean Up That Code, Please!
I select all the code in the window and copy. Ok, now here comes the fun part. Open your word processor and paste that code in a new document. I use Libre Office, and have not tried this on other software. You may be able to improve on my crazy method, please share any tips!
Step 3: Rewrite That Mess a Little Bit.
This is what the pattern generator gives me, and it has some extra information I do not want in my sketch, so we need to remove some characters and make it easier for us to use later. First, get rid of the first and last rows of information, all we want is everything in between those two rows. It should look like this:
Step 4: The Solution!
This is a very short example, so there are not many brackets, but if you program a longer animation removing all those brackets would take too much time. Being an inventor (always looking for a solution, probably due to laziness in my case) I set out to find a different way to accomplish that. The solution? MACRO! I did a search and found out why my "record macro" button was not active in LibreOffice: Here is the answer to that question, follow the instructions and you can then record a macro. What this does is allow us to execute a series of edits once, record those actions as a single command and then perform the edit on the entire document quickly. I'll try and guide us through one example of an edit using my macro and LibreOffice.
First I clicked on Record Macro (see pics)
Next I selected all the text using ctrl A
Then I used Find and Replace to edit out the characters in my program information.
In the find and replace menu, enter the open bracket '{' at the beginning of the text lines like this: Search for {
in the replace line, leave a blank. Click on 'replace all'. You will get a report of how many symbols were replaced.
That's one! Now we repeat that sequence with the next symbol, the close bracket ' }' (no quote) and replace it also with a blank.
Next symbol to replace is the '0' followed by lower case letter 'b' (0b) with an upper case 'B' instead. Have a look at the pictures in this step to see what my text is looking like.
After this last Find and Replace step, we click on the button that says 'stop recording,' in order to make this editing function a macro. (see pictures here) You will be prompted to save the macro, try to remember what you call it!
Now erase all the text and save the document as a blank slate for your routine. I called my document 'blank slate' (duh.)
Next time I want to program I can skip the steps to create a new macro, it should already be there for me to use. I can program short routines and save them as text files, then piece together the snippets into a longer routine at a later time.
Step 5: Using the Macro Function to Edit Another Animation
Now we have programmed our word processor to edit the information generated by the pattern generator into a more friendly format that can be used in our sketch. Let's try the whole procedure so far:
Generate a new animation, say three frames of blinks anywhere. Click on New and enter the number of diodes in your cube. Mine is 3,3,3.
Next I will turn on all the lights in the first frame of our animation.
Next step: add another frame with the 'insert' button. Turn off all but one light anywhere.
Click 'insert' once more and add one more frame, all off.
Now we have a new sequence of three frames, time to generate the code.
Click 'File' and then 'To Code' to see the code window.
Select one of the options offered, I chose 'Depth, Height, Width' (you may have to experiment here)
Select everything inside the code window and copy.
Paste into our word processor document.
Remove the first and last lines as we did earlier.
Select all the text.
Now we want to run that macro. Under 'tools/macro/run macro' we see a list to choose from. You will need to find your macro, probably at the last entry in the list, look around and see if you can find it. This part is difficult for me to describe. I felt that it should have been easier to find, but then I am new to macros. The macro should appear in the window on the right once you have found it.
Click on it and hit run.
That 'should' clean up the code in one step!
Step 6: What Do I Do Now?
Now we have a programmed sequence of frames, and transformed the code to a format that will let us run it inside our Arduino sketch. 'What sketch' you ask? The Arduino sketch I use to drive my cube looks like this:
/*
Based on ledcube.c from Make: September 7, 2007 weekend podcast
http://blog.makezine.com/archive/2007/09/make_a_pocket_led_cube_we.html
Custom animation programmed by Mark Boszko, http://stationinthemetro.com
*/
#include <avr/pgmspace.h> // allows use of PROGMEM to store patterns in flash
#define CUBESIZE 3
#define PLANESIZE CUBESIZE*CUBESIZE
#define PLANETIME 3333 // time each plane is displayed in us -> 100 Hz refresh
#define TIMECONST 5 // multiplies DisplayTime to get ms - why not =100?
// LED Pattern Table in PROGMEM - last column is display time in 100ms units
// TODO this could be a lot more compact but not with binary pattern representation
prog_uchar PROGMEM PatternTable[] = {
B101, B010, B101, B000, B000, B000, B000, B000, B000, 50 ,
B000, B000, B000, B000, B010, B000, B000, B000, B000, 50 ,
B000, B000, B000, B000, B000, B000, B101, B010, B101, 50 ,
B000, B000, B000, B000, B010, B000, B000, B000, B000, 50 ,
}
/*
** Defining pins in array makes it easier to rearrange how cube is wired
** Adjust numbers here until LEDs flash in order - L to R, T to B
** Note that analog inputs 0-5 are also digital outputs 14-19!
** Pin DigitalOut0 (serial RX) and AnalogIn5 are left open for future apps
*/
//int LEDPin[] = {16, 3, 1, 15, 4, 6, 14, 5, 7};
int LEDPin[] = { 2, 3, 4, 5, 6, 7, 8, 9, 10};
//int PlanePin[] = {19, 18, 17};
int PlanePin[] = {13, 12, 11};
// initialization
void setup()
{
int pin; // loop counter
// set up LED pins as output (active HIGH)
for (pin=0; pin<PLANESIZE; pin++) {
pinMode( LEDPin[pin], OUTPUT );
}
// set up plane pins as outputs (active LOW)
for (pin=0; pin<CUBESIZE; pin++) {
pinMode( PlanePin[pin], OUTPUT );
}
}
// display pattern in table until DisplayTime is zero (then repeat)
void loop()
{
// declare variables
byte PatternBuf[ PLANESIZE ]; // saves current pattern from PatternTable
int PatternIdx;
byte DisplayTime; // time*100ms to display pattern
unsigned long EndTime;
int plane; // loop counter for cube refresh
int patbufidx; // indexes which byte from pattern buffer
int ledrow; // counts LEDs in refresh loop
int ledcol; // counts LEDs in refresh loop
int ledpin; // counts LEDs in refresh loop
// Initialize PatternIdx to beginning of pattern table
PatternIdx = 0;
// loop over entries in pattern table - while DisplayTime>0
do {
// read pattern from PROGMEM and save in array
memcpy_P( PatternBuf, PatternTable+PatternIdx, PLANESIZE );
PatternIdx += PLANESIZE;
// read DisplayTime from PROGMEM and increment index
DisplayTime = pgm_read_byte_near( PatternTable + PatternIdx++ );
// compute EndTime from current time (ms) and DisplayTime
EndTime = millis() + ((unsigned long) DisplayTime) * TIMECONST;
// loop while DisplayTime>0 and current time < EndTime
while ( millis() < EndTime ) {
patbufidx = 0; // reset index counter to beginning of buffer
// loop over planes
for (plane=0; plane<CUBESIZE; plane++) {
// turn previous plane off
if (plane==0) {
digitalWrite( PlanePin[CUBESIZE-1], LOW );
} else {
digitalWrite( PlanePin[plane-1], LOW );
}
// load current plane pattern data into ports
ledpin = 0;
for (ledrow=0; ledrow<CUBESIZE; ledrow++) {
for (ledcol=0; ledcol<CUBESIZE; ledcol++) {
digitalWrite( LEDPin[ledpin++], PatternBuf[patbufidx] & (1 << ledcol) );
}
patbufidx++;
}
// turn current plane on
digitalWrite( PlanePin[plane], HIGH );
// delay PLANETIME us
delayMicroseconds( PLANETIME );
} // for plane
} // while <EndTime
} while (DisplayTime > 0); // read patterns until time=0 which signals end
}
Step 7: Modifying the Sketch
There a three things I changed in that sketch to make it run on my cube.
1. The settings for HIGH and LOW level states
2. The order of the output pins
3. The data for the routine, which is what this is all really about!
Let's look at this in detail. I have highlighted the words HIGH and LOW here. I reversed them from the original program. You may or may not have too. Experiment.
// initialization
void setup()
{
int pin; // loop counter
// set up LED pins as output (active HIGH)
for (pin=0; pin<PLANESIZE; pin++) {
pinMode( LEDPin[pin], OUTPUT );
}
// set up plane pins as outputs (active LOW)
for (pin=0; pin<CUBESIZE; pin++) {
pinMode( PlanePin[pin], OUTPUT );
This is the order of the outputs the cube is made after. The first row {16, 3, 1, ...} is simply an example. My numbers are directly underneath that row. {2, 3, 4, ...} be sure to include a space after the comma.
Next is the pins for the plane. Same here, first row is an example, next row is the real deal. You can invert your cube with these numbers. Cool!
//int LEDPin[] = {16, 3, 1, 15, 4, 6, 14, 5, 7};
int LEDPin[] = { 2, 3, 4, 5, 6, 7, 8, 9, 10};
//int PlanePin[] = {19, 18, 17};
int PlanePin[] = {13, 12, 11};
The third change is the animation data, the information we have generated and processed so lovingly. Simply copy it from your word processor document and paste it into the program. Like this:
{
B101, B010, B101, B000, B000, B000, B000, B000, B000, 50 ,
B000, B000, B000, B000, B010, B000, B000, B000, B000, 50 ,
B000, B000, B000, B000, B000, B000, B101, B010, B101, 50 ,
B000, B000, B000, B000, B010, B000, B000, B000, B000, 50 ,
B101, B000, B000, B010, B000, B000, B101, B000, B000, 50 ,
B000, B000, B000, B000, B010, B000, B000, B000, B000, 50 ,
B000, B000, B101, B000, B000, B010, B000, B000, B101, 50 ,
B000, B000, B000, B000, B010, B000, B000, B000, B000, 50 ,
B101, B010, B101, B000, B000, B000, B000, B000, B000, 50 ,
B000, B000, B000, B000, B010, B000, B000, B000, B000, 40 ,
B000, B000, B000, B000, B000, B000, B101, B010, B101, 40 ,
B000, B000, B000, B000, B010, B000, B000, B000, B000, 40 ,
B101, B000, B000, B010, B000, B000, B101, B000, B000, 40 ,
}
See that bracket at the beginning and end of this numerical data? Make sure you include them in the complete sketch.
Load up the sketch and it should run!
I have included an example to show that it DOES work!
Have fun programming your own personal show on that cube. It is a super toy and a great learning tool.
Attachments
Step 8: Conclusion
Have you ever tried to write instructions about how to fold a paper airplane? How can something so simple become so difficult? This may seem as difficult to some of you. I understand completely. To my twisted ways of thinking it makes sense, but my mother always said I was "special." So... If you need help, ask. There are programmers out there and people who can teach us ALL a lot about this subject. All I can say is, hack it!
Give it a try and share some of your own routines here!
Enjoy.
23 Comments
7 years ago
Hey!
I'm having some problems programming a 3x3 LED cube and was wondering if someone could help and send me the correct ARDUINO coding?
Thank you in advance! (Btw, my email is 1loki2loki@gmail.com)
Reply 6 years ago
Here are two examples that should work. One uses a library called ledcube.h that can be found here: https://github.com/gzip/arduino-ledcube/blob/master/LedCube.h
The other works for me.
/*
ledcube.pde - Example sketch for controlling an LED cube.
Created by Gamaiel Zavala (gzip), 2009-2012
MIT License. See accompanying LICENSE file for terms.
*/
#include
#define SIZE 3
#define COLS (SIZE*SIZE)
byte levelPins[SIZE] = {11,12,13};
byte colPins[COLS] = {2,3,4,5,6,7,8,9,10};
LedCube cube(SIZE, levelPins, colPins);
void setup ()
{
}
void loop ()
{
delay(10);
// light each light one at a time
for(byte level=0; level {
for(byte col=0; col {
cube.lightPulse(level, col, 100);
}
}
// light one level at a time, increasing speed each time
for(byte d=25; d>2; d-=2)
{
for(byte l=1; l <= cube.getLevels(); l++)
{
cube.lightLevel(l, d);
}
}
// light each row on each level
for(byte level=1; level<=cube.getLevels(); level++)
{
for(byte row=1; row<=cube.getLevels()*2; row++)
{
cube.lightRow(row, level);
}
}
// light each plane
for(byte i=3; i; i--)
{
for(byte row=1; row<=cube.getLevels()*2; row++)
{
cube.lightPlane(row, 10*i);
}
}
// single random light at a time
cube.randomLight(random(25,100),100);
// random column drop
for(byte x=0; x<=15; x++)
{
cube.lightDrop(random(0,cube.getCols()), random(50,150));
}
// circle around cube at a random level
for(byte x=0; x<=5; x++)
{
cube.lightPerimeter(random(0,cube.getLevels()), random(1,5), random(25,100));
}
// light each face
byte planes[] = {cube.getLevels()+1,cube.getLevels(),cube.getLevels()*2,1};
for(byte i=5; i; i--)
{
for(byte p=0; p {
cube.lightPlane(planes[p], 5*i);
}
}
// random columns
cube.randomColumn(25);
// turn off a single column randomly
cube.enableBuffer();
for(byte c=0; c<30; c++)
{
cube.fillBuffer();
cube.invertBuffer();
cube.randomColumn();
cube.drawBuffer(7);
}
cube.enableBuffer(false);
// cols in and out
for(byte c=1, d=0; c<=10; c++)
{
if(c%2 == 0)
{
for(d=0; d<20; d++)
{
cube.lightColumn(2,1);
cube.lightColumn(4,1);
cube.lightColumn(6,1);
cube.lightColumn(8,1);
}
}
else if(c%4 == 1)
{
for(d=0; d<30; d++)
{
cube.lightColumn(1,1);
cube.lightColumn(3,1);
cube.lightColumn(7,1);
cube.lightColumn(9,1);
}
}
else
{
for(d=0; d<70; d++)
{
cube.lightColumn(5,1);
}
}
}
// diamond and box
byte diamond[] = {0,4, 1,1, 1,3, 1,4, 1,5, 1,7, 2,4};
byte box[] = {
2,0, 2,1, 2,2, 2,3, 2,5, 2,6, 2,7, 2,8,
1,0, 1,2, 1,6, 1,8,
0,0, 0,1, 0,2, 0,3, 0,5, 0,6, 0,7, 0,8
};
cube.lightSequence(box, sizeof(box), 200);
cube.lightSequence(diamond, sizeof(diamond), 400);
// helicopter effect
byte topSeq[8] = {0,3,6,7,8,5,2,1};
byte botSeq[8] = {8,5,2,1,0,3,6,7};
for(byte loops = 0, delay = 50; loops<=8; loops++)
{
for(byte s=0; s<8; s++)
{
byte seq[] = {2,topSeq[s], 1,4, 0,botSeq[s]};
cube.lightSequence(seq, sizeof(seq), delay);
}
if(loops < 5) delay-=10; else delay += 10;
}
// turn off one light at a time
cube.enableBuffer();
cube.fillBuffer();
cube.drawBuffer(25);
for(byte w=0, l, c, max = cube.getNumLights(); w {
// lower bound is inclusive, upper is exclusive
l = random(0, cube.getLevels());
c = random(0, cube.getCols());
if(cube.getBufferAt(l,c) == HIGH)
{
cube.lightOff(l,c);
cube.drawBuffer(5);
w++;
}
}
cube.enableBuffer(false);
}
/////////////////////////////////////////////////////////////////
and the second:
/////////////////////////////////////////////////////////////////
/*
Based on ledcube.c from Make: September 7, 2007 weekend podcast
http://blog.makezine.com/archive/2007/09/make_a_pocket_led_cube_we.html
Custom animation programmed by Mark Boszko, http://stationinthemetro.com
*/
#include // allows use of PROGMEM to store patterns in flash
#define CUBESIZE 3
#define PLANESIZE CUBESIZE*CUBESIZE
#define PLANETIME 3333 // time each plane is displayed in us -> 100 Hz refresh
#define TIMECONST 5 // multiplies DisplayTime to get ms - why not =100?
// LED Pattern Table in PROGMEM - last column is display time in 100ms units
// TODO this could be a lot more compact but not with binary pattern representation
prog_uchar PROGMEM PatternTable[] = {
B101, B010, B101, B000, B000, B000, B000, B000, B000, 50 ,
B000, B000, B000, B000, B010, B000, B000, B000, B000, 50 ,
B000, B000, B000, B000, B000, B000, B101, B010, B101, 50 ,
B000, B000, B000, B000, B010, B000, B000, B000, B000, 50 ,
B101, B000, B000, B010, B000, B000, B101, B000, B000, 50 ,
B000, B000, B000, B000, B010, B000, B000, B000, B000, 50 ,
B000, B000, B101, B000, B000, B010, B000, B000, B101, 50 ,
B000, B000, B000, B000, B010, B000, B000, B000, B000, 50 ,
B101, B010, B101, B000, B000, B000, B000, B000, B000, 50 ,
B000, B000, B000, B000, B010, B000, B000, B000, B000, 40 ,
B000, B000, B000, B000, B000, B000, B101, B010, B101, 40 ,
B000, B000, B000, B000, B010, B000, B000, B000, B000, 40 ,
B101, B000, B000, B010, B000, B000, B101, B000, B000, 40 ,
B000, B000, B000, B000, B010, B000, B000, B000, B000, 40 ,
B000, B000, B101, B000, B000, B010, B000, B000, B101, 30 ,
B000, B000, B000, B000, B010, B000, B000, B000, B000, 30 ,
B101, B010, B101, B000, B000, B000, B000, B000, B000, 30 ,
B000, B000, B000, B000, B010, B000, B000, B000, B000, 30 ,
B000, B000, B000, B000, B000, B000, B101, B010, B101, 30 ,
B000, B000, B000, B000, B010, B000, B000, B000, B000, 20 ,
B011, B101, B111, B000, B000, B000, B000, B000, B000, 10 ,
B101, B101, B111, B000, B000, B000, B000, B000, B000, 10 ,
B110, B101, B111, B000, B000, B000, B000, B000, B000, 10 ,
B111, B100, B111, B000, B000, B000, B000, B000, B000, 10 ,
B111, B101, B110, B000, B000, B000, B000, B000, B000, 10 ,
B111, B101, B101, B000, B000, B000, B000, B000, B000, 10 ,
B111, B101, B011, B000, B000, B000, B000, B000, B000, 10 ,
B111, B001, B111, B000, B000, B000, B000, B000, B000, 10 ,
B011, B101, B111, B011, B101, B111, B000, B000, B000, 10 ,
B101, B101, B111, B101, B101, B111, B000, B000, B000, 10 ,
B110, B101, B111, B110, B101, B111, B000, B000, B000, 10 ,
B111, B100, B111, B111, B100, B111, B000, B000, B000, 10 ,
B111, B101, B110, B111, B101, B110, B000, B000, B000, 10 ,
B111, B101, B101, B111, B101, B101, B000, B000, B000, 10 ,
B111, B101, B011, B111, B101, B011, B000, B000, B000, 10 ,
B111, B001, B111, B111, B001, B011, B000, B000, B000, 10 ,
B011, B101, B111, B011, B101, B111, B011, B101, B111, 10 ,
B101, B101, B111, B101, B101, B111, B101, B101, B111, 10 ,
B110, B101, B111, B110, B101, B111, B110, B101, B111, 10 ,
B111, B100, B111, B111, B100, B111, B111, B100, B111, 10 ,
B111, B101, B110, B111, B101, B110, B111, B101, B110, 10 ,
B111, B101, B101, B111, B101, B101, B111, B101, B101, 10 ,
B111, B101, B011, B111, B101, B011, B111, B101, B011, 10 ,
B111, B001, B111, B111, B001, B111, B111, B001, B110, 10 ,
B011, B101, B111, B000, B000, B000, B000, B000, B000, 10 ,
B101, B101, B111, B000, B000, B000, B000, B000, B000, 10 ,
B110, B101, B111, B000, B000, B000, B000, B000, B000, 10 ,
B111, B100, B111, B000, B000, B000, B000, B000, B000, 10 ,
B111, B101, B110, B000, B000, B000, B000, B000, B000, 10 ,
B111, B101, B101, B000, B000, B000, B000, B000, B000, 10 ,
B111, B101, B011, B000, B000, B000, B000, B000, B000, 10 ,
B111, B001, B111, B000, B000, B000, B000, B000, B000, 10 ,
B011, B101, B111, B011, B101, B111, B000, B000, B000, 10 ,
B101, B101, B111, B101, B101, B111, B000, B000, B000, 10 ,
B110, B101, B111, B110, B101, B111, B000, B000, B000, 10 ,
B111, B100, B111, B111, B100, B111, B000, B000, B000, 10 ,
B111, B101, B110, B111, B101, B110, B000, B000, B000, 10 ,
B111, B101, B101, B111, B101, B101, B000, B000, B000, 10 ,
B111, B101, B011, B111, B101, B011, B000, B000, B000, 10 ,
B111, B001, B111, B111, B001, B011, B000, B000, B000, 10 ,
B011, B101, B111, B011, B101, B111, B011, B101, B111, 10 ,
B101, B101, B111, B101, B101, B111, B101, B101, B111, 10 ,
B110, B101, B111, B110, B101, B111, B110, B101, B111, 10 ,
B111, B100, B111, B111, B100, B111, B111, B100, B111, 10 ,
B111, B101, B110, B111, B101, B110, B111, B101, B110, 10 ,
B111, B101, B101, B111, B101, B101, B111, B101, B101, 10 ,
B111, B101, B011, B111, B101, B011, B111, B101, B011, 10 ,
B111, B001, B111, B111, B001, B111, B111, B001, B110, 10 ,
B011, B101, B111, B000, B000, B000, B000, B000, B000, 10 ,
B101, B101, B111, B000, B000, B000, B000, B000, B000, 10 ,
B110, B101, B111, B000, B000, B000, B000, B000, B000, 10 ,
B111, B100, B111, B000, B000, B000, B000, B000, B000, 10 ,
B111, B101, B110, B000, B000, B000, B000, B000, B000, 10 ,
B111, B101, B101, B000, B000, B000, B000, B000, B000, 10 ,
B111, B101, B011, B000, B000, B000, B000, B000, B000, 10 ,
B111, B001, B111, B000, B000, B000, B000, B000, B000, 10 ,
B011, B101, B111, B011, B101, B111, B000, B000, B000, 10 ,
B101, B101, B111, B101, B101, B111, B000, B000, B000, 10 ,
B110, B101, B111, B110, B101, B111, B000, B000, B000, 10 ,
B111, B100, B111, B111, B100, B111, B000, B000, B000, 10 ,
B111, B101, B110, B111, B101, B110, B000, B000, B000, 10 ,
B111, B101, B101, B111, B101, B101, B000, B000, B000, 10 ,
B111, B101, B011, B111, B101, B011, B000, B000, B000, 10 ,
B111, B001, B111, B111, B001, B011, B000, B000, B000, 10 ,
B011, B101, B111, B011, B101, B111, B011, B101, B111, 10 ,
B101, B101, B111, B101, B101, B111, B101, B101, B111, 10 ,
B110, B101, B111, B110, B101, B111, B110, B101, B111, 10 ,
B111, B100, B111, B111, B100, B111, B111, B100, B111, 10 ,
B111, B101, B110, B111, B101, B110, B111, B101, B110, 10 ,
B111, B101, B101, B111, B101, B101, B111, B101, B101, 10 ,
B111, B101, B011, B111, B101, B011, B111, B101, B011, 10 ,
B111, B001, B111, B111, B001, B111, B111, B001, B110, 10 ,
B000, B000, B000, B000, B000, B000, B100, B000, B000, 20 ,
B000, B000, B000, B100, B000, B000, B000, B000, B000, 20 ,
B100, B000, B000, B000, B000, B000, B000, B000, B000, 20 ,
B010, B000, B000, B000, B000, B000, B000, B000, B000, 20 ,
B000, B000, B000, B010, B000, B000, B000, B000, B000, 20 ,
B000, B000, B000, B000, B000, B000, B010, B000, B000, 20 ,
B000, B000, B000, B000, B000, B000, B001, B000, B000, 20 ,
B000, B000, B000, B001, B000, B000, B000, B000, B000, 20 ,
B001, B000, B000, B000, B000, B000, B000, B000, B000, 20 ,
B000, B001, B000, B000, B000, B000, B000, B000, B000, 20 ,
B000, B000, B000, B000, B001, B000, B000, B000, B000, 20 ,
B000, B000, B000, B000, B000, B000, B000, B001, B000, 20 ,
B000, B000, B000, B000, B000, B000, B000, B000, B001, 20 ,
B000, B000, B000, B000, B000, B001, B000, B000, B000, 20 ,
B000, B000, B001, B000, B000, B000, B000, B000, B000, 20 ,
B000, B000, B010, B000, B000, B000, B000, B000, B000, 20 ,
B000, B000, B000, B000, B000, B010, B000, B000, B000, 20 ,
B000, B000, B000, B000, B000, B000, B000, B000, B010, 20 ,
B000, B000, B000, B000, B000, B000, B000, B000, B100, 20 ,
B000, B000, B000, B000, B000, B100, B000, B000, B000, 20 ,
B000, B000, B100, B000, B000, B000, B000, B000, B000, 20 ,
B000, B100, B000, B000, B000, B000, B000, B000, B000, 20 ,
B000, B000, B000, B000, B100, B000, B000, B000, B000, 20 ,
B000, B000, B000, B000, B000, B000, B000, B100, B000, 20 ,
B000, B000, B000, B000, B000, B000, B000, B000, B000, 20 ,
};
/*
** Defining pins in array makes it easier to rearrange how cube is wired
** Adjust numbers here until LEDs flash in order - L to R, T to B
** Note that analog inputs 0-5 are also digital outputs 14-19!
** Pin DigitalOut0 (serial RX) and AnalogIn5 are left open for future apps
*/
//int LEDPin[] = {16, 3, 1, 15, 4, 6, 14, 5, 7};
int LEDPin[] = { 2, 3, 4, 5, 6, 7, 8, 9, 10};
//int PlanePin[] = {19, 18, 17};
int PlanePin[] = {13, 12, 11};
// initialization
void setup()
{
int pin; // loop counter
// set up LED pins as output (active HIGH)
for (pin=0; pin pinMode( LEDPin[pin], OUTPUT );
}
// set up plane pins as outputs (active LOW)
for (pin=0; pin pinMode( PlanePin[pin], OUTPUT );
}
}
// display pattern in table until DisplayTime is zero (then repeat)
void loop()
{
// declare variables
byte PatternBuf[ PLANESIZE ]; // saves current pattern from PatternTable
int PatternIdx;
byte DisplayTime; // time*100ms to display pattern
unsigned long EndTime;
int plane; // loop counter for cube refresh
int patbufidx; // indexes which byte from pattern buffer
int ledrow; // counts LEDs in refresh loop
int ledcol; // counts LEDs in refresh loop
int ledpin; // counts LEDs in refresh loop
// Initialize PatternIdx to beginning of pattern table
PatternIdx = 0;
// loop over entries in pattern table - while DisplayTime>0
do {
// read pattern from PROGMEM and save in array
memcpy_P( PatternBuf, PatternTable+PatternIdx, PLANESIZE );
PatternIdx += PLANESIZE;
// read DisplayTime from PROGMEM and increment index
DisplayTime = pgm_read_byte_near( PatternTable + PatternIdx++ );
// compute EndTime from current time (ms) and DisplayTime
EndTime = millis() + ((unsigned long) DisplayTime) * TIMECONST;
// loop while DisplayTime>0 and current time < EndTime
while ( millis() < EndTime ) {
patbufidx = 0; // reset index counter to beginning of buffer
// loop over planes
for (plane=0; plane // turn previous plane off
if (plane==0) {
digitalWrite( PlanePin[CUBESIZE-1], LOW );
} else {
digitalWrite( PlanePin[plane-1], LOW );
}
// load current plane pattern data into ports
ledpin = 0;
for (ledrow=0; ledrow for (ledcol=0; ledcol digitalWrite( LEDPin[ledpin++], PatternBuf[patbufidx] & (1 << ledcol) );
}
patbufidx++;
}
// turn current plane on
digitalWrite( PlanePin[plane], HIGH );
// delay PLANETIME us
delayMicroseconds( PLANETIME );
} // for plane
} // while } while (DisplayTime > 0); // read patterns until time=0 which signals end
}
/////////////////////////////////////////
let me know how that works.
Jim
7 years ago
why i get like thi. what i have to do
Reply 7 years ago
Try this website my friend has made. It is so much easier than the method outlined in this instructable.
http://www.ledcube3d.tk/
7 years ago
I'v get an error " prog_uchar " is undefined and that " pgm_read_byte_near " is a expected an expression don't know that means? could i missing a library and if i am which one??
Reply 7 years ago
Jacob,
if you look at this site, designed by my friend to program these
cubes, you will ind a much easier way to program a cube like this one. He is a fantastic coder in India, 15 years old... Check your pin numbers and rewrite them in your sketch to match the ones he uses. Let us know how it works.
A fantastic programming interface! Try it!!
http://www.ledcube3d.tk/
Reply 7 years ago
Will do, Thanks for the advise .
7 years ago
Hello I had build this cube a while back and everything functioned correctly but when I tried to upload the code to my cube I keep getting an error on the very last like of the code. Error goes as follows
Expected 'else' before '}' I have read over the code atleast 20 times and I cannot figure it out. The code is exactly the same as what is posted on this site. Any help would be greatly appreciated
Reply 7 years ago
If you look at earlier comments on this instructable you will see a link I posted to a site my friend made. There you can program your led routine and upload his code. Try that it works and gives you more flexibility. Let me know how it goes.
Reply 7 years ago
OK thank you so much!
7 years ago on Step 6
on the 17th line of code in your example, "prog_uchar PROGMEM PatternTable[] = {" Needs to be modified. "prog_uchar" should be "const char". Im not sure what "prog_uchar" is for, but in the current version of Arduino firmware, it does not know what to do with that. The line of code that worked for me was "const char PROGMEM PatternTable[] = {"
7 years ago on Step 6
8 years ago on Introduction
rkshirsarar,
if you look at this site, designed by my friend to program these cubes, you will see the pin numbers in his setup code located in the code examples he provides. He uses these pins for his setup and it works great!
int g1=4;
int g2=3;
int g3=2 (pins 4,3,2 for the transistors, 5-13 for the anodes, as stated there)
A fantastic programming interface! Try it!!
http://www.ledcube3d.tk/
8 years ago on Introduction
Could you tell which pins of Arduino are connected to transistors and which pins are connected to anode of led's, I mean the schematic of the cube.
9 years ago on Introduction
If anyone tries this... you should have a look at this fantastic online programmer for 3x3x3 cubes: http://ledcube.tk
Lots of great features, and no more need for this workaround!
Thanks Satvik for the coding and work there. You've done a great job!
Reply 9 years ago on Introduction
Hy,
I want to ask u about the
http://ledcube.tk I have done the led cube and I uploaded the code from the site but I have to change something in the code to make it work something like (a1),etc.CAn u help me please?
9 years ago on Introduction
It doesn't work for 4x4x4 cube.
10 years ago on Introduction
i have a question about the code.
When i want to upload it to my arduino, i get an error on line:
int LEDPin[] = { 2, 3, 4, 5, 6, 7, 8, 9, 10};
it says:
sketch_jan07a:34: error: expected ',' or ';' before 'int'
sketch_jan07a.ino: In function 'void setup()':
sketch_jan07a:45: error: 'LEDPin' was not declared in this scope
sketch_jan07a.ino: In function 'void loop()':
sketch_jan07a:95: error: 'LEDPin' was not declared in this scope
what can be the problem?
Reply 10 years ago on Introduction
Your first error indicates a problem *before* inserting the changed lines. Check everything VERY carefully from the beginning and see if you can find a missing semicolon.
10 years ago on Introduction
Hi, this is very interesting, but i have a question, is posible to program 2 or 3 routines and start them whit a button? thanks for afterhand