Help with a king of the hill game box [solved]

Hi,
I am building an objective box that behaves like a king of the hill capture point, but I am having issues making do exactly what I want. The box consists of an LCD display, a red LED, a blue LED, a green LED, and two pushbuttons, one for red team and one for blue team.
What is meant to happen is this:

  1. Power on, screen displays “New Game” and Green LED is on.
  2. If red team presses their team’s button, the LCD displays “Hill Contested”, the Green LED remains on.
  3. If the red button is held down, the red LED will flash once per second a timer will count up to 5 seconds. Once the timer reaches 5 seconds, the LCD changes to “Red in Control”, the red LED stays on, and the green LED turns off. A new timer starts, which is displayed on line two of the LCD, and counts up until it reaches a set value, ending the game. If the button is held for less that 5 seconds and released, the LCD continues to display “Hill Contested”, the green LED stays on, and the count up timer does not start.
  4. If the blue team presses their team’s button, the LCD displays “Hill Contested”, the Green LED turns on, and the red team timer stops (but keeps its value).
  5. If the blue button is held down for 5 seconds, the blue team timer begins counting up and the LCD displays “Blue in Control”. Again, if the button is not held for 5 seconds continuously, the “Hill Contested” mode stays on.
  6. Once one team’s timer reaches the set value, the game ends. It should display some sort of victory message and flash some LEDs (I haven’t gotten this far yet.)
  7. If both buttons are held down for 3 seconds, the game should reset and return to it’s startup state. (Also haven’t gotten to here yet.)

So far, almost everything is working. The problem is when a team has control of the hill, and the other team presses their team’s button, the “Hill Contested” part is skipped, and instead the timer switches immediately (steps 4-5). I’ve tried a variety of if and while loops, but nothing has been getting it right. If anyone could help, it would be much appreciated.
Here is the code I have been using:

// include the library code:
#include <LiquidCrystal.h>

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(7, 8, 9, 10, 11, 12);

// LED pins
int LEDGreen = 2;
int LEDRed = 13;
int LEDBlue = 4;

// Pushbuttons
int ButtonBlue = 5;
int ButtonRed = 6;
int held = 0;
int held_delay = 5; //this sets how long the button must be held to capture the hill
int reset = 0;
int reset_delay = 300; //this sets how long both buttons must be held to reset the game
int timer_blue = 0;
int timer_red = 0;
int timer_win = 300; //this sets how many seconds a team must control the hill to win
int status_blue = HIGH;
int status_red = HIGH;
int holding_blue = 0;
int holding_red = 0;

void setup() {
  //set up the I/O pins
  pinMode(LEDGreen, OUTPUT);
  pinMode(LEDRed, OUTPUT);
  pinMode(LEDBlue, OUTPUT);
  pinMode(ButtonBlue, INPUT_PULLUP);
  pinMode(ButtonRed, INPUT_PULLUP);
  // set up the LCD's number of columns and rows:
  lcd.begin(16, 2);
  // Print a message to the LCD.
  lcd.print("- - NEW GAME - -");
  digitalWrite(LEDGreen, HIGH);
  digitalWrite(LEDRed, LOW);
  digitalWrite(LEDBlue, LOW);
}

