4x stopwatches on 1 LCD, with start/pause buttons

I'm attempting to create 4x stopwatches running and displaying on 1 LCD. (see example - normal lcd.print, not actual stopwatches)
This will be run of an Uno R3.

Scenario is: 4 teams have to "capture" and "hold" timer by pressing colour-coded button.
If RED pushed, RED stopwatch runs, other 3 paused.
Same for BLUE, YELLOW and GREEN.

There has to be a 5th button, RESET that will clear all 4 stopwatches

I have very little experience with Arduino and programming, so any assistance would be greatly appreciated

do you have anything written so far (at least the sketch you used to run the display)

The sketch I'm running is just a basic lcd.print, using the liquidcrystal_i2c library.

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

LiquidCrystal_I2C lcd(0x27, 20, 4);

void setup() {
}

void loop() {
lcd.init();
lcd.backlight();
lcd.setCursor(0, 0);
lcd.print("BLUE : 00:21:16");
lcd.setCursor(0, 1);
lcd.print("RED : 01:02:39");
lcd.setCursor(0, 2);
lcd.print("GREEN : 00:03:11");
lcd.setCursor(0, 3);
lcd.print("YELLOW: 00:00:00");
}

ok at the top of this forum look at learning

you need to read up on INPUT_PULLUP, switch/case

you also need to play with the example in the ide call blink with out delay (its under file-examples-digital)

I have knocked up a unfinished code that will give you a good base to start with. Its using serial print.

once you truly understand the code write in the missing code (I only did red button) then convert the code to the lcd

if you learn what I have asked you im more than willing to help you. If you want to skip the learning part then I will not waste any more time. Ask question on anything you do not understand

const byte button1 = 10; //red
const byte button2 = 9;
const byte button3 = 8;
const byte button4 = 7;
const byte button5 = 6;
const unsigned long interval = 100;
unsigned long previousMillis = 0;
byte redTimer = 0;
byte blueTimer = 0;
byte yellowTimer = 0;
byte greenTimer = 0;
byte redseconds = 0;
byte redminutes = 0;

bool red = 0;
bool blue = 0;
bool yellow = 0;
bool green = 0;
bool reset = 0;
byte timer = 0;


void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  pinMode(button1, INPUT_PULLUP);
  pinMode(button2, INPUT_PULLUP);
  pinMode(button3, INPUT_PULLUP);
  pinMode(button4, INPUT_PULLUP);
  pinMode(button5, INPUT_PULLUP);
}

void loop() {
  // put your main code here, to run repeatedly:
  unsigned long currentMillis = millis();
  red = digitalRead(button1);
  blue = digitalRead(button2);
  yellow = digitalRead(button3);
  green = digitalRead(button4);
  reset = digitalRead(button5);

  timer = 0;
  if (red == LOW) {
    timer = 1;
  }
  if (blue == LOW) {
    timer = 2;
  }
  if (yellow == LOW) {
    timer = 3;
  }
  if (green == LOW) {
    timer = 4;
  }
  if (reset == LOW) {
    timer = 5;
  }

  if (currentMillis - previousMillis >= interval) {//every tenth second
    switch (timer) {
      case 0://no timing
        break;
        
      case 1://red timer
        redTimer++;//add one tenth to counter
        if (redTimer == 10) {//10 tenths equal one second
          redseconds++;//add one second
          redTimer = 0;//reset counter
        }
        if (redseconds == 60) {//60 secs equal one minute
          redseconds = 0;// reset counter
          redminutes++;//add one minute
        }
        Serial.print(redminutes);//show minutes
        Serial.print(":");
        if (redseconds < 10) {//add extra 0 to stop line shift
          Serial.print("0");
        }
        Serial.print(redseconds);//show seconds
        Serial.print("-");
        Serial.println(redTimer);//show tenths
        break;
        
      case 2://blue timer
        blueTimer++;
        break;
        
      case 3://yellow timer
        yellowTimer++;
        break;
        
      case 4://green timer
        greenTimer++;
        break;
        
      case 5://reset timers
        redTimer = 0;
        redseconds = 0;
        redminutes = 0;
        blueTimer = 0;
        yellowTimer = 0;
        greenTimer = 0;
        break;

    }
    previousMillis = currentMillis;
  }
}

