Timer has random error

I paid someone to help me create a stopwatch type timer for split times like used by track coaches. The timer works as planned however it has an error when compared to a stopwatch. The error is always less than one second. The timer reads millis at the start of the race and then at each split time (which was input prior to start) it records millis and subtracts from the expected split time input before the race and displaying the difference. What ever the error as compared to a stopwatch is at the first split time it will be the same for every split time in that session. It seems to be rounding the initial millis reading at the start of the race. The error is different for each test but always consistent for all split times during the test. I made attempts to make changes where it records the initial millis and calls it "error" but no positive results. My skill level is from what I can find posted as tutorials.
Sketch is attached and a schematic of the PCB. I used EasyEDA to open the schematic. The oscillator is a YXC 16.0SDF.
I am no longer able to contact the individual who made this for me and attempting to correct it myself. I would be happy to pay someone to help me work through this.

Ds3231 (5).zip (73.5 KB)

[code]
#include <LiquidCrystal.h>
#include <Wire.h>
#include "DS3231.h"
#include <Keypad.h>

RTClib RTC;
DS3231 Clock;
const byte ROWS = 4; //four rows
const byte COLS = 4; //four columns

#define Red A1
#define Green A0
#define button 3
const int rs = 13, en = 12, d4 = 11, d5 = 10, d6 = 9, d7 = 8;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
char hexaKeys[ROWS][COLS] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};
byte rowPins[ROWS] = {A2, A3, 0, 1}; /* connect to the row pinouts of the keypad */
byte colPins[COLS] = {4, 7, 6, 5}; //connect to the column pinouts of the keypad

Keypad customKeypad = Keypad(makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS);

char customKey, sign[10];
byte num, a = 0;
bool Key_Status = false, button_press = false, Complete = false, Key_Enter = false;
unsigned int split[10][3], split1[10][3], error = 0;
unsigned long int ms, milli[10];
long int pulse = 0;
unsigned long int b;
void setup() {
  pinMode(Red, OUTPUT);
  pinMode(Green, OUTPUT);
  pinMode(2, INPUT_PULLUP);
  pinMode(button, INPUT_PULLUP);
  digitalWrite(Red, LOW);
  digitalWrite(Green, LOW);
  lcd.begin(20, 4);
  lcd.clear();
  lcd.print("Welcome");
  delay(1500);
  lcd.setCursor(0, 0);
  lcd.print("Enter Split Number");
  lcd.setCursor(0, 1);
  Split_Number();
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Enter Split Times");
  Split_Time();
  lcd.clear();
  customKey = ':';
  int j = 0;
  int p = (num - 1) / 4 + 1;
  int Prev_s = 100;
  while (customKey != 'D') {
    customKey = customKeypad.getKey();
    if (customKey && customKey == 'A')
      j = (j + 1) % p;
    if (customKey && customKey == 'B')
      j = (j - 1 + p) % p;
    if (Prev_s != j) {
      Prev_s = j;
      int i = 0;
      lcd.clear();
      while (i < 4 && (4 * j + i) < num) {
        lcd.setCursor(0, i % 4);
        lcd.print(4 * j + i + 1);
        lcd.print(". ");
        lcd_check(split[4 * j + i][0], 3);
        lcd.print(split[4 * j + i][0]); lcd.print("'");
        lcd_check(split[4 * j + i][1], 2);
        lcd.print(split[4 * j + i][1]); lcd.print("''");
        lcd_check(split[4 * j + i][2], 3);
        lcd.print(split[4 * j + i][2]); lcd.print("'''");
        i ++;
      }
    }
  }

  Wire.begin();
  lcd.clear();
  lcd.setCursor(4, 1);
  lcd.print("Press Button");
  lcd.setCursor(3, 2);
  lcd.print("To Start Race");

}

void squr() {
  if (pulse == -1)
    error = millis() - error;
  pulse += 1;
  b = millis();
}

