Lap timer (1s in lap timer is not 1s in real)

Hello,
I am making lap timer. I make it on Arduino UNO 3. Code was found in internet and little bit corrected to apply to my hardware. And now I have a problem, that clock is calculating wrong time (ex. 1s in lap timer is not 1s in real)

Maybe something is wrong with “tickClock” and millis() function? At this moment I do not have ideas what to do.

P.S.
I am not a programer, I am racer

 //Veikia taip kaip noreciau, tik klaidindai laika skaiciuoja
// include the library code:
#include <LiquidCrystal.h>

// initialize the library by associating any needed LCD interface pin
// with the arduino pin number it is connected to
const int rs = 12, en = 11, d4 = 5, d5 = 4, d6 = 3, d7 = 2;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

const int button1 = 8;     //Mygtukas i 2 digital pin
const int button2 = 9;     //Mygtukas i 3 digital pin


long lastPressedBtn1 = 0;    //Last millisecond we pressed button1
long lastPressedBtn2 = 0;    //Last millisecond we pressed button2;

boolean run = false;         //Should the system run?

int button1State = LOW;      //The value the button1 reads
int button1CurrState = LOW;  //The last value of button1
int button2State = LOW;      //The value of button1
int button2CurrState = LOW;  //The last value of button2


unsigned long timer = 0; //The timer

int second = 0;
int minute = 0;
int tenth = 0;
int simt = 0;
int tukst = 0;
/**
 * SETUP
 */
 
void setup() {
                                  
  pinMode(button1, INPUT);
  pinMode(button2, INPUT);
//  lcd.init();                                  
//  lcd.backlight(); 
 lcd.begin(16, 2); 
  lcd.setCursor(0,0);
  lcd.print("Rato laikas");
  lcd.setCursor(0,1);
  lcd.print("Laukiam");
 
}

/**
 * This is the timer function
 * For each tenth of a second call the tick() function
 */



void tickClock() {
  Serial.println(millis()/1); //Kai millis() padaliname is 1000 gauname viena sekunde, musu atveju nereikia dalinti, o tik sumuoti tikus ir teisinai suskirstyti zodziu cia kazkokia kose
  if((timer - millis()/1) >= 10 || timer == 0) {
    tick();
    timer =millis()/1 ;
  }
}
/**
 * Loop
 */
void loop() {
  tickClock(); //Start ticking the clock
  button1State = digitalRead(button1);
  button2State = digitalRead(button2); 
  checkStart();
  checkClear();     
}

/**
 * Check for button1 press
 * If the first button is pressed, 
 * be sure that there is at least 100 ms since the last press
 */
void checkStart() {
  if(button1State != button1CurrState) {    //Check if the button state has changed
    if(button1State == HIGH && (millis()-lastPressedBtn1) > 100 || lastPressedBtn1 == 0) {
      lastPressedBtn1 = millis();
      run = !run; //Switch the running state
    }    
  }
  button1CurrState = button1State;    //Set the current state equals the button state
}

/**
 * Check for button 2 press
 * If the second button is pressed, 
 * be sure that there is at least 100 since the last press
 */
void checkClear() {
  if(button2State != button2CurrState) {
    if(button2State == HIGH && (millis()-lastPressedBtn2) > 100 || lastPressedBtn2 == 0 ) {  
      lastPressedBtn2 = millis();
      run = false;
      minute = 0;
      second = 0;
      tenth = 0;
      simt = 0;
      tukst = 0;
   updateLCD(); 
      lcd.setCursor(0,1);
  lcd.print("*Laukiam*");
    }    
  }
  button2CurrState = button2State;   //Set btn2 current state equals the btn2State
}

/** 
 * Tick one tenth of a second if it should be running
 */