what happens in you game if 2 people press buttons at the same time ?

does only one count, do they both count or does nothing count ?

you might want to check the stopwatch class - Arduino Playground - StopWatchClass

gpop1 - I appreciate your assistance!

As for 2 players pressing a button at the same time... well, the timer would be "captured" by a player/team and "held" so only one button gets pushed.

So far, got all 5 buttons to work, plus converted output to LCD.
Been reading up on pullup to debounce.

Now I just need to figure out how to make the buttons work on momentary contact and not constant contact.
I surmiseI'd have to use a variable for each button, that gets written as "LOW" when button is momentarily pushed and stays that way until another button is pushed.

Here's the sketch so far...

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

LiquidCrystal_I2C lcd(0x27, 20, 4);

const byte button1 = 10; //red
const byte button2 = 9; //blue
const byte button3 = 8; //yellow
const byte button4 = 7; //green
const byte button5 = 6; //reset
const unsigned long interval = 100;
unsigned long previousMillis = 0;
byte redTimer = 0;
byte blueTimer = 0;
byte yellowTimer = 0;
byte greenTimer = 0;

byte redseconds = 0;
byte redminutes = 0;
byte redhours = 0;

byte blueseconds = 0;
byte blueminutes = 0;
byte bluehours = 0;

byte greenseconds = 0;
byte greenminutes = 0;
byte greenhours = 0;

byte yellowseconds = 0;
byte yellowminutes = 0;
byte yellowhours = 0;

bool red = 0;
bool blue = 0;
bool yellow = 0;
bool green = 0;
bool reset = 0;
byte timer = 0;

void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
pinMode(button1, INPUT_PULLUP);
pinMode(button2, INPUT_PULLUP);
pinMode(button3, INPUT_PULLUP);
pinMode(button4, INPUT_PULLUP);
pinMode(button5, INPUT_PULLUP);

lcd.init(); // initialize the lcd and display stopwatches
lcd.backlight();
lcd.setCursor(0, 0);
lcd.print("STARTING GAME TIMER!");
delay(5000);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("RED : 00:00:00");
lcd.setCursor(0, 1);
lcd.print("BLUE : 00:00:00");
lcd.setCursor(0, 2);
lcd.print("YELLOW : 00:00:00");
lcd.setCursor(0, 3);
lcd.print("GREEN : 00:00:00");
}

