Over the past few months I put together this little POV project. I finally posted an instructable here: http://www.instructables.com/id/Ceiling-Fan-LED-Display/
Basically what I did was created a circuit board specifically for this purpose. I wanted to put the uC on one board and be able to use the same board layout throughout the whole project. As I progressed with it of course I ran into a lot of "I shoulda done it like this instead" Also, My programming ability is amateur at best. One concern is the pitfalls of such a large array and another is more effective timing. I'm sure there are better way ways to code and or build this and where better than here to learn it?
It's still a work in progress and I've received a lot of feedback and ideas. It's all good to hear about. Some include RGBs and Inductive power supply.
I wanted to post here because I've learned a lot reading these pages since getting my first Arduino last year and I'm sure with some help it can only get better.
BTW, I had to cut out three of my image arrays out of the code to fit the charachter limit.
#include <avr/pgmspace.h>
// This mess created by UncleBone: unclebone@gmail.com
byte latchPin = 5; // ST_CP of 74HC595
byte clockPin = 4; // SH_CP of 74HC595
byte dataPin[] = {2,6,7,8,9};
byte blade[5][5];
long new_time = 1;
long last_time = 1;
long timing = 1;
volatile int breaker = HIGH;
const byte numRegister = 4; //Number of shift regiters on each blade
const byte numAngle = 24; //Number of angles in each group
const byte numGroup = 5; //Number of groups. each blade is always in a separate group and increments around the circle back to Zero position
int RotaionCnt = 0; //counts each full rotation currently for the purpose of switching between whole images
byte image = 0; //which whole image. the first definition in the array
byte group = 0; //there are 5 groups to separate each fan blade.
byte LightStatus[5][4];
//array of 4 separate images containing 5 groups of 12 angles with 4 bytes of data and another 12 with 2 bytes to help fill in the outer portion of the disc (1200 bytes of ram to begin with)
PROGMEM prog_uint8_t angleDef[4][5][24][4] =
{
{{{192,239,255,31},{192,239,255,31},{192,255,255,3},{192,255,255,3},{128,255,127,0},{128,255,127,0},{192,127,192,15},{192,31,192,15},{192,15,135,63},{192,143,135,63},{192,143,31,126},{192,143,31,126},{128,31,63,126},{128,31,63,126},{0,127,0,254},{0,254,0,254},{0,252,7,255},{0,240,7,255},{0,224,255,255},{0,128,255,255},{0,0,252,255},{0,0,252,255},{0,0,128,127},{0,0,128,127}},{{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}},{{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}},{{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}},{{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,120,0},{0,0,120,0},{0,0,252,3},{0,0,252,3},{0,240,231,15},{0,224,231,15},{128,143,159,31},{31,63,159,31},{126,126,254,63},{248,253,254,63},{248,249,253,255},{240,251,253,255},{224,247,251,63},{224,247,251,63}}}
};
void setup(){
pinMode(latchPin, OUTPUT);
pinMode(clockPin, OUTPUT);
for(byte z = 0; z < numGroup; z++){
pinMode(dataPin[z], OUTPUT);
}
attachInterrupt(1, lap, RISING); //interupt for hall switch
for(byte i = 0; i < numGroup; i++){ //Create a 5x5 array for blade positions based on which blade is in which area of the circle
for(byte n = 0; n < numGroup; n++){
blade[i][n] = i + n;
if (blade[i][n] >= numGroup)
blade[i][n] = blade[i][n] - numGroup;
}
}
}
void loop(){
for(byte angle = 0; angle < numAngle; angle++){
if(RotaionCnt >= (400)){ //images switch every 100 rotations
RotaionCnt = 0;
image = 0;
}
else if (RotaionCnt >= (300))
image = 3;
else if (RotaionCnt >= (200))
image = 2;
else if (RotaionCnt >= (100))
image = 1;
else
image = 0;
if (group >=numGroup) //after looping 5 times, once for each group of 24 angles, reset the base blade back to the begining group
group = 0;
digitalWrite(latchPin, 0);
for(byte i = 0; i<numRegister; i++){ //all 5 blade control data must be known by this point but not yet set
for(byte t = 0; t < numGroup; t++){
LightStatus[t][i] = pgm_read_byte(&(angleDef[image][blade[group][t]][angle][i]));
}
for (byte a = 0; a <= 7; a++){
digitalWrite(clockPin, 0); // the ouput for each byte of all 5 lights must be written between the two clock pins
for(byte z = 0; z < numGroup; z++){
if (LightStatus[z][i] & (1<<a) )
digitalWrite(dataPin[z], 1);
else
digitalWrite(dataPin[z], 0);
}
digitalWrite(clockPin, 1);
}
}
digitalWrite(latchPin, 1);
if(breaker == LOW){
group = 0;
angle = 0;
breaker = HIGH;
new_time = millis();
timing = ((new_time - last_time)/219); //ideally 120 but the code takes a lot of time. adjust denominator to steady image
last_time = new_time;
}
delay(timing);
}
group++;
RotaionCnt++;
delay(timing);
}
void lap(){
breaker = LOW;
}