void tick() {
  if(run) {
    updateLCD();
       if(tukst == 999) {
          tukst = 0;
  // tukst++;
       // if(simt == 9) {
        //   simt = 0;
  // tenth++;
        // if(tenth == 9) {
         //   tenth = 0;
  //  second++;
       if(second == 59) {
          second = 0;
        minute++;     
         
     } else {
    second++;}
    
    
  //  } else {
  //  tenth++;}
    
  //  } else {
   // simt++;}  
      
   } else {
    tukst++;}   
}
}

/** 
 * Update the LCD-display
 * The extra 0s and : is for the sake of beauty :)
 */
 
void updateLCD() {
  lcd.setCursor(0,1);
  
  if(minute < 10) {      // If hour does not have 2 digits
    lcd.print("0");
  }
  lcd.print(minute, DEC);
  lcd.print(":");

  if(second < 10) {    // If minute does not have 2 digits
    lcd.print("0");
  }
  lcd.print(second, DEC);
  lcd.print(".");

//    if(tenth < 10) {    // If tenth does not have 2 digits
//   lcd.print(tenth, DEC);
//  }
 //  if(simt < 10) {    
 //   lcd.print(simt, DEC);
//  }
   if(tukst < 1000) {    // If tenth does not have 2 digits
   lcd.print(tukst, DEC); 
  }
 }

At this moment I do not have ideas what to do.

Please start by following the advice on posting a programming question given in Read this before posting a programming question

In particular note the advice to Auto format code in the IDE and to use code tags when posting code here as it prevents some combinations of characters in code being interpreted as HTML commands such as italics, bold or a smiley character, all of which render the code useless

The timers in an Arduino are not accurate, millis is not exactly 1000th of a second. You've not said how inaccurate the timing is.

kactuz_1: And now I have a problem, that clock is calculating wrong time (ex. 1s in lap timer is not 1s in real)

How much difference is there? Can you give some actual numbers? 10 seconds in real life is how much in the timer?

And what are you using to measure "real time"?

Steve

@kactuz_1 - your duplicate topic in the microcontrollers section has been deleted

Cross-posting is against the rules of the forum. The reason is that duplicate posts can waste the time of the people trying to help. Someone might spend 15 minutes (or more) writing a detailed answer on this topic, without knowing that someone else already did the same in the other topic.

Repeated cross-posting will result in a timeout from the forum.

In the future, please take some time to pick the forum board that best suits the topic of your question and then only post once to that forum board. This is basic forum etiquette, as explained in the sticky “How to use this forum - please read.” post you will find at the top of every forum board. It contains a lot of other useful information. Please read it.

Thanks in advance for your cooperation.

  pinMode(button1, INPUT);
  pinMode(button2, INPUT);

Do you have external pull up or pull down resistors? It helps if you show us your circuit.

=millis()/1Wuh?

your setup-function does not initialise the serial interface. Printing the number of millis() ten times per second will delay your software-clock significantly. Inside tick-clock you should set the if-condition on top and the first thing inside the if-condition is updating your timer-variable. millis()/1 does not change the value but needs execution-time. Simply delete the /1

best regards Stefan

Hello,
I test accuracy and I have :
10s in real-time is 00:00.620 in my lap timer (I test it with my cell phone).

Yes, I am using external pull-down resistors.

I have this accuracy with this code :

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

// Set the LCD address to 0x27 for a 16 chars and 2 line display
LiquidCrystal_I2C lcd(0x27, 16, 2);


const int button1 = 2;     //Mygtukas i 2 digital pin
const int button2 = 3;     //Mygtukas i 3 digital pin


long lastPressedBtn1 = 0;    //Last millisecond we pressed button1
long lastPressedBtn2 = 0;    //Last millisecond we pressed button2;

boolean run = false;         //Should the system run?

int button1State = LOW;      //The value the button1 reads
int button1CurrState = LOW;  //The last value of button1
int button2State = LOW;      //The value of button1
int button2CurrState = LOW;  //The last value of button2


unsigned long timer = 0; //The timer

int second = 0;
int minute = 0;
int tenth = 0;
int simt = 0;
int tukst = 0;
/**
 * SETUP
 */
 