void loop() {
  if (digitalRead(button) == 0) {
    delay(20);
    if (digitalRead(button) == 0) {
      if (a == 0) {
        b = millis();
        error = b;
        Clock.enableOscillator(true, false, 0);
        pulse = -1;
        
        attachInterrupt(digitalPinToInterrupt(2), squr, FALLING);
      }
      else {
        ms = millis() - b;
        if (ms >= 1000)
          ms = random(996, 1000);
        if (pulse < 0){
          pulse = 0;
          
        }
        milli[a - 1] = pulse * 1000 + ms + error;
      }
      a++;
      button_press = true;
    }
  }

  if (button_press && a <= (num + 1))
    sub();

  int i = num - 1;
  int prev = num;
  int s = 0;
  while (Complete) {
    customKey = customKeypad.getKey();
    if (customKey && customKey == 'A')
      i = (i + 1) % num;
    if (customKey && customKey == 'B')
      i = (i - 1 + num) % num;
    if (i != prev) {
      prev = i;
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print(i + 1);
      lcd.print(". ");
      lcd_check(split[i][0], 3);
      lcd.print(split[i][0]);
      lcd.print("'");
      lcd_check(split[i][1], 2);
      lcd.print(split[i][1]);
      lcd.print("''");
      lcd_check(split[i][2], 3);
      lcd.print(split[i][2]);
      lcd.print("'''");
      lcd.setCursor(0, 2);
      lcd.print(i + 1);
      lcd.print(". ");
      s = milli[i] / 60000;
      lcd_check(s, 3);
      lcd.print(s);
      lcd.print("'");
      s = (milli[i] / 1000) % 60;
      lcd_check(s, 2);
      lcd.print(s);
      lcd.print("''");
      s = milli[i] % 1000;
      lcd_check(s, 3);
      lcd.print(s);
      lcd.print("'''");
      lcd.setCursor(0, 3);
      lcd.print(i + 1);
      lcd.print(". ");
      if (sign[i] == '+') {
        digitalWrite(Green, LOW);
        digitalWrite(Red, HIGH);
      }
      else {
        digitalWrite(Green, HIGH);
        digitalWrite(Red, LOW);
      }
      lcd.print(sign[i]);
      lcd_check(split1[i][0], 3);
      lcd.print(split1[i][0]);
      lcd.print("'");
      lcd_check(split1[i][1], 2);
      lcd.print(split1[i][1]);
      lcd.print("''");
      lcd_check(split1[i][2], 3);
      lcd.print(split1[i][2]);
      lcd.print("'''");
    }
  }
}

void lcd_check(int m, int p) {
  if (p == 2 && m / 10 == 0) {
    lcd.print("0");
    return 0;
  }
  if (p == 3 && m / 10 == 0) {
    lcd.print("00");
    return 0;
  }
  if (p == 3 && m / 100 == 0) {
    lcd.print("0");
    return 0;
  }
}

void sub() {
  if ((a - 1) < num) {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Present Time: ");
    lcd.setCursor(0, 1);
    lcd.print(a);
    lcd.print(". ");
    lcd_check(split[a - 1][0], 3);
    lcd.print(split[a - 1][0]);
    lcd.print("'");
    lcd_check(split[a - 1][1], 2);
    lcd.print(split[a - 1][1]);
    lcd.print("''");
    lcd_check(split[a - 1][2], 3);
    lcd.print(split[a - 1][2]);
    lcd.print("'''");
  }
  if (a > 1) {
    unsigned long int d;
    d = split[a - 2][0] * 60000 + split[a - 2][1] * 1000 + split[a - 2][2];
    lcd.setCursor(0, 2);
    lcd.print("Previous Result: ");
    lcd.setCursor(0, 3);
    lcd.print(a - 1);
    lcd.print(". ");

    if (milli[a - 2] > d) {
      split1[a - 2][0] = (milli[a - 2] - d) / 60000;
      split1[a - 2][1] = ((milli[a - 2] - d) / 1000) % 60;
      split1[a - 2][2] = (milli[a - 2] - d) % 1000;
      sign[a - 2] = '+';
      lcd.print("+");
      digitalWrite(Green, LOW);
      digitalWrite(Red, HIGH);
    }
    else {
      split1[a - 2][0] = (d - milli[a - 2]) / 60000;
      split1[a - 2][1] = ((d - milli[a - 2]) / 1000) % 60;
      split1[a - 2][2] = (d - milli[a - 2]) % 1000;
      sign[a - 2] = '-';
      lcd.print("-");
      digitalWrite(Green, HIGH);
      digitalWrite(Red, LOW);
    }
    lcd_check(split1[a - 2][0], 3);
    lcd.print(split1[a - 2][0]);
    lcd.print("'");
    lcd_check(split1[a - 2][1], 2);
    lcd.print(split1[a - 2][1]);
    lcd.print("''");
    lcd_check(split1[a - 2][2], 3);
    lcd.print(split1[a - 2][2]);
    lcd.print("'''");
  }
  button_press = false;
  if (a == (num + 1)) {
    detachInterrupt(digitalPinToInterrupt(2));
    Complete = true;
  }
  while (digitalRead(button) == 0);
  delay(10);
}

