Using 1 button for more than one purpose?

Hi,

I’m still working on my Attiny84 chess clock. I’m having a problem using the buttons for more than one purpose. For instance, I’d like to use the player 1 and player 2 buttons to set the time in an option setting phase. I can do this, with a setOptions loop, but what happens is I set the option, and then the setOptions loop exits and the button activates the clock for the player in there main loop. I need some way to separate the setOptions phase from the timer phase.

I tried debouncing with the Bounce2 library, and also just doing some timing, but that didn’t help. I’ve posted my code here.

#include <avr/sleep.h>
#include <avr/pgmspace.h>

//standard ascii 5x5 font
static unsigned char Font5x5[] PROGMEM = {
  0x00, 0x20, 0x40, 0x60, 0x80,
  0x04, 0x24, 0x44, 0x60, 0x84,
  0x0A, 0x2A, 0x40, 0x60, 0x80,
  0x0A, 0x3F, 0x4A, 0x7F, 0x8A,
  0x0F, 0x34, 0x4E, 0x65, 0x9E,
  0x19, 0x3A, 0x44, 0x6B, 0x93,
  0x08, 0x34, 0x4D, 0x72, 0x8D,
  0x04, 0x24, 0x40, 0x60, 0x80,
  0x02, 0x24, 0x44, 0x64, 0x82,
  0x08, 0x24, 0x44, 0x64, 0x88,
  0x15, 0x2E, 0x5F, 0x6E, 0x95,
  0x04, 0x24, 0x5F, 0x64, 0x84,
  0x00, 0x20, 0x40, 0x64, 0x84,
  0x00, 0x20, 0x4E, 0x60, 0x80,
  0x00, 0x20, 0x40, 0x60, 0x84,
  0x01, 0x22, 0x44, 0x68, 0x90,
  0x0E, 0x33, 0x55, 0x79, 0x8E,
  0x04, 0x2C, 0x44, 0x64, 0x8E,
  0x1E, 0x21, 0x46, 0x68, 0x9F,
  0x1E, 0x21, 0x4E, 0x61, 0x9E,
  0x06, 0x2A, 0x5F, 0x62, 0x82,
  0x1F, 0x30, 0x5E, 0x61, 0x9E,
  0x06, 0x28, 0x5E, 0x71, 0x8E,
  0x1F, 0x22, 0x44, 0x68, 0x88,
  0x0E, 0x31, 0x4E, 0x71, 0x8E,
  0x0E, 0x31, 0x4F, 0x62, 0x8C,
  0x00, 0x24, 0x40, 0x64, 0x80,
  //snipped out some chars here to fit in forum
};

//Display pins 
const int loadPin = 8;
const int sdataPin = 7;
const int sdclkPin = 9;
const int resetPin = 10;
const int ledPin = 3;
//Button Pins
const int button1 = 0;
const int button2 = 2;
const int button3 = 1; 
//Initial Times
double player1Time = 91000;
double player2Time = 91000;
int player1Minutes = 0;
int player1Seconds = 0;
double player1LastCheck = millis();
int player2Minutes = 0;
int player2Seconds = 0;
double player2LastCheck = millis();
//Who's turn is it? 
boolean isPlayer1Turn = false;
boolean isPlayer2Turn = false;
boolean gameOver = false;
boolean ledOn = false;
boolean optionsSet = false;
//Buttons State
boolean button1state = 0;
boolean button2state = 0;
boolean button3state = 0;
//Buffers to hold player times
char player1DisplayTime[6] = "";
char player2DisplayTime[6] = "";

void setup(){
  pinMode(button1, INPUT_PULLUP);
  pinMode(button2, INPUT_PULLUP);
  pinMode(button3, INPUT_PULLUP);
  //pins for controlling the display:
  pinMode(resetPin, OUTPUT);
  pinMode(sdataPin, OUTPUT);
  pinMode(sdclkPin, OUTPUT);
  pinMode(ledPin, OUTPUT);
  pinMode(loadPin, OUTPUT);
  digitalWrite(resetPin, LOW);
  delay(200);
  digitalWrite(resetPin, HIGH);
  setBrightness(4);
  displayBegin();
  delay(30);
  updatePlayer1Time();
  updatePlayer2Time();
}

void writeByte(const char a){
  digitalWrite(loadPin, LOW); 
  for (int b = 0; b <= 7; b++) {
    digitalWrite(sdataPin, bitRead(a, b));
    digitalWrite(sdclkPin, LOW);
    digitalWrite(sdclkPin, HIGH);
  }
  digitalWrite(loadPin, HIGH); 
}
  
  
void writeChar(byte c){
  int d = (c - 0x20) *5;
  for (int i = 0; i < 5; i++) {
    writeByte(pgm_read_byte(&Font5x5[d + i]));
  }
}
  
  
void displayChar(const char myChar, int myPos){
  writeByte(0xB0 + myPos);
  writeChar(myChar);
}

void setBrightness(int brightLevel){
  writeByte(0xF7 - brightLevel);
}

void powerDown(){
  writeByte(0xFF);
}

void pauseGame(){
  isPlayer1Turn = false;
  isPlayer2Turn = false;
}

void displayBegin()
{
  //reset the display
  writeByte(0xC0);
}

