Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature request: be able to set your own _getState() callback function #32

Closed
grayconstruct opened this issue Feb 21, 2022 · 2 comments
Closed

Comments

@grayconstruct
Copy link

grayconstruct commented Feb 21, 2022

I wanted the debounce and longpress capability that I enjoy from Button2 library but my input was a samd21 touch sensor. Might also have other inputs such as analog threshold or buttons on i2c IO expander. If we could set a custom _getState() handler and get around the check for valid pin in Button2::loop() then buttons that are not on digitalRead would be possible and a more generic way than the esp touch button implementation. I was able to do it by changing _getState() to virtual and derived class as follows, but it was extra work and I don't like changing libraries and having a bunch of notes about that in my code. Here's what I got that might give you ideas on a better way:

#pragma once
#ifndef Button2cb_h
#define Button2cb_h
#if defined(ARDUINO_ARCH_ESP32) || defined(ESP8266)
#include
#endif
#include "Arduino.h"
#include <Button2.h>
#define VIRTUAL_PIN 254
typedef byte (*ButtonStateCallbackFunction)(void);
class Button2cb : public Button2 {
protected:
ButtonStateCallbackFunction getState_cb = NULL;
public:
void begin(boolean activeLow = true);
boolean isPressedRaw() const;
void setGetStateHandler(ButtonStateCallbackFunction f);
private:
// note, to properly override this in the base class, it has to be made virtual in Button2.h
byte _getState();
};

/////////////////////////////////////////////////////////////////
/*
Button2cb.cpp
/
/////////////////////////////////////////////////////////////////
#include "Button2cb.h"
void Button2cb::begin(boolean activeLow /
= true */)
{
pin = VIRTUAL_PIN; // this gets us past loop()'s (pin != UNDEFINED_PIN) check
longclick_detected_retriggerable = false;
_pressedState = activeLow ? LOW : HIGH;
setDebounceTime(DEBOUNCE_MS);
setLongClickTime(LONGCLICK_MS);
setDoubleClickTime(DOUBLECLICK_MS);
// this constructor allows us to get around setting pinMode on our VIRTUAL_PIN
state = _getState();
prev_state = state;
}
void Button2cb::setGetStateHandler(ButtonStateCallbackFunction f) {
getState_cb = f;
}
boolean Button2cb::isPressedRaw() const { // another digitalRead to work around
if(getState_cb != NULL)
return (getState_cb() == _pressedState);
return false;
}
/////////////////////////////////////////////////////////////////
// note, to properly override this in the base class, it has to be made virtual in Button2.h
byte Button2cb::_getState() {
if(getState_cb != NULL)
{
return getState_cb(); // use our installed callback function to get the virtual pin's state
}
return 0;
}

/////////////////// Now with the new class, I can use the following

Button2cb DebouncerQtColorChange;
// callback function to read our samd21 qTouch via the measure() function to get the analog value of the touch peripheral
byte ButtonQtColorGetState()
{
return (ButtonQtColorChange.measure() > QtThreshold) ? LOW : HIGH;
}

void setup()
{
DebouncerQtColorChange.begin(); // Button2 subclass
DebouncerQtColorChange.setGetStateHandler(ButtonQtColorGetState); // set my custom way to _getState()
DebouncerQtColorChange.setClickHandler(AnimationColorShiftButtonHandler);
DebouncerQtColorChange.setLongClickHandler(AnimationColorShiftButtonHandler);
}

@LennartHennigs
Copy link
Owner

Hey,
thank you for the suggestion.
Yes, I understand your request and problem. And that's a nice idea.
Give me some time to think about it how to best implement and handle this inside the lib.

Cheers
l.

@LennartHennigs
Copy link
Owner

Hey,
thought about your suggestion a bit.
I think some people might be not too familiar with inheritance when they are into Arduino programming.
Thus I'd like to introduce something like this:

  • a function that allows you to set a custom getState() function, this eliminates the need for inheritance
  • refactored up the code, to there is only digitalRead() used in _getState()
  • kept the VIRTUAL_PIN to skip the pin setup in my begin() function
  • In the example below I use a regular button pin but I think it makes the idea clear
  • the only thing that is now not encapsulated is the initialization of the "custom button", here the pinMode(39, INPUT_PULLUP); but I feel that is ok

@grayconstruct Thoughts?

#include "Button2.h"

Button2 button;

byte myStateHandler() {
  return digitalRead(39);
}

void setup() {
  Serial.begin(9600);
  delay(100);
  Serial.println("\n\nTest");  
  button.begin(VIRTUAL_PIN);

  pinMode(39, INPUT_PULLUP);

  button.setGetStateFunction(myStateHandler);
  button.setTapHandler([](Button2 &b) {
    Serial.print("tap");
  });
}

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants