bxparks / AceButton

An adjustable, compact, event-driven button library for Arduino that debounces and dispatches events to a user-defined event handler.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Button as switch

pietrozanc opened this issue · comments

Is it possible to use the same button once to turn on an output and once to turn off the output? like a switch.. with the same funtion (keventpressed or clicked)

Absolutely! You just have to keep track of the state of your output in a variable outside of the EventHandler function. In other words, something like:

bool isOutputOn = false;

void handleEvent(AceButton*, uint8 eventType, uint8) {
  switch (eventType) {
    case AceButton::kEventClicked:
      processClicked();
      break;
  }
}

void processClicked() {
  if (isOutputOn) {
    setOuputOff();
    isOutputOn = false;
  } else {
    setOutputOn();
    isOutputOn = true;
  }
}

thank you very much, I succeeded! thanks again, now another problem arises, because the project I'm creating there are 4 buttons that have to do the same function as described before (turn on and off an dedicated output like a switch) I tried in some ways but I couldn't do it .. I state that I am using a NodeMCU 0.9

This is my sketch with only one button and it WORK! :)

#include <AceButton.h>

using namespace ace_button;

const int Relay1 = 5;
const int BUTTON_PIN = 14;
bool StRelay1 = false;

AceButton button(BUTTON_PIN);

void handleEvent(AceButton*, uint8_t, uint8_t);

void setup() {
  delay(2000);

  pinMode(Relay1, OUTPUT);
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  button.setEventHandler(handleEvent);

  ButtonConfig* buttonConfig = button.getButtonConfig();
  buttonConfig->setEventHandler(handleEvent);
  buttonConfig->setFeature(ButtonConfig::kFeatureClick);
}

void loop() {
  button.check();
}


void handleEvent(AceButton*, uint8 eventType, uint8) {
  switch (eventType) {
    case AceButton::kEventClicked:
      processClicked();
      break;
  }
}

void processClicked() {
  if (StRelay1) {
    digitalWrite(Relay1, LOW);
    StRelay1 = false;
  } else {
    digitalWrite(Relay1, HIGH);
    StRelay1 = true;
  }
}

And this is a sketch I tried to do to use two buttons with the respective 2 outputs but nothing works


#include <AceButton.h>

using namespace ace_button;

const int LED_PIN = 5;
const int LED_PIN2 = 4;

const int BUTTON_PIN = 14;
const int BUTTON_PIN2 = 12;

bool StRelay1 = false;
bool StRelay2 = false;

AceButton button(BUTTON_PIN);
AceButton button2(BUTTON_PIN);

void handleEvent(AceButton*, uint8_t, uint8_t);
void handleEvent2(AceButton*, uint8_t, uint8_t);

void setup() {
  delay(2000);

  pinMode(LED_PIN, OUTPUT);
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  button.setEventHandler(handleEvent);

  pinMode(LED_PIN2, OUTPUT);
  pinMode(BUTTON_PIN2, INPUT_PULLUP);
  button2.setEventHandler(handleEvent2);

  ButtonConfig* buttonConfig = button.getButtonConfig();
  buttonConfig->setEventHandler(handleEvent);
  buttonConfig->setFeature(ButtonConfig::kFeatureClick);

  ButtonConfig* button2Config = button2.getButtonConfig();
  button2Config->setEventHandler(handleEvent2);
  button2Config->setFeature(ButtonConfig::kFeatureClick);
  
}

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


void handleEvent(AceButton*, uint8 eventType, uint8) {
  switch (eventType) {
    case AceButton::kEventClicked:
      processClicked();
      break;
  }
}

void processClicked() {
  if (StRelay1) {
    digitalWrite(LED_PIN, LOW);
    StRelay1 = false;
  } else {
    digitalWrite(LED_PIN, HIGH);
    StRelay1 = true;
  }
}


void handleEvent2(AceButton*, uint8 eventType, uint8) {
  switch (eventType) {
    case AceButton::kEventClicked:
      processClicked();
      break;
  }
}

void processClicked2() {
  if (StRelay2) {
    digitalWrite(LED_PIN2, LOW);
    StRelay2 = false;
  } else {
    digitalWrite(LED_PIN2, HIGH);
    StRelay2 = true;
  }
}

Someone can help me? sorry for the english i'm italian

Your English is great!

The problem with your latest code (with multiple buttons) is that you forgot (or missed) my comments about Multiple Buttons in the README file. Unless you add some additional code, all AceButton objects by default share the same System ButtonConfig instance. Here is the relevant passage:

Multiple Buttons

When transitioning from a single button to multiple buttons, it's important to remember what's happening underneath the convenience methods. The single AceButton button is assigned to the System ButtonConfig that was created automatically. When an EventHandler is assigned to the button, it is actually assigned to the System ButtonConfig. All subsequent instances of AceButton will also be associated with this event handler, unless another ButtonConfig is explicitly assigned.

So both button and button2 share the same EventHandler, which in this case is the last one defined, so probably handleEvent2(). You can create multiple ButtonConfig instances to define multiple event handlers. Look at the examples/TunerButton code, you can see how to define multiple instances of ButtonConfig.

