/* * Spookotron * * Lights for a Halloween pumpkin. */ // ----- ShiftBrite support ---------------------------------- int datapin = 10; // DI int latchpin = 11; // LI int enablepin = 12; // EI int clockpin = 13; // CI unsigned long SB_CommandPacket; int SB_CommandMode; int SB_ColorCommand[3]; // ----- Graphics -------------------------------------------- // Color constants int colors[12][3] = { { 255, 0, 0}, // Red { 0, 255, 0}, // Green { 0, 0, 255}, // Blue { 255, 0, 255}, // Magenta { 255, 255, 0}, // Yellow { 255, 70, 0}, // Orange { 128, 0, 255}, // Purple { 0, 45, 255}, // Cyan { 0, 0, 0}, // Off (Black?) { 50, 0, 0}, // Dim Red { 0, 25, 0}, // Dim Green { 75, 0, 100} // Dim Magenta }; #define red colors[0] #define green colors[1] #define blue colors[2] #define magenta colors[3] #define yellow colors[4] #define orange colors[5] #define purple colors[6] #define cyan colors[7] // Current frame index in animation. This is used as a central clock // for various functions. int frameIndex = 0; // Current animation that is running int animation = 0; const int maxAnimation = 4; int animationColor1[3]; int animationColor2[3]; int animationState1 = 0; void setup() { int index; pinMode(datapin, OUTPUT); pinMode(latchpin, OUTPUT); pinMode(enablepin, OUTPUT); pinMode(clockpin, OUTPUT); digitalWrite(latchpin, LOW); digitalWrite(enablepin, LOW); Serial.begin(9600); // Clear every ShiftBrite bit. This helps us get out of // reset without setting the damnable "test" bit that // makes them go crazy SB_CommandMode = 0; SB_ColorCommand[0] = 0; SB_ColorCommand[1] = 0; SB_ColorCommand[2] = 0; for (index=0; index<10; ++index) { SB_SendPacket(false); } // Use a bit more current for the blue LEDs as they aren't // anywhere near as bright as the green ones. Red is somewhere // in the middle. SB_CommandMode = B01; // Write to current control registers SB_ColorCommand[0] = 50; SB_ColorCommand[1] = 10; SB_ColorCommand[2] = 100; for (index=0; index<10; ++index) { SB_SendPacket(false); } SB_SendPacket(true); // Init random numbers randomSeed(analogRead(0)); } // www.macetech.com code. The only change is to take a bool // indicating whether to latch the data in place or not. // Also reordered the colors so we can think in RGB instead // of BRG! void SB_SendPacket(boolean latch) { SB_CommandPacket = SB_CommandMode & B11; SB_CommandPacket = (SB_CommandPacket << 10) | (SB_ColorCommand[2] & 1023); SB_CommandPacket = (SB_CommandPacket << 10) | (SB_ColorCommand[0] & 1023); SB_CommandPacket = (SB_CommandPacket << 10) | (SB_ColorCommand[1] & 1023); shiftOut(datapin, clockpin, MSBFIRST, SB_CommandPacket >> 24); shiftOut(datapin, clockpin, MSBFIRST, SB_CommandPacket >> 16); shiftOut(datapin, clockpin, MSBFIRST, SB_CommandPacket >> 8); shiftOut(datapin, clockpin, MSBFIRST, SB_CommandPacket); if (latch) { delay(1); // adjustment may be necessary depending on chain length digitalWrite(latchpin,HIGH); // latch data into registers delay(1); // adjustment may be necessary depending on chain length digitalWrite(latchpin,LOW); } } // Light up the pumpkin with a given color void SetColor(int r, int g, int b) { int lights; // There are 3 ShiftBrites in the chain -- update them all for (lights=3; lights>=0; --lights) { // Send the pixel to the devie SB_ColorCommand[0] = r*4; SB_ColorCommand[1] = g*4; SB_ColorCommand[2] = b*4; // If this is the last pixel, then latch the data to // update the display boolean latch = lights == 0; SB_CommandMode = B00; // Write to PWM control registers SB_SendPacket(latch); } } void SetLerpColor(int color1[3], int color2[3], int lerp, int lerpMax) { int r = ((lerpMax-lerp)*color1[0] + lerp*color2[0]) / lerpMax; int g = ((lerpMax-lerp)*color1[1] + lerp*color2[1]) / lerpMax; int b = ((lerpMax-lerp)*color1[2] + lerp*color2[2]) / lerpMax; SetColor(r, g, b); } bool StepAnimation() { switch (animation) { // Fade into and out of color case 0: if (frameIndex == 0) { // Initialize animationColor1[0] = 0; animationColor1[1] = 0; animationColor1[2] = 0; int rand = random(10); if (rand > 6) { // Pick red most of the time animationColor2[0] = 255; animationColor2[1] = 0; animationColor2[2] = 0; } else { animationColor2[0] = colors[rand][0]; animationColor2[1] = colors[rand][1]; animationColor2[2] = colors[rand][2]; } } if (frameIndex <= 60) { SetLerpColor(animationColor1, animationColor2, frameIndex, 60); } else if (frameIndex > 200) { SetLerpColor(animationColor2, animationColor1, frameIndex-200, 20); if (frameIndex == 220) { return true; } } break; // Lightning case 1: if (frameIndex == 0) { // Init animationState1 = 0; } if (frameIndex >= animationState1) { // Bolt on? if (frameIndex-animationState1 < 3) { SetColor(255,255,255); } else { SetColor(0,0,0); animationState1 += (random(5)+1) * 3; } } if (frameIndex == 200) { SetColor(0,0,0); return true; } break; // Hue ramping case 2: if (frameIndex == 0) { animationState1 = 0; } { int colorIndex = frameIndex / 30; SetLerpColor(colors[colorIndex], colors[colorIndex+1], frameIndex % 30, 30); } if (frameIndex == 269) { SetColor(0,0,0); return true; } break; // Flickering candle case 3: if (frameIndex == 0) { animationColor1[0] = 175; animationColor1[1] = 75; animationColor1[2] = 0; animationColor2[0] = 255; animationColor2[1] = 255; animationColor2[2] = 0; animationState1 = 5; } SetLerpColor(animationColor2, animationColor1, animationState1, 5); if (animationState1 == 0) { int rand = random(80); animationColor1[0] = 175 + rand; animationColor1[1] = 75 + rand*2; animationState1 = 3 + random(6); } else { animationState1--; } if (frameIndex == 300) { SetColor(0,0,0); return true; } break; } return false; } void loop() { bool done = StepAnimation(); if (done) { frameIndex = 0; delay(1000); animation = random(maxAnimation); } else { ++frameIndex; } delay(25); }