getting rid of delay(), fading LEDs

I'm having trouble figuring out how to multitask LEDs. I'm not even sure if this is possible...can I get so many smooth fades?

these are the functions i want to do...sequence3() would be pulsing constantly while the other ones are fading on, staying on, fading off:

sequence1() {
//fade to on after sequence2() fades off
//leave on, at 255 brightness for 10 seconds
//fade off
}
sequence2() {
//fade to on after sequence1() finishes
//leave on, at 255 brightness for 10 seconds
//fade off
}

sequence3() {
//fade up
//fade down
//loop this so this is happening all the time
}

void loop() {
sequence1();
sequence2();
sequence3();
}

this is what i have so far...it works starting out but then the pulsing lights stop turning on

boolean switchOn1, switchOn2;
unsigned long currentTime;
unsigned long loopTime;
unsigned long currentTime1;
unsigned long loopTime1;
int brightness = 0;    // how bright the LED is
int fadeAmount = 1;    // how many points to fade the LED by

int brightness1 = 0;    // how bright the LED is
int fadeAmount1 = 2;    // how many points to fade the LED by

void setup() {
  switchOn1=true;
  switchOn2=false;

  pinMode(11, OUTPUT);
  pinMode(10, OUTPUT);
}


void sequence1() {
  //fade to on after sequence2() fades off
  //leave on, at 255 brightness for 10 seconds
  //fade off

  //if the switch is true
  //fade on the led
  //leave on the led for 2 seconds
  //flip the switch to false
  //flip on the switch that turns on the next set
  //fade it out

  if (switchOn1 == true){
    currentTime=millis();
    if(currentTime >=loopTime+20){
      analogWrite(10, brightness);
      brightness = brightness + fadeAmount;

      loopTime = currentTime;
      if (brightness >=255){
        analogWrite(10, 255);
        if (currentTime >=loopTime+2000){
          analogWrite(10, 255);
        }
        else {
          analogWrite(10,0);
          switchOn1=false;
          switchOn2=true;
        }
        loopTime=currentTime;
      }
      loopTime=currentTime;
    }
  }
}
void sequence2() {
  //fade to on after sequence1() finishes
  //leave on, at 255 brightness for 10 seconds
  //fade off
    if (switchOn2 == true){
    currentTime=millis();
    if(currentTime >=loopTime+20){
      analogWrite(9, brightness);
      brightness = brightness + fadeAmount;

      loopTime = currentTime;
      if (brightness >=255){
        analogWrite(9, 255);
        if (currentTime >=loopTime+2000){
          analogWrite(9, 255);
        }
        else {
          analogWrite(9,0);
          switchOn1=true;
          switchOn2=false;
        }
        loopTime=currentTime;
      }
      loopTime=currentTime;
    }
  }
}

void sequence3() {
  //fade up
  //fade down
  //loop this so this is happening all the time

  currentTime1=millis();
  if(currentTime1 >=loopTime1+20){
    analogWrite(11, brightness1);
    brightness1 = brightness1 + fadeAmount1;
    if (brightness1 ==0 || brightness1 == 255) {
      fadeAmount1 = -fadeAmount1;
    }
    loopTime1 = currentTime1;

  }

}

void loop() {
  sequence1();
  sequence2();
  sequence3();
}

So far so good but...

Look at FSM (finite state machines) in the playground. One way to do it is by using 4 one in loop and one each in your sequences.

loop ends up looking somthing like this

byte loopState = 0;
void loop() {
  // put your main code here, to run repeatedly: 
  switch (loopState){
  case 0:
    if (sequence0()==finished) loopState++;
    break;
  case 1:
    if (sequence1()==finished) loopState++;
    break;
  case 2:
    if (sequence2()==finished) loopState=0;//Start over
    break;
  }
}

I'll leave it to you to do the rest.

Mark

It sounds like a fairly simple problem, and the non-blocking approach you're taking is the right way to go. You're also on the right lines implementing each feature in a separate function.

I suggest you define the variables used by each algorithm as static variables within the function. For example, here is a minimal blink function:

