Trouble timing with millis()

Hi I have the following code. My trouble is with timing....the code keeps going into for loop and the embedded while loop endlessly: serial monitor shows that millis doesn't get updated after exiting the while loop; can't figure out why

#include <Math.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 16, 2);

  static uint32_t last_time, now = 0; // RTC

void setup() {

  Serial.begin(9600);
  pinMode(BPM, INPUT);
  pinMode(Tidal_Volume, INPUT);
  pinMode(IE, INPUT);
  // declare OUTPUT PINS FOR INHALE AND EXHALE PWMs:
  pinMode(motor_inhale, OUTPUT);
  pinMode(motor_dir, OUTPUT);
  // declare buttons as input
  pinMode(motor_home, INPUT);
  pinMode(opto_interrupt, INPUT);
  pinMode(pressure_sensor,INPUT);

  // LCD setup
  lcd.init();                      // initialize the lcd 
  lcd.init();
  // Print a message to the LCD.
  lcd.backlight();

now = millis();
}

void loop(){
    lcd.setCursor(0,0);
    lcd.print("BPM ");
    lcd.print(LCD_BPM);
    lcd.setCursor(7,0);
    lcd.print(" Vol ");
    lcd.print(LCD_Tidal_Volume);
    lcd.setCursor(6,1);
    lcd.print("IE ");
    lcd.print(LCD_IE);

for(int i = 0; i < 5; i++){
      
    Serial.println("Completed =\t");
    Serial.println(i);

    while ((now-last_time) < 300) //delay by millis_t_inhale
  { 
    now = millis();
    Serial.println(now);
  }    
 last_time = now;
    }
}

Did you mean delay (1500);?

Hi rpintln,

I have stripped down your code to the minimum for examining the use of the function milli()
because I don't have a I2C-LCD and the definitions for the constants like in

pinMode(BPM, INPUT);

in your code is missing in the code you posted above.

So this code-version is counting up on an arduino Una-Board

#include <Math.h>
#include <Wire.h>

  static uint32_t last_time, now = 0; // RTC

void setup() {

  Serial.begin(9600);

  now = millis();
}

void loop(){
  /*
    lcd.print("BPM ");
    lcd.print(LCD_BPM);
    lcd.print(" Vol ");
    lcd.print(LCD_Tidal_Volume);
    lcd.print("IE ");
    lcd.print(LCD_IE);
  */
    for(int i = 0; i < 5; i++){
         
        Serial.println("Completed =\t");
        Serial.println(i);
    
        while ((now-last_time) < 300) //delay by millis_t_inhale
        {
          now = millis();
          Serial.println(now);
        }   
        last_time = now;
    }
}

Maybe the reason why your code is not counting up is that you are using another board. Though I doubt that this is the reason.
Anyway what board are you using?

What does the serial monitor show if you upload my code-version?

Can you describe in normal words what your code should do?
The mostly used way how the command millis() is used is a bit different. But maybe for what you would like to do
your version is best suited. Though I don't know (yet) what you want to do in the end.

best regards Stefan

Hi @StefanL38,

Thanks for your attempt. I should have mentioned it in my previous post, the counter code left alone by itself works as intended...as you have correctly assumed by suggesting to run the stripped version. That's why I needed someone else to look at my code and maybe see the source of the problem. So you think that the LCD board is causing the for loop to fail and not reset? The LCD board I'm using is this one:

As far as the function of my code, it has 3 user inputs via potentiometers the values of which calculate certain time durations (let's call it X) within the code. What I am trying to do with the for loop and millis function is to have a non-blocking timer and perform certain actions while the timer value is less than X. I am doing all the math (involving the potentiometer values) within the loop function. (would that cause any problems?)

I ran your code and it still doesn't get out of the for loop.
I'm also attaching the original working code (which I am trying to integrate into mine)

static uint32_t last_time, now = 0; // RTC

void setup() {
  // put your setup code here, to run once:
now = millis();
}

void loop() {
  Serial.begin(9600);
// improved replacement of delay(1000) 
// Much better accuracy, no more dependant of loop execution time

for ( int i=0 ;i<5 ;i++)// make 5 time 300ms loop, for faster Button response
{
  Serial.println("Completed =\t");
  Serial.println(i);

  while ((now-last_time)<300) //delay200ms
  { 
    now=millis();
    Serial.println(now);
  }
 // inner 300ms loop
 last_time = now; // prepare for next loop 
}
}

I am attaching snap shots of the serial output:

  • for your stripped code
  • for the original code

when i tried running the code in the previous post it stopped after about a second.

i then moved the Serial.begin() to setup() and it ran continuously

static uint32_t last_time, now = 0; // RTC
void setup() {
    Serial.begin(9600);
    // put your setup code here, to run once:
    now = millis();
}

void loop() {
    // improved replacement of delay(1000)
    // Much better accuracy, no more dependant of loop execution time
    for ( int i=0 ;i<5 ;i++)// make 5 time 300ms loop, for faster Button response
    {
        Serial.println("Completed =\t");
        Serial.println(i);
        while ((now-last_time)<300) //delay200ms
        {
            now=millis();
            Serial.println(now);
        }

        // inner 300ms loop
        last_time = now; // prepare for next loop
    }

}

This is the output I get

Completed = 0
0
0
0
0
0
0
0
0
0
0
0
0
1
1
1
1
1
1
4
7
10
14
18
22
26
30
34
38
43
47
51
55
59
64
68
72
76
80
84
89
93
97
101
106
111
116
121
126
133
138
143
148
153
158
163
168
174
179
184
189
194
200
205
210
216
221
226
231
236
241
246
251
257
262
268
273
278
283
288
293
299
304
Completed = 1
324
330
335
340
346
351
356
361
366
371
376
381
387
392
398
403
408
413
418
423
429
434
439
444
449
454
459
465
471
476
481
486
491
496
501
506
512
517
522
527
533
538
543
548
553
559
564
569
574
579
584
589
594
601
606
Completed = 2
626
631
636
642
647
652
657
663
668
673
678
684
689
694
699
704
709
714
719
724
731
736
741
746
751
756
761
766
772
777
782
787
792
798
803
808
814
819
824
829
834
839
844
849
855
860
866
871
876
881
886
891
897
902
907
Completed = 3
927
933
939
944
949
954
959
964
969
974
979
985
990
996
1001
1007
1013
1019
1026
1032
1038
1044
1050
1056
1063
1070
1076
1082
1088
1094
1100
1106
1113
1119
1125
1132
1138
1144
1150
1157
1163
1169
1175
1181
1187
1193
1201
1207
Completed = 4
1228
1234
1241
1247
1253
1259
1266
1272
1278
1285
1291
1297
1303
1309
1315
1321
1329
1335
1341
1347
1353
1359
1366
1372
1378
1384
1390
1397
1403
1410
1416
1422
1428
1434
1440
1446
1453
1459
1466
1472
1478
1484
1490
1497
1503
1509

with all the other stuff commented out. You can see that the while() loop spins many times before millis() updates but since you are doing Serial.print() inside that loop, the serial buffer eventually fills up since you are pushing more into it than 9600 baud can spit out. Once full, that statement slows down the loop since println() must block until there is room to add more characters.

This kind of while-loop does have the same effect as a delay(). With the additional disadvantgae that you have to type more code.
The microcontroller stays inside the while-loop and is busy executing the commands inside the while-loop and is unable to execute other commands.

I post a demo-code that shows the principle
in some way it is opposite to what you tried.

You tried to do a delay at a certain point in the sequence of your code. to achieve a delayed execution.

In the demo-code below the big mainloop is executed at high speed and
**only if a certain amount of time has passed by **some of the commands are executed.

This enables fast running through the big loop in combination with "delayed" execution of parts of the code.

The code does printout a lot of dots to show that the main-loop is running fast.
It is still slowed down to make it visible for human eyes what is going on and serial output needs some time too.

every five "time-slices" the printed character is changed to a double-cross
every second an extra line with a number is printed

unsigned long currentMillis;

unsigned long Period = 1000;
unsigned long LastTime_Period_Elapsed;

unsigned long SlowDownPeriod = 20;
unsigned long LastTime_SlowDownPeriod_Elapsed;

unsigned long DoubleCrossPeriod = 5 * SlowDownPeriod;
unsigned long LastTime_DoubleCrossPeriod_Elapsed;

int NumberOfElapsedPeriods = 0;

const char dot = '.';
const char doubleCross = '#';
char CharToPrint;

void setup() {
  Serial.begin(115200);
  LastTime_Period_Elapsed = millis();
  LastTime_SlowDownPeriod_Elapsed = millis();
  LastTime_DoubleCrossPeriod_Elapsed = millis();
}

void loop() {
  currentMillis = millis();

  visualiseLooping();
  
  if (currentMillis - LastTime_Period_Elapsed >= Period) {
    LastTime_Period_Elapsed = currentMillis;
    NumberOfElapsedPeriods ++;
    Serial.println();
    Serial.print(NumberOfElapsedPeriods);
    Serial.println(" period is over");
  }
}


void visualiseLooping() {
  if (currentMillis - LastTime_DoubleCrossPeriod_Elapsed >= DoubleCrossPeriod) {
    LastTime_DoubleCrossPeriod_Elapsed = currentMillis;
    CharToPrint = doubleCross;
  }
  else {
    CharToPrint = dot;
  }

  if (currentMillis - LastTime_SlowDownPeriod_Elapsed >= SlowDownPeriod) {
    LastTime_SlowDownPeriod_Elapsed = currentMillis;
    Serial.print(CharToPrint);
  }  
}

So try to change your code into this direction. If you have any questions just post them.
best regards Stefan