void loop() {
unsigned long currentMillis = millis();
red = digitalRead(button1);
blue = digitalRead(button2);
yellow = digitalRead(button3);
green = digitalRead(button4);
reset = digitalRead(button5);

timer = 0;
if (red == LOW) {
timer = 1;
}
if (blue == LOW) {
timer = 2;
}
if (yellow == LOW) {
timer = 3;
}
if (green == LOW) {
timer = 4;
}
if (reset == LOW) {
timer = 5;
}

if (currentMillis - previousMillis >= interval) {//every tenth second
switch (timer) {
case 0://no timing
break;

case 1://red timer
redTimer++;//add one tenth to counter
if (redTimer == 10) {//10 tenths equal one second
redseconds++;//add one second
redTimer = 0;//reset counter
}
if (redseconds == 60) {//60 secs equal one minute
redseconds = 0;// reset counter
redminutes++;//add one minute
}
if (redminutes == 60) {//60 minutes equal one hour
redminutes = 0;// reset counter
redhours++;//add one hour
}
lcd.setCursor(0, 0);
lcd.print("RED : ");
//hours
if (redhours < 10) {//add extra 0 to stop line shift
lcd.print("0");
}
lcd.print(redhours);//show minutes
lcd.print(":");
//minutes
if (redminutes < 10) {//add extra 0 to stop line shift
lcd.print("0");
}
lcd.print(redminutes);//show minutes
lcd.print(":");
//seconds
if (redseconds < 10) {//add extra 0 to stop line shift
lcd.print("0");
}
lcd.print(redseconds);//show seconds
break;

case 2://blue timer
blueTimer++;//add one tenth to counter
if (blueTimer == 10) {//10 tenths equal one second
blueseconds++;//add one second
blueTimer = 0;//reset counter
}
if (blueseconds == 60) {//60 secs equal one minute
blueseconds = 0;// reset counter
blueminutes++;//add one minute
}
if (blueminutes == 60) {//60 minutes equal one hour
blueminutes = 0;// reset counter
bluehours++;//add one hour
}
lcd.setCursor(0, 1);
lcd.print("BLUE : ");
//hours
if (bluehours < 10) {//add extra 0 to stop line shift
lcd.print("0");
}
lcd.print(bluehours);//show minutes
lcd.print(":");
//minutes
if (blueminutes < 10) {//add extra 0 to stop line shift
lcd.print("0");
}
lcd.print(blueminutes);//show minutes
lcd.print(":");
//seconds
if (blueseconds < 10) {//add extra 0 to stop line shift
lcd.print("0");
}
lcd.print(blueseconds);//show seconds
break;

case 3://yellow timer
yellowTimer++;//add one tenth to counter
if (yellowTimer == 10) {//10 tenths equal one second
yellowseconds++;//add one second
yellowTimer = 0;//reset counter
}
if (yellowseconds == 60) {//60 secs equal one minute
yellowseconds = 0;// reset counter
yellowminutes++;//add one minute
}
if (yellowminutes == 60) {//60 minutes equal one hour
yellowminutes = 0;// reset counter
yellowhours++;//add one hour
}
lcd.setCursor(0, 2);
lcd.print("YELLOW : ");
//hours
if (yellowhours < 10) {//add extra 0 to stop line shift
lcd.print("0");
}
lcd.print(yellowhours);//show minutes
lcd.print(":");
//minutes
if (yellowminutes < 10) {//add extra 0 to stop line shift
lcd.print("0");
}
lcd.print(yellowminutes);//show minutes
lcd.print(":");
//seconds
if (yellowseconds < 10) {//add extra 0 to stop line shift
lcd.print("0");
}
lcd.print(yellowseconds);//show seconds
break;

case 4://green timer
greenTimer++;//add one tenth to counter
if (greenTimer == 10) {//10 tenths equal one second
greenseconds++;//add one second
greenTimer = 0;//reset counter
}
if (greenseconds == 60) {//60 secs equal one minute
greenseconds = 0;// reset counter
greenminutes++;//add one minute
}
if (greenminutes == 60) {//60 minutes equal one hour
greenminutes = 0;// reset counter
greenhours++;//add one hour
}
lcd.setCursor(0, 3);
lcd.print("GREEN : ");
//hours
if (greenhours < 10) {//add extra 0 to stop line shift
lcd.print("0");
}
lcd.print(greenhours);//show minutes
lcd.print(":");
//minutes
if (greenminutes < 10) {//add extra 0 to stop line shift
lcd.print("0");
}
lcd.print(greenminutes);//show minutes
lcd.print(":");
//seconds
if (greenseconds < 10) {//add extra 0 to stop line shift
lcd.print("0");
}
lcd.print(greenseconds);//show seconds

break;

case 5://reset timers
redTimer = 0;
redseconds = 0;
redminutes = 0;
redhours = 0;
blueTimer = 0;
blueseconds = 0;
blueminutes = 0;
bluehours = 0;
yellowTimer = 0;
yellowseconds = 0;
yellowminutes = 0;
yellowhours = 0;
greenTimer = 0;
greenseconds = 0;
greenminutes = 0;
greenhours = 0;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("RESET!");
delay(500);
lcd.setCursor(0, 0);
lcd.print("RED : 00:00:00");
lcd.setCursor(0, 1);
lcd.print("BLUE : 00:00:00");
lcd.setCursor(0, 2);
lcd.print("YELLOW : 00:00:00");
lcd.setCursor(0, 3);
lcd.print("GREEN : 00:00:00");

break;

}
previousMillis = currentMillis;
}
}

Been reading up on pullup to debounce.

That is not what pullups are for. They are to ensure that the pin is always in a known state and never floating at an uncertain voltage.

if you remove the line timer = 0; that's before the buttons the sketch should stay on the last timer a button was pressed....I think

just add the line back in to the reset switch

still example code as my lcd library doesn't agree with yours so I did not compile

moved a few things around to give you some ideas.

also disabled this line //timer = 0; so once button is pressed timers keep running

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

LiquidCrystal_I2C lcd(0x27, 20, 4);

const byte button1 = 10; //red
const byte button2 = 9; //blue
const byte button3 = 8; //yellow
const byte button4 = 7; //green
const byte button5 = 6; //reset
const unsigned long interval = 100;
unsigned long previousMillis = 0;
byte redTimer = 0;
byte blueTimer = 0;
byte yellowTimer = 0;
byte greenTimer = 0;

byte redseconds = 0;
byte redminutes = 0;
byte redhours = 0;

byte blueseconds = 0;
byte blueminutes = 0;
byte bluehours = 0;

byte greenseconds = 0;
byte greenminutes = 0;
byte greenhours = 0;

byte yellowseconds = 0;
byte yellowminutes = 0;
byte yellowhours = 0;

bool red = 0;
bool blue = 0;
bool yellow = 0;
bool green = 0;
bool reset = 0;
byte timer = 0;


void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  pinMode(button1, INPUT_PULLUP);
  pinMode(button2, INPUT_PULLUP);
  pinMode(button3, INPUT_PULLUP);
  pinMode(button4, INPUT_PULLUP);
  pinMode(button5, INPUT_PULLUP);

  lcd.init();                      // initialize the lcd and display stopwatches
  lcd.backlight();
  lcd.setCursor(0, 0);
  lcd.print("STARTING GAME TIMER!");
  delay(5000);
  resetdisplay();
}

void loop() {
  unsigned long currentMillis = millis();
  red = digitalRead(button1);
  blue = digitalRead(button2);
  yellow = digitalRead(button3);
  green = digitalRead(button4);
  reset = digitalRead(button5);

  //timer = 0;
  if (red == LOW) {
    timer = 1;
  }
  if (blue == LOW) {
    timer = 2;
  }
  if (yellow == LOW) {
    timer = 3;
  }
  if (green == LOW) {
    timer = 4;
  }
  if (reset == LOW) {
    timer = 5;
  }

  if (currentMillis - previousMillis >= interval) {//every tenth second
    switch (timer) {
      case 0://no timing
        break;

      case 1://red timer
        redTimer++;//add one tenth to counter
        reddisplay();
        break;

      case 2://blue timer
        blueTimer++;//add one tenth to counter
        bluedisplay();
        break;

      case 3://yellow timer
        yellowTimer++;//add one tenth to counter
        yellowdisplay();
        break;

      case 4://green timer
        greenTimer++;//add one tenth to counter
        greendisplay();

        break;

      case 5://reset timers
        timer = 0;
        redTimer = 0;
        redseconds = 0;
        redminutes = 0;
        redhours = 0;
        blueTimer = 0;
        blueseconds = 0;
        blueminutes = 0;
        bluehours = 0;
        yellowTimer = 0;
        yellowseconds = 0;
        yellowminutes = 0;
        yellowhours = 0;
        greenTimer = 0;
        greenseconds = 0;
        greenminutes = 0;
        greenhours = 0;
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("RESET!");
        delay(500);
        resetdisplay();
        break;
    }
    previousMillis = currentMillis;
  }
}

void reddisplay() {
  if (redTimer == 10) {//10 tenths equal one second
    redseconds++;//add one second
    redTimer = 0;//reset counter
  }
  if (redseconds == 60) {//60 secs equal one minute
    redseconds = 0;// reset counter
    redminutes++;//add one minute
  }
  if (redminutes == 60) {//60 minutes equal one hour
    redminutes = 0;// reset counter
    redhours++;//add one hour
  }
  lcd.setCursor(0, 0);
  lcd.print("RED    : ");
  //hours
  addZero(redhours);
  lcd.print(redhours);//show minutes
  lcd.print(":");
  //minutes
  addZero(redminutes);
  lcd.print(redminutes);//show minutes
  lcd.print(":");
  //seconds
  addZero(redseconds);
  lcd.print(redseconds);//show seconds
}

void bluedisplay() {
  if (blueTimer == 10) {//10 tenths equal one second
    blueseconds++;//add one second
    blueTimer = 0;//reset counter
  }
  if (blueseconds == 60) {//60 secs equal one minute
    blueseconds = 0;// reset counter
    blueminutes++;//add one minute
  }
  if (blueminutes == 60) {//60 minutes equal one hour
    blueminutes = 0;// reset counter
    bluehours++;//add one hour
  }
  lcd.setCursor(0, 1);
  lcd.print("BLUE   : ");
  //hours
  addZero(bluehours);
  lcd.print(bluehours);//show minutes
  lcd.print(":");
  //minutes
  addZero(blueminutes);
  lcd.print(blueminutes);//show minutes
  lcd.print(":");
  //seconds
  addZero(blueseconds);
  lcd.print(blueseconds);//show seconds
}
void yellowdisplay() {
  if (yellowTimer == 10) {//10 tenths equal one second
    yellowseconds++;//add one second
    yellowTimer = 0;//reset counter
  }
  if (yellowseconds == 60) {//60 secs equal one minute
    yellowseconds = 0;// reset counter
    yellowminutes++;//add one minute
  }
  if (yellowminutes == 60) {//60 minutes equal one hour
    yellowminutes = 0;// reset counter
    yellowhours++;//add one hour
  }
  lcd.setCursor(0, 2);
  lcd.print("YELLOW : ");
  //hours
  addZero(yellowhours);
  lcd.print(yellowhours);//show minutes
  lcd.print(":");
  //minutes
  addZero(yellowminutes);
  lcd.print(yellowminutes);//show minutes
  lcd.print(":");
  //seconds
  addZero(yellowseconds);
  lcd.print(yellowseconds);//show seconds
}
void greendisplay() {
  if (greenTimer == 10) {//10 tenths equal one second
    greenseconds++;//add one second
    greenTimer = 0;//reset counter
  }
  if (greenseconds == 60) {//60 secs equal one minute
    greenseconds = 0;// reset counter
    greenminutes++;//add one minute
  }
  if (greenminutes == 60) {//60 minutes equal one hour
    greenminutes = 0;// reset counter
    greenhours++;//add one hour
  }
  lcd.setCursor(0, 3);
  lcd.print("GREEN  : ");
  //hours
  addZero(greenhours);
  lcd.print(greenhours);//show minutes
  lcd.print(":");
  //minutes
  addZero(greenminutes);
  lcd.print(greenminutes);//show minutes
  lcd.print(":");
  //seconds
  addZero(greenseconds);
  lcd.print(greenseconds);//show seconds
}
void resetdisplay() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("RED    : 00:00:00");
  lcd.setCursor(0, 1);
  lcd.print("BLUE   : 00:00:00");
  lcd.setCursor(0, 2);
  lcd.print("YELLOW : 00:00:00");
  lcd.setCursor(0, 3);
  lcd.print("GREEN  : 00:00:00");
}

void addZero(int A) {
  if (A < 10) {//add extra 0 to stop line shift
    lcd.print("0");
  }
}

