Very simple project, timing issue.

I just bought an LCD to get back into making some projects for the Arduino, and have a problem getting some very simple code to work correctly:

All I'm trying to do is this - display a "progress bar" using ASCII ***** stars for each fraction of a minute, and then reset that to zero after each minute. I randomly decided on displaying 8 stars for a full minute, and so needed to add a * to the screen after every 8th of a minute. I'm finding though that the Arduino is skipping a step and not displaying a *.

Here is the sample code:

#include <LiquidCrystal.h>

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(7, 8, 9, 10, 11, 12);
int counter = 7;
long t = 0;


void setup() {
  Serial.begin(9600);
  // set up the LCD's number of columns and rows: 
  lcd.begin(16, 2);
  // Print a message to the LCD.
  lcd.print("Arduino is fun!");
}

void loop() {
  t = millis();
  // set the cursor to column 0, line 1
  // (note: line 1 is the second row, since counting begins with 0):
  lcd.setCursor(0, 1);
  // print the number of seconds since reset:
  lcd.print(t/1000);
  
  // print the number of minutes since reset;
  //lcd.setCursor(8,1);
  //lcd.print(millis()/1000/60);
  
  // print a * for each 8th of a minute to act as a progress bar between 
  // 0 and 1 min
  if (t % 7500 == 0) {
    counter++;
    Serial.print("counter: ");
    Serial.println(counter);
    Serial.print("ms: ");
    Serial.println(t);
    Serial.print("mod%");
    Serial.println(t % 7500);
    Serial.print("seconds: ");
    Serial.println(t / 1000);
    Serial.println("-----------");
      // reset the counter back to 7 after every 1min, 7.5 sec
      if (counter == 16) {
      counter = 8;
      lcd.setCursor(counter,1);
      lcd.print("         ");    
    }

    lcd.setCursor(counter,1);
    lcd.print("*");
    
  }

At 7500 15000 22500 30000 37500 45000 52500 60000 milliseconds, we should print a * to the next space
Here's what actually happens though,
Here is a sample of the debugging output:
counter: 8
ms: 7500
mod%0
seconds: 7

counter: 9
ms: 15000
mod%0
seconds: 15
You'll see that the Arduino skipped 22500 milliseconds right here, and so does not display a * in the progress bar for this portion
-----------
counter: 10
ms: 30000
mod%0
seconds: 30
-----------
counter: 11
ms: 37500
mod%0
seconds: 37

counter: 12
ms: 45000
mod%0
seconds: 45

counter: 13
ms: 52500
mod%0
seconds: 52

counter: 14
ms: 60000
mod%0
seconds: 60

I'm sure there is a simple way to do this, any suggestions?

As you have discovered, you may not be calling millis often enough to get every value that it returns, and it does not necessarily return every value. You can, however, determine the difference between the current time and the last time that you added a *. When that difference is greater than 7500, add another star, and reset the time the last star was added.

I don't know why, but I can't rely on "t % 7500 == 0".

I think about a safer way:

  • Use a "now_time=millis()"
  • Use a "next_asterisk=now_time+7500"
  • Use an "asterisk_count = 0"

bucle:

  • "now_time=millis()"
  • when now is above the time for the next asterisk, update count(++) and calculate the next asterisk time: next_asterisk+=7500
  • if count<=8, draw the asterisk
  • if count>8 clean display and put count to 0

Maybe that will work :slight_smile:

I think about a safer way:

  • Use a "now_time=millis()"
  • Use a "next_asterisk=now_time+7500"

Adding time to a value returned by millis() is not a good idea. Addition is not guaranteed, when the variable can not hold the new value. Subtraction, on the other hand IS guaranteed, even if one of the values has rolled over.

unsigned long then = millis();
// diddle around until millis() rolls over
unsigned long curr = millis();
insigned long delta = curr - then; // will correctly record the amount of time diddled

Thank you this worked.

I actually found that solution in the basic LED lighting code, to avoid using delay(); , but wasn't thinking about it right and thought it would have had the same result.

PaulS:

I think about a safer way:

  • Use a "now_time=millis()"
  • Use a "next_asterisk=now_time+7500"

Adding time to a value returned by millis() is not a good idea. Addition is not guaranteed, when the variable can not hold the new value. Subtraction, on the other hand IS guaranteed, even if one of the values has rolled over.

unsigned long then = millis();

// diddle around until millis() rolls over
unsigned long curr = millis();
insigned long delta = curr - then; // will correctly record the amount of time diddled

Oh yeah, with my proposal, after 596 hours running (only a bit more that 20 days), the millis() will overflow and will be and issue.
Obviously, you are right. Thanks for noticing :slight_smile: