Stopwatch and lap counter

Hello.
I’m trying to make a stopwatch that blinks a led while counting. When a button is pressed, a new lap should start, with the best lap being stored in a variable. I’m having problems because I want to write the variable that stores the best lap, that should store the first lap and upload every time a new lap is faster than the lap already stored. I’ve tried to write it as the best lap starting in 59 for seconds and 59 for minutes, so when comparing it with the first lap, the current lap will become the best lap and so on. But as is the case, the best lap variable isn’t storing anything.

Thank you for reading.

#include <TimeLib.h>

#define ledPin  13                  
#define buttonPin 4                 

int value = LOW;                   
int buttonState;                    
int lastButtonState = LOW;                
int blinking;                      
long interval = 100;               
long previousMillis = 0;          
long startseconds ;
long elapsedseconds ;
long startminutes ;
long elapsedminutes ;
long bestseconds = 59;
long bestminutes = 59;


void setup()
{
   Serial.begin(9600);

   pinMode(ledPin, OUTPUT);         
   pinMode(buttonPin, INPUT);       
   digitalWrite(buttonPin, HIGH);  
}

void loop()
{
   // check for button press
   buttonState = digitalRead(buttonPin);                 

   if (buttonState != lastButtonState  &&  blinking == false){    

      startseconds = second();
      startminutes = minute();
      blinking = true;                                     
      delay(5);                                              
      lastButtonState = buttonState;                          
   }



   else if (buttonState != lastButtonState && blinking == true){  

       elapsedseconds = second() - startseconds;
       elapsedminutes = minute() - startminutes;
       blinking = false;                                  
       lastButtonState = buttonState;                     
   }

   else{
        lastButtonState = buttonState;                       

   }  
      
        elapsedseconds = second() - startseconds;
        elapsedminutes = minute() - startminutes;

        Serial.print( (int)(elapsedminutes));
        Serial.print(":");                           

        if (elapsedseconds < 10){                                  //so it prints :01 second instead of :1 second
           Serial.print("0");
           Serial.print( (int)(elapsedseconds));
           Serial.print('\t'); 
        }

        else {
           Serial.print( (int)(elapsedseconds));         
           Serial.print('\t');
        }
        
        if (elapsedminutes <= bestminutes){                 //trying to find the best lap
           bestminutes = elapsedminutes;
           Serial.print("Best Lap");
           Serial.print('\t');
           Serial.print( (int)(bestminutes));
           Serial.print(":");
        }
        
        if (elapsedseconds <= bestseconds){
            bestseconds = elapsedseconds;
           if (bestseconds < 10){
             Serial.print("0");
             Serial.print( (int)(bestseconds)); 
          }
           else {
             Serial.print( (int)(bestseconds));
          }
        }
        
   Serial.println();


   if ( (millis() - previousMillis > interval) ) {
      if (blinking == true){
         previousMillis = millis();                     

         if (value == LOW)
            value = HIGH;
         else
            value = LOW;

         digitalWrite(ledPin, value);

      }

      else{
         digitalWrite(ledPin, LOW);                    
      }
   }
}
 if (elapsedminutes <= bestminutes){                 //trying to find the best lap
           bestminutes = elapsedminutes;
           Serial.print("Best Lap");
           Serial.print('\t');
           Serial.print( (int)(bestminutes));
           Serial.print(":");
        }
        
        if (elapsedseconds <= bestseconds){
            bestseconds = elapsedseconds;
           if (bestseconds < 10){
             Serial.print("0");
             Serial.print( (int)(bestseconds)); 
          }
           else {
             Serial.print( (int)(bestseconds));
          }

Imagine that the first lap is 4 minutes and 59 seconds and the second lap is minutes and 4 seconds. Now it hits the first if and it sees that the minutes are more, so it doesn’t update the fastest time. Then it sees on the next one that the seconds is less so it calls this the new fastest time. And you end up with the fastest time being 4 minutes and 4 seconds.

Instead of trying to work with minutes and seconds, just keep track of seconds. Let it count up past 60 and just measure in seconds. Get a time of 532 seconds and see if that is faster than 534 seconds. Easy peasy.

The only place you need to convert to minutes is when you display it. That’s trivial to divide by 60 to get that.

Instead of trying to work with minutes and seconds, just keep track of seconds. Let it count up past 60 and just measure in seconds. Get a time of 532 seconds and see if that is faster than 534 seconds. Easy peasy.

The only place you need to convert to minutes is when you display it. That’s trivial to divide by 60 to get that.

Thank you for your reply. I think I fixed that, had to delete the time library because seconds only goes from 0 to 59. So I did all the conversions using the original millis function. I still don’t know how to find the best lap, though. Here’s how the code is now:

#define ledPin  13                  
#define buttonPin 4                 

int value = LOW;                   
int buttonState;                    
int lastButtonState = LOW;                
int blinking;                      
long interval = 100;               
long previousMillis = 0;          
long startmillis ;
long elapsedmillis ;
long elapsedseconds ;
long elapsedminutes ;
long bestmilllis = 59;


void setup()
{
   Serial.begin(9600);

   pinMode(ledPin, OUTPUT);         
   pinMode(buttonPin, INPUT);       
   digitalWrite(buttonPin, HIGH);  
}

void loop()
{
   // check for button press
   buttonState = digitalRead(buttonPin);                 

   if (buttonState != lastButtonState  &&  blinking == false){    
      startmillis = millis();
      blinking = true;                                     
      delay(5);                                              
      lastButtonState = buttonState;                          
   }

   else if (buttonState != lastButtonState && blinking == true){  
       blinking = false;                                  
       lastButtonState = buttonState;                     
   }

   else{
        lastButtonState = buttonState;                       

   }    
        elapsedmillis = millis() - startmillis;
        elapsedseconds = ((elapsedmillis / 1000) % 60);
        elapsedminutes = ((elapsedmillis / 1000) / 60);

        Serial.print( (int)(elapsedminutes));
        Serial.print(":");                           

        if (elapsedseconds < 10){                                  //so it prints :01 second instead of :1 second
           Serial.print("0");
           Serial.print( (int)(elapsedseconds));
           Serial.print('\t'); 
        }

        else {
           Serial.print( (int)(elapsedseconds));         
           Serial.print('\t');
        }
        
        if (elapsedminutes <= bestminutes){                      //trying to find the best lap
           bestminutes = elapsedminutes;
           Serial.print("Best Lap");
           Serial.print('\t');
           Serial.print( (int)(bestminutes));
           Serial.print(":");
        }
        
        if (elapsedseconds <= bestseconds){
            bestseconds = elapsedseconds;
           if (bestseconds < 10){
             Serial.print("0");
             Serial.print( (int)(bestseconds)); 
          }
           else {
             Serial.print( (int)(bestseconds));
          }
        }
        
   Serial.println();


   if ( (millis() - previousMillis > interval) ) {
      if (blinking == true){
         previousMillis = millis();                     

         if (value == LOW)
            value = HIGH;
         else
            value = LOW;

         digitalWrite(ledPin, value);

      }

      else{
         digitalWrite(ledPin, LOW);                    
      }
   }
}
 if (elapsedminutes <= bestminutes){                      //trying to find the best lap
           bestminutes = elapsedminutes;
           Serial.print("Best Lap");
           Serial.print('\t');
           Serial.print( (int)(bestminutes));
           Serial.print(":");
        }
        
        if (elapsedseconds <= bestseconds){
            bestseconds = elapsedseconds;
           if (bestseconds < 10){
             Serial.print("0");
             Serial.print( (int)(bestseconds)); 
          }
           else {
             Serial.print( (int)(bestseconds));
          }
        }

You’re still doing all your comparison in two different units. Just above that you have the lap time in milliseconds. Just compare to the last lap time in milliseconds. Store lap times in milliseconds. Forget minutes and seconds anywhere in your code except for the display.

Pseudocode:

at start of lap:
startTime = millis();

at end of lap
elapsedTime = millis() - startTime;


if(elapsedTime < bestTime){
   bestTime = elapsedTime;
}

Delta_G:
You're still doing all your comparison in two different units. Just above that you have the lap time in milliseconds. Just compare to the last lap time in milliseconds. Store lap times in milliseconds. Forget minutes and seconds anywhere in your code except for the display.

The problem is that the program doesn't actually count the laps. It "starts over" every time the button is pressed, so there's no sense of memory or a last lap to compare the current lap to. I didn't change that part of the code to millis yet because I know the problem is bigger than that

exceptlovingme:
The problem is that the program doesn’t actually count the laps. It “starts over” every time the button is pressed, so there’s no sense of memory or a last lap to compare the current lap to. I didn’t change that part of the code to millis yet because I know the problem is bigger than that

You’re the one writing the code. Why don’t you just define a variable to keep the shortest time in. There’s no problem that the code doesn’t have that already. Just add it.

at global scope:

unsigned long bestTime = 9999999;  // long enough so first lap is better

at start of lap:
startTime = millis();

at end of lap
elapsedTime = millis() - startTime;


if(elapsedTime < bestTime){
   bestTime = elapsedTime;
}

Delta_G:
You’re the one writing the code. Why don’t you just define a variable to keep the shortest time in. There’s no problem that the code doesn’t have that already. Just add it.

There you go. I’ve put that in, but nothing that is written after “if (elapsedTime < bestTime)” ever gets printed.

#define ledPin  13                  
#define buttonPin 4                 

int value = LOW;                   
int buttonState;                    
int lastButtonState = LOW;                
int blinking;                      
long interval = 100;               
long previousMillis = 0;          
long startTime ;
long elapsedTime ;
long elapsedSeconds ;
long elapsedMinutes ;
unsigned long bestTime = 9999999;

void setup() {
   Serial.begin(9600);

   pinMode(ledPin, OUTPUT);         
   pinMode(buttonPin, INPUT);       
   digitalWrite(buttonPin, HIGH);  
}

void loop() {
   buttonState = digitalRead(buttonPin);                 

   if (buttonState != lastButtonState  &&  blinking == false){    
      startTime = millis();
      blinking = true;                                     
      delay(5);                                              
      lastButtonState = buttonState;                          
   }

   else if (buttonState != lastButtonState && blinking == true){  
      blinking = false;                                  
      lastButtonState = buttonState;
   }

   else{
      lastButtonState = buttonState;                       

   }    
      elapsedTime = millis() - startTime;
      elapsedSeconds = ((elapsedTime / 1000) % 60);
      elapsedMinutes = ((elapsedTime / 1000) / 60);

      Serial.print( (int)(elapsedMinutes));
      Serial.print(":");                           

      if (elapsedSeconds < 10){                                  //so it prints :01 second instead of :1 second
         Serial.print("0");
         Serial.print( (int)(elapsedSeconds));
         Serial.print('\t'); 
      }

      else {
         Serial.print( (int)(elapsedSeconds));         
         Serial.print('\t');
      }
       
      if (elapsedTime < bestTime){                       //trying to find the best lap
         bestTime = elapsedTime;
         Serial.print("Best Lap");
         Serial.print('\t');
         Serial.print( (int)((bestTime / 1000) / 60));
         Serial.print(":");
         if (((bestTime / 1000) % 60) < 10){
            Serial.print("0");
            Serial.print( (int)((bestTime / 1000) % 60)); 
         }
         else {
            Serial.print( (int)((bestTime / 1000) % 60));
         }
      }  
        
Serial.println();


   if ( (millis() - previousMillis > interval) ) {
      if (blinking == true){
         previousMillis = millis();                     

         if (value == LOW)
            value = HIGH;
         else
            value = LOW;

         digitalWrite(ledPin, value);
      }

      else{
         digitalWrite(ledPin, LOW);                    
      }
   }
}

Here, I formatted your code so the blocks are easier to see. Control-T in the IDE does this for you. It’s so easy there is really no excuse for poorly indented code. Now can you see that the buttons have nothing to do with the time measuerement? Should they? What triggers the start and end of a lap?

#define ledPin  13
#define buttonPin 4

int value = LOW;
int buttonState;
int lastButtonState = LOW;
int blinking;
long interval = 100;
long previousMillis = 0;
long startTime ;
long elapsedTime ;
long elapsedSeconds ;
long elapsedMinutes ;
unsigned long bestTime = 9999999;

void setup() {
  Serial.begin(9600);

  pinMode(ledPin, OUTPUT);
  pinMode(buttonPin, INPUT);
  digitalWrite(buttonPin, HIGH);
}

void loop() {
  buttonState = digitalRead(buttonPin);

  if (buttonState != lastButtonState  &&  blinking == false) {
    startTime = millis();
    blinking = true;
    delay(5);
    lastButtonState = buttonState;
  }

  else if (buttonState != lastButtonState && blinking == true) {
    blinking = false;
    lastButtonState = buttonState;
  }

  else {
    lastButtonState = buttonState;

  }
  elapsedTime = millis() - startTime;
  elapsedSeconds = ((elapsedTime / 1000) % 60);
  elapsedMinutes = ((elapsedTime / 1000) / 60);

  Serial.print( (int)(elapsedMinutes));
  Serial.print(":");

  if (elapsedSeconds < 10) {                                 //so it prints :01 second instead of :1 second
    Serial.print("0");
    Serial.print( (int)(elapsedSeconds));
    Serial.print('\t');
  }

  else {
    Serial.print( (int)(elapsedSeconds));
    Serial.print('\t');
  }

  if (elapsedTime < bestTime) {                      //trying to find the best lap
    bestTime = elapsedTime;
    Serial.print("Best Lap");
    Serial.print('\t');
    Serial.print( (int)((bestTime / 1000) / 60));
    Serial.print(":");
    if (((bestTime / 1000) % 60) < 10) {
      Serial.print("0");
      Serial.print( (int)((bestTime / 1000) % 60));
    }
    else {
      Serial.print( (int)((bestTime / 1000) % 60));
    }
  }

  Serial.println();


  if ( (millis() - previousMillis > interval) ) {
    if (blinking == true) {
      previousMillis = millis();

      if (value == LOW)
        value = HIGH;
      else
        value = LOW;

      digitalWrite(ledPin, value);
    }

    else {
      digitalWrite(ledPin, LOW);
    }
  }
}

Delta_G:
Here, I formatted your code so the blocks are easier to see. Control-T in the IDE does this for you. It's so easy there is really no excuse for poorly indented code. Now can you see that the buttons have nothing to do with the time measuerement? Should they? What triggers the start and end of a lap?

I'm sorry, I really didn't know about control-t.
What the button does is store a new startTime every time it has been pressed, so when the next elapsedTime occurs, it will be millis - millis which is 0 and then the counter starts again. That's how a new lap is started.
I'm still failing to print the best lap variable.

So what ends a lap then? Seems to me like you would only want to be comparing lap times for complete laps. Otherwise the one you just started always looks like the best one.

I bet you got the best lap stuff printed once at the beginning for some really low number of milliseconds that you can't match again after that.

There's no end of a lap really, the button just makes it so the lap starts from 0 again.

I bet you got the best lap stuff printed once at the beginning for some really low number of milliseconds that you can't match again after that.

Some weird stuff is printed when I first open the serial, before the counter starts. Here's an example:

exceptlovingme:
There's no end of a lap really, the button just makes it so the lap starts from 0 again.

If there's no end to a lap then it makes no sense to talk about a lap time. Elapsed time is a meaningless concept without a beginning and an end.

Think about that one a little harder. If I'm running laps, when does a lap end in relation to the next one starting?

Delta_G:
If there’s no end to a lap then it makes no sense to talk about a lap time.

Think about that one a little harder. If I’m running laps, when does a lap end in relation to the next one starting?

Well, a lap ends when you reach the beginning. I gather that means the lap ends when I press the button again. How does that help me find the best lap?
Thank you for your patience.

exceptlovingme:
Well, a lap ends when you reach the beginning. I gather that means the lap ends when I press the button again.

That's it.

How does that help me find the best lap?

Are you kidding? How can you find the best lap if you can't even find the time for a single lap. You can't find the time for a lap unless you can say when the timing starts and ends.

So think about your code for a second. We press the button and get a start time. Then a few microseconds later it calculates an elapsed time. It isn't waiting for you to complete a lap. It doesn't know anything about laps. It just knows that right after that if statement runs setting the start time the next thing that happens in the code is to calculate an elapsed time.

How long has elapsed? Probably 0ms. It's probably measured in microseconds at that point. So then it hits the if test and see that 0 is less than 999999 so it records 0 as the best time. From this point forward it doesn't matter what time you make, it won't be less than 0 so it will never get recorded as the best time.

All this figuring of best times needs to happen only at the end of the lap when you know how long the lap took. You can't tell me if this lap is faster than the last lap if I have only just started running the second lap. Think about that... not code just think about it. If I run one lap that is 5 minutes, and then I start my second lap and I'v been running for 5 seconds, is the second lap the best lap? No, of course it might be but you can't know until you get to the end of the lap.

Is that really not making sense? Forget code, can you understand in plain english how this has to work?

I managed to make it work! Thank you so much!!!