led chaser without using delay

Hi,

I'm trying to do an LED chaser sketch without using delay. I have 3 leds and want to light them from 1 to 3 sequentially with a 1 second delay between each transition. When the user presses a switch, I want to reverse the sequence of the LED's from 3 to 1

I've tried this a number of ways, but the way I'm currently trying is giving me random results with the LED's. Sometimes led1 stays on for 3 seconds, then it goes from led1 to led3 then to led2. My sketch is below.

I thought that I needed seperate variables for previousMillis (one for each transition of the LED's) but I've tried that and had no luck.

Any help or advice appreciated.

#include 

/*
Lights 3 LED's in a chasing pattern from 1 to 3. Pressing the switch causes the
LED's to chase in the opposite direction.
*/

unsigned long previousMillis; // last the led was updated in milliseconds
unsigned long currentMillis; // Current time the led was updated in milliseconds
//unsigned long previousMillis2;
//unsigned long previousMillis3;

unsigned long changeInterval = 1000; // rate at which to change between led's in milliseconds
unsigned long lastChangedTime; // time of the last led transition


class Led {
  byte ledPin; //Pin of the led
  byte ledState; //current state of the led
  
  //constructor
  public:Led(byte ledpin){
    ledPin = ledpin;
    init(); //setup the pin for the led
  }

  void init(){
    // set up the input/output pins
    pinMode(ledPin, OUTPUT);
    off(); // start the led in a LOW state (off) by calling the off() method
  }

  void off(){
    //turn the led off
    digitalWrite(ledPin, LOW);
    ledState = LOW;
  }

  void on(){
    //turn the led on
    digitalWrite(ledPin, HIGH);
    ledState = HIGH;
  }

  byte GetState(){
    return(ledState);
  }

};



class Switch {
    byte switchPin; //Pin the switch is connected to
    byte switchState; // current state of the switch
    byte lastSwitchState; // last known state of the switch
    unsigned long debounceDelay; // Number of milliseconds to wait before considering a button press valid 
    unsigned long lastDebounceTime; // last time stored in the debounce timer in milliseconds
    unsigned long currentDebounceTime; //Current debounce time in milliseconds
    
    public:Switch(byte pin, unsigned long debounceDelay){
       switchPin = pin;
       lastSwitchState = LOW;
       init(); // call init() to setup switch pin 
    }

    void init(){
      pinMode(switchPin, INPUT);
      update(); // call update() method to get the state of the switch
    }

    void update(){
      // see if the button has been pressed and released and do the debouncing

       // read the state of the switch pin into a local variable
        byte reading = digitalRead(switchPin);

        //get current time for debouncing timer
        currentDebounceTime = millis();

        // check to see if the button was pressed (LOW to HIGH transition)
        // and we've waited long enough since the last press to avoid any noise
        if(reading != lastSwitchState){
          //reset the debouncing timer
          lastDebounceTime = millis();
        }

          if ( (currentDebounceTime - lastDebounceTime) > debounceDelay){  
              //whatever the reading is at, its been there longer than than the debounce delay
              //so this will be its actual state

              //if the button state has changed
              if(reading != switchState){                
                switchState = reading;
                // Check if the switch is currently HIGH (i.e The button has been pressed)
                  if(switchState == HIGH){
                    // switch has been pressed 
                    switchState = reading;
                  }
                
              }
                     
          }
              // save the switch reading. next time through the loop it will be the lastSwitchState  
              lastSwitchState = reading;

    }

    byte getState(){
      // get the state of the switch
      update();
      return switchState;
    }

    bool isPressed(){
      // check if the button has been pressed
      return(getState() == HIGH);
    }

}; 

// led objects
Led led1(5);
Led led2(4);
Led led3(15);

//switch objects
Switch switch1(16, 150);

void setup() {
  // put your setup code here, to run once:
}

void loop() {
  
    currentMillis = millis(); // get current time
  

    /*
    Do it without the delay. This is the jist of what I want
    led1.on();
    delay(1000);
    led1.off();
    led2.on();
    delay(1000);
    led2.off();
    led3.on();
    delay(1000);
    led3.off();
    */
  
    
    if((currentMillis - previousMillis > changeInterval)) {
      previousMillis = currentMillis; // Save last time we changed the LED

      if( led1.GetState() == LOW){
        led1.on();
        led2.off();
        led3.off();
      }
    }

    currentMillis = millis(); // get current time

    if((currentMillis - previousMillis) > changeInterval){     
      previousMillis = currentMillis;
        if(led1.GetState() == HIGH){
          led1.off();
          led2.on();
          led3.off();
        }
    }

    currentMillis = millis(); // get current time

    if((currentMillis - previousMillis) > changeInterval){     
      previousMillis = currentMillis;
        if(led2.GetState() == HIGH){
          led1.off();
          led2.off();
          led3.on();
        }
    }

    currentMillis = millis(); // get current time

    if((currentMillis - previousMillis) > changeInterval){     
      previousMillis = currentMillis;
        if(led3.GetState() == HIGH){
          led1.off();
          led2.off();
          led3.off();
        }
    }

      /*
      if(led2.GetState() == HIGH){
        led1.off();
        led2.off();
        led3.on();
      } */

    


    /*
    if(led1changed && currentMillisLed2 - previousMillisLed2 > changeInterval) {  
      previousMillisLed2 = currentMillisLed2; // Save last time we changed the LED
      led1.off(); // turn off led1  
      led2.on(); // turn on led2
      led3.off(); // turn off led3
      led2changed = true;
    }
    
    
    if(led2changed && currentMillisLed3 - previousMillisLed3 > changeInterval) {  
      previousMillisLed3 = currentMillisLed3; // Save last time we changed the LED
      led1.off(); // turn off led1  
      led2.off(); // turn on led2
      led3.on(); // turn off led3
      led2changed = true;
    }
  */ 


}

I think you're overcomplicating the task. How about a class-less attempting using millis() (compiles, not tested...):

const uint8_t   pinSw = 16;
const uint8_t   pinLED1 = 5;
const uint8_t   pinLED2 = 4;
const uint8_t   pinLED3 = 15;
const uint8_t grLEDs[] = { pinLED1, pinLED2, pinLED3 };

uint8_t lastSw;
bool bChaserMode = true;

void setup( void )
{
    pinMode( pinSw, INPUT_PULLUP );
    for( uint8_t i=0; i<3; i++ )
        pinMode( grLEDs[i], OUTPUT );
        
    lastSw = digitalRead( pinSw );
    
}//setup

void loop( void )
{
    LEDChaser();
    ReadSwitch();
    
}//loop

void LEDChaser( void )
{
    static uint8_t
        currLED=0,
        lastLED=0;        
    static uint32_t
        timeLED;
    uint32_t
        timeNow = millis();

    if( timeNow - timeLED >= 1000ul )
    {
        timeLED = timeNow;
        
        digitalWrite( grLEDs[lastLED], LOW );
        digitalWrite( grLEDs[currLED], HIGH );    
        lastLED = currLED;

        if( bChaserMode )
        {
            currLED++;
            if( currLED == 3 )
                currLED = 0;
        }//if
        else
        {
            if( currLED == 0 )
                currLED = 2;
            else
                currLED--;
            
        }//else

    }//if
        
}//LEDChaser

void ReadSwitch( void )
{
    uint8_t
        nowSw;
    static uint32_t
        timeSwitch;
    uint32_t
        timeNow = millis();

    if( timeNow - timeSwitch >= 50ul )
    {
        timeSwitch = timeNow;
        nowSw = digitalRead( pinSw );
        if( nowSw != lastSw )
        {
            lastSw = nowSw;
            if( nowSw == LOW )
            {
                bChaserMode ^= true;

            }//if
            
        }//if

    }//if
        
}//ReadSwitch

You need one timer variable only, but you also need a direction indicating variable.

Here is some code to illustrate (untested and not even tried to compile)

const uint8_t NUM_LEDS = 3;

const uint8_t LED_PIN[NUM_LEDS] = { 5, 4, 15 };
int8_t scanOffset = 1;   // direct offset for LEDs
uint8_t curLed = 0;       // current LED being worked with

const uint32_t LED_DELAY = 1000;   // milliseconds
uint32_t timeStart = 0;  // milliseconds

void setup()
{
  for (uint8_t i=0; i= LED_DELAY)
  {
    timeStart = millis();   // for next time

    // if we are at the ends of the LED array need to reverse the direction
    if ((curLed == 0 && scanOffset < 0) || (curLed == NUM_LEDS-1 && scanOffset > 0))
      scanOffset = -scanOffset;

    digitalWrite(LED_PIN[curLed], LOW);    // turn off current LED
    curLed += scanOffset;                // move to next LED
    digitalWrite(LED_PIN[curLed], HIGH);   // turn on new LED
  }
}

Hi plavelle,

ouh wow you are already using a class. This is pretty advanced. Adn in my humble opinion oversized for such a "small" thing like blinking three leds. If you see it as exercise to learn about classes. Well choose a functionality that already works.

Back to the main problem blinking without delay. The variable-name says what it is currentMillis holds the current time. You only update it once inside of function loop. Best practice on top of the loop.

You have four different situations: the binary numbers represent the LEDs 1: 000 2: 001 3: 010 4: 100

or 4: 100 3: 010 2: 001 1: 000

so every interval the LED states on/off change

counting up 1,2,3,4 or counting down 4,3,2,1

the counting direction shall change if you press the button counting up means do a

counter++  // increment variable counter by 1

if counter reaches five reset counter to 0

counting down means do a

counter--  // increment variable counter by 1

if counter reaches zero reset counter to 4

So depending of a boolean variable ChaseToTheRight beeing true or false count up or down

each time the if-condition

    if((currentMillis - previousMillis) > changeInterval){     
      previousMillis = currentMillis;

becomes true increment / decrement variable counter

depending on the number of counter turn LEDs on/off in the pattern that belongs to the number

This is a functional description which could be realised in a lot of different ways.

So it is up to you if you want to give this suggestions a try

best regards Stefan

Thank you very much everyone for your help and advice. I will try this tomorrow and let you know how I get on.

What?! We can use classes?!

Try this one then..

#include 
#include 

/*
Lights 3 LED's in a chasing pattern from 1 to 3. Pressing the switch causes the
LED's to chase in the opposite direction.
*/

unsigned long changeInterval = 1000; // rate at which to change between led's in milliseconds



// ********** ORIGINAL LED CLASS. **********
//       Now derived from linkListObj
//


class Led : public linkListObj {

  byte ledPin; //Pin of the led
  byte ledState; //current state of the led
  
  //constructor
  public:Led(byte ledpin){
    ledPin = ledpin;
    init(); //setup the pin for the led
  }

  void init(){
    // set up the input/output pins
    pinMode(ledPin, OUTPUT);
    off(); // start the led in a LOW state (off) by calling the off() method
  }

  void off(){
    //turn the led off
    digitalWrite(ledPin, LOW);
    ledState = LOW;
  }

  void on(){
    //turn the led on
    digitalWrite(ledPin, HIGH);
    ledState = HIGH;
  }

  byte GetState(){
    return(ledState);
  }

   // Added this for the serial monitor.
  void see() {
    Serial.print(ledState);
  }
  
};


// ********** MANAGED LIST OF LEDS.. ********** 

class LEDset : public linkList {
   public:
               LEDset(void);
   virtual     ~LEDset(void);

   void        addLED(Led* anLED);
   void        allOff(void);
   void        showEm(void);
   void        switchFWD(void); 
   void        switchREV(void);
};


LEDset::LEDset(void)
   : linkList() {  }

// Destructor, these LEDs are stack basied. So we can't delete them.   
LEDset::~LEDset(void) { while(!isEmpty()) unlinkTop(); }


// Stuff in an LED object.
void LEDset::addLED(Led* anLED) {

   if (anLED) {
      anLED->init();
      addToTop(anLED);
   } 
}


// Keeping it simple, we turn them all off at once.
void LEDset::allOff(void) {

   Led* trace;

   trace = getFirst();
   while(trace) {
      trace->off();
      trace = trace->getNext();
   }
}


// Swap everything over by one. In direction X we'll call it FWD.
void LEDset::switchFWD(void) {

    Led* trace;

   allOff();
   trace = getFirst();
   if (trace) {
      unlinkTop();
      addToEnd(trace);
      trace = getFirst();
      if (trace) {
         trace->on();
      }
   }
}


// Swap everything over by one. In direction -X we'll call it REV.
void LEDset::switchREV(void) {

   // You can figure this one out for yourself.
}



// ********** THE MAIN PROGRAM **********
//     Don't have the button code here.
//     That can be left as an exersize.


// led objects
Led led1(5);
Led led2(4);
Led led3(15);

LEDset   myLEDs;
timeObj  LEDTimer(changeInterval);
   
void setup() {
   
   Serial.begin(9600);
   myLEDs.addLED(&led1);
   myLEDs.addLED(&led2);
   myLEDs.addLED(&led3);
   led1.on();
}


void showLEDStates(void) {

   led1.see();
   led2.see();
   led3.see();
   Serial.println();
}


void loop() {

   if (LEDTimer.ding()) {
      myLEDs.switchFWD();
      showLEDStates();
      LEDTimer.stepTime();
   } 
}

You'll need LC_baseTools from the library manager to compile it.

-jim lee

Thank you everyone for all your help on this. Managed to get it working. Final sketch is below.

I’ll aim to modify it so the leds change between the 2 directions each time the switch is pressed, but for now I’m more than happy with it.

#include <Arduino.h>

/*
Lights 3 LED's in a chasing pattern from 1 to 3. Pressing the switch causes the
LED's to chase in the opposite direction.
*/

const byte switchPin = 16;
const byte ledPin1 = 5;
const byte ledPin2 = 4;
const byte ledPin3 = 15;
const byte leds[] = {ledPin1, ledPin2, ledPin3};
unsigned long currentMillis;
unsigned long previousMillis;
unsigned long changeInterval = 1000;
int lastLed = 0;
int currentLed = 0;
bool switchPressed = false;

unsigned long currentDebounceTime;
unsigned long lastDebounceTime;
unsigned long debounceDelay = 150;
int switchState;
int lastSwitchState;


void setup() {
    // set up led's.
    for(byte i = 0; i < 3; i++){
        pinMode(leds[i], OUTPUT);
        digitalWrite(leds[i], LOW);
    }

    // set up the switch
    pinMode(switchPin, INPUT);
  
    //Serial port for debugging
    Serial.begin(9600);
}

void LEDChaser(){
    currentMillis = millis(); // get the current time

    if((currentMillis - previousMillis ) > changeInterval){
        previousMillis = currentMillis;
        digitalWrite(leds[lastLed], LOW);
        digitalWrite(leds[currentLed], HIGH);
        lastLed = currentLed; // keep a track of the last led we changed state on
        
        if(switchPressed){
            currentLed--; //decrement current led counter
        } else {
            currentLed++; // increment current led counter
        }

        // if the switch is not pressed and currentLed == 3
        if((currentLed == 3) && !switchPressed){ 
            // reset the counter to 0 when we reach led[3]
            currentLed = 0;
        }

        // if the switch is pressed and currentLed < 0
        if((currentLed < 0) && switchPressed){ 
            // reset the counter to 0 when we reach led[0]
            currentLed = 2;
        }

        //debug
        //Serial.print("lastLed: ");
        //Serial.println(lastLed);

    }

}

void GetSwitchState(){
    // read the state of the switch pin into a local variable
        int reading = digitalRead(switchPin);

        //get current time for debouncing timer
        currentDebounceTime = millis();

        // check to see if the button was pressed (LOW to HIGH transition)
        // and we've waited long enough since the last press to avoid any noise
        if(reading != lastSwitchState){
          //reset the debouncing timer
          lastDebounceTime = millis();
        }

          if ( (currentDebounceTime - lastDebounceTime) > debounceDelay){  
              //whatever the reading is at, its been there longer than than the debounce delay
              //so this will be its actual state

              //lastDebounceTime = currentDebounceTime; //save the current debounce time

              //if the button state has changed
              if(reading != switchState){                
                switchState = reading;
                // Check if the switch is currently HIGH (i.e The button has been pressed)
                  if(switchState == HIGH){
                    switchPressed = true; 
                  }else{
                    //button not pressed and released
                    switchPressed = true;
                  }
                
              }
                     
          }
              // save the switch reading. next time through the loop it will be the lastSwitchState  
              lastSwitchState = reading;

}


void loop() {
    
    GetSwitchState();
    LEDChaser();

}

I want to reverse the sequence of the LED's from 3 to 1

If you want more variations you can have either 1 or 2 LEDs on at a time.

Or, there is something called a "Johnson Counter" which is a ring counter with the output inverted before it's fed back-in. This is fairly easy to do if you set-up a variable with each bit representing one LED (or one channel) and then bit-shift to get the chase effect.

In binary, a Johnson Counter with a left-shift looks like this: 000 001 011 111 110 100 000 (starting over)

That can also be reversed or you can make a ring counter in one direction and a Johnson counter in the other direction but you have to careful about getting a situation where you're "chasing" (with a regular ring/chase) with all of the LEDs on or all of them off.

I've made some sound activated lighting effects (at least 4 channels) with all of these things randomized when the effect starts.

@DVDdoug Thank you. That sounds like an interesting challenge to extend the sketch, I'll try that.