void setup() {
                                  
  pinMode(button1, INPUT);
  pinMode(button2, INPUT);
  lcd.init();                                  
  lcd.backlight(); 
 lcd.begin(16, 2); 
  lcd.setCursor(0,0);
  lcd.print("Rato laikas");
  lcd.setCursor(0,1);
  lcd.print("Laukiam");
 
}

/**
 * This is the timer function
 * For each tenth of a second call the tick() function
 */



void tickClock() {
  Serial.println(millis()); //Kai millis() padaliname is 1000 gauname viena sekunde, musu atveju nereikia dalinti, o tik sumuoti tikus ir teisinai suskirstyti zodziu cia kazkokia kose
  if((timer - millis()) >= 10 || timer == 0) {
    tick();
    timer =millis() ;
  }
}
/**
 * Loop
 */
void loop() {
  tickClock(); //Start ticking the clock
  button1State = digitalRead(button1);
  button2State = digitalRead(button2); 
  checkStart();
  checkClear();     
}

/**
 * Check for button1 press
 * If the first button is pressed, 
 * be sure that there is at least 100 ms since the last press
 */
void checkStart() {
  if(button1State != button1CurrState) {    //Check if the button state has changed
    if(button1State == HIGH && (millis()-lastPressedBtn1) > 100 || lastPressedBtn1 == 0) {
      lastPressedBtn1 = millis();
      run = !run; //Switch the running state
    }    
  }
  button1CurrState = button1State;    //Set the current state equals the button state
}

/**
 * Check for button 2 press
 * If the second button is pressed, 
 * be sure that there is at least 100 since the last press
 */
void checkClear() {
  if(button2State != button2CurrState) {
    if(button2State == HIGH && (millis()-lastPressedBtn2) > 100 || lastPressedBtn2 == 0 ) {  
      lastPressedBtn2 = millis();
      run = false;
      minute = 0;
      second = 0;
      tenth = 0;
      simt = 0;
      tukst = 0;
   updateLCD(); 
      lcd.setCursor(0,1);
  lcd.print("*Laukiam*");
    }    
  }
  button2CurrState = button2State;   //Set btn2 current state equals the btn2State
}

/** 
 * Tick one tenth of a second if it should be running
 */

void tick() {
  if(run) {
    updateLCD();
       if(tukst == 9) {
          tukst = 0;
  // tukst++;
       if(simt == 9) {
        simt = 0;
  // tenth++;
        if(tenth == 9) {
         tenth = 0;
  //  second++;
       if(second == 59) {
          second = 0;
        minute++;     
         
     } else {
    second++;}
    
    
   } else {
    tenth++;}
    
   } else {
    simt++;}  
      
   } else {
    tukst++;}   
}
}

/** 
 * Update the LCD-display
 * The extra 0s and : is for the sake of beauty :)
 */
 
void updateLCD() {
  lcd.setCursor(0,1);
  
  if(minute < 10) {      // If hour does not have 2 digits
    lcd.print("0");
  }
  lcd.print(minute, DEC);
  lcd.print(":");

  if(second < 10) {    // If minute does not have 2 digits
    lcd.print("0");
  }
  lcd.print(second, DEC);
  lcd.print(".");

    if(tenth < 10) {    // If tenth does not have 2 digits
   lcd.print(tenth, DEC);
  }
   if(simt < 10) {    
   lcd.print(simt, DEC);
  }
   if(tukst < 1000) {    // If tenth does not have 2 digits
   lcd.print(tukst, DEC); 
  }
 }

The code you posted has most of the update and display code commented out. Please post the actual code that produces your result. And what does "00:00.620" mean in terms of seconds?

Steve

slipstick: The code you posted has most of the update and display code commented out. Please post the actual code that produces your result. And what does "00:00.620" mean in terms of seconds?

Steve

00:00.620 means 10s, I put all code in upper message.

you should change from

