Question about millis()

I'm starting to see the downside of using delay in loop as the requirements for automation become greater.

I have a Wemos D1 Mini to use with a DS18B20 temperature sensor, a buzzer and three LEDs.

I want the buzzer to signal at intervals where I decide how long it will beep and how long it should be quiet.

I already have a code in loop with if statement which activates a red link when the temperature is too high and this is where I want the buzzer to beep.

Can anyone help me get started on how to get this done without the rest of the code stopping?

Take a look at the BlinkWithoutDelay example in the IDE (File->examples->02.Digital->BlinkWithoutDelay) to see how to track time and elapsed time without blocking other code.

I have looked at that, but the problem is that i do not know how to inplement the code in the code i allready have.

I'm at work now, but i can try later to day.

This can be a little confusing but it's actually pretty easy once you understand.

When the arduino is powered up a counter starts from 0 millis and runs until power has been removed or the unsigned long variable is full and at that point it will start counting from 0 again.

As the name states it counts milliseconds so you want to use this so the microcontroller can keep running.

Here's a simple test sketch for you.

prevMillis is our counter variable, you can have many of these such as prevMillis2 etc... Use names that are easy for you to follow. This variable must be an unsigned long.

When you use millis() you are calling the actual milliseconds the Arduino has been on, if this is 10,000 that means your Arduino has been powered for 10 seconds.

Now look in the main loop, we have an if statement. It says if millis() is greater than prevMillis + 1000 run the code.

At the end of the if statement we have prevMillis = millis() so we can capture the milliseconds when this code ran. Now that if statement won't run until 1000 milliseconds has passed. If you want one minute you use + 60000.

You can setup as many of these if statements in the main loop as you want, just be sure the prevMillis name is unique to each if statement.

unsigned long prevMillis;  // sub counter

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
}

void loop() {
  // put your main code here, to run repeatedly:

  if (millis() >= (prevMillis + 1000)) 
  {
    Serial.println(F("Code Ran"));
    // code inside these brackets will run every second
    prevMillis = millis();  // get the current millis so we can reset this sub counter
  }
}

I think i'm understand, but how can i get the buzzer to turn on/off in this if statement without use of delay?

Read my reply again, I can confuse. :slight_smile:

That sample sketch will run that if statement over and over every second forever. Right now it's set to run every second, change 1000 to the length you want.

Put another if statement inside that statement. If LED = on then turn LED off, else if LED = off then turn LED on.

Now the LED will turn on/off every second.

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

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

...R

robsworld78:

if (millis() >= (prevMillis + 1000))

@robsworld78, that is how not to use millis().
Never add a value to something that has to do with millis. If you do, it will go wrong when millis() rolls over from 0xFFFFFFFF to 0x00000000.
The Blink Without Delay example shows how to do it: https://www.arduino.cc/en/Tutorial/BlinkWithoutDelay.

This will work, even right in the middle of a rollover:

if (currentMillis - previousMillis >= interval) {
    // save the last time you blinked the LED
    previousMillis = currentMillis;

Koepel:
@robsworld78, that is how NOT to use millis().
Never add a value to something that has to do with millis.

+1

Koepel:
@robsworld78, that is how not to use millis().
Never add a value to something that has to do with millis. If you do, it will go wrong when millis() rolls over from 0xFFFFFFFF to 0x00000000.
The Blink Without Delay example shows how to do it: https://www.arduino.cc/en/Tutorial/BlinkWithoutDelay.

This will work, even right in the middle of a rollover:

if (currentMillis - previousMillis >= interval) {

// save the last time you blinked the LED
   previousMillis = currentMillis;

Thank-you for posting this, I never knew that. Thankfully I typically use the good example you show but have used what I posted.
Is it ok to do prevMillis = millis() or should millis() go into currentMillis variable and that be used throughout the program?
I always use prevMillis = millis().

No, "previousMillis += interval;"

robsworld78:
I always use prevMillis = millis().

That is okay in 99.9% of the situations, but then you might get an other millis value than the millis in the if-stament. Every time the millis() is called it could have been advanced a millisecond (or two). In some special cases and with millisecond timings that might cause a problem.
So it is safer to set a 'currentMillis' to millis() at te beginning of the loop() and use that 'currentMillis' everywhere in the loop.

Suppose the millis() value is used to calculate all kind of things in the loop() and multiple decisions depend on it, then it is convenient when it has the same value throughout the loop().

TheMemberFormerlyKnownAsAWOL:
No, "previousMillis += interval;"

I prefer the Blink With Delay example that set the previousMillis to the currentMillis. That is safer.

The "previousMillis += interval;" keeps the timing exactly in sync with the crystal (or resonator) of the Arduino microcontroller.
When creating a clock with hours and minutes, then it is necessary.
When you have a Arduino board with a crystal instead of a resonator, a clock with millis is as accurate the crystal: millis_clock.ino.
If a exact sync with the time is not needed, then don't use it.
The disadvantage is that a sketch might get into trouble when there is sometimes a delay that is longer than the interval. When there are multiple software timers with millis() and perhaps a delay() inside those software millis timers, then it is hard to tell when it will go wrong. So it is safer to use it as in the Blink Without Delay example.
Another disadvantage is that it will catch up after a delay. When there is a long delay() in the sketch, and the software timer with millis() should have been executed many times during that delay, then it will catch up after the delay and will catch up very fast. That is needed for a clock but not for a led. A burst of something going on and off might even stress hardware.

Koepel:
The "previousMillis += interval;" keeps the timing exactly in sync with the crystal (or resonator) of the Arduino microcontroller.

A little bit of hyperbole there.
Misleading too.

If the crystal is inaccurate a certain percentage, so will a clock when using "previousMillis += interval;". No more, no less.
You don't explain what the problem is or how it is misleading. If my English can be improved, then let me know.
Some think that the millis() is not really accurate to the millisecond because of the divider, but there is a correction in the code to make it accurate.

The advantage of using the style

previousMillis += interval;

is that it automatically corrects for the situation where loop() might actually be a little late calling the function that has

if (millis() - previousMillis >= interval)

The downside is that if the interval is relatively short and the first call to check the time happens a long time after millis() starts there can be several almost-instant workings of the code until previousMillis eventually catches up with millis(). For example, suppose previousMillis starts at 0 and increments by 100 and actual millis() is at 510 then the sequence will have to go like this

0
100
200
300
400
500

all happening with only a few microsecs between them before any the 100msec timing actually takes effect.

(I know because I got bitten and it took me a long time to see what the problem was).

...R

Robin2:
The downside is that if the interval is relatively short and the first call to check the time happens a long time after millis() starts there can be several almost-instant workings of the code until previousMillis eventually catches up with millis().

That could actually be an advantage in a situation where you must guarantee that every instance of a time increment does execute something.

aarg:
That could actually be an advantage in a situation where you must guarantee that every instance of a time increment does execute something.

The point I was trying to make is that the initial "triggers" do not represent time increments - they all happen almost instantly.

...R

Robin2:
The advantage of using the style

previousMillis += interval;

is that it automatically corrects for the situation where loop() might actually be a little late calling the function that has

if (millis() - previousMillis >= interval)

The downside is that if the interval is relatively short and the first call to check the time happens a long time after millis() starts there can be several almost-instant workings of the code until previousMillis eventually catches up with millis(). For example, suppose previousMillis starts at 0 and increments by 100 and actual millis() is at 510 then the sequence will have to go like this

0

100
200
300
400
500



all happening with only a few microsecs between them before any the 100msec timing actually takes effect.

(I know because I got bitten and it took me a long time to see what the problem was).

...R

Then we would need:

while(millis() - previousMillis >= interval)
{
previousMillis += interval;
}

larryd:
Then we would need:

while(millis() - previousMillis >= interval)

{
 previousMillis += interval;
}

I don't think so. if the test is coded as an 'if' statement, it still gets executed multiple times nearly instantaneously, if it is executed inside loop(), where cooperative multitasking is maintained.

aarg:
I don't think so. if the test is coded as an 'if' statement, it still gets executed multiple times nearly instantaneously, if it is executed inside loop(), where cooperative multitasking is maintained.

Have you read this post?

https://forum.arduino.cc/index.php?topic=396591.0;wap