Arduino millis Timer

Hello everyone,

I’d like to share a simple and useful function for creating non-blocking delays in Arduino using millis(). This function allows you to perform tasks at specific intervals without blocking the rest of your code, unlike the delay() function.

Here’s the code:

// Variable to store the previous time in milliseconds
unsigned long previousMillis = 0;  

// Function to check if the specified interval has passed without blocking the program
bool checkTimer(unsigned long interval) {
    // Get the current time in milliseconds
    unsigned long currentMillis = millis(); 

    // If the difference between current time and previous time is greater than or equal to the interval
    if (currentMillis - previousMillis >= interval) {
        // Update the previous time to the current time
        previousMillis = currentMillis;  
        
        // Return true to indicate the interval has passed
        return true;  
    }
    
    // Return false if the interval hasn't passed yet
    return false;  
}

void setup() {
    // Initialize Serial communication at 9600 baud rate
    Serial.begin(9600);
}

void loop() {
    // Check if 1000 milliseconds (1 second) has passed
    if (checkTimer(1000)) {
        // Print message to Serial Monitor
        Serial.println("1000ms passed!");
    }

    // The rest of the code can run without being blocked by delay()
}

How it works:

  • The checkTimer() function checks if a specified interval (in milliseconds) has passed since the last time it was called.
  • If the interval has passed, it updates the timer and returns true.
  • If the interval hasn’t passed yet, it returns false.
  • This allows you to run other code in the loop() without being blocked by delay().

Example usage:

In the loop(), the function checks if 1000 milliseconds (1 second) have passed. If so, it prints a message to the Serial Monitor.

Feel free to use this code in your projects, and let me know if you have any questions or suggestions!

Thanks!

2 Likes

What happens if some hypothetical code doesn't call checkTimer until things have been running for longer than the timeout? Wouldn't your function return true immediately?

And if I needed a second timer? Perhaps a class would be better. You could add a method to initialize the timer to the current millis() value to start the timer running.

2 Likes

Thank you for your feedback! :blush:
You're absolutely right — the code would return true immediately if the function isn't called for longer than the timeout. I kept the example simple because I aimed to explain the non-blocking timer concept for beginners.

However, your suggestion of using a class is definitely a much better approach, especially for projects that need multiple timers or more flexible control.

I really appreciate your advice, and I'll update the code to make it more scalable by adding a timer class with reset functionality.

Thanks again! :rocket:

What happens when 3 or 4 functions need this service? HINT, you only have one previousMillis.

/*
Multiple Independent Timers Using millis()

This example shows how to create multiple **independent non-blocking timers**
using millis() without delay(). Each timer runs separately without affecting others.

How It Works:
The checkTimer() function checks if the specified interval has passed without blocking the program.
Each timer works independently by storing its own previousMillis value.
This method is perfect for running multiple tasks at different intervals simultaneously.
*/

// Variables to store the previous time for each timer
unsigned long previousMillisA = 0;
unsigned long previousMillisB = 0;
unsigned long previousMillisC = 0;

/*
Function to check if the specified interval has passed.

Parameters:
- previousMillis: Last stored time (passed by reference)
- interval: Interval time in milliseconds

Returns:
- true if the interval has passed
- false if not
*/
bool checkTimer(unsigned long &previousMillis, unsigned long interval) {
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis; // Update the timer
    return true;
  }
  return false;
}

void setup() {
  // Initialize Serial communication at 9600 baud rate
  Serial.begin(9600);
}

void loop() {
  /*
  Timer for Function A (Every 1000ms)
  */
  if (checkTimer(previousMillisA, 1000)) {
    Serial.println("Function A: 1000ms passed!");
  }

  /*
  Timer for Function B (Every 500ms)
  */
  if (checkTimer(previousMillisB, 500)) {
    Serial.println("Function B: 500ms passed!");
  }

  /*
  Timer for Function C (Every 2000ms)
  */
  if (checkTimer(previousMillisC, 2000)) {
    Serial.println("Function C: 2000ms passed!");
  }

  // Place any other non-blocking code here
}

/*

If you have any questions or suggestions, feel free to share! 

It might be better to use classes in this case to encapsulate the timer logic and make the code more organized.
This will allow you to manage timers more easily, especially as the number of timers increases.

Are you posting content generated by AI? If so, why?

No, I wrote the code myself, but I used the texts to explain artificial intelligence because I'm not good at explaining things, nor am I good at English.

How does your code differ from the standard BlinkWithoutDelay example included with the IDE?

So when people ask questions, you will use an AI to generate a response. You are basically acting as an intermediary for an AI.

If people want to use an AI to explain code, that's their choice, but why not let them ask the AI directly instead of going through you, who admits they are not good at explaining things? There are plenty of free services available.

1 Like

Hello taha_zarif90

I short words STRUCTs are your friends.

Thanks for your comment. I understand the concepts but I will use AI to double check and make sure my explanations are accurate and useful.

You should not be relying on the AI to be correct, in fact you should be checking that the AI is correct.

However, it seems there is no point reading your content when people can just go to the AI and get it themself.

Dear friend, I wrote the code myself and only used AI to help explain it. Reading my code can still be useful because, as one of our friends pointed out, there might be issues like needing to set a specific value in millis, and if it’s different, it won’t work. Another friend also suggested using classes, which is a great idea. So, while the explanations are AI assisted, the code itself and the insights from others can still be valuable!

So basically you want us to review your AI generated content?

No, as I said, I write my own code. I want to understand the flaws or ideas that can improve my code.

Well, to understand your code, should we read the AI description or not? It seems like you are saying you want us to review your code, but ignore the AI content.

Why not just post your own code for review and comment, and if people want it "explained" by AI they can feed it to their own AI?

Congratulations on learning about not blocking execution and how to use time based code to do so.

You have identified the tutorial example and made a function to do that.

There are many other ways to fold time based code into algorithms that it would be better if the programmer writing that code knows well how timing works and to be honest, won't learn well at all by using a crutch to "do it for them". That will not result in faster code.

Thanks for the feedback! I agree timing fundamentals are key. I made checkTimer as a learning tool but am also practicing manual timing to improve. Your advice is appreciated!

I write functions with state machines to run tasks in void loop(). I put the state machine inside of a timer that the state machine can set interval for, when time is up the interval is set to 0 and the state machine runs.
In the state machine cases, start time and interval may be set to delay the next time it runs.
The time variables are made static in the function and may be unsigned long or unsigned int to fit the task.

Understanding timers and Arduino millis() fundamentally changed how I debounce switches though for beginner examples I do it the old way.
How I do my best way depends on having void loop() run at > 1000 Hz (usually over 50 KHz) so right there it is up to me to be sure of that. Watching the switch (or switches) becomes a task function itself that produces a status byte per switch, the value of the byte determines if the switch is bouncing or steady up/down or just changed. Other tasks inside of loop() read the status, don't deal with the pin at all.
The debounce never does the unsigned time subtraction. It changes the status every time that millis changes, status is a history of the last 8 pin reads. It uses few cycles at all.
That is an example of folding time concepts into code, I have no idea how many more ways there will ever be.

Once your code does not block, you can write event-driven tasks on a single thread. Time elapsed is just one type of event. A simple AVR-duino can run literally 100's of tasks in parallel as long as on average they don't overload the machine.

Your method is very good. Can you send me a sample of your code?