Using millis () between for loops [solved]

Hi! I have this simple application with WS2812 Led Ring, which makes a 'wheel' effect, lights the LEDs one after the other. I'm trying to learn how to use millis () with loops, the delay () function causes a pause in the operation of an application, or so I understood.

void loop() {

unsigned long currentTime = millis();

for(s = 0; s < 73; s++){ // 6 colors x 12 LEDs

Serial.println(s); // just print the value

for(s = 0; s < 12; s++){

// here I would write

if ( currentTime - previousTime >= eventTime) { // eventTime = 1000

for(i = 0; i < NUMPIXELS; i++){

// pixels.Color takes RGB values, from 0,0,0 up to 255,255,255

pixels.setPixelColor(i, pixels.Color(bright,0,0)); // Moderately bright green color.

pixels.show(); // This sends the updated pixel color to the hardware.

//delay(delayval); // Delay for a period of time (in milliseconds).

}

if(i==NUMPIXELS){

pixels.clear();

break;}

previousTime = currentTime;}

}// 1st for

for(s = 11; s < 24; s++){

for(j = 0; j < NUMPIXELS; j++){

pixels.setPixelColor(j, pixels.Color(0,bright,0)); // Moderately bright green color.

pixels.show(); // This sends the updated pixel color to the hardware.

delay(delayval); // 1s delay

}

if(j==NUMPIXELS){

pixels.clear();

break;}

}// 2st for

//************** other for loops follow

if((s > 71) && (s < 73)){

pixels.setPixelColor(n, pixels.Color(bright,bright,bright)); // Moderately bright green color.

pixels.show(); // This sends the updated pixel color to the hardware.

delay(delayval);

} // if

}// BIG For

}// loop

You can see that I use several for loops, but I can't create the 1s delay between them with millis (). Where am I wrong ? Where should I put the millis () function?

simple states machine example

For more info on State Machine's use the search.

You shouldn't, generally, have nested for-loops using the same variable.

You wouldn't, generally, have a millis() interval timer inside a for-loop. For example:

This means, check 12 times to see if it is time to do something. After those 12 tries, give up.

What you want doesn't look like a for-loop:

  if ( currentTime - previousTime >= eventTime)
  {
    if (s < 12)
    {
      // Do something with 's'
     s++;
    }
 }

Then you set s to 0 when you want to start doing those things and you will know you have finished doing those things when s reaches 12.

I want to describe in my own words what I assume what your code should do:

There is a ring with 12 LEDs
the 12 LEDs shall do one "run" which means each of the 12 LEDs lights up goes off next LED lights up

This shall be repeated for six different colors

After each "run" you would like to pause for eventime with eventime = 1000 milliseconds = 1 second

Then some different patterns follow.

If you analyse my description this description has no programming-terms
This is my pure intention: first a description with zero programming-terms.
Anybody us very familiar with everyday's language. About programming-terms there might be misconceptions and misconceptions are bad base to communicate on.

I'm not sure if I understood what you want to do right. Please confirm or correct my description what functionality you want to achieve with your code. Avoid programming-terms in this description to make sure it is easy to understand and to keep potential misconceptions out.

If all you want to do is a fixed sequence of LED pattern and you want some pausing between LEDs light up and that's all using delay is sufficient. If all your Ardiuno shall do is pausing delay() can be used.

As soon as you want to do another thing in parallel to this LED-switching-sequence the programming has to change to non-blocking timing based on function millis().
For example you want your code to be very responsive to button-presses with minimal latency = fastest reaction as long as a delay() is executed the code could not respond to the button-press

as an everyday example with easy to follow numbers
delay() is blocking. As long as the delay is "delaying" nothing else of the code can be executed.
Now there is a technique of non-blocking timing.
The basic principle of non-blocking timing is fundamental different from using delay()

You have to understand the difference first and then look into the code.