void tickClock() {
  Serial.println(millis()); //Kai millis() padaliname is 1000 gauname viena sekunde, musu atveju nereikia dalinti, o tik sumuoti tikus ir teisinai suskirstyti zodziu cia kazkokia kose
  if((timer - millis()) >= 10 || timer == 0) {
    tick();
    timer =millis() ;
  }
}

to

void tickClock() {
  if((millis() - timer) >= 10) { // as the very first thing check if 0,1 seconds have passed by
    timer = millis() ; // IMMETIATELY update timer-variable
    tick();
    Serial.println(millis()); 
  }
}

to gain execution -speed place the whole RTC-code in loop without any function-calls

best regards Stefan

Nothing change :frowning: In real time 10s - In my lap timer 00:00.620, maybe a problem not here? Maybe something is wrong in “tick” counting?

Because I have old code version where I calculating only two values after comma. And there works properly ( 10s is 10s ).

 #include <LiquidCrystal.h> 
 
 LiquidCrystal lcd(12, 11, 5, 4, 3, 2); 
 
 const int button1 = 9;     //Mygtukas i 2 digital pin 
 const int button2 = 8;     //Mygtukas i 3 digital pin 
 
 long lastPressedBtn1 = 0;    //Last millisecond we pressed button1 
 long lastPressedBtn2 = 0;    //Last millisecond we pressed button2; 
 
 boolean run = false;         //Should the system run? 
 
 int button1State = LOW;        //The value the button1 reads 
 int button1CurrState = LOW;  //The last value of button1 
 int button2State = LOW;        //The value of button1 
 int button2CurrState = LOW;  //The last value of button2 
 
 
 long timer = 0; //The timer 
 
 int second = 0; 
 int minute = 0; 
 int tenth = 0; 
 
 /** 
  * SETUP 
  */ 
   
 void setup() { 
   pinMode(button1, INPUT); 
   pinMode(button2, INPUT); 
   lcd.begin(16, 2); 
   lcd.setCursor(0,0); 
   lcd.print("Rato laikas"); 
   lcd.setCursor(0,1); 
   lcd.print("Laukiam"); 
 
 } 
 
 /** 
  * This is the timer function 
  * For each tenth of a second call the tick() function 
 */ 
 void tickClock() { 
   Serial.println(millis()/10000); 
  if((timer - millis()/10000) >= 10 || timer == 0) { 
    tick(); 
    timer = millis()/10000; 
   } 
 } 
 
 
 void loop() { 
   tickClock(); //Start ticking the clock 
   button1State = digitalRead(button1); 
   button2State = digitalRead(button2); 
   checkStart(); 
   checkClear();      
 } 
 
 /** 
  * Check for button1 press 
  * If the first button is pressed, 
  * be sure that there is at least 100 ms since the last press 
  */ 
 void checkStart() { 
   if(button1State != button1CurrState) {    //Check if the button state has changed 
     if(button1State == HIGH && (millis()-lastPressedBtn1) > 100 || lastPressedBtn1 == 0) { 
       lastPressedBtn1 = millis(); 
       run = !run; //Switch the running state 
     }    
   } 
   button1CurrState = button1State;    //Set the current state equals the button state 
 } 
 
 /** 
  * Check for button 2 press 
  * If the second button is pressed, 
  * be sure that there is at least 100 since the last press 
  */ 
 void checkClear() { 
   if(button2State != button2CurrState) { 
     if(button2State == HIGH && (millis()-lastPressedBtn2) > 100 || lastPressedBtn2 == 0) {  
       lastPressedBtn2 = millis(); 
     // lcd.clearLine(1) ; //Clear the display 
     }    
   } 
   button2CurrState = button2State;   //Set btn2 current state equals the btn2State 
   
   
 
 } 
 
 /** 
  * Tick one tenth of a second if it should be running 
  */ 
 void tick() { 
   if(run) { 
     updateLCD(); 
         
     if(tenth == 99) { 
       tenth = 0; 
       
       if(second == 59) { 
         second = 0; 
         minute++; 
         
       } else { 
         second++; 
       } 
     } else { 
     tenth++; 
     } 
   } 
 } 
 /** 
  * Update the LCD-display 
  * The extra 0s and : is for the sake of beauty :) 
  */ 
 void updateLCD() { 
   lcd.setCursor(0,1); 
   
   if(minute < 10) {      // If hour does not have 2 digits 
     lcd.print("0"); 
   } 
   lcd.print(minute, DEC); 
   lcd.print(":"); 
 
   if(second < 10) {    // If minute does not have 2 digits 
     lcd.print("0"); 
   } 
   
   lcd.print(second, DEC); 
   lcd.print(":"); 
   
    lcd.print(tenth, DEC ); 
     lcd.print(" "); 
   } 
 
 
 /** 
  * Clear the display 
  * Set all time variables equal 0 
   
 void clearAll() { 
   run = false; 
    second = 0; 
   minute = 0; 
   tenth = 0; 
   updateLCD(); 
 
 }*/