void blinkLed()
{
	static const int LED_PIN = 13;
	static const unsigned long BLINK_INTERVAL = 500; // duration of each blink phase
	static unsigned long lastBlinkTime = 0;
	
	if(millis() - lastBlinkTime >= BLINK_INTERVAL)
	{
		digitalWrite(LED_PIN, !digitalRead(LED_PIN));
		lastBlinkTime += BLINK_INTERVAL
	}
}

In your case instead of just blinking the LED on and off you will be fading it, but the same general approach will work. Instead of just inverting the LED state each time, you would need to have a variable recording the current analogWrite value and the direction of the current fade (i.er. getting brighter or dimmer). This would be very similar to the blink example - add the current direction to the brightness, if it becomes negative or greater than 255 then invert the direction, constrain the brightness to be 0 .. 255 and apply it to the LED. The fade speed and smoothness are determined by the interval between brightness changes, and the magnitude of the change.

For the fade in / hold / fade out / hold you will need a simple state machine with a state for each of those steps. Again, the sdtate variable could be a local static. A switch/case statement is a very convenient way to implement simple state machines like this.

The really nice thing about this non-blocking approach is that you can implement all these function independently and just call whichever ones you want to use - so you can start your sketch with a few copies of blink at different frequencies and then make some of them more elaborate.

By the way, note that the timing in that example is slightly different to your code - the difference is subtle, but if you use subtraction the code will handle timer rollover correctly whereas your original code doesn't.

I wrote this demo sketch to show how a few LEDs can be controlled at the same time. You may find it useful. Demonstration code for several things at the same time - Project Guidance - Arduino Forum

...R

thank you all for your help!

man...this is REALLY hard to wrap my head around. is it absolutely necessary to use an FSM? i'm having a hard time even getting an LED to blink using switch() and case.

what does this mean exactly? if (sequence0()==finished) ?

so i put the sequence0() function somewhere else and when that's done it breaks and goes to the next case... but what is the 'finished' variable? that wasn't defined anywhere. is it a boolean or something? that i switch from false to true in the functions?

also since this is a case by case code...i put the constantly pulsing pins in the void loop, followed by the FSM for the fade in, on, fade out, off ? then after i put the functions that actually do the things i want?

thanks for any more explanation. this seems so simple but it's so hard for me to understand!!

treebykooba:
...i put the constantly pulsing pins in the void loop, followed by the FSM for the fade in, on, fade out, off ? then after i put the functions that actually do the things i want?

In the demo sketch that I gave you the link for I have been very careful to put none of the detail coding in loop(). Putting the details in their own function makes the coding much easier to follow.

That demo sketch probably does half or more of what you want. You will just need to add stuff to make the LEDs fade.

...R

Hi Robin,

Thanks so much for the help. I'm trying to pair down your sketch to get it to work with fading LEDs.

I've got the loop going and stuff coming on and off using analogWrite. I'm having trouble implementing the fades....right now led_A_State is set so i thought it would fade...it's just very dim. led_B_State is just set to on and off (0 or 255) and is working fine. Anyway, here's what I have so far. Any help understanding what's going on is much appreciated! Thanks.

edit - also, weirdly, after a few minutes, the A and B LEDs just stop turning on and off. What is going on?!

thanks!

// --------CONSTANTS (won't change)

const int LoopLedPin =  11;      // the pin numbers for the LEDs
const int led_A_Pin = 10;
const int led_B_Pin = 9;


//const int onBoardLedInterval = 500; // number of millisecs between blinks

//#of secs between blinks
const int led_A_Interval = 8500;
const int led_B_Interval = 19000;

const int blinkDuration = 5000; // number of millisecs that Led's are on



//------------ VARIABLES (will change)

//byte onBoardLedState = LOW;             // used to record whether the LEDs are on or off
//byte led_A_State = LOW;           //   LOW = off
//byte led_B_State = LOW;

int led_A_State = 0;
int led_B_State=0;

unsigned long currentMillis = 0;    // stores the value of millis() in each iteration of loop()
unsigned long previousOnBoardLedMillis = 0;   // will store last time the LED was updated
unsigned long previousLed_A_Millis = 0;
unsigned long previousLed_B_Millis = 0;

int brightness1=0;
int fadeAmount1=2;
int loopTime;

int periode = 3000;
int displace = 500;
int value2, time;
//==============

void setup() {

  // set the Led pins as output:
  pinMode(LoopLedPin, OUTPUT);
  pinMode(led_A_Pin, OUTPUT);
  pinMode(led_B_Pin, OUTPUT);

}

//===========

void loop() {

  // Notice that none of the action happens in loop() apart from reading millis()
  //   it just calls the functions that have the action code

  currentMillis = millis();   // capture the latest value of millis()
  //   this is equivalent to noting the time from a clock
  //   use the same time for all LED flashes to keep them synchronized

  loopingLedState();
  updateLed_A_State();
  updateLed_B_State();
  switchLeds();
}

//============

void loopingLedState() {
   time = millis();
   value2 = 128+127*cos(2*PI/periode*(displace-time));
}

//===========

void updateLed_A_State() {

  if (led_A_State == 0) {
    if (currentMillis - previousLed_A_Millis >= led_A_Interval) { //fade on and keep on
     // led_A_State = 255;
     led_A_State = led_A_State+2;
     previousLed_A_Millis=currentMillis;
     
     if (led_A_State >= 255){
       led_A_State = 255;
     }
      previousLed_A_Millis = currentMillis;
    }
  }
  else {
    if (currentMillis - previousLed_A_Millis >= blinkDuration) { //fade off
      led_A_State = 0;
      previousLed_A_Millis = currentMillis;
    }
  }
}

//=========

void updateLed_B_State() {

  if (led_B_State == 0) {
    if (currentMillis - previousLed_B_Millis >= led_B_Interval) { // fade on and keep on
      led_B_State = 255;
      previousLed_B_Millis = currentMillis;
    }
  }
  else {
    if (currentMillis - previousLed_B_Millis >= blinkDuration) { //fade off
      led_B_State = 0;
      previousLed_B_Millis = currentMillis;
    }
  }
}

//============

void switchLeds() {
  // this is the code that actually switches the LEDs on and off

  analogWrite(LoopLedPin, value2);
  analogWrite(led_A_Pin, led_A_State);
  analogWrite(led_B_Pin, led_B_State);
}

//=============



//==========END

I hope this makes sense. I am just going through your code and making comments as they occur to me ...

Using variables called "led_A_State" to record the value of the LED brightness (as I think you are doing) will work but it confuses the concept of "state" which is usually just yes or no. I suggest calling the variable "led_A_brightness".

Why have you "time = millis()" when currentMillis already has a reasonably up to date value?

You may get strange results for value2 because you seem to be trying to do floating point maths on integers. Try to avoid floating point maths if you can. Perhaps use large integers - i.e. long or unsigned long.

Edit to add ...
Also, I don't think you can use the value of time in the way you are trying to get value2. millis() is a number that keeps growing while the Arduino is running. It eventually gets to about 4 billion (2^32) and then goes back to zero and starts counting again. 4 billion millisecs is a lot of days.
... end of edit

I can't follow what I think is your code to fade up (and down?) the LED. You are only increasing the brightness if the LED is at 0. Then, when it is no longer zero you are putting it straight back to zero. I suspect the problem might be more obvious if you called it ledBrightness as I suggested earlier. Also, work through that little piece of code with a pencil and paper and you will probably see what is needed.

...R

hello,

i was posting a similar question elsewhere but i wanted to start a new topic with a more precise question.

i want to fade an LED on, keep it on, fade it off, keep it off, and then go to the next one and do the same thing, 4 times...all without using delay() anywhere.

I am close. I can fade it on, and keep it off. for some reason i am not able to wrap my head around how to fade it off or keep it on. Just wondering if anyone has any guidance. here is the function:

void one() {
  unsigned long currentTime=millis();

  if (ONEswitch==true) {

    if (currentTime>=loopTime+20){
      brightness1 = brightness1 + fadeAmount;
      analogWrite(led1, brightness1);
      loopTime=currentTime;


      if (brightness1 >= 255) {  //once it peaks
        if (currentTime - previousMillis > interval){    //delay(3000); (i wish this worked :-(//
          previousMillis=currentTime;
        }

        for (int i=255;i>0;i=i-2){  //fade it back down 
          analogWrite(led1, i);
          delay(20); //how can i get rid of this?        
        }

        //switch to change to 2nd LED
        ONEswitch=false;
        TWOswitch=true;
        THREEswitch=false;
        FOURswitch=false;
      }
    }
  }
  else {
    brightness1=0;
    digitalWrite(led1, LOW);
  }

}

