Arduino Hack – How to Make your Own Knight Rider Scanner!
My old friend Garrett Mace over at MaceTech.com (also @macegr on Twitter) has been tinkering and released his own ShiftBrite RGB LED module. It’s currently out of stock but he says he will have more of them soon. (You can also check out Garrett’s previous DIYs he did on Zedomax.com here)
You can make an easy Knight Rider Scanner with these ShiftBrites that Garrett personally made. The best part about his Knight Rider Scanner (that he made for one of his customers) is the fact that it can use all the colors that RGB can handle, meaning you can make a rainbow too if you wanted to.
Here’s a cool New Years Ball Garrett made with the ShiftBrites, way too cool. (Garrett, next time you tweet me when you make some cool stuff like this!)
After the soldering, I decided to attack a problem that a customer asked me about. He’s working on a Mustang and wants to use ShiftBrites to make a Knight Rider style scanner (2008 TV show version). I wrote some code, it allows any chain length divisible by two, any solid or mixed color, and custom leading and trailing gradients.

Here’ some video of it:
Click Here to View in Full Screen Mode
Click Here to View in Full Screen Mode
Here’s the video of working on his customer Mustang:
Click Here to View in Full Screen Mode
Here’s the Arduino code used for it (verified that it works):
#define clockpin 13 // DI
#define enablepin 10 // LI
#define latchpin 9 // EI
#define datapin 11 // CI
#define NumLEDs 16
#define CenterWidth 8
int LEDChannels[NumLEDs * 2][3] = {0};
int SB_CommandMode;
int SB_RedCommand;
int SB_GreenCommand;
int SB_BlueCommand;
// these are small so i'll leave them in RAM, larger data should be moved to PROGMEM
int KittLeadingEdge[] = {600, 400, 200};
int KittTrailingEdge[] = {200, 400, 600};
int KittLeadSize = sizeof(KittLeadingEdge) / sizeof(int);
int KittTrailSize = sizeof(KittTrailingEdge) / sizeof(int);
int KittTotalSize = KittLeadSize + KittTrailSize + CenterWidth;
void setup() {
pinMode(datapin, OUTPUT);
pinMode(latchpin, OUTPUT);
pinMode(enablepin, OUTPUT);
pinMode(clockpin, OUTPUT);
SPCR = (1<<SPE)|(1<<MSTR)|(0<<SPR1)|(0<<SPR0);
digitalWrite(latchpin, LOW);
digitalWrite(enablepin, LOW);
}
void SB_SendPacket() {
if (SB_CommandMode == B01) {
SB_RedCommand = 127;
SB_GreenCommand = 127;
SB_BlueCommand = 127;
}
SPDR = SB_CommandMode << 6 | SB_BlueCommand>>4;
while(!(SPSR & (1<<SPIF)));
SPDR = SB_BlueCommand<<4 | SB_RedCommand>>6;
while(!(SPSR & (1<<SPIF)));
SPDR = SB_RedCommand << 2 | SB_GreenCommand>>8;
while(!(SPSR & (1<<SPIF)));
SPDR = SB_GreenCommand;
while(!(SPSR & (1<<SPIF)));
}
void WriteLEDArray() {
SB_CommandMode = B00; // Write to PWM control registers
for (int h = 0;h<NumLEDs*2;h++) {
SB_RedCommand = LEDChannels[h][0];
SB_GreenCommand = LEDChannels[h][1];
SB_BlueCommand = LEDChannels[h][2];
SB_SendPacket();
}
delayMicroseconds(15);
digitalWrite(latchpin,HIGH); // latch data into registers
delayMicroseconds(15);
digitalWrite(latchpin,LOW);
SB_CommandMode = B01; // Write to current control registers
for (int z = 0; z < NumLEDs*2; z++) SB_SendPacket();
delayMicroseconds(15);
digitalWrite(latchpin,HIGH); // latch data into registers
delayMicroseconds(15);
digitalWrite(latchpin,LOW);
}
// Re-use my old array rotating code, but use collapsed fake array to save memory
// (at expense of CPU)
int KittFakeArray(int index) {
if (index >= 0 && index < KittTrailSize) {
return KittTrailingEdge[index];
} else if (index >= KittTrailSize && index < (CenterWidth + KittTrailSize)) {
return 1023;
} else if (index >= (CenterWidth + KittTrailSize) && index < KittTotalSize) {
return KittLeadingEdge[index-CenterWidth-KittTrailSize];
} else {
return 0;
}
}
void NewKittFill(int dir, int FillSpeed, int FadeSteps, int red, int green, int blue) {
int KittFadeFactor = 0;
int revIndex;
for (int FillCount = 0; FillCount < (KittTotalSize+NumLEDs); FillCount++) {
for (int FadeCount = 0; FadeCount < FadeSteps; FadeCount++) {
for (int i = 0; i < NumLEDs; i++) {
KittFadeFactor = ((KittFakeArray(KittTotalSize - FillCount + i)) * (FadeSteps - FadeCount - 1) + (KittFakeArray(KittTotalSize - FillCount - 1 + i)) * (FadeCount)) / (2 * FadeSteps);
if (dir == 0) {
LEDChannels[i][0] = (long) red * KittFadeFactor / 1023;
LEDChannels[i][1] = (long) green * KittFadeFactor / 1023;
LEDChannels[i][2] = (long) blue * KittFadeFactor / 1023;
revIndex = NumLEDs * 2 - i - 1;
LEDChannels[revIndex][0] = LEDChannels[i][0];
LEDChannels[revIndex][1] = LEDChannels[i][1];
LEDChannels[revIndex][2] = LEDChannels[i][2];
} else {
revIndex = NumLEDs - 1 - i;
LEDChannels[revIndex][0] = (long) red * KittFadeFactor / 1023;
LEDChannels[revIndex][1] = (long) green * KittFadeFactor / 1023;
LEDChannels[revIndex][2] = (long) blue * KittFadeFactor / 1023;
LEDChannels[NumLEDs + i][0] = LEDChannels[revIndex][0];
LEDChannels[NumLEDs + i][1] = LEDChannels[revIndex][1];
LEDChannels[NumLEDs + i][2] = LEDChannels[revIndex][2];
}
}
WriteLEDArray();
delay(FillSpeed);
}
}
}
void loop() {
for (int cycles = 1; cycles < 10; cycles++) {
NewKittFill(0, 12, 8, 1023, 0, 0);
delay(200);
NewKittFill(1, 10, 7, 1023, 0, 0);
delay(200);
}
NewKittFill(0, 10, 7, 0, 1023, 0);
delay(300);
NewKittFill(1, 10, 7, 0, 1023, 0);
delay(300);
NewKittFill(0, 10, 7, 0, 0, 1023);
delay(300);
NewKittFill(1, 10, 7, 0, 0, 1023);
delay(300);
NewKittFill(0, 10, 7, 800, 0, 1023);
delay(300);
NewKittFill(1, 10, 7, 800, 0, 1023);
delay(300);
NewKittFill(0, 10, 7, 200, 0, 0);
delay(300);
NewKittFill(1, 10, 7, 200, 0, 0);
delay(300);
}













