How to fade an LED up and down without using delay

I struggled and struggled to try and figure out how to fade an LED up and down continuously without using the delay() function in between each increase/ decrease, so that other areas of the programme wouldn’t be halted and you could do other things.

My original idea was to have a white led gradually fading up and down slowly, while a row of blue LEDs lit, one by one, each time a button was pressed. Simultaneously, the speed of fading on the white LED would increase with each press of the button.

I couldn’t find anything anywhere that explained timed events without delay, but for fading LEDs. I messed about with some code given to me (I am very very grateful!) after a long search.

Here’s the code to fade the white LEDs:

if(millis() - lastFadeTime > fadeTime) {
if(fadeup) {
analogWrite(ledWhite, ledWhiteValue);
ledWhiteValue += switchPressCount;
if(ledWhiteValue >= 255) {
ledWhiteValue = 255;
fadeup = false;
}
} else {
analogWrite(ledWhite, ledWhiteValue);
ledWhiteValue -= switchPressCount;
if(ledWhiteValue <= 0) {
ledWhiteValue = 0;
fadeup = true;
}
}
lastFadeTime = millis();
}

ledWhiteValue is the value to fade the white led to, switchPressCount is the number of times the switch has been pressed (re-sets to 1 after it reaches the number of blue leds +1)fadeTime is the interval at which I want each fadeup step to happen (in this case 10ms)

Hope this is of use to someone! :wink:
Em :smiley:

Why dont you add the LED on a PWM pin and control it via PWM?

I did have it on a PWM pin, but I guess I don't know how to control it with PWM, as I just thought the analogRead using a value was using PWM?

Could you give me some ideas on using PWM properly?

thanks

The following example comes from the example->analog folder of the Arduino:

// Fading LED 
// by BARRAGAN <http://people.interaction-ivrea.it/h.barragan> 

int value = 0;                            // variable to keep the actual value 
int ledpin = 9;                           // light connected to digital pin 9
 
void setup() 
{ 
  // nothing for setup 
} 
 
void loop() 
{ 
  for(value = 0 ; value <= 255; value+=5) // fade in (from min to max) 
  { 
    analogWrite(ledpin, value);           // sets the value (range from 0 to 255) 
    delay(30);                            // waits for 30 milli seconds to see the dimming effect 
  } 
  for(value = 255; value >=0; value-=5)   // fade out (from max to min) 
  { 
    analogWrite(ledpin, value); 
    delay(30); 
  }  
}

I guess this one is quite self explainatory - and hopefully what you need :0)

This is what I was using before, but if you notice, my post is about doing this very thing without using delay. Using delay stops the whole program, so I couldn't do anything else (light the blue leds for example) so I was searching for code that didn't use delay, and couldn't find anything.

The code above works without delay().

Thanks anyway.

I know this isn't a simple "drop-in" suggestion, but maybe you can look through how this code works. I'm at the office currently and can't write a full custom solution for you here.

http://www.arduino.cc/playground/Main/ADXL330

Take a look at the use of the "Pummer" class there. It's geared to an RGB trio of LEDs, but the approach could be set up for any number of LEDs. It doesn't use delay(), and you just have to call the pummer's .loop() method whenever you happen to get a chance (such as once per sketch loop). In short, it looks at the time with millis(), looks at the old color and the new color, figures out what color it should be currently by using the standard map() function, and writes new colors to the PWM.

My abbreviations might not make sense, but nR = map(now, last, when, lR, wR); can be read as "the new Red value is calculated as a mapping of values ( the time now, between the time of the last goal set, and when the new goal should be reached, interpolated to the range from the last Red value known, to the wanted Red value*)*."

This is a simple example I use: Basicly the idea is: - Use millis() to get the "time" - Use a sin/cos function to determine the fade-level of the diode. Benefit of using sinus is, that you get a much more smooth and pretty fade. Linear fade is dull. :-)

int value, value2 ;
int ledpin = 10;                           // light connected to digital pin 10
int ledpin2 = 11;                           // light connected to digital pin 11
long time=0;

int periode = 2000;
int displace = 500;

void setup() 
{ 
  // nothing for setup 
} 

void loop() 
{ 
  time = millis();
  value = 128+127*cos(2*PI/periode*time);
  value2 = 128+127*cos(2*PI/periode*(displace-time));
  analogWrite(ledpin, value);           // sets the value (range from 0 to 255) 
  analogWrite(ledpin2, value2);           // sets the value (range from 0 to 255) 
}

Thanks Guys,

You are right - the sin/ cos fade is much prettier than linear!

thanks!

As far as the timing without using delay, use a hardware timer interrupt. There’s a nice library in the playground called FrequencyTimer2 that does most of the work for you.

I implement delays in my code like this:

int howLongToWait = 250;                 // Wait this many millis()
int lastTimeItHappened = 0;              // The clock time in millis()
int howLongItsBeen;                         // A calculated value

void setup () {
// nothing for this example
}

void loop () {
  howLongItsBeen = millis() - lastTimeItHappened;
  if ( howLongItsBeen >= howLongToWait ) {
    // do it (again)
  lastTimeItHappened = millis();
  }
}

It's a quick check and the code moves on to other things in the mean time. Just make sure you can get back to it in a reasonable amount of time.

1 Like

Being quite new to this, I don’t think I’ll have time to fully understand the FrequencyTimer2 library (I need to finish something in a week) so any suggestions would be welcome. The Arduino Playground entry doesn’t quite explain simply enough for me to get it (dumb newbies!)

