I have a chain of 7 ShiftBrite units that I just started playing with. I would like to have them gradually shift through a range of colors that loops seamlessly, each ShiftBrite will be a different hue and the colors move down the line as the animation progresses. (edit: I found a video of the exact effect I am looking for.) Usually I can look through other people's code and learn from there, but this is proving a bit more difficult for me to figure out.
Right now I am using a portion of code from this Spookotron project. The code is working great, and it does cycle through a hue ramp, but it cycles the entire chain of lights at the same time so that all lights are the same color. I would like to have each ShiftBrite be a unique color. Unfortunately using shift registers and LEDs are new to me so I haven't been able to figure out how to approach this.
What's the best way to approach the math behind the color cycles? Either direct help or even just pointing me to a similar project is appreciated.
Below is the code I am currently using. I've stripped it down to just the basics. It runs the hue ramp code stripped from the spookotron project, alternates a backlight, and after 30 seconds shuts itself down using a Pololu power switch.
#include <Metro.h>
int dataPin = 11; // DI
int latchPin = 9; // LI
int enablePin = 10; // EI
int clockPin = 13; // CI
int backlightPin = 14;
int boostPin = 17;
int shutdownPin = 8;
int backlightState = LOW;
unsigned long SB_CommandPacket;
int SB_CommandMode;
int SB_ColorCommand[3];
Metro shutdownTimer = Metro(30000);
Metro backlightTimer = Metro(5000);
// 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;
int animationColor1[3];
int animationColor2[3];
void setup()
{
int index;
pinMode(dataPin, OUTPUT);
pinMode(latchPin, OUTPUT);
pinMode(enablePin, OUTPUT);
pinMode(clockPin, OUTPUT);
pinMode(backlightPin, OUTPUT);
pinMode(boostPin, OUTPUT);
pinMode(shutdownPin, OUTPUT);
digitalWrite(latchPin, LOW);
digitalWrite(enablePin, LOW);
digitalWrite(backlightPin, backlightState);
digitalWrite(boostPin, LOW);
digitalWrite(shutdownPin, 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);
}
// 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 a given color
void SetColor(int r, int g, int b)
{
int lights;
// There are 7 ShiftBrites in the chain -- update them all
for (lights=7; lights>=0; --lights)
{
// Send the pixel to the device
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()
{
// Hue ramping
int colorIndex = frameIndex / 30;
SetLerpColor(colors[colorIndex], colors[colorIndex+1], frameIndex % 30, 30);
if (frameIndex == 269) {
SetColor(0,0,0);
return true;
}
return false;
}
void loop()
{
if (shutdownTimer.check() == 1) {
digitalWrite(shutdownPin,HIGH);
}
if (backlightTimer.check() == 1) {
backlightState = !backlightState;
digitalWrite(backlightPin,backlightState);
}
bool done = StepAnimation();
if (done)
{
frameIndex = 0;
}
else
{
++frameIndex;
}
delay(25);
}