otherwise you might try to "see" a "delay-analog-thing" in the millis()-code which it really isn't
Trying to see a "delay-analog-thing" in millis() makes it hard to understand millis()
Having understood the basic principle of non-blocking timing based on millis() makes it easy to understand.

imagine baking a frosted pizza
the cover says for preparation heat up oven to 200°C
then put pizza in.
Baking time 10 minutes

You are estimating heating up needs 3 minutes
You take a look onto your watch it is 13:02 (snapshot of time)
You start reading the newspaper and from time to time looking onto your watch
watch shows 13:02. 13:02 - 13:02 = 0 minutes passed by not yet time
watch shows 13:03. 13:03 - 13:02 = 1 minute passed by not yet time
watch shows 13:04. 13:04 - 13:02 = 2 minutes passed by not yet time

watch shows 13:05 when did I start 13:02? OK 13:05 - 13:02 = 3 minutes time to put pizza into the oven

New basetime 13:05 (the snapshot of time)
watch 13:06 not yet time
watch 13:07 not yet time
watch 13:08 not yet time (13:08 - 13:05 = 3 minutes is less than 10 minutes
watch 13:09 not yet time
watch 13:10 not yet time
watch 13:11 not yet time
watch 13:12 not yet time
watch 13:13 not yet time
watch 13:14 not yet time (13:14 - 13:05 = 9 minutes is less than 10 minutes
watch 13:15 when did I start 13:05 OK 13:15 - 13:05 = 10 minutes time to eat pizza (yum yum)

You did a repeated comparing how much time has passed by
This is what non-blocking timing does

In the code looking at "How much time has passed by" is done

currentTime - startTime >= bakingTime

bakingTime is 10 minutes

13:06 - 13:05 = 1 minute >= bakingTime is false
13:07 - 13:05 = 2 minutes >= bakingTime is false
...
13:14 - 13:05 = 9 minutes >= bakingTime is false
13:15 - 13:05 = 10 minutes >= bakingTime is TRUE time for timed action!!

So your loop() is doing

void loop()
  // doing all kinds of stuff like reading the newspaper
  
  if (currentTime - previousTime >= period) {
    previousTime = currentTime; // first thing to do is updating the snapshot of time 
    // time for timed action
  }

it has to be coded exactly this way because in this way it manages the rollover from Max back to zero of the function millis() automatically

Edit to make everything without for-loops the programming-technique of state-machines can be used
I have posted a demo-code that shall show how this works
state-machine demo
best regards Stefan

1 Like

First, I have to say that I'm not a programmer, I don't know logic / algorithm in programming too much, I use Arduino as a hooby, besides building circuits, but, I don't want to be sorry for your explanation, I read your explanation carefully. I have many components in the drawer, and I want to use them in a way.
There are six colors, each cycle consists of lighting the LEDs one after the other at an interval of 1s.
Second, you're right, I decided that this program should not run on its own, I will add in parallel an RTC DS3231 + display, so I did not want to use delay (). I think I will add another sensor, an LM35.
I will also try as @johnwasser proposed.

Thanks for reply. I will rebuild the code and came back with the solution.

So you light up the 12 LEDs at a rate of one per second. When all are lit you switch to the next color.
Here is an example of how that part might be done. Not complete and not tested.

const int NumColors = 6;
const CRGB colorList[NumColors] = {
  {200, 0, 0}, // Red
  {200, 200, 0}, // Yellow
  {0, 200, 0}, // Green
  {0, 200, 200}, // Cyan
  {0, 0, 200}, // Blue
  {200, 0, 200}, // Magenta
};

const int NumLights = 12;

void loop()
{
  unsigned long currentMillis = millis();
  static unsigned long lastLightTime = 0;
  static int colorIndex = 0;
  static int lightIndex = 0;

  if (currentMillis - lastLightTime >= 1000)
  {
    lastLightTime = currentMillis;

    if (lightIndex >= NumLights)
    {
      lightIndex = 0;
      colorIndex++;
      if (colorIndex >= NumColors)
        colorIndex = 0;
    }

    pixels.setPixelColor(lightIndex, colorList[colorIndex]);
    pixels.show(); // This sends the updated pixel color to the hardware.

    lightIndex++;
  }
}

This is proof of a well-thought-out program. You kind of solved in a few lines of code what I wrote in the dozens.
I tested the sketch. I have a first error:

const int colorList[NumColors][3] = {
  {32, 0, 0}, // Red
  {32, 32, 0}, // Yellow
  {0, 32, 0}, // Green
  {0, 32, 32}, // Cyan
  {0, 0, 32}, // Blue
  {32, 0, 32}, // Magenta
};

It must be a bidimensional array. Now the code has no errors, but, still not working well. I used Serial.print () to see the two indexes. Indexes increase, but the color of the LEDs remains the same, starting with blue and continuing the same color, At certain times it tends to form other colors.


I tried to clear the pixels:

    if (lightIndex >= NumLights)
    {
      pixels.clear(); // clear the pixels for the next color
      lightIndex = 0;
      colorIndex++;
      if (colorIndex >= NumColors){
        colorIndex = 0;
        }
    }

I think that after indexLight reaches 12, I have to reset it and I have to reset the LEDs as well, so that the next color appears. This does not happen.

I assume you are using WS2812-based LEDs. The basic principle of WS2812 LEDs is send them a color and this color lights up until you send a command clear or a different color.

The WS2812-chips themselves keep their values brightness of red, green, blue where each combination of these three brightness-values form a color

green brightness zero, blue brightness zero red brightness max = pure red
green brightness zero, blue brightness max red brightness max = violet
etc.

So if colors are changing in a way you don't want it your code is sending the wrong values.

You should post your complete sketch from the very first to the very last line.
beeing a beginner in programming means that very often the cause for a bug is at a very different place than you assume.

If others should really help you you have to post the complete sketch
You should post code by using code-tags

Just three steps:

  1. press Ctrl-T for autoformatting your code
  2. do a rightclick with the mouse and choose "copy for forum"
  3. paste clipboard into write-window of a posting

best regards Stefan

@cristian10001 I suggest that you start with a reduced version of the problem. Can you cycle a single LED through different colours using timed events based on millis()? Can you make simple patterns with just a few LEDs? Can you add a button that will reset your pattern anytime you press it? If you work in small increments, it will be easier to make the big thing in the end.

[code]
// NeoPixel Ring simple sketch (c) 2013 Shae Erisson
// released under the GPLv3 license to match the rest of the AdaFruit NeoPixel library

#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
#include <avr/power.h>
#endif

// On a Trinket or Gemma we suggest changing this to 1
#define PIN            6  // DI Pin

// How many NeoPixels are attached to the Arduino?
#define NUMPIXELS      12

// When we setup the NeoPixel library, we tell it how many pixels, and which pin to use to send signals.
// Note that for older NeoPixel strips you might need to change the third parameter--see the strandtest
// example for more information on possible values.
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

//int delayval = 1000; // delay for half a second
int bright = 32;
//int s, i, j, k, l, m, n;

const int NumLights = 12;
const int NumColors = 6;
const int colorList[NumColors][3] = {
  {32, 0, 0}, // Red
  {32, 32, 0}, // Yellow
  {0, 32, 0}, // Green
  {0, 32, 32}, // Cyan
  {0, 0, 32}, // Blue
  {32, 0, 32}, // Magenta
};

void setup() {
  Serial.begin(9600);
  // This is for Trinket 5V 16MHz, you can remove these three lines if you are not using a Trinket
#if defined (__AVR_ATtiny85__)
  if (F_CPU == 16000000) clock_prescale_set(clock_div_1);
#endif
  // End of trinket special code

  pixels.begin(); // This initializes the NeoPixel library.
}

void loop()
{
  unsigned long currentMillis = millis();
  static unsigned long lastLightTime = 0;
  static int colorIndex = 0;
  static int lightIndex = 0;

  if (currentMillis - lastLightTime >= 1000)
  {
    lastLightTime = currentMillis;
    //colorIndex++;
    if (lightIndex >= NumLights)
    {
      //pixels.clear();
      lightIndex = 0;
      colorIndex++;
      if (colorIndex >= NumColors) {
        colorIndex = 0;
        pixels.clear();
      }
    }

    pixels.setPixelColor(lightIndex, colorList[colorIndex]);
    pixels.show(); // This sends the updated pixel color to the hardware.
    //colorIndex++;
    //pixels.clear();
    lightIndex++;
    Serial.print("lightIndex= ");
    Serial.print(lightIndex);
    Serial.print("  ");
    Serial.print("colorIndex= ");
    Serial.println(colorIndex);
  }
}

[/code]

I posted the whole code, it's not complex, I gave up the for loops and I tried with if instructions as suggested by @johnwasser (I explained what situation I encountered). I'm still working.

Thanks for your reply. I ran the example codes from the library. Maybe you saw at first that I approached a program with for loops and delay () loops, the idea was to give up delay (), and a man above suggested writing with if statements and also in fewer lines than I had, but it doesn't work too well. That would be the details.

is a very vague description of what the problem is.

Describe in normal words

  • what behaviour do you see?
  • what behaviour would you like to have?
  • how does the result that you get with the above posted code deviate from the result that you want to have?

to the c++-experts:

const int colorList[NumColors][3] = {
  {32, 0, 0}, // Red
  {32, 32, 0}, // Yellow
  {0, 32, 0}, // Green
  {0, 32, 32}, // Cyan
  {0, 0, 32}, // Blue
  {32, 0, 32}, // Magenta
};

colorlist is defined as an two dimensional array
but does using this 2-dimesional array as a paremeter in this way

pixels.setPixelColor(lightIndex, colorList[colorIndex]);

really work?

in the file Adafruit_NeoPixel.h setPixelColor has three variants

  void              setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b);
  void              setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b,
                      uint8_t w);
  void              setPixelColor(uint16_t n, uint32_t c);

