bxparks / AceButton

An adjustable, compact, event-driven button library for Arduino that debounces and dispatches events to a user-defined event handler.
MIT License
385 stars 37 forks source link

Multiple buttons not working #31

Closed codinghusi closed 4 years ago

codinghusi commented 4 years ago

I tested a bit around with this and figured out that I can't use more buttons than one? The following code will print "button2" if I press button1 or button2. It doesn't matter.

#include <AceButton.h>
using namespace ace_button;

AceButton button1(6);
AceButton button2(7);

void button1Handler(AceButton* button, uint8_t eventType, uint8_t buttonState) {
  Serial.println("button1");
}

void button2Handler(AceButton* button, uint8_t eventType, uint8_t buttonState) {
  Serial.println("button2");
}

void setup() {
  Serial.begin(9600);
  pinMode(6, INPUT_PULLUP);
  pinMode(7, INPUT_PULLUP);
  button1.setEventHandler(button1Handler);
  button2.setEventHandler(button2Handler);
}

void loop() {
  button1.check();
  button2.check();
}
bxparks commented 4 years ago

Hi, The problem is that the EventHandler is stored in the ButtonConfig class. By default, both button1 and button2 point to the same, default SystemButtonConfig. The second call to setEventHandler() clobbers the first setEventHandler(). For multiple buttons, you have 2 options.

Option 1: Two ButtonConfigs

Create multiple ButtonConfigs, as shown in examples/TunerButtons:

#include <AceButton.h>
using namespace ace_button;

ButtonConfig config1;
AceButton button1(&config1);
ButtonConfig config2;
AceButton button2(&config2);

void button1Handler(AceButton* button, uint8_t eventType, uint8_t buttonState) {
  Serial.println("button1");
}

void button2Handler(AceButton* button, uint8_t eventType, uint8_t buttonState) {
  Serial.println("button2");
}

void setup() {
  Serial.begin(9600);
  pinMode(6, INPUT_PULLUP);
  pinMode(7, INPUT_PULLUP);
  config1.setEventHandler(button1Handler);
  config2.setEventHandler(button2Handler);
  button1.init(6);
  button2.init(7);
}

void loop() {
  button1.check();
  button2.check();
}

Option 2: One ButtonConfig using getPin() to Discriminate

Use the AceButton::getPin() method to discriminate between the two buttons.

#include <AceButton.h>
using namespace ace_button;

AceButton button1(6);
AceButton button2(7);

void button1Handler(AceButton* button, uint8_t eventType, uint8_t buttonState) {
  Serial.println("button1");
}

void button2Handler(AceButton* button, uint8_t eventType, uint8_t buttonState) {
  Serial.println("button2");
}

void buttonHandler(AceButton* button, uint8_t eventType, uint8_t buttonState) {
  switch (button->getPin()) {
    case 6:
      button1Handler(button, eventType, buttonState);
      break;
    case 7:
      button2Handler(button, eventType, buttonState);
      break;
  }
}

void setup() {
  Serial.begin(9600);
  pinMode(6, INPUT_PULLUP);
  pinMode(7, INPUT_PULLUP);
  ButtonConfig* config = ButtonConfig::getSystemButtonConfig();
  config->setEventHandler(buttonHandler);
}

void loop() {
  button1.check();
  button2.check();
}

I tried to explain this in https://github.com/bxparks/AceButton#multiple-buttons, but you are not the first person to get confused by this. I will update the README.md with better explanation and examples.

codinghusi commented 4 years ago

Okay thanks!

T-vK commented 4 years ago

I ran into this problem as well, I couldn't get bxparks' option 1 to work. His second option worked like a charm for me though. Here is a clean example for 3 buttons:

#include <AceButton.h>
using namespace ace_button;

const int BUTTON_1_PIN = 7;
const int BUTTON_2_PIN = 5;
const int BUTTON_3_PIN = 6;

AceButton button1(BUTTON_1_PIN);
AceButton button2(BUTTON_2_PIN);
AceButton button3(BUTTON_3_PIN);

void buttonHandler(AceButton*, uint8_t, uint8_t);

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

  pinMode(BUTTON_1_PIN, INPUT_PULLUP);
  pinMode(BUTTON_2_PIN, INPUT_PULLUP);
  pinMode(BUTTON_3_PIN, INPUT_PULLUP);

  ButtonConfig* buttonConfig = ButtonConfig::getSystemButtonConfig();
  buttonConfig->setEventHandler(buttonHandler);
}

void loop() {
  button1.check();
  button2.check();
  button3.check();
}

void buttonHandler(AceButton* button , uint8_t eventType, uint8_t /* buttonState */) {
  switch (eventType) {
    case AceButton::kEventPressed:
      switch (button->getPin()) {
        case BUTTON_1_PIN:
          Serial.println("Button 1");
          break;
        case BUTTON_2_PIN:
          Serial.println("Button 2");
          break;
        case BUTTON_3_PIN:
          Serial.println("Button 3");
          break;
      }
      break;
  }
}
bxparks commented 4 years ago

@T-vK: Good job, your posted example is exactly how it should look. But I'm surprised to hear that Option 1 didn't work. It's possible that I made a typo in my post, but the example code in examples/TunerButtons (https://github.com/bxparks/AceButton/tree/develop/examples/TunerButtons) should work.

T-vK commented 4 years ago

I think it would be useful to have a minimal multi-button example in this library because the TunerButtons example seems rather complex. What do you think?

bxparks commented 4 years ago

In the latest release, v1.6, I added 2 more examples:

T-vK commented 4 years ago

Love it!