Using millis() in a for loop as delay

Hello all,

I'm working on a project that includes an LCD5110. So in the middle of the code progress, I want to make a loading effect by using three dots. I want them to be printed one by one periodically (let's say it's 350 ms). So the first dot comes, after350 ms the second one comes, after 350 ms the third one comes and after 350 ms, all of the dots go.

I don't want to use delay(), cause while this loading effect playing on the screen, there will be another loading effect running. I know how both of these loops will work with delay(), but I just couldn't make it work with millis(). So I need your help to make this code work with millis() function. Thanks for your help in advance.

Here's my code:

#include <LCD5110_Graph.h>

LCD5110 lcd(8, 9, 10, 11, 12);

extern uint8_t SmallFont[];

void setup()

void loop()
  for (int x = 35; x <= 43; x = x + 4) {

    lcd.print(".", x, 16);

    if (x == 43)
      lcd.print ("   ", CENTER, 16);

You can’t use delay(), and indeed, you shouldn’t use a for() loop in this way - they both block your code from executing anything else.

You need to run your millis() timer and increment a ‘dot’ counter every (350ms)… then as the timer elapses, bump the counter, and print a dot.
After your desired number of dots, clear the output and zero the dot counter to begin another pass.

This should run by itself within the loop(), and not interfere with any other code executing.

As you understand it more, most of the functionality could be contained in a single function, and called repeatedly each time loop executes.

Here is a way you could do it. This has a call to start printing dots. One to print them (For you to fill out. And, a call to check if the printing is done to shut down the printing of dots.

#include <timeObj.h>

timeObj  dotTimer(350); // A timer for the 350 ms.
int      dotVal;        // What dot are we doing?

void setup(void) {

   Serial.begin(57600); // Serial for this example.
   dotVal = 0;          // We are not doing any dots now.
   startDots();         // Lest start them.

// Starts off a dot sequence.
void startDots(void) {

   dotVal = 1;          // Lets print dot #1
   printDot();          // Do the printing of it.
   dotTimer.start();    // Start the timer for the next dot.

// Prints a dot. this needs to be changed for the user program.
// For now it just prints them to the serial monitor.
void printDot(void) {


// Check to see if we sbould stop prointing dots.
void checkDots(void) {

   if (dotVal && dotTimer.ding()) { // If we are printing dots and the timer has expired.
       dotVal++;                    // Bump up the dot count.
       printDot();                  // Do the dot printout.
       if (dotVal>=3 ) {            // If we have run out of dots
         dotTimer.reset();          // Reset the dot timer.
         dotVal = 0;                // reset to dot zero, no dots now.
       } else {                     // Else we are NOT done doing dots..
         dotTimer.start();          // Start the timer for the next dot.

// Loop just calls the checkDot funtion.
void loop(void) {


You will need to grab LC_baseTools from the library manager of the IDE to compile this.

Good luck!

-jim lee

This is a great library- I am surprised more people aren't using it to set up and use millis() timers. It takes the messiness out of doing the millis computation in the code.

I totally agree. And I find myself using your reset() method all the time now.

The code reenactors seem to have taken a stand against it. What can I do?

-jim lee

Thank you guys for your replies!

Hi Jim,

I didn't understand the word reenactor. "actor" means miming persons on stage or film. But reenactor?
finally found reenact play it again. Still doesn't make to much sense to me.
I think I got an idea what it should mean.

Well this comment is an example of how you present LC_baseTools.

"insert this code-snippet and it works"
(but you have to install my library)

No explaining how it works. No explaining how to install a library. After having seen a lot of posts from you advertising the use of LCBasetools I took a look into the library. Two examples about blinking
The first example is a oneshot timer used with repeating to make it an endless blinker.

the second example explains almost nothing.
The comment may even guide newcomers in the worng direction "a single call of idle() is sufficient".

And this lack of documentation was the reason to decide against it.

best regards Stefan

Actually I'm glad you said that, because I was struggling to understand it, too. @jimLee: I really appreciate your LC_baseTools library, but I definitely don't understand the underlying concepts and principles, nor how to use it properly. For example, what does idle() do, when should I call it, and why should I call it? I've looked at your code, and it is too advanced for me to understand it.

I'm not asking you to explain it here, but perhaps some kind of user guide could be included, which addresses some of the points mentioned above?

Thanks for writing the library and making it available - it looks great.

PS: Oh, and what's this reset() thing that got mentioned?


Think of a curb that people keep tripping on to get into your shop.

You can..
A) ignore it.
B) Document it : "There's a curb here, step to the left." Kind of thing.
C) Remove the damn curb.

Micro controllers traditionally had a rough learning curve to use. There were a LOT of stumbling blocks in the way of a new user. The Arduino group chose to remove all of the stumbling blocks that they could. This is why the Arduino basically took over the micro controller landscape. Suddenly everyone could focus on the problem they wanted to solve, without dealing with all the nonsense of getting a micro controller running.

The Arduino people have done an amazing job removing the stumbling blocks so mr/mrs average can do something with a micro controller. But now new stumbling blocks have revealed themselves. For example delay() has become a trap for the unwary. Do we ignore it? Document the heck out of ways around it? Or just remove it?

I choose to remove it. And this is what LC_baseTools is all about. Removing the most common stumbling blocks that I see are left over. Also, why I didn’t go into how they work. Because its all about getting beyond them. Not to dwell on them.

I grant you, I’m a geek and therefor tend to be not the best at documentation. I should actually get someone else to do the documentation because I’m too close in.

The timeObj had two calls start() and stepTime(). start() started timing from now and stepTime() did the calculation from the last time it started. stepTime was for taking out accumulated error. Things like square waves. Anyhow, @SteveMann asked me to put in reset() to bring it back to a pre-timing state. It caused a bit of a re-thinking and rewriting of timeObj’s guts, but in the end I found that it was a very good addition. I wanted him to know I appreciated his idea and that I thought it was the right call.

Imagine you wrote a library that needs time during the main loop() to do periodic things. Like blinking an LED? You could make the user of your library call a function for this. OR.. You can add idle() to your loop, and everything from any library that needs time from loop() suddenly gets it. All in one go. For example, mechButton uses it to debounce stuff and calling callbacks. Blinker does it’s blinking during idle. I have a entire GUI library for doing handhelds that is completely run by.. idle().

But really, the first stepping stone is to get beyond millis and that’s why there is timeObj.

-jim lee

1 Like

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