Asynchronous fading using MsTimer instead of delay

I am writing a sketch that is meant to fade several LEDS randomly. I ran into a problem using delay() since I wanted the fading to work asynchronous - I don’t want to wait for one LED to complete it’s fade in/out before the next starts.

I started playing around with MsTimer2 to replace the delay but I can’t seem to get it working correctly. My understanding is, if I use MsTimer within a loop and set it to, say 500ms, each iteration of the loop should take at least 500ms to complete. Instead it seems the loop is completing almost immediately and then the LED just comes on to full brightness instead of fading in.

Perhaps I’m not understanding the usage of MsTimer or I have some other bonehead mistake in my code. Here is a simplified version of what I’m trying to do:

#include <MsTimer2.h>

int ledPin[] = {8, 9, 10, 11};            // pwm pins - only using pin 8 for this example
int fadeInto = 1;

void setup() {  
  // nothing to set up 
}

void fadeIn() {
  analogWrite(ledPin[1], fadeInto);      // sets the value (range from 0 to 255)
}

void loop() {   

   while(fadeInto < 255){
      MsTimer2::set(500, fadeIn);        // 500ms period
      MsTimer2::start();
      fadeInto++;
   }
}

:question Can someone please enlighten me on what I’m doing wrong or not understanding :question

I’m not familiar with MsTimer2, however, I’m not sure why you even need it, or delay?

That is to say, couldn’t you simply count the amount of ms passed since you started fading, and choose the next level there? For example:

....

bool run_fade            = false;
unsigned long cur_tm = 0;
unsigned long pre_tm = 0;
unsigned long diff_tm = 0;

byte cur_led_lvl         = 1;

void loop()
{

  if( start_fade ==  true && cur_led_lvl < 255) 
  {
    
     if( cur_tm == 0 ) 
     {
        cur_tm = millis();
        pre_tm = cur_tm;
     }
     else 
     {
          pre_tm = cur_tm;
          cur_tm = millis();
     }

     diff_tm += cur_tm - pre_tm; 
     
     if( diff_tm >= 500 ) 
     {
        analogWrite(LEDPIN, cur_led_lvl);
        cur_led_lvl++;
          diff_tm = 0;
     }
 }
}

Certainly, that can be done more efficiently, but this way you can do it with no interrupts and no delays.

!c

Edit: it’s worth noting that I use a similar strategy in my current project for moving several motors “in conjunction”, wherein the controllers require a high pulse for a specified minimum ms to move one step. I set the pin high for a certain period of time, and keep checking to see if I need to turn it back low, and how many times I have to move this amount to fulfill the required number of steps. Each motor moves “in unison” as it seems to the eye, even though they may be a few ms off from each other.

Actually, I see the exact problem you’re having.

You tell the timer to trigger every 500 ms.

But, you’re upgrading the fadeInto value at a different rate.

try this, instead:

#include <MsTimer2.h>

int ledPin[] = {8, 9, 10, 11};            // pwm pins - only using pin 8 for this example
int fadeInto = 1;

void setup() {  
  // you only need to register the timer once
 
 MsTimer2::set(500, fadeIn);        // 500ms period
 MsTimer2::start();
}

void fadeIn() {
  static byte fadeInto;

  analogWrite(ledPin[1], fadeInto);      // sets the value (range from 0 to 255)

 if( fadeInto < 255 ) {
    fadeInto++;
  } 
   else {
      fadeInto = 0;
   }
}

void loop() {   

  // nothing to do here
}

You register the timer only once. You update fadeInto only after having made a change to the previous fadeInto set.

In your existing code, the while() loop executes in less than 500ms, so by the time the timer triggers, it’s already 255.

!c

I was originally using delay to randomize the speed of fade in and fade out.

Anyway, thanks for the advice. It works nicely. I am trying to build a firefly simulator so my intended purpose is to have 3-4 LEDs fading in and out simultaneously and randomly. I am new to C and am still confused how to implement this for this purpose. How do I use this method for multiple LEDs?

If you could just post some quick pseudocode, that would be wonderful.

Thanks again!

Well, there are a handful of ways to go about it. I figure if you’re using the MsTimer2, and your current handler, then you could simply state which pins you want to change…

byte fadeVals[4] = { 0, 0, 0, 0 };
bool doFade[4]    = { false, false, false, false };

....

void fadeIn() {
  
  for(byte i = 0; i <= 3; i++)
   {
      if( doFade[i] == true ) 
        {
           analogWrite(ledPin[i], fadeVals[i]);

           if( fadeVals[i] == 255 ) 
           {
              fadeVals[i] = 0;
            }
             else {
                 fadeVals[i]++;
             }
         } // end if( doFade...
    } // end for(...
}

That would allow you to control (say, in your loop()) which LEDs are active in the next trigger cycle. Now, you can control the rate at which each lights up and such by not allowing a change for that led on the next trigger of MsTimer2. Just set doFade[lednum] = false.

You’ll probably want to control shorter fade periods and longer ones, so in that case, set your timer for MsTimer2 much lower (say 100ms), and then to make other LEDs light up slower, check for time difference in ms in your main loop, and use that to control turning them on and off.

e.g.:

....

 // make each successive LED take twice as long to fade to high
 // need to make MsTimer2 trigger often, say every 25ms

unsigned long fadeTm[4] = { 50, 100, 200, 400 };

void loop() {

   // this should add up ms past since last call
   // need one for each LED (see below)

 unsigned long diff[4] = getTimeDiff();

 for( byte i = 0; i <= 3; i++ )
  {

    if( diff[i] >= fadeTm[i] ) 
     {
     
          // you need to set elapsed ms back to zero
        resetDiffTm(i);

        doFade[i] = true;
     }
  }
}

In the above example (provide own getTimeDiff and resetDiffTm() functions), LED2 would take twice as long as LED1 to light up, and LED4 take 8x as long as LED1. Can base code off of this to get it to randomly turn them off, etc.

A note: make sure you set doFade[LEDNUM] = false in your MsTimer2 callback using the above example. (It knows when it triggers, loop() doesn’t, so if loop() turns off the doFade value for an LED if the time isn’t right, then it might never light up. Given that less than 1ms will pass between loop executions, if you checked for values less than time diff in loop(), and turned the LED off, it’d be turned off before MsTimer2 could trigger.)

!c

Sorry for the delay in replying... Thanks a ton for the code!

I haven't had a chance to play with it yet but wanted to be sure you knew it was very much appreciated. I'm looking forward to giving it a try and will let you know how it goes.

Thanks again!!