non-blocking timing. Execute code only from time to time.
There are a lot of different ways to learn programming. This thread wants to add another approach that is different to the yet existing ones.
UPDATE 06.01.2023
if you are mainly interested in applying non-blocking timing you can do a quick read of this
short tutorial / demonstration
If you are intersted in understanding the details how it works go on reading here.
I use an everyday analogon in this post to explain the basic principle
and in the posts
3 simple democode
4 three timers with different periods
etc there are an easy to use example-codes for non-blocking timing
for things that shall happen after a certain interval like
update 21.07.22
in post #7
there is a new visualisisation how non-blocking code works
- non-blocking blinking an LED = switching on/off the LED at a certain frequency
- non-blocking printing to the serial monitor once every x seconds
pizza baking example
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.
delay() makes the microcontroller "whirl" at maximum-speed occupying 100% calculation-power
The function delay() should have this name
freeze_microcontroller_completely_STOP_code_execution_until_freezingtime_is_over()
Now there is a technique of non-blocking (non-freezing) 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 (13:06 - 13:05 = 1 minute is less than 10 minutes
watch 13:07 not yet time (13:07 - 13:05 = 2 minutes is less than 10 minutes
watch 13:08 not yet time (13:08 - 13:05 = 3 minutes is less than 10 minutes
watch 13:09 not yet time (13:09 - 13:05 = 4 minutes is less than 10 minutes
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 (by the way: this if-condition handles the rollover of millis() 100% correctly)
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!!
to stay in the pizza-example as pseudo-code
the non-blocking timing looks like this
void loop() {
readingNewsPaper(); // doing
answerWhatsAppMsg(); // OTHER things
take_A_Sip_of_Coffee(); // in PARALLEL to
// checking if TimePeriodIsOver
if ( TimePeriodIsOver(myPizzaTimer,10Minutes) ) {
// IF timeperiod IS over then execute timed action
take_pizza_out_of_the_oven();
}
}
the user-defined function TimePeriodIsOver() results in true if: self-explaining name: the defined period of time is over
the user-defined function TimePeriodIsOver() results in false if: self-explaining name: the defined period of time is NOT yet over
You could extend this pseudo-code. For example you want to add a special kind of cheese to the pizza
But this cheese shall only melt slightly for 3 minutes.
The complete baking-time is 10 minutes. If the added cheese shall only melt for 3 minutes
this means add the cheese after 7 minutes of baking
So in the pseudo-code you would write
void loop() {
readingNewsPaper(); // doing
answerWhatsAppMsg(); // OTHER things
take_a_sip_of_coffee(); // in PARALLEL to
// checking if TimePeriodIsOver
if ( TimePeriodIsOver(myPizzaTimer,10Minutes) ) {
// IF timeperiod IS over then execute timed action
take_pizza_out_of_the_oven();
}
// checking if TimePeriodIsOver
if ( TimePeriodIsOver(myPizzaTimer,7Minutes) ) { // <=== see the difference in the period-of-time
// IF timeperiod IS over then execute timed action
add_cheese_to_pizza();
}
}
This explains the basic principle. There are a few more things to do for real coding.
But it is very important to understand the difference:
non-blocking timing does a very often and repeated checking of how much time has passed by.
And only if the right amount of time has passed by execute that part of the code that shall only
be executed from "time to time"
remove all delays()
To be able to check how much time has passed by very often
all function-calls delay(nnnn) must be removed.
If you still have delay(nnnn) in your code the delay is still blocking!
As long as there are still delays() in the code the fast repeated checking can't be done