Using Millis() in nested for / switch / if statements

I'm looking to starting programming a series of LED arrays in parallel with a number of other things happening but I cannot work out how to write the LED controlling code without having it add delays.

Below is a good small piece of code as an example. In this code just fades an RGB LED strip in and out through the three colours, first cycling the Red LED brightness from 0 to 255 to 0, the green, then blue. Each step is held for 3ms.

I would like advice on how to remove that 3ms delay and use a non blocking method. My understanding of the 'for' function is limited.

The way I understand this code is that it runs like this:

  • loop starts (top level master loop)
  • runs for(int j = 0; j < 3; j++ ) (effectively a sub-loop that runs 3 times)
  • Runs for(int k = 0; k < 256; k++) (effectively a sub-sub-loop that runs 256 times)
  • switch(j) -this changes the parameters used within the k sub-sub-loop, each time the sub-loop (j) runs
  • showstrip() and delay() are both within the k sub-sub-loop.

So, where can I put an if statement using Millis() instead of the delay without it messing up the 'for' and 'switch' loops?

void RGBLoop(){

  for(int j = 0; j < 3; j++ ) { 

    // Fade IN

    for(int k = 0; k < 256; k++) { 

      switch(j) { 

        case 0: setAll(k,0,0); break;

        case 1: setAll(0,k,0); break;

        case 2: setAll(0,0,k); break;

      }

      showStrip();

      delay(3);

    }

    // Fade OUT

    for(int k = 255; k >= 0; k--) { 

      switch(j) { 

        case 0: setAll(k,0,0); break;

        case 1: setAll(0,k,0); break;

        case 2: setAll(0,0,k); break;

      }

      showStrip();

      delay(3);

    }

  }

}

Thanks

You can't use for loops if you want to prevent blocking so you'll need to restructure your code completely.

The loop() function will do ALL of the looping. You'll need to initialize the variables that are currently used as the for loop index variables, at the appropriate time. You'll need to increment the variables that are currently incremented at the end of the for loops, at the appropriate time.

@OP

After the execution of the showStrip() subroutine, your program is blocked for 3ms. You want to replace it with a non blocking method like the use of millis() function. The millis() function is implemented in the following style:

unsigned long presentMillis = millis();
while((millis() - presentMillis) < 3)   //EDIT
{
     ;  //wait here

}

My query is: During the 'waiting window' in the above structure, do you have any task for the program/MCU to do? If so, what is that task? If not, what benefit is there that you will gain by replacing the delay() function by the millis() function?

EDIT: In respect of Post#4.

GolamMostafa:
@OP

After the execution of the showStrip() subroutine, your program is blocked for 3ms. You want to replace it with a non blocking method like the use of millis() function. The millis() function is implemented in the following style:

unsigned long presentMillis = millis();

while(millis() - presentMillis) < 3)
{
    ;  //wait here

}

Congratulations on reinventing the blocking delay() command

UKHeliBob:
Congratulations on reinventing the blocking delay() command

...badly.

UKHeliBob:
Congratulations on reinventing the blocking delay() command

Why have you been so shy to point out that there is a shortage of a matching opening parenthesis (() or there is an extra closing parenthesis ())?

@AWOL brings this kind of typo mistake with the following style:
unsigned long presentMillis = millis();
while(millis() - presentMillis**)** < 3)
{
; //wait here

}

There -- it is clarified.

GolamMostafa:
@OP

My query is: During the 'waiting window' in the above structure, do you have any task for the program/MCU to do? If so, what is that task? If not, what benefit is there that you will gain by replacing the delay() function by the millis() function?

This subroutine will be part of a many others. I'm building a hybrid (Petrol electric) model locomotive with audio visual built into it. So there will be subroutines like audio playback & audio frequency sampling, which will kick off new LED patterns. Other subroutines for controlling the vehicle speed, throttle servo etc. All in all I certainly can't have 3ms pauses scattered throughout.

PaulS:
The loop() function will do ALL of the looping. You'll need to initialize the variables that are currently used as the for loop index variables, at the appropriate time. You'll need to increment the variables that are currently incremented at the end of the for loops, at the appropriate time.

I think I see what you mean -I put the RGBLoop() subroutine call inside a millis() if statement -so it only calls that subroutine once every 3ms. Then I need to rewrite the subroutine to increment J & k correctly each time that subroutine runs.

The demo Several Things at a Time illustrates the use of millis() to manage timing without blocking. It may help with understanding the technique.

It flashes three LEDs separately.

Note how each function runs very briefly and returns to loop() so the next one can be called. None of the functions tries to complete a task in one call. And there may be dozens of calls to a function before it is actually time for it to do anything.

Have a look at Using millis() for timing. A beginners guide if you need more explanation.

...R

No, I was implying that delay() is implemented using micros(), not millis(), but if you've found other problems, all's good.

Cracked it -not sure this is the most efficient -but it works

  • Brightness state subroutine switches between increasing and decreasing brightness
  • K is the PWM brightness value (increasing or decreasing step by step)
  • J is which LED is illuminated (R, G or B) and increments every brightness cycle (511 steps)
  • M is just a cumulative count of the k value to switch J when required.
const int ondelay = 50000; 
const int offdelay = 50000; 

