A popular LED project is the “Larson Scanner.” This scanner emulates the effect seen on KIT from Knight Rider and the Cylons in Battlestar Galactica. The code is usually written using “delay()” which means you can’t combine it with anything else. The following code could be put into a function, called periodically and allow your code to scan while doing other things.
One thing to note, a proper Larson Scanner has some persistence to the next and previous LEDs, this code does not. Maybe that would be a good exercise for the reader?
Inspired from this Arduino Forum post.
The Code
// Simple macros to remember which direction to shift LEDs
#define UP true
#define DOWN false
// Will be used to track how long since last event "fired"
unsigned long previousMillis=0;
// Delay to determine when we see the next LED
unsigned long interval = 250;
// Array with Arduino pins containing LEDs in sequence
byte LEDpins[] = {
2,3,4,5,6,7};
// Variable to track which LED to turn on, start at 000001
int LEDstate=0x01;
// State variable to know which direction to shift
boolean direction=UP;
void setup() {
// Optional: Push Button to turn on all LEDs (to test)
pinMode(12, INPUT_PULLUP);
// Set Pins with LEDs to OUTPUT
for (int x=0; x < 6; x++)
pinMode(LEDpins[x], OUTPUT);
}
void loop() {
// Set the pins of each LED during each iteration
// You'll only see something change when "LEDpins" gets updated
for (int x=0; x < 6; x++)
digitalWrite(LEDpins[x], bitRead(LEDstate,x));
// Test Funciton to turn on all LEDs with pushbutton
if (digitalRead(12) == LOW)
turnOnAll();
// Get current time and determine how long since last check
unsigned long currentMillis = millis();
if ((unsigned long)(currentMillis - previousMillis) >= interval) {
// We've waited "interval" amount of time, so let's do some stuff!
// "Reset" our clock
previousMillis = currentMillis;
if (direction==UP) {
// Use "<<" to "bit-shift" everything to the left once
LEDstate = LEDstate << 1;
// 0x20 is the "last" LED, another shift makes the value 0x40
if (LEDstate == 0x40) {
// turn on the one before "0x20" and reverse direction
LEDstate = 0x10;
direction = DOWN;
}
} else {
// use ">>" to "bit-shift" all bits in LEDstate once to the right
LEDstate = LEDstate >> 1;
// This means we ran out of bits!
if (LEDstate == 0x00) {
// set one ahead so no extra delay
LEDstate = 0x02;
direction = UP;
}
}
}
}
void turnOnAll() {
// Simple code to turn on all LEDs, this "blocks"
// so when code returns, the same LED in LEDstate will be "saved"
while (digitalRead(12)==LOW) {
for (int x=0; x< 6; x++)
digitalWrite(LEDpins[x], HIGH);
}
}
Notes
A couple of notes in this code.
Defines
The defines give us which direction the counter is running. I used the arbitrary names “UP” and “DOWN”, but could have used “LEFT” and “RIGHT.”
Array of Pins
To simplify addressing which pins are connected to LEDs (and in which order) an array is created with the order which the LEDs are connected. If this is the only thing you’re doing in your project, you’ll probably have the pins in order like the example. Using the array lets you use whatever pins are available.
LEDstate and Shifting
The “magic” in this code is the shifting done with LEDstate. The HEX value 0x01 is the initial state. With each iteration, the variable is shifted once to the left or right. So this means it’ll be:
0000 0001 0000 0010 0000 0100 0000 1000 0001 0000 0010 0000 0001 0000 0000 1000 0000 0010 0000 0001
When the sequence goes one bit too far, the sequence resets with one bit “seeded.”
turnAllOn
To test to make sure the LEDs are connected correctly, I included a function to turn on all the LEDs.
12 Comments
Dear Mr.James
Thank you for this clear tutorial. I find the way to do this for long. I felt that its should be use some kind of bit function that I’m not quite good enough. I modified a little of yours code to fit with my 8 led bar and Yes, it’s can also do another stuff that I want. This nice and smooth running led make me happy.
Best Regards
Piyagas
There’s a trap for young players that tinker with this once you hit 16 LEDs .
I figured out the up direction change point is 0x10000 and that makes the next LED sate 0x4000 for the reverse sweep. Uploaded and it sweeps one direction and that’s it????
Had me going for a bit till I worked out that 0x10000 exceeds the value for an int. Changing to an unsigned int we still have the problem by a value of + one (65536). The solution is to make the LED state a 32bit value like “int32_t LEDstate = 0x01;”…………….. KIT rides once again!
Hey James,
Thanks so much for this super helpful and clear guide! I’ve been directed to your site from google quite a lot recently and your website is a great resource for clear descriptions of arduino concepts.
I’ve been playing with this sketch and been having some difficulties…
Could you let me know how you might approach creating two ‘Larsen Scanner’ chases which have independent interval times? I’m a bit new to using arrays so I think I’m going wrong somewhere routing a second set of 8 pins in the for loop..
I’m ultimately trying to use this method to create a circular light installation where a 16-way relay has 2 independent chases of 8 wired lights, which chase in and out of phase by adjusting with a potentiometer.
If there is any way you could let me know how you might expand this for a second independent chase pattern that would be very much appreciated!
Thanks a lot!
Matt
Sorry that’s beyond the scope of one-on-one help I can provide. I would suggest posting the question in a forum, like arduino.cc’s “Project Help” so more people can weigh in.
How can I do this with 8 or 10 led’s I cant get it to work.
Thanks Ed
In all of the for() loops, you would need to change the references from 6 to the number of LEDs you have. Then you would need to match the value in line 52 to be the binary or hex for the number of LEDs + 1. (8 = 0x100 or 10 = 0x400, I think).
Thank You very much, For 8 led’s 0x100 worked when I also changed line 54 to 0x40 it was skipping led’s 7 and 6 on the way back with line 54 at 0x10. I’m going to try 10 led’s now.
Awesome! Glad to hear you got it work. Send some pictures if you get 10 going.
10 led’s are going good Had to make line 54 0x100 Don’t know how to send pics on here
There is a typo in your sample code.
in the if (direction == UP) branch, you have a <<1 for LEDstate
But you also have <>1 here otherwise the state never decreases.
Regards
Jean-Luc
Code has been corrected
how i can make this work with neopixel
my code down here is perfect result i want but i need replace delay() by millis()
any clue ?
[arduino]
#include
#define NUM_LEDS1 10
#define NUM_LEDS2 6
#define DATA_PIN1 6
#define DATA_PIN2 7
CRGB leds1[NUM_LEDS1];
CRGB leds2[NUM_LEDS2];
void setup() {
FastLED.addLeds(leds1, NUM_LEDS1);
FastLED.addLeds(leds2, NUM_LEDS2);
}
int dot_delay1[ ] = { 100,200,300,400,500,600,700,800,900,1000 };
int dot_delay2[ ] = { 100,200,300,400,500,600 };
void loop() {
for(int dot = 0; dot < NUM_LEDS1; dot++)
for(int dot = 0; dot < NUM_LEDS2; dot++)
{
leds1[dot] = CRGB::Blue;
leds2[dot] = CRGB::Blue;
FastLED.show();
leds1[dot] = CRGB::Black;
leds2[dot] = CRGB::Black;
delay( dot_delay1[ dot ] );
// this is where i need to put the second delay but i cant put more then 1 delay
// need to refork my code with millis() function instead delay()
}
}
[/arduino]