How: Combinations Of Push Buttons Call Different Functions

Hi All,

I hope I'm posting this in the right category. I'm a first timer here. For that matter, I am a first timer to arduino/microcontrollers in general. Here the question goes:

I have a breadboard with 3 buttons hooked up to pins of an Arduino set to INPUT_PULLUP, and ground, so that when they are closed, the buttons are pulled low. I want to call a function when a given button or combination of buttons is pressed. IE:

Imagine There are 3 Buttons, A, B, and C. When I push just A, I want to call function x, and when I push B, I want to call function Y. However, when I push buttons A AND B, I want to call function Z. I tried the below code to start:

TopButton and MidButton are the above mentioned buttens, and A(), B(), and AB() are three void loop() {

if(digitalRead(TopButton)==LOW){ if(digitalRead(MidButton)==LOW){ AB(); {delay(1);} while (TopButton && MidButton == HIGH);

} A();}

if(digitalRead(MidButton)==LOW){ if(digitalRead(TopButton)==LOW){ AB(); {delay(1);} while (TopButton && MidButton == HIGH); } B();}

And various other combinations. The issue becomes that when one releases the TopButton and MidButton buttons at slightly different times, it goes back around, to selecting the single input. How would I do this best?

Marty

Use millis() to time when the first button press was detected.

Use state detection: you want to react to the start of a button press (the signal going low), rather than the button still being pressed (the signal being low).

Also do look into debouncing your buttons as that’s another source of problems.

You also have to decide on exactly how you want to have the buttons behave. Both press and release will never happen at the exact same time, so how long to wait before deciding it’s just button A, not both? For this you also have to keep track of the state of the button: keep checking that a signal remains LOW for some time.

So now you have an A+B button press, of course one gets released first. What now? Do you want to react to the other button as single button press? I don’t think so - that’s where you have that state detection. You don’t do anything new as long as the button remains low. Just wait for the button signal to become HIGH before doing anything else.

As you have multiple buttons, and need to determine if the buttons are being pressed, or are pressed (the Arduino is a lot faster than your fingers!)

As wvmarle said, button state detection and debouncing is mandatory - followed by ‘scoring’ your buttons so you can determine when a valid combination has been pressed - (I’d suggest around 200mS to avoid mistriggering), only then you can choose which function to be called. (Same when buttons are released because your fingers will come off at slightly different speeds)

If you plan it right, the buttons can accumulate as powers of two - so then you simply need to switch-case for each possible ^2 value. (0 is no buttons)

Hi,
Welcome to the forum.

Please read the first post in any forum entitled how to use this forum.
http://forum.arduino.cc/index.php/topic,148850.0.html then look down to item #7 about how to post your code.
It will be formatted in a scrolling window that makes it easier to read.

As @lastchancename as suggested, give each of your 3 buttons a value to the power of 2.
A=1
B=2
C=4
If you use these values to tell which button is pressed you will get unique values.
valA + valB + valC = buttonfunction.

NO buttons pressed. buttonfunction = 0;
A button pressed. buttonfunction = 1;
B button pressed. buttonfunction = 2;
C button pressed. buttonfunction = 4;

A and B pressed. buttonfunction = 1+2 =3
B and C pressed. buttonfunction = 2+4 =6
A and C pressed. buttonfunction = 1+4 =5

A,B,C pressed. buttonfunction = 1+2+4 = 7.

Then use select…case to run your different functions.

This will save all those if …else… statements as you will have a simple select and a cascade of case 0, case 1, case 2 etc statements.

I hope that is understandable, its late here, I have to get my beauty sleep. :o

Tom… :slight_smile:

Hi Tom,

I tried some of what you talked about here. I have two questions. First, how do I most effectively have the Arduino try adding and checking who is high? IE, if either of my buttons are pushed, see if which ones are pushed. I tried using this:

if (digitalRead(TopButton) || digitalRead(MidButton) == LOW)

It seemed unreliable. For some reason, whichever digitalRead in comes first seems to not always trigger the function. The full sketch can be found below. Similarly, I seem to have an unreliable system going with the not changing when you accidentally release one button first problem. I tried doing this:

 if (digitalRead(TopButton) == HIGH  && digitalRead(MidButton) != HIGH) //some lame attempt at making things not change back on button release. Not so effective.
        {
          delay(1000);
        }

But it proved to be a tad bit buggy. I am pondering how to debounce with this setup. Perhaps I need to take a far different approach?

Thanks again!

Here's the code!

const int TopButton = 5;    // Button A - For Top Out
const int TopLed = 12;      // LED A, When 1 button pushed, this is corrisponds to A.
const int MidButton = 6;    //"" But B
const int MidLed = 11;  // ""But B
byte Stateread = 0;// Keeps track of buttons pushed

void A()
{
  digitalWrite(TopLed, HIGH);
  digitalWrite(MidLed, LOW);

}


void AB()
{
  digitalWrite(TopLed, HIGH);
  digitalWrite(MidLed, HIGH);

}


void B() { 
  digitalWrite(TopLed, LOW);
  digitalWrite(MidLed, HIGH);

}

void setup() {
  pinMode(TopButton, INPUT_PULLUP);
  pinMode(TopLed, OUTPUT);
  pinMode(MidButton, INPUT_PULLUP);
  pinMode(MidLed, OUTPUT);
  Serial.begin(9600);
  AB();

}

void loop() {

  if (digitalRead(TopButton) || digitalRead(MidButton) == LOW) {   
    Stateread = 0;
    if (digitalRead(TopButton) == LOW) {  //If A reads HIGH, then add 2 to the Stateread variable
      Stateread = Stateread + 2;
    }
    if (digitalRead(MidButton) == LOW) {  //If B reads HIGH, then add 4 to the Stateread variable
      Stateread = Stateread + 4;
    }

    // do something different depending on the range value:
    switch (Stateread) {
      case 2:    // your hand is on the sensor
        Serial.println("TOP");
        A();
        break;
      case 4:    // your hand is close to the sensor
        Serial.println("MID");
        B();
        break;
      case 6:    // your hand is a few inches from the sensor
        Serial.println("BOTH");
        AB();
        if (digitalRead(TopButton) == HIGH  && digitalRead(MidButton) != HIGH) //some lame attempt at making things not change back on button release. Not so effective.
        {
          delay(1000);
        }
        break;

    }
    delay(10);  // delay in between reads for stability
  }
}

Marty

It's not that trivial. It requires a few steps in your code.

1) you're going to be checking all your buttons, then store the state in an array, and the moment they went LOW in a separate array (use millis()). 2 ) you're going to check the buttons whether they are LOW and whether they are so for a minimum amount of time - to allow for bounce (10-20 ms is usually enough, especially if you reset the timer every time the button goes LOW). Any button that passed that time is considered a "pressed" button. 3) you're going to see whether these buttons have been pressed long enough to react to (200 ms sound like a good time to me, this should be long enough for the second button to be added yet short enough to still feel "instantly" to the user), and if any is pressed long enough, check whether any other button is pressed at that same time to form a button combination. 4) act on the button press or combo press as determined in step 3.