unsigned long currentMicros = 0;    
unsigned long elapsedMicros1 = 0;    
unsigned long nextMicros1 = 0;    
unsigned long elapsedMicros2 = 0;    
unsigned long nextMicros2 = 0;   
byte Brightness = 0; 
int k = 1;
int j = 0;
int m = 0;

void setup() 
{
  Serial.begin(9600);
  Serial.println("Starting timertest");  // so we know what sketch is running
 }

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

void loop() 
{
  currentMicros = micros();
  if(Brightness == 0)
  {
    k_increment();
  }
  else if(Brightness == 1)
  {
    k_decrease();
  }
  BrightnessUpState();
  BrightnessDownState();
  RGBState();
  JState();
}

//========================================
void BrightnessUpState()
{
  if (k == 0)
  {
    Brightness = 0;
    Serial.println("Brightness reset to 0");
  }
}
//========================================
void BrightnessDownState()
{
  if (k == 255)
  {
    Brightness = 1;
    Serial.println("Brightness set to 1");
  }
}
//==========================================
void RGBState()
{
  if (m == 511)
  {
    j = j+1;
    Serial.print("J =");  
    Serial.println(j);  
    m = 0;
    Serial.println("m reset to 0");
  }
}
//=========================================
void JState()
{
  if (j == 3)
  {
    j=0;
    Serial.println("J reset to 0");
  } 
}

//========================================
void k_increment()
{
  //timer 1
  elapsedMicros1 = currentMicros - nextMicros1; //start of loop
  if (elapsedMicros1 >= ondelay) //defines when next check WILL happen
  {
/*      switch(j)
    {
        case 0: setAll(k,0,0); break; //k is PWM LED intensity
        case 1: setAll(0,k,0); break;
        case 2: setAll(0,0,k); break;
      }
  showStrip();
*/ 
  k = k +1;
  Serial.print(j);
  Serial.print("  ");
  Serial.println(k);
  m = m +1;
  nextMicros1 = nextMicros1 + ondelay;
  }
}

//==============================================
void k_decrease()
{
    //timer 2
    elapsedMicros1 = currentMicros - nextMicros1; //start of loop
  if (elapsedMicros1 >= offdelay) //defines when next check WILL happen
  {
/*    switch(j)
    {
        case 0: setAll(k,0,0); break; //k is PWM LED intensity
        case 1: setAll(0,k,0); break;
        case 2: setAll(0,0,k); break;
      }
  showStrip();
*/ k = k-1;
  Serial.println(k);
  m = m+1;
  nextMicros1 = nextMicros1 + offdelay;
  }
}

GolamMostafa:
There -- it is clarified.

The concern surely wasn't about your brackets being out of whack, but rather with your crazy notion of using millis() inside a while() to while() away the time and to (as your comment correctly puts it):

//wait here

neiklot:
The concern surely wasn't about your brackets being out of whack, but rather with your crazy notion of using millis() inside a while() to while() away the time and to (as your comment correctly puts it):

//wait here

This is something called 'waiting on the fly' during which it is possible to check if the set time has expired or not. If not spending 'the idle waiting time', do something else if there is something to do like 'refreshing a multiplexed display unit'.

This is something called 'waiting on the fly' while checking if the set time has expired or not; if not spending idle waiting, do something else if there is something to do like 'refreshing a multiplexed display unit'.

So why suggest using millis() in this half baked way instead of using delay() as the OP had ?

Remember what you said ?

After the execution of the showStrip() subroutine, your program is blocked for 3ms. You want to replace it with a non blocking method like the use of millis() function.

Then you proceeded to suggest using millis() in a way (if your code had been correct) that would have been blocking

Just admit that you were wrong and let's move on

UKHeliBob:
Just admit that you were wrong and let's move on

I was trying to compose a reply that expressed that sentiment, but ended up deleting all my attempts before posting, because I'm not as diplomatic as you :wink:

UKHeliBob:
Then you proceeded to suggest using millis() in a way (if your code had been correct) that would have been blocking

It's (the millis() function) blocking nature is not like the delay() function which offers the MCU no chance to do anything until the opted 'delay time' has been fully exhausted. The millis() function is flexible; it offers the MCU an opportunity to do some other job while waiting to see that the desired elapsed time is yet to get exhausted. This feature of the millis() function is widely used in refreshing multiplexed display unit while checking the 'data ready status bit' of a sensor/button.

Do you understand what is meant by -- 'waiting on the fly'?

unsigned long presentMillis = millis();
while(millis()-presentMillis < 3)
{
   //do if have something to do
}

GolamMostafa:
Do you understand what is meant by -- 'waiting on the fly'?

That's new... when I read your earlier post it said something like "flying waiting" which made absolutely no sense but Aha!, I see you went and did an edit some time later, after I read it. It's naughty to edit without strikethrough.

(Not that "waiting on the fly" makes any sense either.)

GolamMostafa:
Do you understand what is meant by -- 'waiting on the fly'?

I believe it is the same as delay()

...R

neiklot:
(Not that "waiting on the fly" makes any sense either.)

Do you know what is meant by 'reading on the fly'?

Robin2:
I believe it is the same as delay()

The bird is flying; how could there be a waiting state? This is the duality -- that's what has been implemented in the millis() function.