the third declaration has only two parameters is this using a HUE-color-value?
Or some kind of html-color where each 8 bit are red green, blue white?

best regards Stefan

No. The original sketch fragment used:
`pixels.setPixelColor(i, pixels.Color(bright,0,0));'
That was not enough information to tell me what type was being passed so I just guessed. Knowing that the function can take three bytes named r, g, and b makes me think the correct implementation would be:

const uint8_t colorList[NumColors][3] = {
  {32, 0, 0}, // Red
  {32, 32, 0}, // Yellow
  {0, 32, 0}, // Green
  {0, 32, 32}, // Cyan
  {0, 0, 32}, // Blue
  {32, 0, 32}, // Magenta
};

pixels.setPixelColor(lightIndex, colorList[colorIndex][0], colorList[colorIndex][1], colorList[colorIndex][2]);

Another possible approach would be to redefine colorList as an array of structures, with members {red, green, blue} and to write a wrapper function around setPixelColor that will unfold the structure and pass the arguments to setPixelColor itself. As it appears to be an overloaded function name, the 4-argument version should be used in this case.

Also, this

would make the LEDs quite dim (is that what the OP wants?): I should expect the range of values to go from 0 to 255.

This should also work but it uses 32 bits per color instead of 24:

const uint32_t colorList[NumColors] =
{
  Adafruit_NeoPixel::Color(32,  0,  0,  0), // Red
  Adafruit_NeoPixel::Color(32, 32,  0,  0), // Yellow
  Adafruit_NeoPixel::Color( 0, 32,  0,  0), // Green
  Adafruit_NeoPixel::Color( 0, 32, 32,  0), // Cyan
  Adafruit_NeoPixel::Color( 0,  0, 32,  0), // Blue
  Adafruit_NeoPixel::Color(32,  0, 32,  0), // Magenta
};

pixels.setPixelColor(lightIndex, colorList[colorIndex]);

Hello! I have the impression that we are a bit confused, I apologize; I described above how the application works (at # 8, at that time it only ran on one color), now I posted the whole code, functional (it also works with for loops, but as I said for the first time, the idea was not to use delay ()), the two methods suggested by @johnwasser seems to work the same. All I have to do is add a clear pixel after each color cycle.
I also uploaded a video. Sometimes there is a flicker of the LEDs, but I think it's because of the wires and the fact that I do not use an optimal power supply, but this will be solved.

[demo.zip|attachment](upload://pZgXDh7Cs5Vgc1edqTTboYP1FLk.zip) (2.3 MB)

// NeoPixel Ring simple sketch (c) 2013 Shae Erisson
// released under the GPLv3 license to match the rest of the AdaFruit NeoPixel library

#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
#include <avr/power.h>
#endif

// On a Trinket or Gemma we suggest changing this to 1
#define PIN            6  // DI Pin

// How many NeoPixels are attached to the Arduino?
#define NUMPIXELS      12

// When we setup the NeoPixel library, we tell it how many pixels, and which pin to use to send signals.
// Note that for older NeoPixel strips you might need to change the third parameter--see the strandtest
// example for more information on possible values.
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

int bright = 32;


const int NumLights = 12;
const int NumColors = 6;
/*const uint8_t colorList[NumColors][3] = {
  {32, 0, 0}, // Red
  {0, 32, 0}, // Green
  {0, 0, 32}, // Blue
  {32, 32, 0}, // Yellow
  {32, 0, 32}, // Magenta
  {0, 32, 32}, // Cyan
};*/
const uint32_t colorList[NumColors] =
{
  Adafruit_NeoPixel::Color(32,  0,  0,  0), // Red
  Adafruit_NeoPixel::Color( 0, 32,  0,  0), // Green 
  Adafruit_NeoPixel::Color( 0,  0, 32,  0), // Blue   
  Adafruit_NeoPixel::Color(32, 32,  0,  0), // Yellow
  Adafruit_NeoPixel::Color(32,  0, 32,  0), // Magenta
  Adafruit_NeoPixel::Color( 0, 32, 32,  0), // Cyan
};
void setup() {
  Serial.begin(9600);
  // This is for Trinket 5V 16MHz, you can remove these three lines if you are not using a Trinket
#if defined (__AVR_ATtiny85__)
  if (F_CPU == 16000000) clock_prescale_set(clock_div_1);
#endif
  // End of trinket special code

  pixels.begin(); // This initializes the NeoPixel library.
}
void loop()
{
  unsigned long currentMillis = millis();
  static unsigned long lastLightTime = 0;
  static int colorIndex = 0;
  static int lightIndex = 0;

  if (currentMillis - lastLightTime >= 1000)
  {
    lastLightTime = currentMillis;
    if (lightIndex >= NumLights)
    {
      pixels.clear(); // reset the pixels after each color
      lightIndex = 0;
      colorIndex++;
      if (colorIndex >= NumColors) {
        colorIndex = 0;
      }
    }
    //pixels.setPixelColor(lightIndex, colorList[colorIndex][0], colorList[colorIndex][1], colorList[colorIndex][2]);
    pixels.setPixelColor(lightIndex, colorList[colorIndex]);
    pixels.show(); // This sends the updated pixel color to the hardware.
    lightIndex++;
    Serial.print("lightIndex= ");
    Serial.print(lightIndex);
    Serial.print("  ");
    Serial.print("colorIndex= ");
    Serial.println(colorIndex);
  }
}

I think that was it, it works well. See #17 for other details.
Thanks.

Indeed, that function for pixels receives values from 0 to 255, but it is not necessary, they light quite brightly, that's why I chose 32.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.