But, before you go down that road, I think in your simple use case, you may not need to use multiple ButtonConfigs. Because if all 4 of your buttons perform essentially the same function, but on different LEDs or motors, you can distinguish your buttons using the PIN number retrieved from button->getPin(). So something like this:

#include <AceButton.h>

using namespace ace_button;

const int LED_PIN = 5;
const int LED_PIN2 = 4;

const int BUTTON_PIN = 14;
const int BUTTON_PIN2 = 12;

bool StRelay1 = false;
bool StRelay2 = false;

AceButton button(BUTTON_PIN);
AceButton button2(BUTTON_PIN2); // fix typo in original code, should be BUTTON_PIN2

void handleEvent(AceButton*, uint8_t, uint8_t);

void setup() {
  delay(2000);

  pinMode(LED_PIN, OUTPUT);
  pinMode(BUTTON_PIN, INPUT_PULLUP);

  pinMode(LED_PIN2, OUTPUT);
  pinMode(BUTTON_PIN2, INPUT_PULLUP);

  // Configure the System ButtonConfig, assigned to all buttons by default
  ButtonConfig* buttonConfig = button.getButtonConfig();
  buttonConfig->setEventHandler(handleEvent);
  buttonConfig->setFeature(ButtonConfig::kFeatureClick);
}

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

void handleEvent(AceButton* button, uint8 eventType, uint8) {
  switch (eventType) {
    case AceButton::kEventClicked:
      processClicked(button->getPin());
      break;
  }
}

void processClicked(uint8_t buttonPin) {
  if (buttonPin == BUTTON_PIN) {
    if (StRelay1) {
      digitalWrite(LED_PIN, LOW);
      StRelay1 = false;
    } else {
      digitalWrite(LED_PIN, HIGH);
      StRelay1 = true;
    }
  } else if (buttonPin == BUTTON_PIN2) {
    if (StRelay2) {
      digitalWrite(LED_PIN2, LOW);
      StRelay2 = false;
    } else {
      digitalWrite(LED_PIN2, HIGH);
      StRelay2 = true;
    }
  }
}

Notice that your processClicked() method now takes the buttonPin as a parameter, which allows you to distinguish the different buttons.

For only 2 buttons, the code that I wrote above is probably good enough. However, you said that you have 4 buttons, which means that your implementation of processClicked() for 4 buttons will have an if-then-else statement that's essentially duplicated 4 times, which is tedious to type, and even more tedious to debug and maintain in the future.

An alternative solution that you might consider is to use the AceButton::getId() parameter that can be user-defined in the AceButton constructor. I added that extra parameter to solve problems exactly like this. The idea is that you assign an index to each button, which is used to look up the various pin numbers and other stateful information from an array, which will eliminate the duplicated if-then-else statement. Here's the code:

#include <AceButton.h>

using namespace ace_button;

// A simple data struct to keep a button, its led, and its relay state together in one place
struct ButtonInfo {
  uint8_t buttonPin;
  uint8_t ledPin;
  bool relayState;
};

// Cannot be 'const' because 'relayState' is mutable
ButtonInfo BUTTONS[] = {
  {5 , 14, false}, // button1, index 0
  {4 , 12, false}, // button2, index 1
  {..., false}, // button3, index 2
  {..., false}, // button4, index 3
};

const int NUM_BUTTONS = sizeof(BUTTONS) / sizeof(ButtonInfo);

AceButton button1(BUTTONS[0].buttonPin, HIGH, 0);
AceButton button2(BUTTONS[1].buttonPin, HIGH, 1);
AceButton button3(BUTTONS[2].buttonPin, HIGH, 2);
AceButton button4(BUTTONS[3].buttonPin, HIGH, 3);

void handleEvent(AceButton*, uint8_t, uint8_t);

void setup() {
  delay(2000);

  for (int i = 0; i < NUM_BUTTONS; i++) {
    pinMode(BUTTONS[i].buttonPin, INPUT_PULLUP);
    pinMode(BUTTONS[i].ledPin, OUTPUT);
  }

  // Configure the System ButtonConfig
  ButtonConfig* buttonConfig = ButtonConfig::getSystemButtonConfig();
  buttonConfig->setEventHandler(handleEvent);
  buttonConfig->setFeature(ButtonConfig::kFeatureClick);
}

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

void handleEvent(AceButton* button, uint8 eventType, uint8) {
  switch (eventType) {
    case AceButton::kEventClicked:
      processClicked(button->getId());
      break;
  }
}

void processClicked(uint8_t buttonId) {
  if (buttonId >= NUM_BUTTONS) return; // defensive programming...

  ButtonInfo& info = BUTTONS[buttonId];
  if (info.relayState) {
    digitalWrite(info.ledPin, LOW);
    info.relayState = false;
  } else {
    digitalWrite(info.ledPin, HIGH);
    info.relayState = true;
  }
}

The beauty of using an array index is that if you need to add more buttons, like 8 or 10 buttons, your code does not grow much bigger than this, so it's much easier to maintain. Does this help? Or too complicated?

thanks perfect explanation! I managed to do what I wanted, and it integrates perfectly with the other sketch dedicated to home automation! I don't know what to say, thanks again!

No problems. Can I close this ticket?

yes sure, thanks again