simple LED chaser without delay

Standard semi newbie disclaimer....

I'm trying to do something relatively simple, and getting stuck. My goal is to have a simple 1-2-3-1-2-3-1-2-3... chaser of 3 LEDs. I can do this using a delay function easily, but want to use a millis() type solution like Blink Without Delay. So, in short;

All LEDs off
Turn on LED 1,
check to see if fixed interval passed
if true, turn off LED 1, turn LED 2 on
Check timer
if true, turn off LED 1, 2, turn LED 3 on

My current code example only turns on the fisrt LED constantly:

/*This sketch borrows the array setup from the 5 random blink
without sketch


*/
const int numberOfBlades = 3;
int ledPin[] = { 6, 7, 9}; // LED pins to use.
int ledState[3];
long interval = 83;
long previousMillis = 0;        // will store last time LED was updated
int i = 0;

void setup() {
 for(int i = 0; i<numberOfBlades; i++){
   pinMode(ledPin[i],OUTPUT);
   ledState[i] = LOW;
   digitalWrite(ledPin[i], LOW); // all LEDs off
 }

}

void loop() {
  unsigned long currentMillis = millis();   
  if(currentMillis - previousMillis > interval) {             // Arduino run time - changeTime value greater then ledDelay variable? Yes then
    previousMillis = currentMillis; 
    changeLED();       // Go to changeLED routine.
    i = i++ % 2;       //i is either 0, 1 or 2
  }
}  
  
void changeLED(){
for(; ;){
  if (i % 0 == 0){
    ledState[0] = HIGH;
    ledState[1] = LOW;   
    ledState[2] = LOW;  
    digitalWrite(ledPin[i],ledState[i]);
  }
  else if (i % 1 == 0){
    ledState[0] = LOW;
    ledState[1] = HIGH;   
    ledState[2] = LOW;  
    digitalWrite(ledPin[i],ledState[i]);
  }
  else (i % 2 == 0);  {
    ledState[0] = LOW;
    ledState[1] = LOW;   
    ledState[2] = HIGH;  
    digitalWrite(ledPin[i],ledState[i]);
  }

} 
}

Any help appreciated!

for (;:wink: { } is an infinite loop. So the first time that the changeLed() function is called, it just executes the for loop forever, i never gets changed, etc.

Maybe just taking the for loop out is all that is needed.

Oh one more thing, should i = i++ % 2; instead be i = i++ % 3; ?

Check out my MultiBlink sketch in the code repository (linked below) for ideas on how this is done. You can also use the sketch directly if you want.

Removing the for loop helped somewhat, now all three LEDs turn on. The problem is they turn on very solely, and semi randomly blink.

That tells me that the lighting isn't working on the 0.083 seconds cycle.

Couple more things.

i = i++ % 3;

is probably not doing what you want. It's an odd construct, the right side modifies i by incrementing it then of course the assignment changes i too. I'd have to research what the expected behavior would be. It might be better to break it down into two statements. I might just increment i then test if it's 3 or greater, in which case set it back to 0. Simpler, easier to read and understand.

The if clauses in changeLed() are incorrect. The modulo operator is not needed. Just test for 0, 1, and 2. Simpler.
Each if only does one digitalWrite... at least two will always be needed, one to turn off the current LED, one to turn on the next.

The last else is missing an if. Either that, or the condition clause in parens isn't needed.

Keep at it!

You could simplify it a bit:

/*
This sketch borrows the array setup from the 5 random blink without sketch
*/
const int numberOfBlades = 3;
int ledPin[] = { 
  6, 7, 9}; // LED pins to use.
int ledState[3];
unsigned long currentMillis;
unsigned long interval = 83;
unsigned long previousMillis = 0;        // will store last time LED was updated
int i = 0;

void setup() {
  for(int i = 0; i<numberOfBlades; i++){
    pinMode(ledPin[i],OUTPUT);
    ledState[i] = LOW;
    digitalWrite(ledPin[i], LOW); // all LEDs off
  }

}

void loop() {
  currentMillis = millis();   
  if( (currentMillis - previousMillis) >= interval) {    // Arduino run time - changeTime value greater then ledDelay variable? Yes then
    previousMillis = previousMillis + interval; 

    i = i+1;       //i is either 0, 1 or 2
    if (i==3){
      i = 0;
    }
    switch(i){
    case 0:
      digitalWrite (ledState[0], HIGH);
      digitalWrite (ledState[1], LOW);   
      digitalWrite (ledState[2], LOW);  
      break;
    case 1:
      digitalWrite (ledState[0], LOW);
      digitalWrite (ledState[1], HIGH);   
      digitalWrite (ledState[2], LOW);  
      break;
    case 2:
      digitalWrite (ledState[0], LOW);
      digitalWrite (ledState[1], LOW);   
      digitalWrite (ledState[2], HIGH);  
      break;
    }
  }
}

One way is to use my state machine library found here: Arduino Playground - SMlib
Its very easy to use and give clean readable code. Here is an example of what u are requesting

#include <SM.h>

SM ledChaser(Display, Wait);

const int numberOfBlades = 3;
int ledPin[numberOfBlades] = { 6, 7, 9}; // LED pins to use.
int currentLed;
long interval = 83;

void setup() {
 for(int i = 0; i<numberOfBlades; i++){
   pinMode(ledPin[i],OUTPUT);
   digitalWrite(ledPin[i], LOW); // all LEDs off
 }//for(i)
}//setup()

void loop(){
  EXEC(ledChaser);
}//loop()

State Display(){
  digitalWrite(ledPin[currentLed++], 0);//turn last led off
  currentLed %= numberOfBlades;//limit count
  digitalWrite(ledPin[currentLed], 1);//turn new led on
}//Display()

State Wait(){
  if(ledChaser.Timeout(interval)) ledChaser.Restart();
}//Wait()

CrossRoads:
You could simplify it a bit:

/*

This sketch borrows the array setup from the 5 random blink without sketch
*/
const int numberOfBlades = 3;
int ledPin[] = {
  6, 7, 9}; // LED pins to use.
int ledState[3];
unsigned long currentMillis;
unsigned long interval = 83;
unsigned long previousMillis = 0;        // will store last time LED was updated
int i = 0;

void setup() {
  for(int i = 0; i<numberOfBlades; i++){
    pinMode(ledPin[i],OUTPUT);
    ledState[i] = LOW;
    digitalWrite(ledPin[i], LOW); // all LEDs off
  }

}

void loop() {
  currentMillis = millis();   
  if( (currentMillis - previousMillis) >= interval) {    // Arduino run time - changeTime value greater then ledDelay variable? Yes then
    previousMillis = previousMillis + interval;

i = i+1;       //i is either 0, 1 or 2
    if (i==3){
      i = 0;
    }
    switch(i){
    case 0:
      digitalWrite (ledState[0], HIGH);
      digitalWrite (ledState[1], LOW);   
      digitalWrite (ledState[2], LOW); 
      break;
    case 1:
      digitalWrite (ledState[0], LOW);
      digitalWrite (ledState[1], HIGH);   
      digitalWrite (ledState[2], LOW); 
      break;
    case 2:
      digitalWrite (ledState[0], LOW);
      digitalWrite (ledState[1], LOW);   
      digitalWrite (ledState[2], HIGH); 
      break;
    }
  }
}

Thanks, I'll have to peel this apart a bit more. Currently it shuts all LEDs off.

nilton61:
One way is to use my state machine library found here: Arduino Playground - HomePage
Its very easy to use and give clean readable code. Here is an example of what u are requesting

#include <SM.h>

This is very interesting. I wasn't able to figure out from that page how/where the state machine library is installed on my PC so it compiles with the rest of the code.

It should be installed in a folder named SM under the libraries folder in the place where your programs are normally stored. Stop and restart the IDE after you install the library. You can check that it is available from the IDE by doing Sketch/Import Library

Once you have advanced i (I would have named it ptr) you can just display all the leds based on it. You could display the leds every time through loop or just when i changes. To display all the leds just cycle through them with a for loop.

/*
This sketch borrows the array setup from the 5 random blink without sketch
 */
const int numberOfBlades = 3;
int ledPin[numberOfBlades] = { 
  6, 7, 9}; // LED pins to use.
unsigned long currentMillis;
unsigned long interval = 83;
unsigned long previousMillis = 0;        // will store last time LED was updated
int i = 0;

void setup() {
  for(int i = 0; i< numberOfBlades; i++)
  {
    pinMode(ledPin[i],OUTPUT);
    digitalWrite(ledPin[i], LOW); // all LEDs off
  }
}

void loop() {
  currentMillis = millis();   
  if( currentMillis - previousMillis >= interval) // Arduino run time - changeTime value greater then ledDelay variable? Yes then
  {  
    previousMillis = currentMillis;  // remember the time now 
    i = ++i % (numberOfBlades - 1);        // advance i handle rollover
    for (int n = 0; n < numberOfBlades; n++) // display with new i
    {
      if ( n == i ) digitalWrite(ledPin[n], HIGH);
      else digitalWrite(ledPin[n], LOW);
    }
  } 
}

Jimmy60, Thanks! That was the ultimate direction I was planning to go, but with the LED lighting sub not working I backed off the more complete full array setup.

I did test my counter routine, which was working:

void loop() {
  unsigned long currentMillis = millis();   
  if(currentMillis - previousMillis > interval) {             // Arduino run time - changeTime value greater then ledDelay variable? Yes then
    previousMillis = currentMillis; 
 
    i = i++;       
    i= i%3;            //i is either 0, 1 or 2
      Serial.println("on");
      Serial.print("i:  ");
      Serial.println(i);

This produced a series of 0, 1, 2, 0, 1, 2, 0,... which I read on the serial monitor.

When I ran you code as is, the third LED (pin 9) wasn't lighting. I modified the code from:

    i = ++i % (numberOfBlades - 1);        // advance i handle rollover

which as modulo 2 of numbers was producing 1, 0, 1, 1, 0, 1... To:

    i = ++i % (numberOfBlades);        // advance i handle rollover

so the pointers would go though 0, 1 and 2. That completely fixed it.

I also see how ++i versus i++ makes the i = i++ % 3 not work, while i = ++i % 3 works.

Modulo arithmetic is a little tricky but very useful:

++i %= numberOfBlades works..

modularity is your friend. I would do somthing like:

boolean delay_without_delaying(unsigned long time) {
// return false if we're still "delaying", true if time ms has passed.
// this should look a lot like "blink without delay"
  static unsigned long previousmillis = 0;
  unsigned long currentmillis = millis();
  if (currentmillis - previousmillis >= time) {
    previousmillis = currentmillis;
    return true;
  }
  return false;
}

void loop() {
  if (delay_without_delaying(1000)) {
    led_off(i++);
    if (i >= 3)
      i = 0;
    led_on(i);
  }
  // Other code.

Hmm. I like that idea. You could actually use some c++ and do delay_without_delaying(unsigned long &since, unsigned long time) as well, if you want different start times for different things. (oohh. I'm learning something from my c++ class!)

And just to add to the pile: here is an n-channel multi chaser example I wrote a while back. It was for someone who wanted to make a DaftPunk helmet where there is one chase pattern on the cheeks and a different chase pattern on the chin. I put in the capability of having the timing be different for each step of the patterns if desired.

const byte CheekLEDPins[] = {
  3, 4, 5, 6, 7, 8, 9, 8, 7, 6, 5, 4
};  // gives forward and back
const int CHEEK_COUNT = sizeof CheekLEDPins / sizeof CheekLEDPins[0];
const unsigned CheekIntervals[CHEEK_COUNT] = {  // Milliseconds
  300, 300, 300, 300, 300, 300,
  300, 300, 300, 300, 300, 300
};
unsigned long CheekTime = millis();
int CheekStep = 0;

const int ChinLEDPins[] = {
  10, 11, 12, 13
};
const int CHIN_COUNT = sizeof ChinLEDPins / sizeof ChinLEDPins[0];
const unsigned ChinIntervals[] = {  // Milliseconds
  300, 300, 300, 300
};
unsigned long ChinTime = millis();
int ChinStep = 0;

void setup() {
  int i;
  /* Cheek LED's */
  for (i = 0; i < CHEEK_COUNT; i++)
    pinMode(CheekLEDPins[i], OUTPUT);  // Yes, it's OK to set the pinMode twice on some pins

  /* Chin LED's */
  for (i = 0; i < CHIN_COUNT; i++)
    pinMode(ChinLEDPins[i], OUTPUT);
}

void loop() {
  unsigned long currentMillis = millis();
  
  // Do the cheek animation
  if ((currentMillis - CheekTime) >= CheekIntervals[CheekStep])
  {
    CheekTime += CheekIntervals[CheekStep];
    digitalWrite(CheekLEDPins[CheekStep], LOW); // Previous pin OFF
    CheekStep = (CheekStep + 1) % CHEEK_COUNT;
    digitalWrite(CheekLEDPins[CheekStep], HIGH);  // New pin ON
  }

  // Do the chin animation
  if ((currentMillis - ChinTime) >= ChinIntervals[ChinStep])
  {
    ChinTime += ChinIntervals[ChinStep];
    digitalWrite(ChinLEDPins[ChinStep], LOW); // Previous pin OFF
    ChinStep = (ChinStep + 1) % CHIN_COUNT;
    digitalWrite(ChinLEDPins[ChinStep], HIGH);  // New pin ON
  }
}