theres still repeating style code that could be handled by a function (not sure I would complicate the code to handle it)

gpop1, thank you very much!
Your assistance has been invaluable. You've helped me from nowhere to finished project in 2 days!

It's working as advertised, just by removing timer = 0;

Keeping the sketch as is for now while I clue up on C/C++ and Arduino.
Your last sketch is quite interesting, writing seperate functions and then calling the functions in the loop.
I'm not there yet.. so if it isn't broken, don't fix it... :slight_smile:

I was on vacation last week and read this thread. I didn't like where the code ended up going and thought it would benefit from the use of arrays. So I wrote out a quick sketch that uses arrays and revived the thread. Hopefully someone can get something from it.

// four momentary switches control four stopwathces
// when a switch closes that timer only starts running
// if a timer restarts it will continue where it left off
// all times accumulate until a reset switch closes.

const byte SWITCH_PIN[] = {2, 3, 4, 5, 6}; // reset switch must be first element
const int SWITCH_COUNT = sizeof(SWITCH_PIN) / sizeof(SWITCH_PIN[0]);
const unsigned long DEBOUNCE_MILLIS = 20UL;
const unsigned long DELAY_SECONDS = 1000UL * 5UL;
const long BAUD_RATE = 115200;

int switchState[SWITCH_COUNT];
boolean debounceFlag[SWITCH_COUNT];
unsigned long debounceTimer[SWITCH_COUNT];
unsigned long timeAccumulator[SWITCH_COUNT];
unsigned long currentMillis;
unsigned long lastMillis;

byte n; // to use as index to arrays
byte activeAccumulator; // index to active accumulator

void setup()
{
  Serial.begin(BAUD_RATE);
  for (n = 0; n < SWITCH_COUNT; n++)
  {
    pinMode(SWITCH_PIN[n], INPUT_PULLUP);
    switchState[n] = digitalRead(SWITCH_PIN[n]);
  }
  currentMillis = millis();
}

void loop()
{
  lastMillis = currentMillis; // save last loop time
  currentMillis = millis(); // get current loop time
  n = (n + 1) % SWITCH_COUNT; // advance n check for rollover
  int pinSignal = digitalRead(SWITCH_PIN[n]); // Read signal of switch n

  // test for state change. Start debouncing
  if (switchState[n] != pinSignal)
  {
    switchState[n] = pinSignal; // record new switch state
    debounceFlag[n] = true;  
    debounceTimer[n] = currentMillis; // start debounce timer
  }

  // check if debouncing cleared
  if (debounceFlag[n] && currentMillis - debounceTimer[n] >= DEBOUNCE_MILLIS)
  {
    debounceFlag[n] = false; 
    // check for button press
    if (switchState[n] == LOW)
    {
      activeAccumulator = n; // set pointer to active accumulator
    }
  }

  // update active accumulator
  if (activeAccumulator) // test for non-zero
  {
    timeAccumulator[activeAccumulator] += currentMillis - lastMillis;
  }
  else // activeAccumulator == 0 the reset switch
  {
    for (byte i = 0; i < SWITCH_COUNT; i++)
    {
      timeAccumulator[n] = 0;
    }
  }

  // at this point the accumulators could be displayed
  displayAccumulators(currentMillis);
}

void displayAccumulators(unsigned long loopMillis)
{
  static unsigned long lastDisplayMillis;
  if (loopMillis - lastDisplayMillis >= DELAY_SECONDS)
  {
    lastDisplayMillis = loopMillis;
    Serial.println(F("Current Times in Seconds"));
    for (int i = 1; i < SWITCH_COUNT; i++)
    {
      Serial.print(F("Timer:"));
      Serial.print(i);
      Serial.print(' ');
      Serial.println(timeAccumulator[i] / 1000.0);
    }
  }
}

With this code you could add or remove switches for timers simply by changing the SWITCH_PIN array initialization. It is based on momentary switches with a press starting a timer. Switch can be released and timer continues to run. I've tested this on an UNO.