How to check 4 buttons at the same time?

So i want to make a simple game where 4 players have to press their buttons as fast as possible after an LED turns on. the problem is that if I do it like this

if(button1==LOW){Serial.println("player 1");}
if(button2==LOW){Serial.println("player 2");}
if(button3==LOW){Serial.println("player 3");}
if(button4==LOW){Serial.println("player 4");}

or like this

for(int i=0;i!=3;i++){                                 /*Assuming the Buttons are connected to pins 0-3*/
  if(digitalRead(i)==LOW){
  Serial.print("player");
  Serial.println(i+1);
}
}

And more than one button is pressed at exact same time it will always show player 1 as the winner because it's the first one that it tests. Is there any way to check all of the buttons at the same time?

Definitely not like that :wink:

If they're all on the same port, yes.

My bad with the for loop, i forgot to add i++, now it's edited to be correct

For this game you also need a lock out once the "winner" has pressed their button.

1 Like

I find " < 4" more intuitive for the condition too.

Also, not a great idea to use the serial I/o pins for the buttons.

Yes. I just happened to do a delish deepish dive into the mechanism and rules for the Jeopardy ring-in system, there's quite a few little tricks in there.

Well worth a google and a few minutes. And it looks like it would be fun to code.

For example, lockout for early ringing in, 250 ms punishment/disadvantage.

But the OP has the first part just right. It is essential to collect all inputs at the same moment.

  direct port manipulation Arduino

will turn up the needed info, it may sound complicate or intimidating, but it is fairly straightforward.

a7

1 Like

Yes, as mentioned, but any Arduino is so fast compared to human reaction times, it really isn't necessary.

Maybe so. But since it can be done, it should be done. There is still the possibility of a tie, anything to avoid that, and to be able to say there is absolutely no advantage.

I will allow different cable lengths to the switches. Before you laugh read about Wall Street and the advantages that make ppl chase microseconds…

 Flash Boys by Michael Lewis

a7

consider

// check multiple buttons and toggle LEDs

enum { Off = HIGH, On = LOW };

byte pinsLed [] = { 10, 11, 12 };
byte pinsBut [] = { A1, A2, A3 };


struct But  {
    const byte    pin;
    byte          state;
    unsigned long msecLst;
};

But buts [] = { {A1}, {A2}, {A3}};
#define N_BUT   (sizeof(buts)/sizeof(But))

// -----------------------------------------------------------------------------
#define DebounceMsec    10
int
chkButtons ()
{
    unsigned long  msec = millis ();

    But *b = & buts [0];
    for (unsigned n = 0; n < sizeof(pinsBut); n++, b++)  {
        if (b->msecLst && (msec - b->msecLst) < DebounceMsec)
            continue;

        b->msecLst = 0;

        byte but = digitalRead (pinsBut [n]);

        if (b->state != but)  {
            b->state = but;
            b->msecLst  = msec ? msec : 1;      // make sure non-zero

            if (On == but)
                return n;
        }
    }
    return -1;
}

// -----------------------------------------------------------------------------
void
loop ()
{
    switch (chkButtons ())  {
    case 2:
        digitalWrite (pinsLed [2], ! digitalRead (pinsLed [2]));
        break;

    case 1:
        digitalWrite (pinsLed [1], ! digitalRead (pinsLed [1]));
        break;

    case 0:
        digitalWrite (pinsLed [0], ! digitalRead (pinsLed [0]));
        break;
    }
}

// -----------------------------------------------------------------------------
void
setup ()
{
    Serial.begin (9600);

    for (unsigned n = 0; n < sizeof(pinsBut); n++)  {
        pinMode (buts [n].pin, INPUT_PULLUP);
        buts [n].state = digitalRead (buts [n].pin);
    }

    for (unsigned n = 0; n < sizeof(pinsLed); n++)  {
        digitalWrite (pinsLed [n], Off);
        pinMode      (pinsLed [n], OUTPUT);
    }
}

There is absolutely no need to denounce the buttons in this case.

No matter whether you do it correctly or not.

a7

74HC165 shift register?

All inputs collected at once, figure out who's first in software.

Configure MCP23008 inputs with interrupts to trigger Arduino to read 23008 port.

No need. Just make sure all the buttons are on portB.

1 Like

If you add processing between checks, like printing messages through a 9600 baud serial port, the difference could be significant.

Then there's the low-level fiddly stuff with the PCICR, PCMSK0 registers and an ISR(PCINT0_vect).

/// edit -- nevermind. If there's any possibility of bouncing, you're probably better off just reading PORTB and watching for changes.

won't this repeatedly report "player 1" for as long as the button is held down? don't you want to recognize the "press" of a button?

Here's a start

// ring in system. Not Yet Jeopardy work in progress

const byte lamps[4] = {8, 9, 10, 11};



void setup()
{
   Serial.begin(9600);
   Serial.println("\nNot Yet Jeopardy\n");

   for (unsigned char tt = 0; tt < 4; tt++)
      pinMode(lamps[tt], OUTPUT);

   lampTest(); lampTest();

/*   DDRD &= 0b00001111;      // 7..4 are inputs
   PORTD |= 0x11110000;     // pulled high
*/

   pinMode(4, INPUT_PULLUP);
   pinMode(5, INPUT_PULLUP);
   pinMode(6, INPUT_PULLUP);
   pinMode(7, INPUT_PULLUP);
}

void loop()
{
   byte theSwitches = PIND;    // grab all four switches at a given moment
   theSwitches >>= 4;

   Serial.print(theSwitches, HEX); Serial.print(" :    ");

   for (unsigned char tt = 0; tt < 4; tt++) {
      Serial.print(tt); Serial.print(" ");
      if (theSwitches & (1 << tt)) {
         Serial.print("OPEN     ");
         digitalWrite(lamps[tt], LOW);
      }
      else {
         Serial.print("CLOSED   ");
         digitalWrite(lamps[tt], HIGH);
      }
   }

   Serial.println("");
}

void lampTest()
{
   for (unsigned char tt = 0; tt < 4; tt++) {
      digitalWrite(lamps[tt], HIGH);
      delay(177);
      digitalWrite(lamps[tt], LOW);
      delay(177);
   }

   delay(200);
}

I couldn't figure out setting the pinMode directly, this was what google told me

   DDRD &= 0b00001111;      // 7..4 are inputs
   PORTD |= 0x11110000;     // pulled high

But it did not work, so I used pinMode as a temporary.

How do you set the pinMode using the registers direct, and how do you inlcude the internal pullup?

a7

while not enabled

	read switches and
	
	start/restart lockout timer on (disable) any that were pressed
	
	if the game master's switch closes
		light the GO lamp to indicate OK to ring in
		enable the ringing in

forever

    read switches and
    
    if an able switch is pressed, break with winner
    
    count down any disabled (locked out) timers if the switch is up
    

light contestant's podium ring the bell etcetera

There's still a few subtleties I haven't found other than anecdotes about.

a7

DDRD &= 0b00001111;
makes bits 7, 6, 5, 4 Inputs.
The Input pins are read with PIND.

OK, well I will try that, which is what I thought did, and expect it to work this time!

THX

a7

Cool, but I think somethings wrong with pin 6

If DDRD = 0, then (DDRD &= 0) == 0.

They start out as inputs to begin with. You just need to enable the pullups:

Try:

   PORTD |= 0b11110000;
   Serial.println(DDRD,BIN);
   Serial.println(PORTD,BIN);