// Domination using the LiquidCrystal library
#include <LiquidCrystal.h>
/*******************************************************
Jamie Bull, February 2016
Modified chess clock, Legion 02/07/16
********************************************************/
// select the pins used on the LCD panel
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
// define some values used by the panel and buttons
int lcdKey = 0;
int adc_key_in = 0;
enum Button
{
btnRIGHT,
btnUP,
btnDOWN,
btnLEFT,
btnSELECT,
btnNONE
};
// read the buttons
int read_LCD_buttons()
{
adc_key_in = analogRead(0); // read the value from the sensor
// buttons when read are centered at these values: 0, 144, 329, 504, 741
// we add approx 50 to those values and check to see if we are close
// We make this the 1st option for speed reasons since it will be the most likely result
if (adc_key_in > 1000) return btnNONE;
if (adc_key_in < 50) return btnRIGHT;
if (adc_key_in < 250) return btnUP;
if (adc_key_in < 450) return btnDOWN;
if (adc_key_in < 650) return btnLEFT;
if (adc_key_in < 850) return btnSELECT;
return btnNONE; // when all others fail, return this...
}
//================================================================
// Initialise some global parameters
bool isUsingMenu = false; // whether we are in the time-setting menu
bool isGameOver = false;
class Player
{
// Represents a player.
public:
int minutes; // number of minutes allowed
String menuText; // text for the time-setting menu
String playIndicator; // indicator for when active
long secondsRun; // seconds run while the player was active
void IncrementMinutes()
{
minutes = min(minutes + 1, 99);
delay(250);
}
void DecrementMinutes()
{
minutes = max(minutes - 1, 1);
delay(250);
}
int SecondsRemaining()
{
// number of seconds remaining on the player's clock
int minsAllowed = minutes;
int secondsRunSoFar = secondsRun;
return minsAllowed * 60 + secondsRunSoFar;
}
void UpdateCounter(Player p0, Player red, Player blue)
{
secondsRun += millis()/1000 - (p0.secondsRun + red.secondsRun + blue.secondsRun);
}
};
// initialise the players
Player p0 = {0, "", " -- ", 0}; // dummy player, active when player clocks are not counting down
Player red = {0, "Red mins: ", " <- ", 0};
Player blue = {0, "Blue mins: ", " -> ", 0};
// set a pointer to the first player to be shown in the time-setting menu
Player* menuPlayer = &red;
// set the active player pointer to the player to start with
Player* activePlayer = &p0;
void setup()
{
lcd.begin(16, 2);
}
// Main loop
void loop()
{
activePlayer->UpdateCounter(p0, red, blue);; // do this every loop
lcdKey = read_LCD_buttons();
if (isUsingMenu)
{
updateMenuScreen();
} else {
updateTimerScreen();
}
}
void resetGame()
// reset game to starting state
{
p0.secondsRun += (red.secondsRun + blue.secondsRun);
red.secondsRun = 0;
blue.secondsRun = 0;
activePlayer = &p0;
isGameOver = false;
}
void updateTimerScreen()
{
switch (lcdKey)
{
case btnLEFT: { activePlayer = &red; ; break; } // set player 1 active
case btnRIGHT: { activePlayer = &blue; break; } // set player 2 active
case btnUP: { activePlayer = &p0; break; } // pause the timers
case btnDOWN: { break; } // not used
case btnSELECT: // to activate menu (or reset if game is over)
{
if (isGameOver)
{
resetGame();
} else {
// activate the menu
isUsingMenu = true;
activePlayer = &p0;
}
delay(500); // delay required otherwise the button fires repeatedly
break;
}
case btnNONE: // update timer if no button pressed
{
if (isGameOver) {
// do nothing and wait for btnSELECT to reset the game
} else {
// check if either player is out of time
int loser = getLoserNumber();
lcd.setCursor(0,0);
if (loser > 10) {
lcd.print(" Game Over! ");
lcd.setCursor(0,1);
lcd.print(" Team" + String(loser) + " Captured ");
isGameOver = true;
} else {
// update timer display
lcd.print("1 Red 2 Blue");
lcd.setCursor(0,1);
lcd.print(timeString(red.SecondsRemaining()));
lcd.setCursor(5,1);
lcd.print(activePlayer->playIndicator);
lcd.setCursor(11,1);
lcd.print(timeString(blue.SecondsRemaining()));
}
}
}
}
}
void updateMenuScreen()
{
switch (lcdKey)
{
case btnUP:
{ menuPlayer->IncrementMinutes(); break; } // increase minutes
case btnDOWN:
{ menuPlayer->DecrementMinutes(); break; } // decrease minutes
case btnLEFT:
{
// set pointer to player 1 as player to change time for
menuPlayer = &red;
break;
}
case btnRIGHT:
{
// set pointer to player 2 as player to change time for
menuPlayer = &blue;
break;
}
case btnSELECT:
{
// return to timer screen
isUsingMenu = false;
delay(500); // delay required otherwise the button fires repeatedly
break;
}
case btnNONE:
{
// update menu display
lcd.setCursor(0,0);
lcd.print(" MENU ");
lcd.setCursor(0,1);
String playerMins;
if (menuPlayer->minutes < 1) {
playerMins = " " + String(menuPlayer->minutes);
} else {
playerMins = String(menuPlayer->minutes);
}
lcd.print(menuPlayer->menuText + playerMins);
break;
}
}
}
int getLoserNumber()
// return the number of the losing player, or 0 if both have time remaining
{
if (red.SecondsRemaining() >= 1)
{
return 1;
} else if (blue.SecondsRemaining() >= 1)
{
return 2;
}
return 0;
}
String timeString(long seconds)
// Convert seconds to an MM:SS display.
{
int runMins = seconds / 60;
int runSecs = seconds % 60;
return pad(runMins) + ":" + pad(runSecs);
};
String pad(int toPad)
// Zero-pad to ensure two chars in the string for mins/secs
{
if (toPad < 10) {
return "0" + String(toPad);
} else {
return String(toPad);
}
};
Yea - the code is a mess - but it was the only way I could get it to work - I did have this, which was much simpler - but the clocks started at the start and the button reset the clocks instead of clock at zero to start and starting each timer and ending the other with button push
#include <LiquidCrystal.h>
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
int readkey;
void setup() {
lcd.begin(16, 2);
lcd.print("Trigger Happy");
lcd.clear();
}
void loop() {
// start time when buttons were pressed
static unsigned long starttimeRed = 0;
static unsigned long starttimeBlue = 0;
// total time that buttons were pressed
unsigned long totaltimeRed;
unsigned long totaltimeBlue;
lcd.setCursor(0, 0);
lcd.print("Blue");
lcd.setCursor(9, 0);
lcd.print("Red");
readkey = analogRead(0);
// if red pressed
if (readkey < 50)
{
// if no starttime set, set it to current time (millis())
starttimeRed = millis();
}
// if blue pressed
else if (readkey < 790)
{
// if no starttime set, set it to current time (millis())
if (starttimeBlue == 0)
{
starttimeBlue = millis();
}
}
// nothing or anything else pressed
else
{
// calculate total time
totaltimeRed = millis() - starttimeRed;
totaltimeBlue = millis() - starttimeBlue;
lcd.setCursor(0, 1);
lcd.print(totaltimeRed / 1000);
lcd.setCursor(9, 1);
lcd.print(totaltimeBlue / 1000);
}
}
Its basically a domination box for two teams to play
Also, it is easier to debug (and to get help) if your example code uses Serial output versus LCD. That way people don't have to hook up a device to see what you are seeing...
Legioniw3:
Ok I am stumped - have tried 3 different ways but cant get it to work.
Basically I want the player to hold the button down for five seconds to do this
case btnLEFT: { activePlayer = &red; ; break; } // set player 1 active
case btnRIGHT: { activePlayer = &blue; break; } // set player 2 active
I have tried so many ways - and none have worked. Any ideas?
I don't see whether you have solved your problem meanwhile or not.
The code is for some kind of "LCD Keypad Shield" with some additional color buttons attached, isn't it?
Here is my suggested code for selecting Player RED or BLUE by pressing btnLEFT or btnRIGHT for 5 seconds.
#include <LiquidCrystal.h>
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
enum {NONE, RED, BLUE};
enum Button
{
btnRIGHT,
btnUP,
btnDOWN,
btnLEFT,
btnSELECT,
btnNONE,
};
byte read_LCD_buttons()
{
int adc_key_in = analogRead(0); // read the value from the sensor
// buttons when read are centered at these values: 0, 144, 329, 504, 741
// we add approx 50 to those values and check to see if we are close
// We make this the 1st option for speed reasons since it will be the most likely result
if (adc_key_in > 1000) return btnNONE;
if (adc_key_in < 50) return btnRIGHT;
if (adc_key_in < 195) return btnUP;
if (adc_key_in < 380) return btnDOWN;
if (adc_key_in < 555) return btnLEFT;
if (adc_key_in < 790) return btnSELECT;
return btnNONE; // when all others fail, return this...
}
byte userColor=NONE;
void setup() {
Serial.begin(9600);
lcd.begin(16, 2); delay(100);
while(userColor==NONE) userColor=selectColor();
}
byte selectColor()
{
static unsigned long timer;
lcd.clear();lcd.print ("Select Color");delay(100);
if (read_LCD_buttons()== btnLEFT)
{
timer=millis();
while (read_LCD_buttons()== btnLEFT && millis()-timer<5000) ;
if (millis()-timer>=5000) return RED; else return NONE;
}
else if (read_LCD_buttons()== btnRIGHT)
{
unsigned long timer=millis();
while (read_LCD_buttons()== btnRIGHT && millis()-timer<5000) ;
if (millis()-timer>=5000) return BLUE; else return NONE;
}
else return NONE;
}
void doPlayerMove()
{
}
void loop()
{
lcd.setCursor(0,1);
lcd.print("Your move - ");
if (userColor==RED) lcd.print ("RED");
if (userColor==BLUE) lcd.print ("BLUE");
doPlayerMove();
;
}
What do you think?
And what kind of game is it about?
A two player game human vs. human? Or a game human vs. Arduino?
Wow that works great. Basically I am trying to make a domination point, think call of duty. Two teams two buttons. Red team stops blue team timer and starts their own. And vice versa.
I run an indoor airsoft site, and at the minute I use flags (well colored pillowcases) on a little pully - but thought this would look great as boxes with screens.
I am still learning - I have got a bomb box working - with a code and keypad - but this is stumping me. I thought it would be so easy, two timers and two buttons - but am lost in a sea of code, that I only mildly know what it means.
Thanks for testing the example sketch and sending some friendly feedback!
Legioniw3:
Red team stops blue team timer and starts their own. And vice versa.
Sounds easy.
This is what I understand:
After BLUE ihas been selected initially, a BLUE timer starts running and it is RED to do the next step and press the red button. Then the BLUE timer stops and the RED timer starts.
Then BLUE must do the next step and press theblue buton.
Then the blue timer starts at the time when it was stopped and continues running while the red timer stops.
And so on and so on.
One problem when using the Arduino boards internal timing may be timing accuracy.
All boards with a "ceramic resonator for creating the boards 16 MHz clocking are only accurate to 0.8 %, wnich means 8 seconds of inaccuracy in 1000 seconds. Or even more . All of the "R3" design boards are such inaccurate,like "UNO R3" or "MEGA25650 R3". Is such inaccuracy suitable for your application? Which Arduino board are you planning to use? And is there any condidition for a gameover() function? Or is the game not limited in ime and runs forever (or until reset)?
Please let me know if you need additional assistance with some more lines of code.
Well the timing issue is not a problem - Before I was counting in my head which team had won.
I am using the Arduino Mega 2560, because if I try to solder a pcb board I will probably burn down the building.
The code I had at the start works - even though there are alot of contradictions in it. I used a chess timer and messed around with what little of the code I knew to get it working. But was unable to change using the lcd buttons to using external buttons into digitalout.
I could even live without the delay in the button push (but it is so nice).
I am just going to have this run - with no end time, as I will have three units, and just call the game when I see fit