Two Step Timer Help

I am making a game timer that has two stages.
Stage 1 a countdown until the game begins, this can be 1-3 minutes for example so people can get ready.
Then a long and loud from a buzzer (people will be up to 100 feet away) and then the game timer begins.
At the end the buzzer sounds again for 5 seconds or so loudly and then stops.

This is the code I have made but I am having trouble with making it presentable as it sometimes displays elements like the prep time after the time has finished.

In addition, in the future I want to link a single channel relay to attach a siren, but not sure how the code the relay into it as I never really used one before.

Thank you for your time.

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

#define BUTTON_START_PIN 2
#define BUTTON_SET_UP_PIN 3
#define BUTTON_SET_DOWN_PIN 4
#define BUZZER_PIN 7

LiquidCrystal_I2C lcd(0x27, 16, 2);

unsigned long previousMillis = 0;
const long interval = 1000; // 1 second interval
bool lastTenSeconds = false;

int prepCountdownTimer = 0;
int settingPrepTime = 0; // initial setting time in seconds for prep timer
int gameCountdownTimer = 0;
int settingGameTime = 0; // initial setting time in seconds for game timer
bool settingPrep = false;
bool settingGame = false;
bool prepTimeFinished = false;
bool gameStarted = false;

void setup() {
  pinMode(BUTTON_START_PIN, INPUT_PULLUP);
  pinMode(BUTTON_SET_UP_PIN, INPUT_PULLUP);
  pinMode(BUTTON_SET_DOWN_PIN, INPUT_PULLUP);
  pinMode(BUZZER_PIN, OUTPUT);

  lcd.init();
  lcd.backlight();
  lcd.setCursor(0, 0);
  lcd.print("Prep Time: 0:00");
}

void loop() {
  unsigned long currentMillis = millis();

  if (!settingPrep && !settingGame) {
    handleSettingTime(settingPrepTime, prepCountdownTimer, "Prep Time: ");
  } else if (settingPrep && !settingGame) {
    handleSettingTime(settingGameTime, gameCountdownTimer, "Game Time: ");
  } else if (settingPrep && settingGame) {
    if (!prepTimeFinished) {
      handlePrepCountdown();
    } else {
      handleGameCountdown();
    }
  }
}

void handleSettingTime(int &settingTime, int &countdown, String title) {
  if (digitalRead(BUTTON_SET_UP_PIN) == LOW) {
    settingTime += 60; // Increase the time by one minute
    if (!settingGame) {
      displaySettingTime(settingTime, 0, title);
    } else {
      displaySettingTime(settingTime, 1, title);
    }
    delay(200); // Debounce delay
  }

  if (digitalRead(BUTTON_SET_DOWN_PIN) == LOW && settingTime >= 60) {
    settingTime -= 60; // Decrease the time by one minute
    if (!settingGame) {
      displaySettingTime(settingTime, 0, title);
    } else {
      displaySettingTime(settingTime, 1, title);
    }
    delay(200); // Debounce delay
  }

  if (digitalRead(BUTTON_START_PIN) == LOW && settingTime > 0) {
    if (!settingPrep) {
      settingPrep = true;
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("Prep Time: ");
      displaySettingTime(settingPrepTime, 0, "Game Time: ");
    } else {
      settingGame = true;
      lcd.clear();
      lcd.print("Starting");
      delay(2000);
      lcd.clear();
      prepCountdownTimer = settingPrepTime;
    }
  }
}

void handlePrepCountdown() {
  handleCountdownHelper(prepCountdownTimer, "Prep Time: ");

  if (prepCountdownTimer == 0) {
    prepTimeFinished = true;
    longBuzzer();
    lcd.clear();
    lcd.print("Game Started!");
    delay(2000);
    lcd.clear();
    lcd.setCursor(0, 1);
    lcd.print("Game Time: ");
    displaySettingTime(settingGameTime, 1, "Game Time: ");
    gameCountdownTimer = settingGameTime;
  }
}

void handleGameCountdown() {
  handleCountdownHelper(gameCountdownTimer, "Game Time: ");

  if (gameCountdownTimer == 0) {
    longBuzzer();
    lcd.clear();
    lcd.print("GAME OVER");
    while (true) {
      // Game over, do nothing
    }
  }
}

void handleCountdownHelper(int &countdown, String title) {
  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    if (countdown > 0) {
      if (countdown <= 10) {
        lastTenSeconds = true;
        digitalWrite(BUZZER_PIN, HIGH);
        delay(1000);
        digitalWrite(BUZZER_PIN, LOW);
      } else {
        lastTenSeconds = false;
      }
      countdown--;
    }
    if (!settingGame) {
      displaySettingTime(countdown, 0, title);
    } else {
      displaySettingTime(countdown, 1, title);
    }
  }
}

void displaySettingTime(int time, int row, String title) {
  lcd.setCursor(0, row);
  lcd.print(title);
  lcd.print(time / 60);
  lcd.print(":");
  if ((time % 60) < 10) {
    lcd.print("0");
  }
  lcd.print(time % 60);
}

void longBuzzer() {
  digitalWrite(BUZZER_PIN, HIGH);
  delay(5000);
  digitalWrite(BUZZER_PIN, LOW);
}

Hello airsoftonlinejapan

It seem to be a Domination Timer.

I have done a small code review.
The programme looks like it has grown organically.
The combination of the delay() and millis() function is not a propper system design.
My recommendation:
Read and study the IPO model to restructure the program logically and functionally.

A very general programme structure would be:

  • INPUT: Read and debounce buttons, save status.
  • PROCESSING: use the button status to control the timers
  • OUTPUT: use the timer and button status to control the outputs and the LCD when the status changes.

Try it out.
Arrays and structs are your friends.

Not so much a domination timer as it will be used for various game modes and set at the centre of the area. So want to set it and it gives enough time for me to get back to my area before it starts.

Unfortunately, I am not great with the code aspect and this took me 4 months to get this far and hoping to complete it by mid-November.

Using delay() is not usually a good practice, especially within the main loop, as it blocks any other code from executing during the delay. You will especially run into problems calling functions that use delay() with functions that use millis() for timing. Check out the examples in the IDE for "BlinkWithoutDelay" and "Debounce" to rid yourself of the delay() in your code. For example use a millis() timer function to count down and change states.


unsigned int prepSeconds = 120;
unsigned int gameSeconds = 120;
bool prepTimerIsRunning = true;
bool gameTimerIsRunning = false;
bool gameStarted = false;

void countDownTimer(){
static unsigned long countDownMillis = millis();
  static unsigned int countDownRate = 1000;

  if (millis() - countDownMillis >= countDownRate) {
    countDownMillis = millis();

    if (prepTimerIsRunning) {
      prepSeconds --;
      if (prepSeconds <= 0) {
        prepTimerIsRunning = false;
        gameTimerIsRunning = true;
        gameStarted = true;
        
      }
    }
    if (gameTimerIsRunning) {
      gameSeconds --;
      if (gameSeconds == 0) {
        gameTimerIsRunning = false;
        
      }
    }
  }
}

Good Luck!

It took some time as I am new to this, but I took your advice and removed almost all delays and changed to millis and debounce. Things flow much smoother now.

Thanks for the advice.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.