I say, you are at 10%.

Can you create an array with the led pins ?

const int pinLed[] = { led1, led2, led3, led4 };

And use that to select the led.

int ledNumber = 0;
...
analogWrite ( pinled[ledNumber], ...);
...
ledNumber++;
if (ledNumber == 4)
  ledNumber = 0;

You have to know what you are going to do, before typing code.
You have the time of millis(), how will you use that to fade in and out ?

It think you want to create a timer 'tick' of 20ms, but I'm not sure.
But if you do, that is a good way to do it.
Can you make a loop() function that creates a timer 'tick' of 20ms ?

delay(20); //how can i get rid of this?

You get rid of it by doing similar code as this:

    if (brightness1 >= 255) {  //once it peaks
        if (currentTime - previousMillis > interval){    //delay(3000); (i don't totally understand why this works but it does//
          previousMillis=currentTime;
        }

You check the 'time' the fade down started, when next fade interval is reached you change the value and do analog write again.
So in effect you break the for:loop and do it "manually":

if (brightness1 >= 255) {  //once it peaks
        if ((currentTime - previousMillis) >= interval20){    // interval of time has passed, go to next fade level
        previousMillis=previousMillis + interval20 ; // set time for next change, interval20 = 20

        i = i-2;
        analogWrite(led1, i);
       
        if (i == 1){ i = 255;}  // check & reset for next time
        } // end time check
     } // end brightness check

A lot of your code derives from stuff in your other thread getting rid of delay(), fading LEDs - #7 by treebykooba - Programming Questions - Arduino Forum

I think people might have a better overall picture if you had continued this discussion in that thread.

Perhaps you can ask the moderator to merge them?

...R

hi robin
oh sorry i thought maybe it'b be better to start over. how do i ask the moderator to merge?

i'm getting real close to giving up and just using 2 arduinos and delay(). this is making me feel so dumb!

are there any libraries or anything that can make this easier that anyone knows about? it'd be so nice to have a library that made a delay() not stop code and that made an led fade on, stay on or fade off with just one function. ugh. my brain is just not able to wrap around this stuff!
would the timer library be worth using here? i don't understand why everything in the examples with that happens in setup(), though.

thanks again for all the help!

merged.

I'll try to have another look at this in the morning.

...R

You need to use time involving millis() like this

currentTime = millis()

if (currentTime - (loopTime + 20) >= 0) {
}

Because this continues to give the correct answer when millis() rolls over from 2^32 to 0.

Also, if you have set currentTime = millis() in loop() I would use that value rather than re-reading it as it will give more consistent timing across all the events.

You need more global state variables to manage your code. It seems to me your brightness can be in three states - rising, full-bright, falling (lets call them r, b and f, and lets call the state variable bstate).

Also, I am using nextFadeTime instead of loopTime so I can use the same variable for all the timing steps.

Your routine would then look like this pseudo code

 if (ONEswitch==true) {

    if (currentTime - (nextFadeTime) >= 0){
       if (bstate == 'r') {
         brightness1 = brightness1 + fadeAmount;
         nextFadeTime = currentMillis + 20;
         if (brightness1 >= 255) {
            bstate = 'b';
            nextFadeTime = currentMillis + 3000;
         }
       }
       if (bstate == 'b') {
          bstate = 'f';
          nextFadeTime = currentMillis + 20;
       }
       if (bstate == 'f') {
       brightness1 = brightness1 - fadeAmount;
         nextFadeTime = currentMillis + 20;
         if (brightness1 <= 0) {
            bstate = 'r';
            ONEswitch=false;
            TWOswitch=true;
            THREEswitch=false;
            FOURswitch=false;
         }
      } 
      analogWrite(led1, brightness1); 
   }
}

...R