void loop() {
//monitoring buttons for changes in input
  status_blue = digitalRead(ButtonBlue);
  status_red = digitalRead(ButtonRed);
//holding loop for blue team timer
  if (holding_blue == 1 && status_red == HIGH && timer_blue < timer_win){
    digitalWrite(LEDGreen, LOW);
    digitalWrite(LEDBlue, HIGH);
    digitalWrite(LEDRed, LOW);
    lcd.setCursor(0, 0);
    lcd.print("BLUE IN CONTROL ");
    lcd.setCursor(0, 1);
    lcd.print(timer_blue);
    delay(100);
    status_red = digitalRead(ButtonRed);
    timer_blue++;
  }
//holding loop for red team timer
  if (holding_red == 1 && status_blue == HIGH && timer_red < timer_win){
    digitalWrite(LEDGreen, LOW);
    digitalWrite(LEDBlue, LOW);
    digitalWrite(LEDRed, HIGH);
    lcd.setCursor(0, 0);
    lcd.print(" RED IN CONTROL ");
    lcd.setCursor(0, 1);
    lcd.print(timer_red);
    delay(100);
    status_blue = digitalRead(ButtonBlue);
    timer_red++;
  }
//blue button monitoring loop, need to keep button held for a set delay to turn holding_blue on
  if (status_blue == LOW && status_red == HIGH)
  {
    holding_red = 0;
    digitalWrite(LEDGreen, HIGH);
    digitalWrite(LEDBlue, LOW);
    digitalWrite(LEDRed, LOW);
    lcd.setCursor(0, 0);
    lcd.print(" HILL CONTESTED ");
    while (status_blue == LOW && held < held_delay){
      digitalWrite(LEDBlue, HIGH);
      delay(10);
      digitalWrite(LEDBlue, LOW);
      delay(990);
      status_blue = digitalRead(ButtonBlue);
      held++;
    }
    if (held >= held_delay)
    {
      holding_blue = 1;
    }
    else
    {
      held = 0;
    }
  }
 //red monitoring loop
  if (status_red == LOW && status_blue == HIGH)
  {
    holding_blue = 0;
    digitalWrite(LEDGreen, HIGH);
    digitalWrite(LEDBlue, LOW);
    digitalWrite(LEDRed, LOW);
    lcd.setCursor(0, 0);
    lcd.print(" HILL CONTESTED ");
    while (status_red == LOW && held < held_delay){
      digitalWrite(LEDRed, HIGH);
      delay(10);
      digitalWrite(LEDRed, LOW);
      delay(990);
      status_red = digitalRead(ButtonRed);
      held++;
    }
    if (held >= held_delay)
    {
      holding_red = 1;
    }
    else
    {
      held = 0;
    }
  }
  while (status_red == LOW && status_blue == LOW && reset < reset_delay)
  {
    delay(100);
    status_red = digitalRead(ButtonRed);
    status_blue = digitalRead(ButtonBlue);
    reset++;
  }
  if (reset >= reset_delay)
  {
    lcd.setCursor(0, 0);
    lcd.print("- - NEW GAME - -");
    lcd.setCursor(0, 1);
    lcd.print("                ");
    digitalWrite(LEDGreen, HIGH);
    digitalWrite(LEDRed, LOW);
    digitalWrite(LEDBlue, LOW);
    held = 0;
    reset = 0;
    timer_blue = 0;
    timer_red = 0;
    status_blue = HIGH;
    status_red = HIGH;
    holding_blue = 0;
    holding_red = 0;
  }
  else
  {
    reset = 0;
  }
}

The solution is below, but basically I needed to add some “break” functions into the while loops.

try using millis() + time to check in for and while loops
needs an unsigned long variable

// includes here

int blue = 4; // blue led pin
int red = 13; // red led pin
int green = 2; // green led pin
int points_blue = 0; // total points
int points_red = 0; // total points
int points_win = 300; // points to win
int holding_red = 0;  // has the point
int holding_blue = 0; // has the point
int held_delay = 5; // time to hold in seconds
int status_red = 6; // red button
int status_blue = 5; // blue button

unsigned long button_hold = 0;

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

  pinMode (red, OUTPUT);
  pinMode (blue, OUTPUT);
  pinMode (green, OUTPUT);
  pinMode (status_red, INPUT_PULLUP);
  pinMode (status_blue, INPUT_PULLUP);
  // lcd setup here
  Serial.println ("Start");
}