do me a favor write your posts in your native language as gramatically correct sentences and let do google-translate the translation into english.

With this too short broken english i find it hard to understand.

two values after comma means 1/100 second. Your software-RTC counts 1/10 seconds. So the maximum precision you can get from your laptimer is 0,1 seconds which is a single digit behind comma

If you want to two digits you would have to change the whole software counting the 1/100-seconds without any printing to anything nor serial nor lcd printing to serial and/or lcd needs time. more time than 1/100 = 0,01 seconds

checking for 10 milliseconds is correct this results in 0,01 seconds resolution If you would like to have 0,01 seconds or even 0,001 seconds you should by a professional lap-timer

best regards Stefan

Also if you really have the UNO, the processor clock is not very accurate because it’s a resonator not a quartz crystal.

I haven’t a I2C-display handy to connect to an Arduino Uno
I use an arduino-clone which seems to have a chrystal)

If you put back in your function updateLCD you should measure how much time this function-call consumes.
If it is more than 0,01 seconds ticks of 0,01 seconds can’t work.

For a most acurate working software RTC with a resoluton of 0,01 seconds you have to add a contant instead of actuall millis() because millis() counts while the code gets executed and this causes some extra-time passing by.
3 to 4 milliseconds.

In this version the serial baudrate is adjusted to the max 2.000.000 baud to keep the time needed for serial output as short as possible

With adding the constant the software-RTC stays pretty precise

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

// Set the LCD address to 0x27 for a 16 chars and 2 line display
LiquidCrystal_I2C lcd(0x27, 16, 2);


const int button1 = 2;     //Mygtukas i 2 digital pin
const int button2 = 3;     //Mygtukas i 3 digital pin
const byte OnBoardLED = 13;

long lastPressedBtn1 = 0;    //Last millisecond we pressed button1
long lastPressedBtn2 = 0;    //Last millisecond we pressed button2;

boolean run = false;         //Should the system run?

int button1State = LOW;      //The value the button1 reads
int button1CurrState = LOW;  //The last value of button1
int button2State = LOW;      //The value of button1
int button2CurrState = LOW;  //The last value of button2


unsigned long timer = 0; //The timer
unsigned long currentMillis;
unsigned long previousMillis = 0;


int second   = 0;
int minute   = 0;
int tenth    = 0;
int hundreds = 0; 

void setup() {
  Serial.begin(2000000); 
  Serial.println("Setup-Start");                              
  pinMode(button1, INPUT);
  pinMode(button2, INPUT);
  pinMode(OnBoardLED, OUTPUT);
  lcd.init();                                 
  lcd.backlight();
  lcd.begin(16, 2);
  lcd.setCursor(0,0);
  lcd.print("Rato laikas");
  lcd.setCursor(0,1);
  lcd.print("Laukiam");
  previousMillis = 0;
  currentMillis = millis();  
}

