For people who are new to writing Arduino sketches, or anyone new to programming in general, it is important to understand the difference between code that blocks vs code that does not.
Blocking is a term that is very simple. It means that the code in question seizes control of the CPU, thus blocking the rest of your code from being able to do anything at all until that block is released.
In most usage scenarios, there is never a good reason to seize control of a CPU in your code and when we're talking about code that executes on a full-blown computer that runs an operating system, it is actually almost impossible to block access to the CPU except within the confines of a specific program. Which is the scope of this blog.
Code that blocks access to a CPU could be as simple as a for...next loop. This code snippet would seize control of the CPU for the duration of the loop:
for (x=0; x < 5000; x++) {
print("Hello!");
}
That code would print out Hello! 5000 times and while it's doing that, no other code in your program would be able to run or do anything at all. Which is fine if that is what you need to do.
The Arduino Ecosphere
In Arduino or in any microprocessor application, there is always some kind of interaction with hardware that is connected to the microcontroller. This hardware can be as simple as a single LED or as complicated as a TFT touchscreen. And when you write code that interacts with the connected hardware, it is essential that your code be able to always interact with that hardware without exception.
Where this becomes even more critical, is when you utilize interrupt pins on the microcontroller so that when a user engages the connected hardware and an interrupt is triggered. The methods that you write in your program to handle those interrupts need to be free to respond to those interrupts and blocking code would prevent those interrupts from running the associated code.
Delay()
In a simple for...next loop, an Arduino is NOT blocked from being able to handle interrupts, but in the dreaded delay() method, it is!
The delay() method was created to give people an easy way to pause the execution of code. If you look at the classic blink sketch that almost all beginners are exposed to as the first example of Arduino code that they study, the delay() method is what is used to turn an LED on, then keep it on for a short period of time, then turn it off and keep it off for a short period of time.
The unfortunate side effect of introducing people to Arduino coding with the delay() method right off the bat is that people see that method and start using it to control the timing of events in their code and when your code starts to become more complicated, the result can be catastrophic because you will find that your code does not respond as you intended it to.
Non-blocking Timers
This is where it becomes necessary to utilize non-blocking timers throughout your code, so that your code can execute continuously without ever stopping, yielding programs that are responsive and perform as expected.
As I began to learn how to write code for Arduinos, I was using the delay() method as most beginners do, but the limitations of it also began to manifest which led me to understand how to implement non-blocking timers.
What non-blocking timers do, is they leverage the built-in methods millis() or micros() which simply return a number (of the long datatype) representing the amount of time that has passed since the Arduino was first powered up. millis() gives you the number of milliseconds that have passed since powerup and micros() gives you the number of microseconds that have passed since powerup.
For simplicity, I will only be referencing millis() but this discussion applies to both methods.
A non-blocking timer simply records the value given from millis() into a variable. Let's call that variable startTime. Then during code execution, the millis() method is run again and startTime is subtracted from that value, which yields the amount of time that has passed since startTime was first recorded and if that amount of time is equal to or greater than a desired period of time, we execute whatever code we wish to execute at that time interval.
Here is an example:
long startTime = millis();
long duration = 2500;
loop() {
long endTime = millis();
if ((endTime - startTime) >= duration) {
startTime = endTime;
//Code to execute every 2.5 seconds
}
}
This example executes code every 2.5 seconds without stopping any of the other code in a program from running and it also keeps interrupts free to be triggered as needed.
I should add, that you need to ALWAYS use greater than or equal to when comparing the time difference to your desired duration because using only equal to will almost always return false because it would be rare to impossible to have that check executed at the precise time interval in question. This becomes more critical when using micros().
Non-Blocking Libraries
There are libraries out there that make it easier to implement non-blocking timers in your code. I wrote one such library called
BlockNot which I believe not only makes it easier to write and use such timers, it makes your code much simpler and easier to read using simple and easy-to-understand terms.
This yields code that someone can read without ever having to process the technical aspects of what is happening in a non-blocking timer. And writing code that is easy to read and understand is in my opinion more essential than writing code that just works because inevitably, you will find yourself needing to look at code that you wrote in the past (or you might need to share your code with someone) and it can become very difficult to think your way through that code when it is written using ambiguous terms or verbiage that doesn't flow well in your mind.
Here is an example of the same non-blocking timer, only utilizing the
BlockNot library:
#import
BlockNot myTimer(2500);
loop() {
if (myTimer.TRIGGERED) {
//code to execute every 2.5 seconds
}
}
As you can see, using the library renders code that is easier to write and easier to understand as opposed to using the manual method of a non-blocking timer.
BlockNot has many different
example programs contained in the library that will show you different ways to leverage the power of non-blocking timers in your Arduino code.
I hope you found this post to be valuable in your microcontroller endeavors.