Arduino: Chasing LEDs with millis()

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 "&gt;&gt;" 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.

Your questions, comments, and even corrections are encouraged and very much appreciated! However. I have zero-tolerance for inappropriate or harassing comments. I try to reply to everyone... -James

Leave a comment

One thought on “Arduino: Chasing LEDs with millis()

  1. 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 ?

    #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 &lt; NUM_LEDS1; dot++)
           for(int dot = 0; dot &lt; 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()
            }
        }