void tick() {
  if(run) {  //|| true) {
    updateLCD();

    hundreds++;
    if (hundreds == 10) {
      hundreds = 0;
      tenth++;
    }
    
    if(tenth == 10) {
     tenth = 0;
     second++;
     Serial.print(millis()); 
     Serial.print("  "); 
     Serial.print(currentMillis); 
     Serial.print("  "); 
     Serial.print(second); 
     Serial.print(","); 
     Serial.print(tenth); 
     Serial.println(hundreds); 
    
     digitalWrite(OnBoardLED,!digitalRead(OnBoardLED));
    }
         
    if(second == 59) {
      second = -1;
      minute++;     
    } 
    
    if (minute == 59) {
      minute = -1;
    }
  }
}

void tickClock() {
  // store value of millis ONCE at the top 
  // then use stored value all the way through the loop
  currentMillis = millis(); 
  
  //Serial.println(millis()); 
  if(currentMillis - previousMillis >= 10 ) {
    // executing the if-condition needs time too 
    // so add a constant will result in a more accurate clock
    // this will work accurate as long as the next call to the function is done before 
    // the amount of time of the constant is over
    previousMillis += 10; 
    //previousMillis = currentMillis;
    //previousMillis = millis();
    tick();    
  }
}

void loop() {
  tickClock(); //Start ticking the clock
  button1State = digitalRead(button1);
  button2State = digitalRead(button2);
  checkStart();
  checkClear();     
}

void checkStart() {
  if(button1State != button1CurrState) {    //Check if the button state has changed
    if(button1State == HIGH && (millis()-lastPressedBtn1) > 100 || lastPressedBtn1 == 0) {
      lastPressedBtn1 = currentMillis;//millis();
      run = !run; //Switch the running state
    }   
  }
  button1CurrState = button1State;    //Set the current state equals the button state
}

void checkClear() {
  if(button2State != button2CurrState) {
    if(button2State == HIGH && (millis()-lastPressedBtn2) > 100 || lastPressedBtn2 == 0 ) { 
      lastPressedBtn2 = currentMillis; //millis();
      run = false;
      minute = 0;
      second = 0;
      tenth = 0;
      updateLCD();
      lcd.setCursor(0,1);
      lcd.print("*Laukiam*");
    }   
  }
  button2CurrState = button2State;   //Set btn2 current state equals the btn2State
}



void updateLCD() {
  lcd.setCursor(0,1);
 
  if(minute < 10) {      // If hour does not have 2 digits
    lcd.print("0");
  }
  lcd.print(minute, DEC);
  lcd.print(":");

  if(second < 10) {    // If minute does not have 2 digits
    lcd.print("0");
  }
  lcd.print(second, DEC);
  lcd.print(".");

  if(tenth < 10) {    // If tenth does not have 2 digits
   lcd.print(tenth, DEC);
  }
}

best regards Stefan

  if(run || true) {

What ?!

:o

vaj4088:  if(run || true) {

What ?! :o

Ah ! good catch. I had no buttons laying around handy for testing it with buttons. So I put in the || true. Of course the

|| true

has to be removed for normal function so it is just

if(run) {

I commented it in the code-section best regards Stefan

I am sorry for my broken English :) sometimes I am confused in my nature langue with terminology....

Now code is working, with accuracy 1/10 of second.

Which one of Arduino board I must take, for reach 1/1000 accuracy?

a teensy 4.1

kactuz_1: I am sorry for my broken English :) sometimes I am confused in my nature langue with terminology.... Now code is working, with accuracy 1/10 of second. Which one of Arduino board I must take, for reach 1/1000 accuracy?

for such a task I would go straight to a high-speed microcontroller. a teensy 4.1 (600 Mhz) compared to 16 Mhz of a Arduino Uno https://www.pjrc.com/store/teensy41.html or an ESP32 (240 Mhz)

both can be programmed with the Arduino-IDE after installing some additional components

best regards Stefan best regards Stefan