void Split_Number()
{
  while (1)
  {
    customKey = customKeypad.getKey();
    delay(100);
    if (customKey <= 57 && customKey != '*' && customKey != '#' && customKey)
    {
      num = num * 10 + customKey - 48;
      lcd.print(customKey);
      Key_Enter = true;
    }
    if (customKey == 'C' || num > 10)
    {
      num = 0;
      lcd.setCursor(0, 1);
      lcd.print("                ");
      lcd.setCursor(0, 1);
      Key_Enter = false;
    }
    if (customKey == 'D' && customKey && num != 0 && Key_Enter)
      break;
  }
}

void Split_Time() {
  int k = 1;
  for (int i = 0; i < num; i++) {
    int j = 0;
    lcd.setCursor(0, k);
    lcd.print(i + 1);
    lcd.print(". ");
    Key_Enter = false;
    while (1) {
      customKey = customKeypad.getKey();

      if (customKey <= 57 && customKey >= 48 && customKey && j < 3) {
        split[i][j] = split[i][j] * 10 + customKey - 48;
        if (split[i][1] >= 60) {
          split[i][1] = 0;
          lcd.setCursor(3, k);
          lcd.print("             ");
          lcd.setCursor(3, k);
          lcd.print(split[i][0]);
          lcd.print("'");
          Key_Enter = false;
        }
        else if (split[i][2] >= 1000) {
          split[i][2] = 0;
          lcd.setCursor(3, k);
          lcd.print("             ");
          lcd.setCursor(3, k);
          lcd.print(split[i][0]);
          lcd.print("'");
          lcd.print(split[i][1]);
          lcd.print("''");
          Key_Enter = false;
        }
        else {
          lcd.print(customKey);
          Key_Enter = true;
        }
      }

      if (customKey == 'C') {
        j = 0;
        split[i][0] = 0;
        split[i][1] = 0;
        split[i][2] = 0;
        lcd.setCursor(3, k);
        lcd.print("             ");
        lcd.setCursor(3, k);
        Key_Enter = false;
      }

      if (customKey == '*' && j < 3 && Key_Enter) {
        j++;
        for (int l = 0; l < j; l++)
          lcd.print("'");
        Key_Enter = false;
      }
      if (i > 0 && ((split[i][0] < split[i - 1][0] && j == 1) || (split[i][0] == split[i - 1][0] && split[i][1] < split[i - 1][1] && j == 2) || (split[i][0] == split[i - 1][0] && split[i][1] == split[i - 1][1] && split[i][2] < split[i - 1][2] && j == 3))) {
        j = 0;
        j = 0;
        split[i][0] = 0;
        split[i][1] = 0;
        split[i][2] = 0;
        lcd.setCursor(3, k);
        lcd.print("             ");
        lcd.setCursor(3, k);
        Key_Enter = false;
      }
      if (customKey == 'D' && customKey && j == 3) {
        if (k == 3) {
          for (k = 1; k <= 3; k++) {
            lcd.setCursor(0, k);
            lcd.print("                   ");
          }
          k = 0;
        }
        k++;
        break;
      }
    }
  }
}
[/code]

Welcome to the forum.

Which Arduino board do you use ?
I hope that you did not pay 100 dollars/euros or more. It is the kind of code that I see at instructables.com
A good programmer writes code with the focus on the overall structure of code, the explanation, why a line of code is like that.

I'm sorry, but your 'error' variable makes it impossible to fix the sketch.
Can you show the original sketch ?
How can I see the schematic ?

The PCB is a custom unit he supplied to do this not a typical Arduino board. I do not have the original sketch only the one he sent me to correct the problem. I have some PCB boards with the original sketch on them but not knowledgeable to be able to get the sketch out. I was able to upload the sketch I included to one of the boards to test.
I attempted to attach a file (DS3231)to the post which has the schematic and it opens using EASYEDA free program. If there is a better way to include that let me know.

You could try posting on the Jobs and Paid Consultancy forum section, but that is simply awful code. No comments, dicey array indexing, variables with meaningless names, etc. Few people would want to take on the task of figuring out what it does.

There must be other stopwatch code on the web, so look for better examples.

The error is different for each test

This sounds like problem with array indexing: violating array bounds and picking up or altering random contents of memory. And that is precisely where the code is exceptionally weak and undocumented.

Edit: I just noticed the code uses an interrupt routine, but the shared variables are not declared volatile as required, or protected from corruption by the interrupt. The code is guaranteed to fail.

Thank you for looking at this and offering your knowledge.
I will post as you suggested and if someone can start over from scratch that would be fine. Not necessary to work with htis sketch or board if easier to start over.

Okay, I can open it online at EasyEDA without logging in.

It is not possible to get a readable sketch out of a Arduino board.
Are you sure that you don't have a sketch without the 'error' variable ?

There is this in the sketch: #include "DS3231.h"
Can you show that file ?

Do you know the saying: "When in danger or in doubt, run in circles, scream and shout".
The schematic shows two problems: decoupling capacitors are missing (very bad) and AREF is connected to 5V (wrong).

Is the button used for the timing ? but a button bounces.

but a button bounces.

This looks like an attempt to debounce, but there are other serious problems.

  if (digitalRead(button) == 0) {
    delay(20);
    if (digitalRead(button) == 0) {

He sent the PCB boards with the original sketch already loaded and it had a similar problem as i am having now. The sketch I have is the one he sent to correct the problem with the boards sent originally. There was no sketch I could find in the DS3231.h file he sent it had the schematic and there was a .ino file with the new sketch.
There is a physical button on the PCB but I use a remote button attached to the board. The button is used to begin the race and for each split time.

The circuit in the Wokwi simulator: TimerWithError.ino - Wokwi Arduino and ESP32 Simulator
It has still your sketch and the DS3231.h file is missing.

To debounce a button, the button is delayed by about 20 ms. I suppose that as soon as a LOW level seen, that is accepted as valid, and only a debounce is needed when releasing the button.
What is the minimal time that you need to measure with two button presses ?

I think I can make the changes suggested on the button.
It is more than several seconds between pushing the button.

I do not understand the comment about the Wokwi Simulator but i went back to the e mail where he sent the schematic and the sketch and there is no sketch in the zip file that was labeled Ds3231

I misunderstood. I see the attempt to debounce is in the current sketch.

Can you click on that link to the Wokwi simulator ?
Now everyone can try their code in the simulator.
It does not work with the missing file though.

Yes now I followed the link and see it is missing te DS3231.h file.
It appears DS3231 is a clock,

I'm still working on it: TimerWithError.ino - Wokwi Arduino and ESP32 Simulator
What are the split numbers and split times that I have to enter, and how do I enter them ?

Split times are the time when it is expected i will pass a landmark.
First it will ask "enter split number" and with the keyboard you enter the number of times in the race you will check the time. I suggest 3 for a test. After enter 3 from keyboard then push "D" on keyboard. Enter 0 and then press * for zero minutes, 10 and press * for ten seconds, 000 and * for zero milliseconds, then press D to enter the first split time. Enter two more times for the three split times, maybe 20 seconds and 30 seconds. then it will show the entered times and you press D.
From there on you press the button to start and at each split time. It will display the time more or less than the expected split time. A and B on the keyboard allow you to scroll the reults of each split time after the race.
It shows me if I am early or late and by how much at each split time based on me pushing the button.

Got it, thank you.

A stopwatch and calculating the difference for a positive or negative value is always a problem. In my millis_count_down.ino I tried to fix that at line 60 and 61. It is not pretty.

Displaying a value in a certain way is not interesting for the sketch, yet a large part of the sketch deals with splitting the values. That should be a single function to display the value in a certain way.

I think I see what you did in your timer that is similar to what i need to do. Yours i think took the current time and subtracted the previous time and maybe tried to see if positive or negative.
Sounds like what I have is a mess in a lot of areas. The display info works but the less than one second error is not something I can use. I need to be closer than that to win the contests.

For those asking about the .h file, try
Timer library
It's in the Arduino reference section

I made a proof of concept with only millis().
Entering the times is not implemented. I always try to avoid a menu and entering values with a LCD display, because I don't like it. A Nextion display is a better way to enter data.

// For: https://forum.arduino.cc/t/timer-has-random-error/1037211/
//
// Wokwi project with sketch from user: https://wokwi.com/projects/344260957282763346
//
// 2 Oct 2022, Version 1, by Koepel, Public Domain.
//   Now purely based on millis().
//   Removed the RTC.
//   NOT READY, JUST A PROOF OF CONCEPT
//
// To do:
//   1. (optional)
//     The accuracy can be improved by using a interrupt
//     for the button. In case I2C or something slow is used.
//     Not needed at this moment.
//
//   2.
//     Entering the number of times is not needed.
//     The user can just stop the input by pressing 'D' twice.
//  
//   3.
//     Make a Finite State Machine
//
//   4.
//     Use sprintf() in showTimes().
//
//   5.
//     Add custom characters. For example a running person.
//
//

#include <LiquidCrystal.h>
#include <Keypad.h>

const int redLedPin = A1;
const int greenLedPin = A0;
const int buttonPin = 3;             // pin 3 is also a interrupt input

const int rs = 13, en = 12, d4 = 11, d5 = 10, d6 = 9, d7 = 8;
LiquidCrystal lcd( rs, en, d4, d5, d6, d7);

const byte ROWS = 4;                 // four rows
const byte COLS = 4;                 // four columns
char hexaKeys[ROWS][COLS] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};
byte rowPins[ROWS] = { A2, A3, 0, 1}; // connect to the row pinouts of the keypad
byte colPins[COLS] = { 4, 7, 6, 5};   // connect to the column pinouts of the keypad

Keypad customKeypad = Keypad(makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS);

// The 'startMillis' is the millis value of time zero.
// The tables contain the times in milliseconds since start as a 'long'.
// The index of an array starts at zero, but that is shown as "time 1."
unsigned long startMillis;
#define MAX_TIMES 20                 // maximum number of times
long measuredTimes[MAX_TIMES];       // the table with measured times
int index;                           // current index in the table when running

// PRE-FILLED WITH VALUES FOR TESTING
long setTimes[MAX_TIMES] =           // the table with the set times
{
  10000L, 20000L, 30000L,
};
int nTimes = 3;                      // The number of times determined runtime

unsigned long previousMillisDisplay;
const unsigned long intervalDisplay = 200UL;

void setup() 
{
  pinMode( redLedPin, OUTPUT);       // set to output and LOW
  pinMode( greenLedPin, OUTPUT);     // set to output and LOW
  pinMode( buttonPin, INPUT_PULLUP);

  lcd.begin( 20, 4);
  lcd.clear();

  lcd.setCursor( 6, 1);
  lcd.print( F("Welcome"));
  delay( 5000);

  Enter_Times();

  lcd.clear();
  lcd.setCursor( 0, 0);
  lcd.print( F("The first press of"));
  lcd.setCursor(0, 1);
  lcd.print( F("the button starts"));
  lcd.setCursor( 0, 2);
  lcd.print( F("the race ..."));

  while( !isButtonPressed( buttonPin));        // wait for the button to be pressed
  startMillis = millis();                      // timestamp for starting point

  lcd.clear();
  lcd.setCursor( 5, 2);
  lcd.print( F("Running !"));
  lcd.setCursor( 0, 3);
  lcd.print( F("press at 10,20,30 s"));

}

void loop() 
{
  // The millis values should always be 'unsigned long' to always work,
  // even during a rollover of millis().
  // Once the elapsed millis value is calculated, it can be converted to 'long'.
  // It is more convenient for the sketch to use 'long'. 
  unsigned long currentMillis = millis();
  unsigned long elapsedMillis = currentMillis - startMillis;  // rollover-proof
  long timeSinceStart = (long) elapsedMillis;  // a 'long' to be used in the sketch

  if( isButtonPressed( buttonPin))
  {
    // Fill the array with the current time.
    measuredTimes[index] = timeSinceStart;
    
    // Calculate the difference and show it on the display.
    // Line 1: The set time
    // Line 2: The measured time
    // Line 3: The difference
    showTime( 1, index, setTimes[index], false);
    showTime( 2, index, measuredTimes[index], false);
    showTime( 3, index, measuredTimes[index] - setTimes[index], true);

    // Go to the next position in the array.
    index++;
    if( index >= MAX_TIMES)
    {
      index = MAX_TIMES - 1;      // hang around at the last position
    }
  }

  // A 7-segment display is fast, but the liquid crystals of a LCD display is slow.
  // Using a millis-timer to update the current time.
  // This code reduces the accuracy for the time to capture the button.
  if( currentMillis - previousMillisDisplay >= intervalDisplay)
  {
    previousMillisDisplay = currentMillis;
    showTime( 0, -1, (long) elapsedMillis, false);   // -1 to avoid showing index
  }
}

void Enter_Times() 
{
  lcd.setCursor( 0, 0);
  lcd.print( F("Enter the times"));
  lcd.setCursor( 0, 1);
  lcd.print( F("* for s and ms"));
  lcd.setCursor( 0, 2);
  lcd.print( F("C for backspace"));
  lcd.setCursor( 0, 3);
  lcd.print( F("D for next and ready"));
  delay( 5000);

  lcd.clear();
  lcd.setCursor( 0, 0);
  lcd.print( F("Sorry"));
  lcd.setCursor( 0, 1);
  lcd.print( F("not implemented"));
  delay( 5000);
}


// showTime
// --------
//
// Show the times on a line of the display, with optional sign
// 
// Parameters:
//     row  : 0 = top row, 3 = bottom row
//     indx : index, starting from zero. Zero is displayed as time "1".
//            A value of -1 for not showing the index and not the dot.
//     t    : the time in milliseconds as a 'long'
//     sign : true to show '+' or '-' sign. false for no sign.
//
// Format  : nn. +mm:ss.xxx
// Examples:  1.  00:10.000
//           12. -05:42.998
//
// A sprintf() should make this function better
//
void showTime( int row, int indx, long t, bool sign)
{
  // Split the variable into minutes, seconds, milliseconds.
  // The index starts at 0, that is time "1", and so on.
  lcd.setCursor( 0, row);
  int n = 0;                  // keep track of the number of characters written

  if( indx == -1)             // not showing the index ?
  {
    n += lcd.print( F("    "));
  }
  else
  {
    int number = indx + 1;      // show index zero as time "1" and so on.
    if( number < 10)
      n += lcd.print( F(" "));
    n += lcd.print( number);
    n += lcd.print( F(". "));
  }

  if( sign)
  {
    if( t < 0)
    {
      n += lcd.print( F("-"));
    }
    else
    {
      n += lcd.print( F("+"));
    }
  }
  else
  {
    n += lcd.print( F(" "));
  }

  if( t < 0)
    t = -t;                  // make 't' always positive
  int minutes = t / ( 60L * 1000L );
  int seconds = ( t / 1000L ) % 60L;
  int milliseconds = t % 1000L;

  // safety check for maximum
  if( minutes > 99)
  {
    n += lcd.print( F("##:##.###"));
  }
  else
  {
    if( minutes < 10)
      n += lcd.print( F("0"));
    n += lcd.print( minutes);    
    n += lcd.print( F(":"));
    if( seconds < 10)
      n += lcd.print( F("0"));
    n += lcd.print( seconds);
    n += lcd.print( F("."));
    if( milliseconds < 10)
      n += lcd.print( F("0"));
    if( milliseconds < 100)
      n += lcd.print( F("0"));
    n += lcd.print( milliseconds);
  }

  // clear the rest of the line.
  for( int i=0; i<(20-n); i++)
  {
    lcd.print( F(" "));
  }
}


// isButtonPressed (half-a-debounce)
// ---------------------------------
// 
// For maximum accuracy, the debouncing is minimized when the button is pressed.
// Two samples are taken, to filter out a glitch.
// If both samples are LOW, then the button is assumed to be pressed.
// The button is accepted as released with it is steady HIGH for at least 50 ms.

bool debounceMode = false;               // for debouncing the release of the button
unsigned long debounceMillis;

bool isButtonPressed( int pin)
{
  // Take two samples, to filter out a glitch.
  int sample1 = digitalRead( pin);
  int sample2 = digitalRead( pin);

  if( !debounceMode)
  {
    // check if the button is pressed
    if( sample1 == LOW and sample2 == LOW)
    {
      // As soon as the button is accepted as pressed,
      // the debounce mode for releasing is started.
      debounceMode = true;
      debounceMillis = millis();
      return( true);                   // immediate return with true (button pressed)
    }
  }
  else
  {
    // in debounce mode
    if( sample1 == LOW or sample2 == LOW)
    {
      // Still something low, not a valid high yet.
      // Restart the timer
      debounceMillis = millis();
    }
    else
    {
      if( millis() - debounceMillis >= 50)
      {
        // Steady HIGH for at least 50ms, that is accepted.
        debounceMode = false;
      }
    }
  }
  return( false);
}

You might recognize some things:

  1. The times that are set and the times that are measured are in a table.
  2. The part that is not interesting for the structure of the code is displaying the times. The function showTime() takes care of that.
  3. I calculated the 'elapsedMillis'. That is the milliseconds since start. Then I convert it to a 'long' to be used in the sketch.

The sketch in Wokwi:

Where do you go from here ?
Can you check on your hardware if it works ? Maybe someone else can make the function to enter the times. You should also give a complete specification for the sketch, what it should do. I did this just for fun, I don't think that I will do more with it :slightly_frowning_face: