Delay in Arduino stopwatch compared to real stopwatch

Hello all,

I have run a simple sketch to see a stopwatch running on my LCD whenever I input ‘1’ and stop when I input ‘2’.

When compared to the interval on a real stopwatch, the one in Arduino made using ‘delay’ function stays behind much.

What are the reasons for this?

Thank you for your responses!

Below is the comparison.

Trial Stop watch (real) Stop watch (on Arduino
1 00:10:00 00:06:10
2 00:10:00 00:06:10
3 00:10:00 00:06:13
4 00:10:00 00:06:10
5 00:10:00 00:06:05

#include <LiquidCrystal.h> // include (import) the library code
LiquidCrystal lcd(10,9, 5,4,3,2); // initialize the library with the numbers of interface pins

int info;
int i = 0; // define new integer variables for time couniting(i,s,m)
int s = 0;
int m = 0;

void setup() 
{
  // put your setup code here, to run once:
  lcd.begin(16,2); // set up numbers of rows and columns on the LCD
  Serial.begin(9600); // send and receive at 9600 baud
  Serial.println("Press 1 for Start/reset, 2 for stop"); // print the message on the IDE’s serial monitor

}

void loop() 
{
  // put your main code here, to run repeatedly:
  lcd.setCursor(0,0); // set curosor to column 0, line 0
  if (Serial.available()>0) { // check if there is a single character available 
    info = Serial.read(); 
  }

    if (info =='1'){ // check if info if info character is equal to 1
     i = i + 1 ; // increase i by 1
    }
    else if (info == '2'){
      i = i; //don’t change i
    }

  
  lcd.clear(); // clear the screen and move cursor to (0,0)
  if (i ==100){
    s = s+1; // change second value after 100 centisecond
    i = 0;
  }
  if (s == 60){
    m = m+1; // change the minute value after 60 second value
    s = 0;
  }
  lcd.print(m); //print m(minute value)
  lcd.setCursor(2,0); // move the cursor to row 3 column 1
  lcd.print(':');
  lcd.setCursor(3,0);
  lcd.print(s);
  lcd.setCursor(5,0);
  lcd.print(':');
  lcd.setCursor(6,0);
  lcd.print(i);
  
  delay(10); // delay for 1 cent seconds
  
}

The actual delay is the delay(10) + whatever time that it takes to do all that printing.

You could use the millis() function instead of delay() to get better timing. See the blink without delay tutorial.

And using millis() for timing

groundFungus:
The actual delay is the delay(10) + whatever time that it takes to do all that printing.

Arduino has a free running clock which is accessed via the "millis()" function. Using this would avoid the problem "Fungus" describes although millis still depends upon the accuracy of the Arduino oscillator which typically isn't great.

See examples here: Using millis() for timing. A beginners guide - Introductory Tutorials - Arduino Forum

…So all you gotta’ do is read millis() to get the start time (and save it in a variable), read millis() again to get the stop time, and subtract.

Yeah, millis is good enough for government work, they say.

Arduino's millis() timer is probably about as good as a cheap stop watch. Not suitable for recording official world records, but the crystal is very stable so the error will always be the same making it just fine for comparing times.

wvmarle:
Arduino's millis() timer is probably about as good as a cheap stop watch. Not suitable for recording official world records, but the crystal is very stable so the error will always be the same making it just fine for comparing times.

Does it use a crystal for timing? Or just a ceramic resonator?

Have a look at my stopwatch class and its examples

https://github.com/RobTillaart/Arduino/tree/master/libraries/StopWatch

groundFungus:
The actual delay is the delay(10) + whatever time that it takes to do all that printing.

You could use the millis() function instead of delay() to get better timing.

@iberekety:

groundFungus is right.

I’ve dealt with this problem before. I have modified your original code to include the solution. This solution involves a variable called last_tick_msec. Please study it to see how it works.

I have also changed some of the LCD printing to make your clock display look nicer. Please also study the code for that. Also, I moved the lcd.clear() into setup() because you only need to clear the display once.

#include <LiquidCrystal.h> // include (import) the library code
LiquidCrystal lcd(10,9, 5,4,3,2); // initialize the library with the numbers of interface pins

int info;
int i = 0; // define new integer variables for time couniting(i,s,m)
int s = 0;
int m = 0;
unsigned long last_tick_msec = 0; // millisecond value of most recent clock tick
int new_ticks = 0; // number of new ticks since last check

void setup()
{
  // put your setup code here, to run once:
  lcd.begin(16,2); // set up numbers of rows and columns on the LCD
  Serial.begin(9600); // send and receive at 9600 baud
  Serial.println("Press 1 for Start/reset, 2 for stop"); // print the message on the IDE's serial monitor

  lcd.clear(); // clear the screen and move cursor to (0,0)
}

void loop()
{
  // put your main code here, to run repeatedly:

  if ((millis() - last_tick_msec) >= 10) {
    // it is time to make the clock tick
    last_tick_msec = last_tick_msec + 10; // one tick is 10 milliseconds
    new_ticks = 1;
  }   
  else {
    // not yet time to make the clock tick
    new_ticks = 0;
  }
  
  if (Serial.available()>0) { // check if there is a single character available
    info = Serial.read();
  }

  if (info =='1'){ // check if info if info character is equal to 1
    i = i + new_ticks ; // add any new ticks to i
  }
  else if (info == '2'){
    i = i; //don't change i
  }
 
  if (i ==100){
    s = s+1; // change second value after 100 centisecond
    i = 0;
  }
  if (s == 60){
    m = m+1; // change the minute value after 60 second value
    s = 0;
  }

  lcd.setCursor(0,0); // set cursor to column 0, line 0
  if (m < 10) {
    lcd.print('0'); // print zero in front of single digits
    lcd.setCursor(1,0);
    lcd.print(m); // print m(minute value)    
  }
  else {
    lcd.print(m); // print m(minute value)    
  }

  lcd.setCursor(2,0); // move the cursor to column 3 row 1
  lcd.print(':');  
  if (s < 10) {    
    lcd.setCursor(3,0);
    lcd.print('0'); // print zero in front of single digits
    lcd.setCursor(4,0);
    lcd.print(s); // print s (seconds value)
  }
  else {
    lcd.setCursor(3,0);
    lcd.print(s); // print s (seconds value)    
  }
  lcd.setCursor(5,0);
  lcd.print(':');
  if (i < 10) {
    lcd.setCursor(6,0);
    lcd.print('0'); // print zero in front of single digits
    lcd.setCursor(7,0);
    lcd.print(i); // print i (hundredths of seconds)  
  }
  else {
    lcd.setCursor(6,0);
    lcd.print(i); // print i (hundredths of seconds)
  }
}