Reputation: 856
Yes,,, I know there is many threads about this, and I've think I've read most of them, but either I don't understand the answers or I have not been able to adapt them to my "case".
Be aware, my background is electronic design, not software design so for some of you, my question maybe seem to be stupid, but... I am stuck.
I have designed a pcb board for iot purposes. It is based on a ESP32 module. I have 5 buttons connected to the ESP. ESP32-IDF is far to complicated for me so I've tried to go for the Ardiuno framework. Now it starts to get complicated.
To detect and debounce the buttons I created a C++ class called Button. A skeleton can be seen below.
class Button {
..
..
private:
void memberCallback() {
...
}
public:
Button(const uint8_t gpio ) {
..
attachInterrup(digitalPinToInterrupt(gpio), memberCallback, FALLING);
..
}
..
}
I have not found any way to define "memberCallback" without not causing compile errors or not working at all.
This must be a common problem so, please suggest i solution :)
Edit. Seems like I not have expressed myself clearly enough, sorry. - I am aware that if I make memberCallback static, it will at least compile. Problem is that I planned to use 5 instances of this. A static callback means that all instances will run same code. 5 instances means 5 different interrupts. How do I identify them.
Upvotes: 0
Views: 1548
Reputation: 7089
The ESP32 Arduino Core has an example of how to do exactly this at
I'll quote code from it here:
#include <Arduino.h>
#include <FunctionalInterrupt.h>
#define BUTTON1 16
#define BUTTON2 17
class Button
{
public:
Button(uint8_t reqPin) : PIN(reqPin){
pinMode(PIN, INPUT_PULLUP);
attachInterrupt(PIN, std::bind(&Button::isr,this), FALLING);
};
~Button() {
detachInterrupt(PIN);
}
void IRAM_ATTR isr() {
numberKeyPresses += 1;
pressed = true;
}
private:
const uint8_t PIN;
volatile uint32_t numberKeyPresses;
volatile bool pressed;
}
The important things are:
#include <FunctionalInterrupt.h>
- this gets you std::bind()
and a slightly different attachInterrupt()
declaration that makes this workstd::bind(&Button::isr,this)
to bind your interrupt handler to your object.IRAM_ATTR
to ensure the code for it will remain loaded in RAM.volatile
so that the C++ compiler will know they make change without warning.std::bind()
is a standard C++ library function; there's documentation about it online.
That said, I'm concerned about what you plan to do inside your interrupt handler.
Interrupt handlers need to run for a very brief amount of time, and they need to be very careful about calling other functions and making sure that data structures are in an inconsistent state. Unless you lock out interrupts (which you should do as little as possible) outside of the interrupt handler, data structures can easily be in inconsistent states.
The example in the Arduino Core is good - it just changes a couple of variables. Doing more than that - calling functions like Serial.println()
or allocating memory or creating objects is not safe.
Upvotes: 6
Reputation: 153955
When you look at the documentation of attachInterrupt
it says that the ISR is a function taking no argument. Its type is void(*)()
(or most likely actually extern "C" void(*)()
but I haven’t researched the exact type thoroughly enough). Your memberCallback
looks as if it does not take an argument but that is actually not true: in a [non-static
] member function you got an implicit argument: this
, the pointer to the object which is need to determine which Button
object is used. The type of memberCallback
is void (Button::*)()
and is incompatible with void(*)()
. Th implication is that you will not be able to use a [non-static
] member function directly.
As was suggest in other answers, you could use a static
member function. However, that has two problems:
static
members (both function and variables).extern
"C"`.You will need one function for every Button
you want to register. For just one Button
that would look like that:
class Button
{
// ....
public:
void memberCallback();
Button(int gpio, Button*& ptr, void(*isr)()) {
ptr = this;
attachInterrupt(digitalPinToInterrupt(gpio), isr, FALLING);
}
};
Button* button1;
extern "C" void button1ISR() {
button1ISR->memberCallback();
}
// create you Button somewhere, e.g.:
int main() {
Button b1(gpio, button1, button1ISR);
// ...
}
Upvotes: 1
Reputation: 877
Make your memberCallback
function static. Change your code to this:
class Button {
..
..
private:
static void memberCallback() {
...
}
public:
Button(const uint8_t gpio ) {
..
attachInterrup(digitalPinToInterrupt(gpio), memberCallback, FALLING);
..
}
..
}
Upvotes: 0