I am using the sin/cos fade in my code now, which works nicely, only problem is, the pin this is fading seems to stick on HIGH, when I use a ‘while’ or ‘do…while’ function on my other pins (flashing them on and off). Is there something I’m missing?

I use a ‘while’ or ‘do…while’ function on my other pins (flashing them on and off)

If you are using delay() in your other functions then this will stop your LED fading. Not only that but anything that does not exit loop() will hog the processors time. That includes a while if the exit condition is not reached soon.

Also make sure that your fading and your other code is within the loop()

OK, so I had removed all the delays, and am using the millis() instead to implement things. One question I have, and again, I know this is something I really should be able to work out, but just can’t, is…

…most info I can find on using millis(), stopwatch, if(millis() - last time > wait Time) blah blah, to do something etc. etc. etc is all geared to calling a function, or whatever you’re doing, every time the counter (that is the difference in millis between now and when you started) gets longer than the time you want it to be (an interval). i.e. something repeats every 10ms, or whatever you set the interval at.

How can I call a function only whilst the ‘time now’ minus the time I started, is less than a certain interval (so it repeats while the elapsed time is less than my ‘interval’, but without using a do or while loop).

So, for example, I start a stopwatch and do something until the time reaches 150ms or whatever time. Using the above type of code, but having (millis() - previousTime < theInterval) causes the variable storing ‘previousTime’ (which needs to remain the same while the whole condition is tested) to get reset, as the whole void loop() is run.

I am…again…completely…lost…

Sorry for all the pointless sounding questions :-[

How can I call a function only whilst the 'time now' minus the time I started, is less than a certain interval (so it repeats while the elapsed time is less than my 'interval', but without using a do or while loop).

The simple answer is you can't. This sort of function is only available in high level languages and there they are constructed internally like you have to here.

Ahhhhh...... Thanks Mike. I was starting to wonder...

I'm trying to work out the conditions on paper.........and I'm slowly coming to the realisation that as the length of delay time I need will be quite long (probably something near 5 minutes in reality) that if the variable storing the start time is equal to millis at the beginning of the 'if' statement, by the time it gets to the end of the statement and the condition isn't met (which it won't as it's very long) it'll continue to loop and re-set the start time.

Is this something I can overcome by using the frequencyTimer2 as suggested? If it's the only way, I'll try and get stuck in as fast as I can!

Thanks again

Not to argue with someone more experienced than myself, but you can do that without a for or while loop. It won’t dedicate the CPU to the task though. It will just execute at every pass of the code until the time has elapsed. And the checks will run at every pass of the code regardless.

int howLongToExecute = 250; // Execute for this many millis()
int whenItStarted = 0; // The clock time in millis()

void setup () {
// nothing for this example
}

void loop () {
  if ( some sort of trigger criteria ) {
    whenItStarted = millis();
    //do it the first time
  }
  if ( millis() - whenItStarted <= howLongToExecute ) {
    // do it again
  }
}

this is looking good! I’d already bowed to the limitations and changed my code, but if it can be done, then that’ll be great! One question, the trigger that makes it happen, what kind of thing dos this need to be, so that it doesn’t trigger this again when the code loops and re-set the start time ti millis() again?

:slight_smile:

Does this look like something along the lines of what you meant? (I’ll try in a sec after my much needed run around the park)

int flashedLightsOnce = 0;  //how many times function to run has run

if (buttonPressedTimes > num_pins){  //if the button is pressed more than the number of LED counters
  timeStart = millis(); //start timer
  flashLightsFast();    //flash lights function first time
  flashedLightsOnce = 1;   //set counter for how many times function has run
 buttonPressedTimes = 0;  //reset button pressed to 0, so this condition won't run twice
}
if(flashedLightsOnce == 1 && millis() - timeStart <= penaltyTime){ //if time now minus timer start is less than penalty time
  
  flashLightsFast();  //keep running function
  flashedLightsOnce ++;  //and count how many times it's run (so it's more than one, and above function won't run again
}

does this sound about right?

The trigger criteria needs to be something controlled and intentional such as a button press, a defined interval, etc. Your trigger criterium is cupDown > num_pins - 1 which should be fine as long as you know what it means. ;)

Initialize timeStart = 0 outside of void loop(). Each time this event takes place, timeStart will be set to millis(), which is always going to be less than or equal the next time you make a comparison to it (unless the Arduino is powered up for more than 50 hours straight, then millis rolls over)

Since you're comparing to flashedLightsOnce, you will run flashLightsFast() a total of twice and no more because flashedLightsOnce will be 2 after the second run. There are easier ways to do that. :) If you want it to run flashLightsFast() for a specific amount of time, simply get rid of the flashedLightsOnce comparison and go with a time-only comparison. Just be careful not to use timeStart elsewhere in the sketch or you may run flashLightsFast() inadvertently in the second 'if' statement.

BTW: Also keep in mind that this method of running for an interval is subject to the speed at which your code loops overall. If you have a huge, intensive program, the time between iterations of the loop may be significantly long, resulting in fewer executions in a time period.

(A run around the park. LOL! It's after midnight here. I'm about to hit the hay!)

yep, I've realised all the things I need to correct after testing it (and the run!)

will read your suggestion carefully now and alter my code! :-[

Thanks!! :D