void loop () {

  if ( (digitalRead(status_red) == 1) && holding_red == 0) { // initial push
    Serial.println ("Red Holding");
    button_hold = millis() + (held_delay * 1000 ); // set hold time +1 sec
    while (button_hold >= millis() && (digitalRead(status_red) == 1)) { // check for holding time and blink
      Serial.println ( 1 + (button_hold - millis()) / 1000);
      delay(100);
      digitalWrite (red, HIGH);
      digitalWrite (green, LOW);
      digitalWrite (blue, LOW);
      delay(100);
      digitalWrite (red, LOW);
      digitalWrite (green, HIGH);
      digitalWrite (blue, LOW);
    }
    delay (100);
    digitalWrite (red, LOW); // set previous color status
    digitalWrite (blue, LOW);
    digitalWrite (green, HIGH);
    if (holding_blue == 1) {
      digitalWrite (blue, HIGH);
      digitalWrite (green, LOW);
    }
    if (button_hold < millis() && (digitalRead(status_red) == 1)) { // set winner color if button if held
      digitalWrite (red, HIGH);
      digitalWrite (green, LOW);
      digitalWrite (blue, LOW);
      holding_red = 1;
      holding_blue = 0;
      Serial.println ("Red Capture");
    }
    Serial.println ("Red release");
    button_hold = millis() + 1000; // reset hold time to +1000ms
  }

  // repeat for blue holding

  if ((digitalRead(status_blue) == 1) && holding_blue == 0) { // initial push
    Serial.println ("Blue Holding");
    button_hold = millis() + (held_delay * 1000 ); // set hold time +1 sec
    while (button_hold >= millis() && (digitalRead(status_blue) == 1)) { // check for holding time and blink
      Serial.println ( 1 + (button_hold - millis()) / 1000);
      delay(100);
      digitalWrite (blue, HIGH);
      digitalWrite (green, LOW);
      digitalWrite (blue, LOW);
      delay(100);
      digitalWrite (red, LOW);
      digitalWrite (green, HIGH);
      digitalWrite (blue, LOW);
    }
    delay (100);
    digitalWrite (red, LOW); // set previous color status
    digitalWrite (blue, LOW);
    digitalWrite (green, HIGH);
    if (holding_red == 1) {
      digitalWrite (red, HIGH);
      digitalWrite (green, LOW);
    }
    if (button_hold < millis() && (digitalRead(status_blue) == 1)) { // set winner color if button if held
      digitalWrite (red, LOW);
      digitalWrite (green, LOW);
      digitalWrite (blue, HIGH);
      holding_red = 0;
      holding_blue = 1;
      Serial.println ("Blue Capture");
    }
    button_hold = millis() + 1000; // reset hold time to +1000ms
    Serial.println ("Blue Release");
  }

  if (button_hold <= millis()) { // add points to holding team add time to check again
    points_red = points_red + holding_red;
    points_blue = points_blue + holding_blue;
    button_hold = millis() + 1000; // reset hold time to +1000ms
    Serial.print ("Points   Red = ");
    Serial.print (points_red);
    Serial.print (",  Blue = ");
    Serial.println (points_blue);
  }

  if (points_red >= points_win || points_blue >= points_win) { // print winner set colors
    if (points_red > points_blue)
      Serial.println ("Red Wins");
    if (points_red < points_blue)
      Serial.println ("Blue Wins");
    points_red = 0;
    points_blue = 0;
    holding_red = 0;
    holding_blue = 0;
    digitalWrite (red, LOW);
    digitalWrite (blue, LOW);
    digitalWrite (green, HIGH);
    while ((digitalRead(status_red) == 1) || (digitalRead(status_blue) == 1)) {} // wait for both to push
    Serial.println ("Reset");
    delay (100); // clear button bounce
    while ((digitalRead(status_red) == 0) && (digitalRead(status_blue) == 0)) {} // wait for both release
    delay (100);
    Serial.println ("Start");
  }
}

Awesome, thank you!

a bunch of mistakes were in the code above

should be fixed now

I took your advice and added the millis() to run the button timers, but I was having issues with the while loops. Once one team captured the hill, I couldn’t interrupt the loop. I had to insert an if loop with a break command in the while loops so that it would go back and redo the button timers as the hill changed hands. This is what I came up with:

//Library originally added 18 Apr 2008
//by David A. Mellis
//library modified 5 Jul 2009
//by Limor Fried (http://www.ladyada.net)
//example added 9 Jul 2009
//by Tom Igoe
//modified 22 Nov 2010
//by Tom Igoe

//This example code is in the public domain.

//http://www.arduino.cc/en/Tutorial/LiquidCrystal

// include the library code:
#include <LiquidCrystal.h>

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(7, 8, 9, 10, 11, 12);

// LED pins
int led_green = 2;
int led_red = 13;
int led_blue = 4;
int buzzer = 3;