void setOptions(){
   if (button2state == LOW){
      player1Time = 150000;
      player2Time = 150000;
      isPlayer1Turn = false;
      isPlayer2Turn = false;
      refreshClocks();
      delay(50);
      optionsSet = true;
      pauseGame();
    } else if (button1state == LOW){
      player1Time = 300000;
      player2Time = 300000;
      isPlayer1Turn = false;
      isPlayer2Turn = false;
      refreshClocks();
      delay(50);
      optionsSet = true;
      pauseGame();
    } else if (button3state == LOW){
      player1Time = 600000;
      player2Time = 600000;
      isPlayer1Turn = false;
      isPlayer2Turn = false;
      refreshClocks();
      delay(50);
      optionsSet = true;
      pauseGame();
     
    } else {}
    delay(20);
    pauseGame();
    isPlayer1Turn = false;
    isPlayer2Turn = false;   
}

  
void loop(){
  button1state = digitalRead(button1);
  button2state = digitalRead(button2);
  button3state = digitalRead(button3);
  if (optionsSet == false){
    setOptions();
  }
  if (button3state == LOW) {
    startPlayer1Turn();
  }
  if (button1state == LOW) {
    startPlayer2Turn();
  }
  if (button2state == LOW) {
    pauseGame();
  }
  if (isPlayer1Turn) {
    updatePlayer1Time();
  } else if (isPlayer2Turn) {
    updatePlayer2Time();
  
  } else {
  }
  updateDisplay();
}

void updateDisplay(){;
    for(int i = 0; i < 5; i++) {
      displayChar(player1DisplayTime[i], i );
    }
    for(int i = 0; i < 5; i++){
      displayChar(player2DisplayTime[i], i+5 );
    }
    setBrightness(7);
    delay(10);
}
  
  
void refreshClocks(){
  player1Minutes = floor(player1Time / 60000);
  player1Seconds = floor(player1Time / 1000) - player1Minutes * 60;
  sprintf(player1DisplayTime, "%2d:%02d", player1Minutes, player1Seconds);
  player2Minutes = floor(player2Time / 60000);
  player2Seconds = floor(player2Time / 1000) - player2Minutes * 60;
  sprintf(player2DisplayTime, "%2d:%02d", player2Minutes, player2Seconds);
  updateDisplay();
}
  
    
void updatePlayer1Time(){
  player1Time -= ((millis() - player1LastCheck));
  player1LastCheck = millis();
  if (player1Time <= 0) {
    gameOver = true;
    flagPlayer(1);
    return;
  }
  
  player1Minutes = floor(player1Time / 60000);
  player1Seconds = floor(player1Time / 1000) - player1Minutes * 60;
  sprintf(player1DisplayTime, "%2d:%02d", player1Minutes, player1Seconds);
  for(int i = 0; i < 6; i++) {
    updateDisplay();
  }
}

void flagPlayer(int playerNumber){
  digitalWrite(3, HIGH);
  isPlayer1Turn = false;
  isPlayer2Turn = false;
    if (playerNumber == 1){
      displayChar('F', 0);
      displayChar('L', 1);
      displayChar('A', 2);
      displayChar('G', 3);
      displayChar(' ', 4);
    }else{
      displayChar(' ', 5);
      displayChar('F', 6);
      displayChar('L', 7);
      displayChar('A', 8);
      displayChar('G', 9);
    }
    delay(30000);
    powerDown();
    digitalWrite(3, LOW);
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
    delay(100);
    sleep_enable();
    sleep_cpu();
}
      
void updatePlayer2Time(){
  player2Time -= ((millis() - player2LastCheck));
  player2LastCheck = millis();
  if (player2Time <= 0) {
    gameOver = true;
    flagPlayer(2);
    return;
  }
  player2Minutes = floor(player2Time / 60000);
  player2Seconds = floor(player2Time / 1000) - player2Minutes * 60;
  sprintf(player2DisplayTime, "%2d:%02d", player2Minutes, player2Seconds);
  for(int i = 0; i < 6; i++){
    updateDisplay();
  }
  
}
   
void startPlayer1Turn () {
  if (isPlayer1Turn) {
    return;
  }
  isPlayer1Turn = true;
  isPlayer2Turn = false;
  player1LastCheck = millis();
}

void startPlayer2Turn () {
  if (isPlayer2Turn) {
    return;
  }
  isPlayer2Turn = true;
  isPlayer1Turn = false;
  player2LastCheck = millis();
}

A schematic is attached in this thread: http://forum.arduino.cc/index.php?topic=293679

Thanks

There are multiple instances in your program where a button state is check and actions are taken. I have not looked in detail but it is almost certain that some of these would be better handled by detecting when the button BECOMES pressed and not when the button IS pressed. This would prevent the fact that the button is still pressed when the required actions are complete causing subsequent unwanted actions.

For an example of how to do this look at the StateChangeDetection example in the IDE.

and replace your if (button…state) statements with else if statements.

Could also do things like

if ((button1state == LOW) && (button2state == LOW)){
// go into other mode
// add a time check too - capture millis() when first pressed, read it again 1000mS later or something, if still pressed go into the other mode
}

if (button1State == LOW && button2State == HIGH){ 
// valid button 1 press
}

if (button1State == HIGH && button2State == LOW){ 
// valid button 2 press
}

Thanks CrossRoads and arduinodlb also!

Jimmy