Simultanous button reading?












3















I'm currently working on a project that requires that I simultanously check the state of two buttons. Each button HIGH state is assigned to one if loop. Here's the basic concept:



void loop() {
S1_State = digitalRead(S1_Pin);
S2_State = digitalRead(S2_Pin);
ButtonPRESSED = false;

P1_Time = random(2000, 5000);
delay(P1_Time);
digitalWrite(P1_Pin, HIGH);

while(ButtonPRESSED == false) {
if (S1_State == HIGH) {
Serial.print("Player 1 wins");
Serial.print("");
digitalWrite(P1_Pin, LOW);
ButtonPRESSED = true;
delay(5000); }
if (S2_State == HIGH) {
Serial.print("Player 2 wins");
Serial.print("");
digitalWrite(P1_Pin, LOW);
ButtonPRESSED = true;
delay(5000); }


The problem is that checking for the button states this way requires that the S1_State loop _has to be read before the S2_State loop_, thus giving player 1 an advantage. Although I'm not sure how pronounced this issue is in practice, it certainly was noticable when I was first trying this project out through python code on a Pi through similar means.



Is it possible check the conditions to both if loops simultanously? If not, are there other ways to circumvent this problem?










share|improve this question









New contributor




oh double-you oh is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.





















  • read through your code line by line ..... imagine that both the buttons are pressed at the same time ....... is that the expected behavior?

    – jsotola
    2 days ago
















3















I'm currently working on a project that requires that I simultanously check the state of two buttons. Each button HIGH state is assigned to one if loop. Here's the basic concept:



void loop() {
S1_State = digitalRead(S1_Pin);
S2_State = digitalRead(S2_Pin);
ButtonPRESSED = false;

P1_Time = random(2000, 5000);
delay(P1_Time);
digitalWrite(P1_Pin, HIGH);

while(ButtonPRESSED == false) {
if (S1_State == HIGH) {
Serial.print("Player 1 wins");
Serial.print("");
digitalWrite(P1_Pin, LOW);
ButtonPRESSED = true;
delay(5000); }
if (S2_State == HIGH) {
Serial.print("Player 2 wins");
Serial.print("");
digitalWrite(P1_Pin, LOW);
ButtonPRESSED = true;
delay(5000); }


The problem is that checking for the button states this way requires that the S1_State loop _has to be read before the S2_State loop_, thus giving player 1 an advantage. Although I'm not sure how pronounced this issue is in practice, it certainly was noticable when I was first trying this project out through python code on a Pi through similar means.



Is it possible check the conditions to both if loops simultanously? If not, are there other ways to circumvent this problem?










share|improve this question









New contributor




oh double-you oh is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.





















  • read through your code line by line ..... imagine that both the buttons are pressed at the same time ....... is that the expected behavior?

    – jsotola
    2 days ago














3












3








3








I'm currently working on a project that requires that I simultanously check the state of two buttons. Each button HIGH state is assigned to one if loop. Here's the basic concept:



void loop() {
S1_State = digitalRead(S1_Pin);
S2_State = digitalRead(S2_Pin);
ButtonPRESSED = false;

P1_Time = random(2000, 5000);
delay(P1_Time);
digitalWrite(P1_Pin, HIGH);

while(ButtonPRESSED == false) {
if (S1_State == HIGH) {
Serial.print("Player 1 wins");
Serial.print("");
digitalWrite(P1_Pin, LOW);
ButtonPRESSED = true;
delay(5000); }
if (S2_State == HIGH) {
Serial.print("Player 2 wins");
Serial.print("");
digitalWrite(P1_Pin, LOW);
ButtonPRESSED = true;
delay(5000); }


The problem is that checking for the button states this way requires that the S1_State loop _has to be read before the S2_State loop_, thus giving player 1 an advantage. Although I'm not sure how pronounced this issue is in practice, it certainly was noticable when I was first trying this project out through python code on a Pi through similar means.



Is it possible check the conditions to both if loops simultanously? If not, are there other ways to circumvent this problem?










share|improve this question









New contributor




oh double-you oh is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.












I'm currently working on a project that requires that I simultanously check the state of two buttons. Each button HIGH state is assigned to one if loop. Here's the basic concept:



void loop() {
S1_State = digitalRead(S1_Pin);
S2_State = digitalRead(S2_Pin);
ButtonPRESSED = false;

P1_Time = random(2000, 5000);
delay(P1_Time);
digitalWrite(P1_Pin, HIGH);

while(ButtonPRESSED == false) {
if (S1_State == HIGH) {
Serial.print("Player 1 wins");
Serial.print("");
digitalWrite(P1_Pin, LOW);
ButtonPRESSED = true;
delay(5000); }
if (S2_State == HIGH) {
Serial.print("Player 2 wins");
Serial.print("");
digitalWrite(P1_Pin, LOW);
ButtonPRESSED = true;
delay(5000); }


The problem is that checking for the button states this way requires that the S1_State loop _has to be read before the S2_State loop_, thus giving player 1 an advantage. Although I'm not sure how pronounced this issue is in practice, it certainly was noticable when I was first trying this project out through python code on a Pi through similar means.



Is it possible check the conditions to both if loops simultanously? If not, are there other ways to circumvent this problem?







loop






share|improve this question









New contributor




oh double-you oh is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.











share|improve this question









New contributor




oh double-you oh is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.









share|improve this question




share|improve this question








edited 2 days ago







oh double-you oh













New contributor




oh double-you oh is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.









asked 2 days ago









oh double-you ohoh double-you oh

184




184




New contributor




oh double-you oh is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.





New contributor





oh double-you oh is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.






oh double-you oh is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.













  • read through your code line by line ..... imagine that both the buttons are pressed at the same time ....... is that the expected behavior?

    – jsotola
    2 days ago



















  • read through your code line by line ..... imagine that both the buttons are pressed at the same time ....... is that the expected behavior?

    – jsotola
    2 days ago

















read through your code line by line ..... imagine that both the buttons are pressed at the same time ....... is that the expected behavior?

– jsotola
2 days ago





read through your code line by line ..... imagine that both the buttons are pressed at the same time ....... is that the expected behavior?

– jsotola
2 days ago










5 Answers
5






active

oldest

votes


















10














There is a very easy solution to your problem, and it's caller "interrupts".



The processes inside loop method are executed synchronously, so always one read will be done before second one, and always one check will be done before a second one (even if they are inside one if statement)



Fortunately there is a way of simulating asynchronous behavior in ATmega. It's called "external interrupt". You can setup ATmega inside Arduino to stop processing loop method and instead immediately run a different method whenever a voltage on one of the pins change. You are "interrupting" execution of loop hence the name "interrupt". In Arduino Uno this can be done (only) on pins 2 and 3.



https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/



I made a little model i Tinkercad



Arduino with two push buttons



Whenever you push left button PIN2 of the arduino gets shorted to ground.
Whenever you push right button PIN3 of the arduino gets shorted to ground.



And now my source code



void setup()
{
pinMode(2, INPUT_PULLUP);
pinMode(3, INPUT_PULLUP);

interrupts();
attachInterrupt(digitalPinToInterrupt(2), Player1ButtonPress, FALLING);
attachInterrupt(digitalPinToInterrupt(3), Player2ButtonPress, FALLING);

Serial.begin(9600);
}

void Player1ButtonPress() {
Serial.println("Player 1 wins");
}

void Player2ButtonPress() {
Serial.println("Player 2 wins");
}

void loop()
{
//nothing here
}


And now some explanation.
In setup we first setup PIN2 and PIN3 as "input with pullup resistor". In our design this means that when button is not pressed those pins will have 5V on them, but as soon as the buttons are pressed voltages go down to 0V. Then we make sure that interrupts are turned on (they are on by default, but I like to show everyone who is reading the code that I will be using them) and then we do some attachInterrupt magic. We make that a "falling edge" (change from 5V to 0V) on PIN2 and PIN3 will immediately run methods Player1ButtonPress and Player2ButtonPress.



When I was playing with buttons they worked perfectly.



enter image description here



And a word about possible cheating. Keeping the button pressed does not create a "falling edge" (voltage on a pin is constantly 0) so if you only add some global variables with info when game starts and who won. Then add some conditions in the two methods responsible for interrupts. You can then use the loop method to start the game, and show result.






share|improve this answer


























  • This seems just about perfect, thank you!

    – oh double-you oh
    2 days ago











  • Does the interrupt handling give either player an advantage? What happens in the event of a tie?

    – Craig
    2 days ago











  • @Craig The more I think about it the more I think that there still will be a tiny bias, because ATmega will analyze values of pins with 16MHz frequency so there still is possible that a "tie" will happen and one button will be chosen by processor to be a "winner" incorrectly. But this time the decision will take only 1 clock cycle (6 nano seconds). digitalRead take about 50 clock cycles, and we need two of them. So we gained 100 times more resolution and I think it's enough for a "game" to risk suck event. Nothing is perfect in electronics.

    – Filip Franik
    2 days ago





















10














Instead of using the high level digitalRead() function access the low level port registers. See this documentation.



A port register is one byte and each bit represents one of the digital inputs on the arduino.



Do something like this:



pin_status = PIND; //Input port D (pins 0-7)
button_1 = bitRead(pin_status, 2); // digital pin 2
button_2 = bitRead(pin_status, 3); // digital pin 3


The values are read simultaneously so it is possible to have a tie.






share|improve this answer































    3














    I think you want after a random time, a 'Start' LED to be lit, and whoever presses his/her button first wins.



    There are some problems in your sketch:




    • It seems even when a button is pressed to early, the button is still taken into account. You should check for this 'cheating'. So check directly after the 'start' LED is lit, that no buttons are pressed. If so, that player loses.

    • Assuming the players are honest, you check for their buttons, which probably the first iteration it will not be pressed (that would be an immediate reaction speed). However, than you wait 5 seconds; probably both players pressed their buttons, and you check the button of the first player first. It will help, if you change the delay to 1 ms, or even remove it completely.


    Untested proposal:



    int playerXWon = 0; // No player won yet, 1 = player 1 wins, 2 = player 2 wins, 3 = draw

    void loop()
    {
    // Delay for a random time.
    P1_Time = random(2000, 5000);
    delay(P1_Time);

    CheatCheck();

    if (playerXWon == 0)
    {
    // Start
    digitalWrite(P1_Pin, HIGH);

    checkFirstPress();
    }
    }

    void CheatCheck()
    {
    // Check if a player did not press its button too early.
    // (Btw, it does not matter if the player presses and release its button before the LED goes on.
    S1_State = digitalRead(S1_Pin);
    S2_State = digitalRead(S2_Pin);

    if (S1_State == HIGH)
    {
    else if (S2_State == HIGH)
    {
    PlayerWin(3);
    }
    else
    {
    PlayerWin(2);
    }
    }
    else
    {
    if (S2_State == HIGH)
    {
    PlayerWin(1);
    }
    }
    }

    void checkFirstPress()
    {
    // Whoever presses first wins.
    while (playerXWon == 0)
    {
    S1_State = digitalRead(S1_Pin);
    S2_State = digitalRead(S2_Pin);

    if (S1_State == HIGH)
    {
    if (S2_State == HIGH)
    {
    PlayerWin(3);
    }
    else if (S1_State == HIGH)
    {
    PlayerWin(1);
    }
    }
    else if (S2_State == HIGH)
    {
    PlayerWin(2);
    }
    }
    }

    void playerWins(int player)
    {
    switch (player)
    {
    case 1:
    Serial.print("Player 1 winsn");
    break;

    case 2:
    Serial.print("Player 2 winsn");
    break;

    case 3:
    Serial.print("Both players winn");
    break;

    default:
    assert(NULL); // Programming error
    break;
    }

    playerXWon = player;

    digitalWrite(P1_Pin, LOW);
    }





    share|improve this answer


























    • The cheating pevention will come later, I'm aware of that problem and will add it later. I wanted to tackle this first as it's more important and unlike the cheating prevention I don't know how to do it.

      – oh double-you oh
      2 days ago











    • I'm not sure how much that approach would change things as it would most likely still require one button to be read after the other in whatever loop those 5 seconds take place in. Could you clarify what you mean by "change the delay"?

      – oh double-you oh
      2 days ago











    • Actually, it's better to remove the delay. When it's removed, many times per ms the buttons are checked, so whoever presses the button first will win. Now it is only checked every 5 seconds, and probably both players press the button within this time easily.

      – Michel Keijzers
      2 days ago











    • I also split the big function in smaller functions.

      – Michel Keijzers
      2 days ago











    • Lot's of stuff here I haven't learned yet like switch functions and using void loops as functions, thanks for the pointers.

      – oh double-you oh
      2 days ago



















    1














    I was not sure about posting this as an answer, since it is basically some more considerations on the three existing answers (for reference, they are Michel Keijzers's one - using digitalRead -, Craig's one - read the PIND variable at once -, Filip Franik's one - use interrupts), but it became quite long and so a single comment could not fit all of this.



    What you and the other answerers wrote is in theory correct. Checking the button of player 1 before player 2 gives an advantage to player one. What you lack is... How much advantage?



    Mechanical actions (such as the pressing of a button) have a time duration that is around 50ms*.



    Since I assume your application is a reaction time tester, human reaction times are around 250ms.



    Moreover mechanical buttons have "bounces". Typical debounce times are in the range 20-100ms. You can avoid this by just checking the first edge.



    A player gets an advantage if he is awarded as winner even if the button was pressed at the same time.





    _* I tried to find a source for this, but I was not able to get some data. I tried with an online stopwatch and pressing the two spacebars (notebook and USB keyboard) roughly at the same time, obtaining results around 75ms. This is not a real value, so if someone has some measured values or estimations feel free to comment





    Now, let's assume you properly coded your program so that you check for ties and avoid delays, since these will greatly affect the following measurements.



    In the digitalRead case you are executing this series of actions:




    1. Read status of pin 1 - 3.6us

    2. Read status of pin 2 - 3.6us

    3. Check the statuses and decide who won - some tens of instructions (about 1us)

    4. Loop - a couple instructions (0.5us)


    The 60 instructions comes from this thread (3.6us = 57.8 instructions @16MHz); as for the others it's a rough measurement. Let's assume that the function actually samples the pin at the very end of the function.



    Now, since you are checking for ties, if both buttons get pressed during phases 3, 4 or 1 you will get a tie (since both buttons will be read as pressed). If both buttons get pressed during phase 2, button 2 will be marked as pressed and button 1 not, thus giving advantage to that player. The time when this happens is about 3.6us long. So you are giving a 3.6us advantage to button 2 player.



    In the PIND case you are reading the buttons exactly at the same time. The advantage is thus 0.



    In the interrupt case when both EXT0 and EXT1 interrupts are triggered at the same time EXT0 gets executed, since it is before in the ISR. Consequently the player pressing the button on EXT0 has one interrupt checking cycle of advantage. I admit I do not know how often the inputs are checked for the EXT interrupt, but I assume it performs a check every clock cycle (consequently the advantage is 62.5ns).





    Now, let's sum up the advantage




    • digitalRead: 3.6us

    • PIND: 0

    • interrupt: 62.5ns


    The solution is pretty obvious to me, and it is... Don't care! Even in the worst case scenario you are four order of magnitude faster than your phenomenon. You are giving one player a 4us advantage in a 250ms game (0.0016% of advantage). I'm pretty sure that the mechanical differences between the two buttons (maybe one is a bit stiffer, or has a slightly higher size, or has a slightly lower contact) affect the reading much more than this.



    In the end, with this kind of setup you will reasonably be able to have an accuracy that is at most some tens of milliseconds. With other measurement setups (maybe optically based) you can go as low as 1ms. Adding 4us of advantage to one player will not influence the result.



    Try to focus on the rest of the program, and for this problem use whichever solution you find more understandable.



    NOTE: This is based on the assumption that a proper program is developed, with tie checks and no delays and so on. The program as you wrote in the question is not ok from this point of view (as it is now it will stay in the loop forever; if you change Sx_State to digitalRead it will become roughly fair - something like 4us of advantage to player one and 3.8us of advantage to player two)






    share|improve this answer































      1














      I have to concur with frarugi87 here. The few microseconds taken by
      digitalRead() are completely inconsequential for your application. But
      let me add that, even if your players were super-humans capable of
      sub-millisecond reaction times, you are not giving an advantage to any
      of them if you alternatively check button 1, then button 2,
      then button 1, and so on. The reason is that, in the small time
      window between reading button 1 and reading button 2, you give
      an advantage to player 2: he will win if both buttons are pressed
      simultaneously. In the following time window (between checking
      button 2 and button 1) the same advantage goes to
      player 1.



      The net result is that the game has a tiny amount of randomness: if both
      buttons are pressed simultaneously, the result is seemingly random. But
      again, the chances of this happening are so tiny that it makes no sense
      to worry about it.



      Here is how I would determine the winner. I think this code makes clear
      that we are alternatively checking button 1, then button 2,
      then button 1...



      int find_winner()
      {
      for (;;) {
      if (digitalRead(S1_Pin) == HIGH)
      return 1;
      if (digitalRead(S2_Pin) == HIGH)
      return 2;
      }
      }





      share|improve this answer























        Your Answer






        StackExchange.ifUsing("editor", function () {
        return StackExchange.using("schematics", function () {
        StackExchange.schematics.init();
        });
        }, "cicuitlab");

        StackExchange.ready(function() {
        var channelOptions = {
        tags: "".split(" "),
        id: "540"
        };
        initTagRenderer("".split(" "), "".split(" "), channelOptions);

        StackExchange.using("externalEditor", function() {
        // Have to fire editor after snippets, if snippets enabled
        if (StackExchange.settings.snippets.snippetsEnabled) {
        StackExchange.using("snippets", function() {
        createEditor();
        });
        }
        else {
        createEditor();
        }
        });

        function createEditor() {
        StackExchange.prepareEditor({
        heartbeatType: 'answer',
        autoActivateHeartbeat: false,
        convertImagesToLinks: false,
        noModals: true,
        showLowRepImageUploadWarning: true,
        reputationToPostImages: null,
        bindNavPrevention: true,
        postfix: "",
        imageUploader: {
        brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
        contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
        allowUrls: true
        },
        onDemand: true,
        discardSelector: ".discard-answer"
        ,immediatelyShowMarkdownHelp:true
        });


        }
        });






        oh double-you oh is a new contributor. Be nice, and check out our Code of Conduct.










        draft saved

        draft discarded


















        StackExchange.ready(
        function () {
        StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2farduino.stackexchange.com%2fquestions%2f60618%2fsimultanous-button-reading%23new-answer', 'question_page');
        }
        );

        Post as a guest















        Required, but never shown

























        5 Answers
        5






        active

        oldest

        votes








        5 Answers
        5






        active

        oldest

        votes









        active

        oldest

        votes






        active

        oldest

        votes









        10














        There is a very easy solution to your problem, and it's caller "interrupts".



        The processes inside loop method are executed synchronously, so always one read will be done before second one, and always one check will be done before a second one (even if they are inside one if statement)



        Fortunately there is a way of simulating asynchronous behavior in ATmega. It's called "external interrupt". You can setup ATmega inside Arduino to stop processing loop method and instead immediately run a different method whenever a voltage on one of the pins change. You are "interrupting" execution of loop hence the name "interrupt". In Arduino Uno this can be done (only) on pins 2 and 3.



        https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/



        I made a little model i Tinkercad



        Arduino with two push buttons



        Whenever you push left button PIN2 of the arduino gets shorted to ground.
        Whenever you push right button PIN3 of the arduino gets shorted to ground.



        And now my source code



        void setup()
        {
        pinMode(2, INPUT_PULLUP);
        pinMode(3, INPUT_PULLUP);

        interrupts();
        attachInterrupt(digitalPinToInterrupt(2), Player1ButtonPress, FALLING);
        attachInterrupt(digitalPinToInterrupt(3), Player2ButtonPress, FALLING);

        Serial.begin(9600);
        }

        void Player1ButtonPress() {
        Serial.println("Player 1 wins");
        }

        void Player2ButtonPress() {
        Serial.println("Player 2 wins");
        }

        void loop()
        {
        //nothing here
        }


        And now some explanation.
        In setup we first setup PIN2 and PIN3 as "input with pullup resistor". In our design this means that when button is not pressed those pins will have 5V on them, but as soon as the buttons are pressed voltages go down to 0V. Then we make sure that interrupts are turned on (they are on by default, but I like to show everyone who is reading the code that I will be using them) and then we do some attachInterrupt magic. We make that a "falling edge" (change from 5V to 0V) on PIN2 and PIN3 will immediately run methods Player1ButtonPress and Player2ButtonPress.



        When I was playing with buttons they worked perfectly.



        enter image description here



        And a word about possible cheating. Keeping the button pressed does not create a "falling edge" (voltage on a pin is constantly 0) so if you only add some global variables with info when game starts and who won. Then add some conditions in the two methods responsible for interrupts. You can then use the loop method to start the game, and show result.






        share|improve this answer


























        • This seems just about perfect, thank you!

          – oh double-you oh
          2 days ago











        • Does the interrupt handling give either player an advantage? What happens in the event of a tie?

          – Craig
          2 days ago











        • @Craig The more I think about it the more I think that there still will be a tiny bias, because ATmega will analyze values of pins with 16MHz frequency so there still is possible that a "tie" will happen and one button will be chosen by processor to be a "winner" incorrectly. But this time the decision will take only 1 clock cycle (6 nano seconds). digitalRead take about 50 clock cycles, and we need two of them. So we gained 100 times more resolution and I think it's enough for a "game" to risk suck event. Nothing is perfect in electronics.

          – Filip Franik
          2 days ago


















        10














        There is a very easy solution to your problem, and it's caller "interrupts".



        The processes inside loop method are executed synchronously, so always one read will be done before second one, and always one check will be done before a second one (even if they are inside one if statement)



        Fortunately there is a way of simulating asynchronous behavior in ATmega. It's called "external interrupt". You can setup ATmega inside Arduino to stop processing loop method and instead immediately run a different method whenever a voltage on one of the pins change. You are "interrupting" execution of loop hence the name "interrupt". In Arduino Uno this can be done (only) on pins 2 and 3.



        https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/



        I made a little model i Tinkercad



        Arduino with two push buttons



        Whenever you push left button PIN2 of the arduino gets shorted to ground.
        Whenever you push right button PIN3 of the arduino gets shorted to ground.



        And now my source code



        void setup()
        {
        pinMode(2, INPUT_PULLUP);
        pinMode(3, INPUT_PULLUP);

        interrupts();
        attachInterrupt(digitalPinToInterrupt(2), Player1ButtonPress, FALLING);
        attachInterrupt(digitalPinToInterrupt(3), Player2ButtonPress, FALLING);

        Serial.begin(9600);
        }

        void Player1ButtonPress() {
        Serial.println("Player 1 wins");
        }

        void Player2ButtonPress() {
        Serial.println("Player 2 wins");
        }

        void loop()
        {
        //nothing here
        }


        And now some explanation.
        In setup we first setup PIN2 and PIN3 as "input with pullup resistor". In our design this means that when button is not pressed those pins will have 5V on them, but as soon as the buttons are pressed voltages go down to 0V. Then we make sure that interrupts are turned on (they are on by default, but I like to show everyone who is reading the code that I will be using them) and then we do some attachInterrupt magic. We make that a "falling edge" (change from 5V to 0V) on PIN2 and PIN3 will immediately run methods Player1ButtonPress and Player2ButtonPress.



        When I was playing with buttons they worked perfectly.



        enter image description here



        And a word about possible cheating. Keeping the button pressed does not create a "falling edge" (voltage on a pin is constantly 0) so if you only add some global variables with info when game starts and who won. Then add some conditions in the two methods responsible for interrupts. You can then use the loop method to start the game, and show result.






        share|improve this answer


























        • This seems just about perfect, thank you!

          – oh double-you oh
          2 days ago











        • Does the interrupt handling give either player an advantage? What happens in the event of a tie?

          – Craig
          2 days ago











        • @Craig The more I think about it the more I think that there still will be a tiny bias, because ATmega will analyze values of pins with 16MHz frequency so there still is possible that a "tie" will happen and one button will be chosen by processor to be a "winner" incorrectly. But this time the decision will take only 1 clock cycle (6 nano seconds). digitalRead take about 50 clock cycles, and we need two of them. So we gained 100 times more resolution and I think it's enough for a "game" to risk suck event. Nothing is perfect in electronics.

          – Filip Franik
          2 days ago
















        10












        10








        10







        There is a very easy solution to your problem, and it's caller "interrupts".



        The processes inside loop method are executed synchronously, so always one read will be done before second one, and always one check will be done before a second one (even if they are inside one if statement)



        Fortunately there is a way of simulating asynchronous behavior in ATmega. It's called "external interrupt". You can setup ATmega inside Arduino to stop processing loop method and instead immediately run a different method whenever a voltage on one of the pins change. You are "interrupting" execution of loop hence the name "interrupt". In Arduino Uno this can be done (only) on pins 2 and 3.



        https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/



        I made a little model i Tinkercad



        Arduino with two push buttons



        Whenever you push left button PIN2 of the arduino gets shorted to ground.
        Whenever you push right button PIN3 of the arduino gets shorted to ground.



        And now my source code



        void setup()
        {
        pinMode(2, INPUT_PULLUP);
        pinMode(3, INPUT_PULLUP);

        interrupts();
        attachInterrupt(digitalPinToInterrupt(2), Player1ButtonPress, FALLING);
        attachInterrupt(digitalPinToInterrupt(3), Player2ButtonPress, FALLING);

        Serial.begin(9600);
        }

        void Player1ButtonPress() {
        Serial.println("Player 1 wins");
        }

        void Player2ButtonPress() {
        Serial.println("Player 2 wins");
        }

        void loop()
        {
        //nothing here
        }


        And now some explanation.
        In setup we first setup PIN2 and PIN3 as "input with pullup resistor". In our design this means that when button is not pressed those pins will have 5V on them, but as soon as the buttons are pressed voltages go down to 0V. Then we make sure that interrupts are turned on (they are on by default, but I like to show everyone who is reading the code that I will be using them) and then we do some attachInterrupt magic. We make that a "falling edge" (change from 5V to 0V) on PIN2 and PIN3 will immediately run methods Player1ButtonPress and Player2ButtonPress.



        When I was playing with buttons they worked perfectly.



        enter image description here



        And a word about possible cheating. Keeping the button pressed does not create a "falling edge" (voltage on a pin is constantly 0) so if you only add some global variables with info when game starts and who won. Then add some conditions in the two methods responsible for interrupts. You can then use the loop method to start the game, and show result.






        share|improve this answer















        There is a very easy solution to your problem, and it's caller "interrupts".



        The processes inside loop method are executed synchronously, so always one read will be done before second one, and always one check will be done before a second one (even if they are inside one if statement)



        Fortunately there is a way of simulating asynchronous behavior in ATmega. It's called "external interrupt". You can setup ATmega inside Arduino to stop processing loop method and instead immediately run a different method whenever a voltage on one of the pins change. You are "interrupting" execution of loop hence the name "interrupt". In Arduino Uno this can be done (only) on pins 2 and 3.



        https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/



        I made a little model i Tinkercad



        Arduino with two push buttons



        Whenever you push left button PIN2 of the arduino gets shorted to ground.
        Whenever you push right button PIN3 of the arduino gets shorted to ground.



        And now my source code



        void setup()
        {
        pinMode(2, INPUT_PULLUP);
        pinMode(3, INPUT_PULLUP);

        interrupts();
        attachInterrupt(digitalPinToInterrupt(2), Player1ButtonPress, FALLING);
        attachInterrupt(digitalPinToInterrupt(3), Player2ButtonPress, FALLING);

        Serial.begin(9600);
        }

        void Player1ButtonPress() {
        Serial.println("Player 1 wins");
        }

        void Player2ButtonPress() {
        Serial.println("Player 2 wins");
        }

        void loop()
        {
        //nothing here
        }


        And now some explanation.
        In setup we first setup PIN2 and PIN3 as "input with pullup resistor". In our design this means that when button is not pressed those pins will have 5V on them, but as soon as the buttons are pressed voltages go down to 0V. Then we make sure that interrupts are turned on (they are on by default, but I like to show everyone who is reading the code that I will be using them) and then we do some attachInterrupt magic. We make that a "falling edge" (change from 5V to 0V) on PIN2 and PIN3 will immediately run methods Player1ButtonPress and Player2ButtonPress.



        When I was playing with buttons they worked perfectly.



        enter image description here



        And a word about possible cheating. Keeping the button pressed does not create a "falling edge" (voltage on a pin is constantly 0) so if you only add some global variables with info when game starts and who won. Then add some conditions in the two methods responsible for interrupts. You can then use the loop method to start the game, and show result.







        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited 2 days ago

























        answered 2 days ago









        Filip FranikFilip Franik

        32027




        32027













        • This seems just about perfect, thank you!

          – oh double-you oh
          2 days ago











        • Does the interrupt handling give either player an advantage? What happens in the event of a tie?

          – Craig
          2 days ago











        • @Craig The more I think about it the more I think that there still will be a tiny bias, because ATmega will analyze values of pins with 16MHz frequency so there still is possible that a "tie" will happen and one button will be chosen by processor to be a "winner" incorrectly. But this time the decision will take only 1 clock cycle (6 nano seconds). digitalRead take about 50 clock cycles, and we need two of them. So we gained 100 times more resolution and I think it's enough for a "game" to risk suck event. Nothing is perfect in electronics.

          – Filip Franik
          2 days ago





















        • This seems just about perfect, thank you!

          – oh double-you oh
          2 days ago











        • Does the interrupt handling give either player an advantage? What happens in the event of a tie?

          – Craig
          2 days ago











        • @Craig The more I think about it the more I think that there still will be a tiny bias, because ATmega will analyze values of pins with 16MHz frequency so there still is possible that a "tie" will happen and one button will be chosen by processor to be a "winner" incorrectly. But this time the decision will take only 1 clock cycle (6 nano seconds). digitalRead take about 50 clock cycles, and we need two of them. So we gained 100 times more resolution and I think it's enough for a "game" to risk suck event. Nothing is perfect in electronics.

          – Filip Franik
          2 days ago



















        This seems just about perfect, thank you!

        – oh double-you oh
        2 days ago





        This seems just about perfect, thank you!

        – oh double-you oh
        2 days ago













        Does the interrupt handling give either player an advantage? What happens in the event of a tie?

        – Craig
        2 days ago





        Does the interrupt handling give either player an advantage? What happens in the event of a tie?

        – Craig
        2 days ago













        @Craig The more I think about it the more I think that there still will be a tiny bias, because ATmega will analyze values of pins with 16MHz frequency so there still is possible that a "tie" will happen and one button will be chosen by processor to be a "winner" incorrectly. But this time the decision will take only 1 clock cycle (6 nano seconds). digitalRead take about 50 clock cycles, and we need two of them. So we gained 100 times more resolution and I think it's enough for a "game" to risk suck event. Nothing is perfect in electronics.

        – Filip Franik
        2 days ago







        @Craig The more I think about it the more I think that there still will be a tiny bias, because ATmega will analyze values of pins with 16MHz frequency so there still is possible that a "tie" will happen and one button will be chosen by processor to be a "winner" incorrectly. But this time the decision will take only 1 clock cycle (6 nano seconds). digitalRead take about 50 clock cycles, and we need two of them. So we gained 100 times more resolution and I think it's enough for a "game" to risk suck event. Nothing is perfect in electronics.

        – Filip Franik
        2 days ago













        10














        Instead of using the high level digitalRead() function access the low level port registers. See this documentation.



        A port register is one byte and each bit represents one of the digital inputs on the arduino.



        Do something like this:



        pin_status = PIND; //Input port D (pins 0-7)
        button_1 = bitRead(pin_status, 2); // digital pin 2
        button_2 = bitRead(pin_status, 3); // digital pin 3


        The values are read simultaneously so it is possible to have a tie.






        share|improve this answer




























          10














          Instead of using the high level digitalRead() function access the low level port registers. See this documentation.



          A port register is one byte and each bit represents one of the digital inputs on the arduino.



          Do something like this:



          pin_status = PIND; //Input port D (pins 0-7)
          button_1 = bitRead(pin_status, 2); // digital pin 2
          button_2 = bitRead(pin_status, 3); // digital pin 3


          The values are read simultaneously so it is possible to have a tie.






          share|improve this answer


























            10












            10








            10







            Instead of using the high level digitalRead() function access the low level port registers. See this documentation.



            A port register is one byte and each bit represents one of the digital inputs on the arduino.



            Do something like this:



            pin_status = PIND; //Input port D (pins 0-7)
            button_1 = bitRead(pin_status, 2); // digital pin 2
            button_2 = bitRead(pin_status, 3); // digital pin 3


            The values are read simultaneously so it is possible to have a tie.






            share|improve this answer













            Instead of using the high level digitalRead() function access the low level port registers. See this documentation.



            A port register is one byte and each bit represents one of the digital inputs on the arduino.



            Do something like this:



            pin_status = PIND; //Input port D (pins 0-7)
            button_1 = bitRead(pin_status, 2); // digital pin 2
            button_2 = bitRead(pin_status, 3); // digital pin 3


            The values are read simultaneously so it is possible to have a tie.







            share|improve this answer












            share|improve this answer



            share|improve this answer










            answered 2 days ago









            CraigCraig

            1,865411




            1,865411























                3














                I think you want after a random time, a 'Start' LED to be lit, and whoever presses his/her button first wins.



                There are some problems in your sketch:




                • It seems even when a button is pressed to early, the button is still taken into account. You should check for this 'cheating'. So check directly after the 'start' LED is lit, that no buttons are pressed. If so, that player loses.

                • Assuming the players are honest, you check for their buttons, which probably the first iteration it will not be pressed (that would be an immediate reaction speed). However, than you wait 5 seconds; probably both players pressed their buttons, and you check the button of the first player first. It will help, if you change the delay to 1 ms, or even remove it completely.


                Untested proposal:



                int playerXWon = 0; // No player won yet, 1 = player 1 wins, 2 = player 2 wins, 3 = draw

                void loop()
                {
                // Delay for a random time.
                P1_Time = random(2000, 5000);
                delay(P1_Time);

                CheatCheck();

                if (playerXWon == 0)
                {
                // Start
                digitalWrite(P1_Pin, HIGH);

                checkFirstPress();
                }
                }

                void CheatCheck()
                {
                // Check if a player did not press its button too early.
                // (Btw, it does not matter if the player presses and release its button before the LED goes on.
                S1_State = digitalRead(S1_Pin);
                S2_State = digitalRead(S2_Pin);

                if (S1_State == HIGH)
                {
                else if (S2_State == HIGH)
                {
                PlayerWin(3);
                }
                else
                {
                PlayerWin(2);
                }
                }
                else
                {
                if (S2_State == HIGH)
                {
                PlayerWin(1);
                }
                }
                }

                void checkFirstPress()
                {
                // Whoever presses first wins.
                while (playerXWon == 0)
                {
                S1_State = digitalRead(S1_Pin);
                S2_State = digitalRead(S2_Pin);

                if (S1_State == HIGH)
                {
                if (S2_State == HIGH)
                {
                PlayerWin(3);
                }
                else if (S1_State == HIGH)
                {
                PlayerWin(1);
                }
                }
                else if (S2_State == HIGH)
                {
                PlayerWin(2);
                }
                }
                }

                void playerWins(int player)
                {
                switch (player)
                {
                case 1:
                Serial.print("Player 1 winsn");
                break;

                case 2:
                Serial.print("Player 2 winsn");
                break;

                case 3:
                Serial.print("Both players winn");
                break;

                default:
                assert(NULL); // Programming error
                break;
                }

                playerXWon = player;

                digitalWrite(P1_Pin, LOW);
                }





                share|improve this answer


























                • The cheating pevention will come later, I'm aware of that problem and will add it later. I wanted to tackle this first as it's more important and unlike the cheating prevention I don't know how to do it.

                  – oh double-you oh
                  2 days ago











                • I'm not sure how much that approach would change things as it would most likely still require one button to be read after the other in whatever loop those 5 seconds take place in. Could you clarify what you mean by "change the delay"?

                  – oh double-you oh
                  2 days ago











                • Actually, it's better to remove the delay. When it's removed, many times per ms the buttons are checked, so whoever presses the button first will win. Now it is only checked every 5 seconds, and probably both players press the button within this time easily.

                  – Michel Keijzers
                  2 days ago











                • I also split the big function in smaller functions.

                  – Michel Keijzers
                  2 days ago











                • Lot's of stuff here I haven't learned yet like switch functions and using void loops as functions, thanks for the pointers.

                  – oh double-you oh
                  2 days ago
















                3














                I think you want after a random time, a 'Start' LED to be lit, and whoever presses his/her button first wins.



                There are some problems in your sketch:




                • It seems even when a button is pressed to early, the button is still taken into account. You should check for this 'cheating'. So check directly after the 'start' LED is lit, that no buttons are pressed. If so, that player loses.

                • Assuming the players are honest, you check for their buttons, which probably the first iteration it will not be pressed (that would be an immediate reaction speed). However, than you wait 5 seconds; probably both players pressed their buttons, and you check the button of the first player first. It will help, if you change the delay to 1 ms, or even remove it completely.


                Untested proposal:



                int playerXWon = 0; // No player won yet, 1 = player 1 wins, 2 = player 2 wins, 3 = draw

                void loop()
                {
                // Delay for a random time.
                P1_Time = random(2000, 5000);
                delay(P1_Time);

                CheatCheck();

                if (playerXWon == 0)
                {
                // Start
                digitalWrite(P1_Pin, HIGH);

                checkFirstPress();
                }
                }

                void CheatCheck()
                {
                // Check if a player did not press its button too early.
                // (Btw, it does not matter if the player presses and release its button before the LED goes on.
                S1_State = digitalRead(S1_Pin);
                S2_State = digitalRead(S2_Pin);

                if (S1_State == HIGH)
                {
                else if (S2_State == HIGH)
                {
                PlayerWin(3);
                }
                else
                {
                PlayerWin(2);
                }
                }
                else
                {
                if (S2_State == HIGH)
                {
                PlayerWin(1);
                }
                }
                }

                void checkFirstPress()
                {
                // Whoever presses first wins.
                while (playerXWon == 0)
                {
                S1_State = digitalRead(S1_Pin);
                S2_State = digitalRead(S2_Pin);

                if (S1_State == HIGH)
                {
                if (S2_State == HIGH)
                {
                PlayerWin(3);
                }
                else if (S1_State == HIGH)
                {
                PlayerWin(1);
                }
                }
                else if (S2_State == HIGH)
                {
                PlayerWin(2);
                }
                }
                }

                void playerWins(int player)
                {
                switch (player)
                {
                case 1:
                Serial.print("Player 1 winsn");
                break;

                case 2:
                Serial.print("Player 2 winsn");
                break;

                case 3:
                Serial.print("Both players winn");
                break;

                default:
                assert(NULL); // Programming error
                break;
                }

                playerXWon = player;

                digitalWrite(P1_Pin, LOW);
                }





                share|improve this answer


























                • The cheating pevention will come later, I'm aware of that problem and will add it later. I wanted to tackle this first as it's more important and unlike the cheating prevention I don't know how to do it.

                  – oh double-you oh
                  2 days ago











                • I'm not sure how much that approach would change things as it would most likely still require one button to be read after the other in whatever loop those 5 seconds take place in. Could you clarify what you mean by "change the delay"?

                  – oh double-you oh
                  2 days ago











                • Actually, it's better to remove the delay. When it's removed, many times per ms the buttons are checked, so whoever presses the button first will win. Now it is only checked every 5 seconds, and probably both players press the button within this time easily.

                  – Michel Keijzers
                  2 days ago











                • I also split the big function in smaller functions.

                  – Michel Keijzers
                  2 days ago











                • Lot's of stuff here I haven't learned yet like switch functions and using void loops as functions, thanks for the pointers.

                  – oh double-you oh
                  2 days ago














                3












                3








                3







                I think you want after a random time, a 'Start' LED to be lit, and whoever presses his/her button first wins.



                There are some problems in your sketch:




                • It seems even when a button is pressed to early, the button is still taken into account. You should check for this 'cheating'. So check directly after the 'start' LED is lit, that no buttons are pressed. If so, that player loses.

                • Assuming the players are honest, you check for their buttons, which probably the first iteration it will not be pressed (that would be an immediate reaction speed). However, than you wait 5 seconds; probably both players pressed their buttons, and you check the button of the first player first. It will help, if you change the delay to 1 ms, or even remove it completely.


                Untested proposal:



                int playerXWon = 0; // No player won yet, 1 = player 1 wins, 2 = player 2 wins, 3 = draw

                void loop()
                {
                // Delay for a random time.
                P1_Time = random(2000, 5000);
                delay(P1_Time);

                CheatCheck();

                if (playerXWon == 0)
                {
                // Start
                digitalWrite(P1_Pin, HIGH);

                checkFirstPress();
                }
                }

                void CheatCheck()
                {
                // Check if a player did not press its button too early.
                // (Btw, it does not matter if the player presses and release its button before the LED goes on.
                S1_State = digitalRead(S1_Pin);
                S2_State = digitalRead(S2_Pin);

                if (S1_State == HIGH)
                {
                else if (S2_State == HIGH)
                {
                PlayerWin(3);
                }
                else
                {
                PlayerWin(2);
                }
                }
                else
                {
                if (S2_State == HIGH)
                {
                PlayerWin(1);
                }
                }
                }

                void checkFirstPress()
                {
                // Whoever presses first wins.
                while (playerXWon == 0)
                {
                S1_State = digitalRead(S1_Pin);
                S2_State = digitalRead(S2_Pin);

                if (S1_State == HIGH)
                {
                if (S2_State == HIGH)
                {
                PlayerWin(3);
                }
                else if (S1_State == HIGH)
                {
                PlayerWin(1);
                }
                }
                else if (S2_State == HIGH)
                {
                PlayerWin(2);
                }
                }
                }

                void playerWins(int player)
                {
                switch (player)
                {
                case 1:
                Serial.print("Player 1 winsn");
                break;

                case 2:
                Serial.print("Player 2 winsn");
                break;

                case 3:
                Serial.print("Both players winn");
                break;

                default:
                assert(NULL); // Programming error
                break;
                }

                playerXWon = player;

                digitalWrite(P1_Pin, LOW);
                }





                share|improve this answer















                I think you want after a random time, a 'Start' LED to be lit, and whoever presses his/her button first wins.



                There are some problems in your sketch:




                • It seems even when a button is pressed to early, the button is still taken into account. You should check for this 'cheating'. So check directly after the 'start' LED is lit, that no buttons are pressed. If so, that player loses.

                • Assuming the players are honest, you check for their buttons, which probably the first iteration it will not be pressed (that would be an immediate reaction speed). However, than you wait 5 seconds; probably both players pressed their buttons, and you check the button of the first player first. It will help, if you change the delay to 1 ms, or even remove it completely.


                Untested proposal:



                int playerXWon = 0; // No player won yet, 1 = player 1 wins, 2 = player 2 wins, 3 = draw

                void loop()
                {
                // Delay for a random time.
                P1_Time = random(2000, 5000);
                delay(P1_Time);

                CheatCheck();

                if (playerXWon == 0)
                {
                // Start
                digitalWrite(P1_Pin, HIGH);

                checkFirstPress();
                }
                }

                void CheatCheck()
                {
                // Check if a player did not press its button too early.
                // (Btw, it does not matter if the player presses and release its button before the LED goes on.
                S1_State = digitalRead(S1_Pin);
                S2_State = digitalRead(S2_Pin);

                if (S1_State == HIGH)
                {
                else if (S2_State == HIGH)
                {
                PlayerWin(3);
                }
                else
                {
                PlayerWin(2);
                }
                }
                else
                {
                if (S2_State == HIGH)
                {
                PlayerWin(1);
                }
                }
                }

                void checkFirstPress()
                {
                // Whoever presses first wins.
                while (playerXWon == 0)
                {
                S1_State = digitalRead(S1_Pin);
                S2_State = digitalRead(S2_Pin);

                if (S1_State == HIGH)
                {
                if (S2_State == HIGH)
                {
                PlayerWin(3);
                }
                else if (S1_State == HIGH)
                {
                PlayerWin(1);
                }
                }
                else if (S2_State == HIGH)
                {
                PlayerWin(2);
                }
                }
                }

                void playerWins(int player)
                {
                switch (player)
                {
                case 1:
                Serial.print("Player 1 winsn");
                break;

                case 2:
                Serial.print("Player 2 winsn");
                break;

                case 3:
                Serial.print("Both players winn");
                break;

                default:
                assert(NULL); // Programming error
                break;
                }

                playerXWon = player;

                digitalWrite(P1_Pin, LOW);
                }






                share|improve this answer














                share|improve this answer



                share|improve this answer








                edited 2 days ago

























                answered 2 days ago









                Michel KeijzersMichel Keijzers

                6,45141738




                6,45141738













                • The cheating pevention will come later, I'm aware of that problem and will add it later. I wanted to tackle this first as it's more important and unlike the cheating prevention I don't know how to do it.

                  – oh double-you oh
                  2 days ago











                • I'm not sure how much that approach would change things as it would most likely still require one button to be read after the other in whatever loop those 5 seconds take place in. Could you clarify what you mean by "change the delay"?

                  – oh double-you oh
                  2 days ago











                • Actually, it's better to remove the delay. When it's removed, many times per ms the buttons are checked, so whoever presses the button first will win. Now it is only checked every 5 seconds, and probably both players press the button within this time easily.

                  – Michel Keijzers
                  2 days ago











                • I also split the big function in smaller functions.

                  – Michel Keijzers
                  2 days ago











                • Lot's of stuff here I haven't learned yet like switch functions and using void loops as functions, thanks for the pointers.

                  – oh double-you oh
                  2 days ago



















                • The cheating pevention will come later, I'm aware of that problem and will add it later. I wanted to tackle this first as it's more important and unlike the cheating prevention I don't know how to do it.

                  – oh double-you oh
                  2 days ago











                • I'm not sure how much that approach would change things as it would most likely still require one button to be read after the other in whatever loop those 5 seconds take place in. Could you clarify what you mean by "change the delay"?

                  – oh double-you oh
                  2 days ago











                • Actually, it's better to remove the delay. When it's removed, many times per ms the buttons are checked, so whoever presses the button first will win. Now it is only checked every 5 seconds, and probably both players press the button within this time easily.

                  – Michel Keijzers
                  2 days ago











                • I also split the big function in smaller functions.

                  – Michel Keijzers
                  2 days ago











                • Lot's of stuff here I haven't learned yet like switch functions and using void loops as functions, thanks for the pointers.

                  – oh double-you oh
                  2 days ago

















                The cheating pevention will come later, I'm aware of that problem and will add it later. I wanted to tackle this first as it's more important and unlike the cheating prevention I don't know how to do it.

                – oh double-you oh
                2 days ago





                The cheating pevention will come later, I'm aware of that problem and will add it later. I wanted to tackle this first as it's more important and unlike the cheating prevention I don't know how to do it.

                – oh double-you oh
                2 days ago













                I'm not sure how much that approach would change things as it would most likely still require one button to be read after the other in whatever loop those 5 seconds take place in. Could you clarify what you mean by "change the delay"?

                – oh double-you oh
                2 days ago





                I'm not sure how much that approach would change things as it would most likely still require one button to be read after the other in whatever loop those 5 seconds take place in. Could you clarify what you mean by "change the delay"?

                – oh double-you oh
                2 days ago













                Actually, it's better to remove the delay. When it's removed, many times per ms the buttons are checked, so whoever presses the button first will win. Now it is only checked every 5 seconds, and probably both players press the button within this time easily.

                – Michel Keijzers
                2 days ago





                Actually, it's better to remove the delay. When it's removed, many times per ms the buttons are checked, so whoever presses the button first will win. Now it is only checked every 5 seconds, and probably both players press the button within this time easily.

                – Michel Keijzers
                2 days ago













                I also split the big function in smaller functions.

                – Michel Keijzers
                2 days ago





                I also split the big function in smaller functions.

                – Michel Keijzers
                2 days ago













                Lot's of stuff here I haven't learned yet like switch functions and using void loops as functions, thanks for the pointers.

                – oh double-you oh
                2 days ago





                Lot's of stuff here I haven't learned yet like switch functions and using void loops as functions, thanks for the pointers.

                – oh double-you oh
                2 days ago











                1














                I was not sure about posting this as an answer, since it is basically some more considerations on the three existing answers (for reference, they are Michel Keijzers's one - using digitalRead -, Craig's one - read the PIND variable at once -, Filip Franik's one - use interrupts), but it became quite long and so a single comment could not fit all of this.



                What you and the other answerers wrote is in theory correct. Checking the button of player 1 before player 2 gives an advantage to player one. What you lack is... How much advantage?



                Mechanical actions (such as the pressing of a button) have a time duration that is around 50ms*.



                Since I assume your application is a reaction time tester, human reaction times are around 250ms.



                Moreover mechanical buttons have "bounces". Typical debounce times are in the range 20-100ms. You can avoid this by just checking the first edge.



                A player gets an advantage if he is awarded as winner even if the button was pressed at the same time.





                _* I tried to find a source for this, but I was not able to get some data. I tried with an online stopwatch and pressing the two spacebars (notebook and USB keyboard) roughly at the same time, obtaining results around 75ms. This is not a real value, so if someone has some measured values or estimations feel free to comment





                Now, let's assume you properly coded your program so that you check for ties and avoid delays, since these will greatly affect the following measurements.



                In the digitalRead case you are executing this series of actions:




                1. Read status of pin 1 - 3.6us

                2. Read status of pin 2 - 3.6us

                3. Check the statuses and decide who won - some tens of instructions (about 1us)

                4. Loop - a couple instructions (0.5us)


                The 60 instructions comes from this thread (3.6us = 57.8 instructions @16MHz); as for the others it's a rough measurement. Let's assume that the function actually samples the pin at the very end of the function.



                Now, since you are checking for ties, if both buttons get pressed during phases 3, 4 or 1 you will get a tie (since both buttons will be read as pressed). If both buttons get pressed during phase 2, button 2 will be marked as pressed and button 1 not, thus giving advantage to that player. The time when this happens is about 3.6us long. So you are giving a 3.6us advantage to button 2 player.



                In the PIND case you are reading the buttons exactly at the same time. The advantage is thus 0.



                In the interrupt case when both EXT0 and EXT1 interrupts are triggered at the same time EXT0 gets executed, since it is before in the ISR. Consequently the player pressing the button on EXT0 has one interrupt checking cycle of advantage. I admit I do not know how often the inputs are checked for the EXT interrupt, but I assume it performs a check every clock cycle (consequently the advantage is 62.5ns).





                Now, let's sum up the advantage




                • digitalRead: 3.6us

                • PIND: 0

                • interrupt: 62.5ns


                The solution is pretty obvious to me, and it is... Don't care! Even in the worst case scenario you are four order of magnitude faster than your phenomenon. You are giving one player a 4us advantage in a 250ms game (0.0016% of advantage). I'm pretty sure that the mechanical differences between the two buttons (maybe one is a bit stiffer, or has a slightly higher size, or has a slightly lower contact) affect the reading much more than this.



                In the end, with this kind of setup you will reasonably be able to have an accuracy that is at most some tens of milliseconds. With other measurement setups (maybe optically based) you can go as low as 1ms. Adding 4us of advantage to one player will not influence the result.



                Try to focus on the rest of the program, and for this problem use whichever solution you find more understandable.



                NOTE: This is based on the assumption that a proper program is developed, with tie checks and no delays and so on. The program as you wrote in the question is not ok from this point of view (as it is now it will stay in the loop forever; if you change Sx_State to digitalRead it will become roughly fair - something like 4us of advantage to player one and 3.8us of advantage to player two)






                share|improve this answer




























                  1














                  I was not sure about posting this as an answer, since it is basically some more considerations on the three existing answers (for reference, they are Michel Keijzers's one - using digitalRead -, Craig's one - read the PIND variable at once -, Filip Franik's one - use interrupts), but it became quite long and so a single comment could not fit all of this.



                  What you and the other answerers wrote is in theory correct. Checking the button of player 1 before player 2 gives an advantage to player one. What you lack is... How much advantage?



                  Mechanical actions (such as the pressing of a button) have a time duration that is around 50ms*.



                  Since I assume your application is a reaction time tester, human reaction times are around 250ms.



                  Moreover mechanical buttons have "bounces". Typical debounce times are in the range 20-100ms. You can avoid this by just checking the first edge.



                  A player gets an advantage if he is awarded as winner even if the button was pressed at the same time.





                  _* I tried to find a source for this, but I was not able to get some data. I tried with an online stopwatch and pressing the two spacebars (notebook and USB keyboard) roughly at the same time, obtaining results around 75ms. This is not a real value, so if someone has some measured values or estimations feel free to comment





                  Now, let's assume you properly coded your program so that you check for ties and avoid delays, since these will greatly affect the following measurements.



                  In the digitalRead case you are executing this series of actions:




                  1. Read status of pin 1 - 3.6us

                  2. Read status of pin 2 - 3.6us

                  3. Check the statuses and decide who won - some tens of instructions (about 1us)

                  4. Loop - a couple instructions (0.5us)


                  The 60 instructions comes from this thread (3.6us = 57.8 instructions @16MHz); as for the others it's a rough measurement. Let's assume that the function actually samples the pin at the very end of the function.



                  Now, since you are checking for ties, if both buttons get pressed during phases 3, 4 or 1 you will get a tie (since both buttons will be read as pressed). If both buttons get pressed during phase 2, button 2 will be marked as pressed and button 1 not, thus giving advantage to that player. The time when this happens is about 3.6us long. So you are giving a 3.6us advantage to button 2 player.



                  In the PIND case you are reading the buttons exactly at the same time. The advantage is thus 0.



                  In the interrupt case when both EXT0 and EXT1 interrupts are triggered at the same time EXT0 gets executed, since it is before in the ISR. Consequently the player pressing the button on EXT0 has one interrupt checking cycle of advantage. I admit I do not know how often the inputs are checked for the EXT interrupt, but I assume it performs a check every clock cycle (consequently the advantage is 62.5ns).





                  Now, let's sum up the advantage




                  • digitalRead: 3.6us

                  • PIND: 0

                  • interrupt: 62.5ns


                  The solution is pretty obvious to me, and it is... Don't care! Even in the worst case scenario you are four order of magnitude faster than your phenomenon. You are giving one player a 4us advantage in a 250ms game (0.0016% of advantage). I'm pretty sure that the mechanical differences between the two buttons (maybe one is a bit stiffer, or has a slightly higher size, or has a slightly lower contact) affect the reading much more than this.



                  In the end, with this kind of setup you will reasonably be able to have an accuracy that is at most some tens of milliseconds. With other measurement setups (maybe optically based) you can go as low as 1ms. Adding 4us of advantage to one player will not influence the result.



                  Try to focus on the rest of the program, and for this problem use whichever solution you find more understandable.



                  NOTE: This is based on the assumption that a proper program is developed, with tie checks and no delays and so on. The program as you wrote in the question is not ok from this point of view (as it is now it will stay in the loop forever; if you change Sx_State to digitalRead it will become roughly fair - something like 4us of advantage to player one and 3.8us of advantage to player two)






                  share|improve this answer


























                    1












                    1








                    1







                    I was not sure about posting this as an answer, since it is basically some more considerations on the three existing answers (for reference, they are Michel Keijzers's one - using digitalRead -, Craig's one - read the PIND variable at once -, Filip Franik's one - use interrupts), but it became quite long and so a single comment could not fit all of this.



                    What you and the other answerers wrote is in theory correct. Checking the button of player 1 before player 2 gives an advantage to player one. What you lack is... How much advantage?



                    Mechanical actions (such as the pressing of a button) have a time duration that is around 50ms*.



                    Since I assume your application is a reaction time tester, human reaction times are around 250ms.



                    Moreover mechanical buttons have "bounces". Typical debounce times are in the range 20-100ms. You can avoid this by just checking the first edge.



                    A player gets an advantage if he is awarded as winner even if the button was pressed at the same time.





                    _* I tried to find a source for this, but I was not able to get some data. I tried with an online stopwatch and pressing the two spacebars (notebook and USB keyboard) roughly at the same time, obtaining results around 75ms. This is not a real value, so if someone has some measured values or estimations feel free to comment





                    Now, let's assume you properly coded your program so that you check for ties and avoid delays, since these will greatly affect the following measurements.



                    In the digitalRead case you are executing this series of actions:




                    1. Read status of pin 1 - 3.6us

                    2. Read status of pin 2 - 3.6us

                    3. Check the statuses and decide who won - some tens of instructions (about 1us)

                    4. Loop - a couple instructions (0.5us)


                    The 60 instructions comes from this thread (3.6us = 57.8 instructions @16MHz); as for the others it's a rough measurement. Let's assume that the function actually samples the pin at the very end of the function.



                    Now, since you are checking for ties, if both buttons get pressed during phases 3, 4 or 1 you will get a tie (since both buttons will be read as pressed). If both buttons get pressed during phase 2, button 2 will be marked as pressed and button 1 not, thus giving advantage to that player. The time when this happens is about 3.6us long. So you are giving a 3.6us advantage to button 2 player.



                    In the PIND case you are reading the buttons exactly at the same time. The advantage is thus 0.



                    In the interrupt case when both EXT0 and EXT1 interrupts are triggered at the same time EXT0 gets executed, since it is before in the ISR. Consequently the player pressing the button on EXT0 has one interrupt checking cycle of advantage. I admit I do not know how often the inputs are checked for the EXT interrupt, but I assume it performs a check every clock cycle (consequently the advantage is 62.5ns).





                    Now, let's sum up the advantage




                    • digitalRead: 3.6us

                    • PIND: 0

                    • interrupt: 62.5ns


                    The solution is pretty obvious to me, and it is... Don't care! Even in the worst case scenario you are four order of magnitude faster than your phenomenon. You are giving one player a 4us advantage in a 250ms game (0.0016% of advantage). I'm pretty sure that the mechanical differences between the two buttons (maybe one is a bit stiffer, or has a slightly higher size, or has a slightly lower contact) affect the reading much more than this.



                    In the end, with this kind of setup you will reasonably be able to have an accuracy that is at most some tens of milliseconds. With other measurement setups (maybe optically based) you can go as low as 1ms. Adding 4us of advantage to one player will not influence the result.



                    Try to focus on the rest of the program, and for this problem use whichever solution you find more understandable.



                    NOTE: This is based on the assumption that a proper program is developed, with tie checks and no delays and so on. The program as you wrote in the question is not ok from this point of view (as it is now it will stay in the loop forever; if you change Sx_State to digitalRead it will become roughly fair - something like 4us of advantage to player one and 3.8us of advantage to player two)






                    share|improve this answer













                    I was not sure about posting this as an answer, since it is basically some more considerations on the three existing answers (for reference, they are Michel Keijzers's one - using digitalRead -, Craig's one - read the PIND variable at once -, Filip Franik's one - use interrupts), but it became quite long and so a single comment could not fit all of this.



                    What you and the other answerers wrote is in theory correct. Checking the button of player 1 before player 2 gives an advantage to player one. What you lack is... How much advantage?



                    Mechanical actions (such as the pressing of a button) have a time duration that is around 50ms*.



                    Since I assume your application is a reaction time tester, human reaction times are around 250ms.



                    Moreover mechanical buttons have "bounces". Typical debounce times are in the range 20-100ms. You can avoid this by just checking the first edge.



                    A player gets an advantage if he is awarded as winner even if the button was pressed at the same time.





                    _* I tried to find a source for this, but I was not able to get some data. I tried with an online stopwatch and pressing the two spacebars (notebook and USB keyboard) roughly at the same time, obtaining results around 75ms. This is not a real value, so if someone has some measured values or estimations feel free to comment





                    Now, let's assume you properly coded your program so that you check for ties and avoid delays, since these will greatly affect the following measurements.



                    In the digitalRead case you are executing this series of actions:




                    1. Read status of pin 1 - 3.6us

                    2. Read status of pin 2 - 3.6us

                    3. Check the statuses and decide who won - some tens of instructions (about 1us)

                    4. Loop - a couple instructions (0.5us)


                    The 60 instructions comes from this thread (3.6us = 57.8 instructions @16MHz); as for the others it's a rough measurement. Let's assume that the function actually samples the pin at the very end of the function.



                    Now, since you are checking for ties, if both buttons get pressed during phases 3, 4 or 1 you will get a tie (since both buttons will be read as pressed). If both buttons get pressed during phase 2, button 2 will be marked as pressed and button 1 not, thus giving advantage to that player. The time when this happens is about 3.6us long. So you are giving a 3.6us advantage to button 2 player.



                    In the PIND case you are reading the buttons exactly at the same time. The advantage is thus 0.



                    In the interrupt case when both EXT0 and EXT1 interrupts are triggered at the same time EXT0 gets executed, since it is before in the ISR. Consequently the player pressing the button on EXT0 has one interrupt checking cycle of advantage. I admit I do not know how often the inputs are checked for the EXT interrupt, but I assume it performs a check every clock cycle (consequently the advantage is 62.5ns).





                    Now, let's sum up the advantage




                    • digitalRead: 3.6us

                    • PIND: 0

                    • interrupt: 62.5ns


                    The solution is pretty obvious to me, and it is... Don't care! Even in the worst case scenario you are four order of magnitude faster than your phenomenon. You are giving one player a 4us advantage in a 250ms game (0.0016% of advantage). I'm pretty sure that the mechanical differences between the two buttons (maybe one is a bit stiffer, or has a slightly higher size, or has a slightly lower contact) affect the reading much more than this.



                    In the end, with this kind of setup you will reasonably be able to have an accuracy that is at most some tens of milliseconds. With other measurement setups (maybe optically based) you can go as low as 1ms. Adding 4us of advantage to one player will not influence the result.



                    Try to focus on the rest of the program, and for this problem use whichever solution you find more understandable.



                    NOTE: This is based on the assumption that a proper program is developed, with tie checks and no delays and so on. The program as you wrote in the question is not ok from this point of view (as it is now it will stay in the loop forever; if you change Sx_State to digitalRead it will become roughly fair - something like 4us of advantage to player one and 3.8us of advantage to player two)







                    share|improve this answer












                    share|improve this answer



                    share|improve this answer










                    answered yesterday









                    frarugi87frarugi87

                    2,325415




                    2,325415























                        1














                        I have to concur with frarugi87 here. The few microseconds taken by
                        digitalRead() are completely inconsequential for your application. But
                        let me add that, even if your players were super-humans capable of
                        sub-millisecond reaction times, you are not giving an advantage to any
                        of them if you alternatively check button 1, then button 2,
                        then button 1, and so on. The reason is that, in the small time
                        window between reading button 1 and reading button 2, you give
                        an advantage to player 2: he will win if both buttons are pressed
                        simultaneously. In the following time window (between checking
                        button 2 and button 1) the same advantage goes to
                        player 1.



                        The net result is that the game has a tiny amount of randomness: if both
                        buttons are pressed simultaneously, the result is seemingly random. But
                        again, the chances of this happening are so tiny that it makes no sense
                        to worry about it.



                        Here is how I would determine the winner. I think this code makes clear
                        that we are alternatively checking button 1, then button 2,
                        then button 1...



                        int find_winner()
                        {
                        for (;;) {
                        if (digitalRead(S1_Pin) == HIGH)
                        return 1;
                        if (digitalRead(S2_Pin) == HIGH)
                        return 2;
                        }
                        }





                        share|improve this answer




























                          1














                          I have to concur with frarugi87 here. The few microseconds taken by
                          digitalRead() are completely inconsequential for your application. But
                          let me add that, even if your players were super-humans capable of
                          sub-millisecond reaction times, you are not giving an advantage to any
                          of them if you alternatively check button 1, then button 2,
                          then button 1, and so on. The reason is that, in the small time
                          window between reading button 1 and reading button 2, you give
                          an advantage to player 2: he will win if both buttons are pressed
                          simultaneously. In the following time window (between checking
                          button 2 and button 1) the same advantage goes to
                          player 1.



                          The net result is that the game has a tiny amount of randomness: if both
                          buttons are pressed simultaneously, the result is seemingly random. But
                          again, the chances of this happening are so tiny that it makes no sense
                          to worry about it.



                          Here is how I would determine the winner. I think this code makes clear
                          that we are alternatively checking button 1, then button 2,
                          then button 1...



                          int find_winner()
                          {
                          for (;;) {
                          if (digitalRead(S1_Pin) == HIGH)
                          return 1;
                          if (digitalRead(S2_Pin) == HIGH)
                          return 2;
                          }
                          }





                          share|improve this answer


























                            1












                            1








                            1







                            I have to concur with frarugi87 here. The few microseconds taken by
                            digitalRead() are completely inconsequential for your application. But
                            let me add that, even if your players were super-humans capable of
                            sub-millisecond reaction times, you are not giving an advantage to any
                            of them if you alternatively check button 1, then button 2,
                            then button 1, and so on. The reason is that, in the small time
                            window between reading button 1 and reading button 2, you give
                            an advantage to player 2: he will win if both buttons are pressed
                            simultaneously. In the following time window (between checking
                            button 2 and button 1) the same advantage goes to
                            player 1.



                            The net result is that the game has a tiny amount of randomness: if both
                            buttons are pressed simultaneously, the result is seemingly random. But
                            again, the chances of this happening are so tiny that it makes no sense
                            to worry about it.



                            Here is how I would determine the winner. I think this code makes clear
                            that we are alternatively checking button 1, then button 2,
                            then button 1...



                            int find_winner()
                            {
                            for (;;) {
                            if (digitalRead(S1_Pin) == HIGH)
                            return 1;
                            if (digitalRead(S2_Pin) == HIGH)
                            return 2;
                            }
                            }





                            share|improve this answer













                            I have to concur with frarugi87 here. The few microseconds taken by
                            digitalRead() are completely inconsequential for your application. But
                            let me add that, even if your players were super-humans capable of
                            sub-millisecond reaction times, you are not giving an advantage to any
                            of them if you alternatively check button 1, then button 2,
                            then button 1, and so on. The reason is that, in the small time
                            window between reading button 1 and reading button 2, you give
                            an advantage to player 2: he will win if both buttons are pressed
                            simultaneously. In the following time window (between checking
                            button 2 and button 1) the same advantage goes to
                            player 1.



                            The net result is that the game has a tiny amount of randomness: if both
                            buttons are pressed simultaneously, the result is seemingly random. But
                            again, the chances of this happening are so tiny that it makes no sense
                            to worry about it.



                            Here is how I would determine the winner. I think this code makes clear
                            that we are alternatively checking button 1, then button 2,
                            then button 1...



                            int find_winner()
                            {
                            for (;;) {
                            if (digitalRead(S1_Pin) == HIGH)
                            return 1;
                            if (digitalRead(S2_Pin) == HIGH)
                            return 2;
                            }
                            }






                            share|improve this answer












                            share|improve this answer



                            share|improve this answer










                            answered 19 hours ago









                            Edgar BonetEdgar Bonet

                            24.2k22345




                            24.2k22345






















                                oh double-you oh is a new contributor. Be nice, and check out our Code of Conduct.










                                draft saved

                                draft discarded


















                                oh double-you oh is a new contributor. Be nice, and check out our Code of Conduct.













                                oh double-you oh is a new contributor. Be nice, and check out our Code of Conduct.












                                oh double-you oh is a new contributor. Be nice, and check out our Code of Conduct.
















                                Thanks for contributing an answer to Arduino Stack Exchange!


                                • Please be sure to answer the question. Provide details and share your research!

                                But avoid



                                • Asking for help, clarification, or responding to other answers.

                                • Making statements based on opinion; back them up with references or personal experience.


                                To learn more, see our tips on writing great answers.




                                draft saved


                                draft discarded














                                StackExchange.ready(
                                function () {
                                StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2farduino.stackexchange.com%2fquestions%2f60618%2fsimultanous-button-reading%23new-answer', 'question_page');
                                }
                                );

                                Post as a guest















                                Required, but never shown





















































                                Required, but never shown














                                Required, but never shown












                                Required, but never shown







                                Required, but never shown

































                                Required, but never shown














                                Required, but never shown












                                Required, but never shown







                                Required, but never shown







                                Popular posts from this blog

                                "Incorrect syntax near the keyword 'ON'. (on update cascade, on delete cascade,)

                                Alcedinidae

                                RAC Tourist Trophy