// Pushbuttons
int button_blue = 5;
int button_red = 6;
unsigned long held = 0;
int held_delay = 4; //this sets how long the button must be held to capture the hill, enter value one less than desired value
int reset = 0;
int reset_delay = 3; //this sets how long both buttons must be held to reset the game
unsigned long timer_blue = 0;
unsigned long timer_red = 0;
int timer_win = 300; //this sets how many seconds a team must control the hill to win
int status_blue;
int status_red;
int holding_blue = 0;
int holding_red = 0;

void setup() {
  //set up the I/O pins
  pinMode(led_green, OUTPUT);
  pinMode(led_red, OUTPUT);
  pinMode(led_blue, OUTPUT);
  pinMode(button_blue, INPUT_PULLUP);
  pinMode(button_red, INPUT_PULLUP);
  // set up the LCD's number of columns and rows:
  lcd.begin(16, 2);
  // Print a message to the LCD.
  lcd.print("- - NEW GAME - -");
  digitalWrite(led_green, HIGH);
  digitalWrite(led_red, LOW);
  digitalWrite(led_blue, LOW);
}

void loop() {
//monitoring buttons for changes in input
  status_red = digitalRead(button_red);
  status_blue = digitalRead(button_blue);
//reset monitoring
  if (status_red == LOW && status_blue == LOW){
    while (reset <= reset_delay){
      digitalWrite(led_green, HIGH);
      delay(10);
      digitalWrite(led_green, LOW);
      delay(990);
      reset++;
      if (digitalRead(button_red) == HIGH || digitalRead(button_blue) == HIGH){
        break;
      }
    }
    
  }
//first level of ladder logic
  if (status_red == LOW){
    if (holding_red == 0){
      lcd.setCursor(0, 0);
      lcd.print(" HILL CONTESTED ");
      lcd.setCursor(0, 1);
      lcd.print("                ");
      digitalWrite(led_green, HIGH);
      digitalWrite(led_blue, LOW);
      digitalWrite(led_red, LOW);
      held = millis() + (held_delay * 1000);
      while (millis() <= held && digitalRead(button_red) == LOW){
        digitalWrite(led_red, HIGH);
        delay(10);
        digitalWrite(led_red, LOW);
        delay(990);
        if (millis() > held){
          break;
        }
      }
      if (millis() >= held){
        holding_red = 1;
        holding_blue = 0;
      }
      else{
        holding_red = 0;
        holding_blue = 0;
      }
    }
  }
  //blue version of level one ladder logic
  if (status_blue == LOW){
    if (holding_blue == 0){
      lcd.setCursor(0, 0);
      lcd.print(" HILL CONTESTED ");
      lcd.setCursor(0, 1);
      lcd.print("                ");
      digitalWrite(led_green, HIGH);
      digitalWrite(led_blue, LOW);
      digitalWrite(led_red, LOW);
      held = millis() + (held_delay * 1000);
      while (millis() <= held && digitalRead(button_blue) == LOW){
        digitalWrite(led_blue, HIGH);
        delay(10);
        digitalWrite(led_blue, LOW);
        delay(990);
        if (millis() > held){
          break;
        }
      }
      if (millis() >= held){
        holding_blue = 1;
        holding_red = 0;
      }
      else{
        holding_blue = 0;
        holding_red = 0;
      }
    }
  }
  //red holding logic
  if (holding_red == 1){
    lcd.setCursor(0, 0);
    lcd.print(" RED IN CONTROL ");
    digitalWrite(led_red, HIGH);
    digitalWrite(led_green, LOW);
    while (timer_red <= timer_win){
      lcd.setCursor(0, 1);
      lcd.print(timer_red);
      timer_red++;
      delay(100);
      if (digitalRead(button_blue) == LOW){
        break;
      }
    }
  }
  //blue holding logic
  if (holding_blue == 1){
    lcd.setCursor(0, 0);
    lcd.print("BLUE IN CONTROL ");
    digitalWrite(led_blue, HIGH);
    digitalWrite(led_green, LOW);
    while (timer_blue <= timer_win){
      lcd.setCursor(0, 1);
      lcd.print(timer_blue);
      timer_blue++;
      delay(100);
      if (digitalRead(button_red) == LOW){
        break;
      }
    }
  }
}

It still needs some clean up, and I need to code in some piezo buzzers, but it seems to be mostly working. Again, thanks for the help.