This is basically your loop(). Check buttons again and again and again, to see if any have been pressed.

I suggested 200ms earlier, allowing the user might want to press multiple buttons in a single action. 20ms is fine for debounce, but might be too short for a multi-press.

That 20 ms is debounce only, after which a button is considered pressed.

But it has to be pressed for 200 ms before any further action is taken - upon which you check for other buttons to be pressed as well, to detect multi-presses.

All in all it's not that trivial. Simply doing a bunch of digitalRead() calls and combining the results and working with that is not going to work well.

wvmarle:
It’s not that trivial. It requires a few steps in your code.

  1. you’re going to be checking all your buttons, then store the state in an array, and the moment they went LOW in a separate array (use millis()).
    2 ) you’re going to check the buttons whether they are LOW and whether they are so for a minimum amount of time - to allow for bounce (10-20 ms is usually enough, especially if you reset the timer every time the button goes LOW). Any button that passed that time is considered a “pressed” button.
  2. you’re going to see whether these buttons have been pressed long enough to react to (200 ms sound like a good time to me, this should be long enough for the second button to be added yet short enough to still feel “instantly” to the user), and if any is pressed long enough, check whether any other button is pressed at that same time to form a button combination.
  3. act on the button press or combo press as determined in step 3.

This is basically your loop(). Check buttons again and again and again, to see if any have been pressed.

GREAT! While I’m not sure EXACTLY how I will do this, this is SUPER SUPER helpful. I really appreciate the detailed response to my dumb, newbie questions.

THANKS SO MUCH!
Marty

Hi, This is the basic code for converting your buttons to a function value,

  TopButVal = 1 * (!digitalRead(TopButton));
  MidButVal = 2 * (!digitalRead(MidButton));
  BotButVal = 4 * (!digitalRead(BitButton));
  ButFuncVal = TopButVal + MidButVal + BotButVal;

If you are debouncing then it may be a bit different.

If you like write some new code just to test it with the IDE monitor.

Tom... :)

wvmarle: It's not that trivial. It requires a few steps in your code.

1) you're going to be checking all your buttons, then store the state in an array, and the moment they went LOW in a separate array (use millis()). 2 ) you're going to check the buttons whether they are LOW and whether they are so for a minimum amount of time - to allow for bounce (10-20 ms is usually enough, especially if you reset the timer every time the button goes LOW). Any button that passed that time is considered a "pressed" button. 3) you're going to see whether these buttons have been pressed long enough to react to (200 ms sound like a good time to me, this should be long enough for the second button to be added yet short enough to still feel "instantly" to the user), and if any is pressed long enough, check whether any other button is pressed at that same time to form a button combination. 4) act on the button press or combo press as determined in step 3.

This is basically your loop(). Check buttons again and again and again, to see if any have been pressed.

Let me make sure I understand this. So I should first log the state of each button, then when they were pushed, then rank, then act.

KC1CWF: Let me make sure I understand this. So I should first log the state of each button, then when they were pushed, then rank, then act.

wvmarle: It's not that trivial. It requires a few steps in your code.

1) you're going to be checking all your buttons, then store the state in an array, and the moment they went LOW in a separate array (use millis()). 2 ) you're going to check the buttons whether they are LOW and whether they are so for a minimum amount of time - to allow for bounce (10-20 ms is usually enough, especially if you reset the timer every time the button goes LOW). Any button that passed that time is considered a "pressed" button. 3) you're going to see whether these buttons have been pressed long enough to react to (200 ms sound like a good time to me, this should be long enough for the second button to be added yet short enough to still feel "instantly" to the user), and if any is pressed long enough, check whether any other button is pressed at that same time to form a button combination. 4) act on the button press or combo press as determined in step 3.

This is basically your loop(). Check buttons again and again and again, to see if any have been pressed.

By chance, does anyone have an example of something quasi-similar to this?

Thanks for all the help!

Marty

Tom gave you a good start in #9...

Wait for the buttons ton’settle’... Then

switch(ButFuncVal). {
   case 1:   // top button 
     // do something
     break;
   case 2::   // mid button 
     // do something
     break;
   case 3::   // top AND mid button 
     // do something
     break;
   case 4::   // bot button 
     // do something
     break;
   default::   // some other combination, or NO buttons
     // do something
}