It seems a difficult thing to do in C++. A callback to a member function in a class in C style, thus making this a void(*)() to be able to use this in a C-style callback function. In this case I want to use this on an ESP32/Arduino to attachInterrupt to a member function.
(I've researched the internet and spend a couple of days to search for a solution, but to no avail).
Good news, I found a solution myself, it's tricky I think, but I haven't seen such a solution before. It works locally running on my Mac, but the bad news, it doesn't work on an ESP32.
On PlatformIO the line:
attachInterrupt(pin,_lambdas.back(),RISING) ;
gives a compile error:
"no suitable conversion function from "std::function<void ()>" to "void (*)()" exists"
whilst the definition of of the attachInterrupt function is exactly the same.
The code is pretty straightforward and simple to understand. The tricky thing I do is storing a Lambda function in a vector. I'm actually surprised that it works, but it does on my Mac. I'm a bit of a noob, but any help to get this to work on an EPS32 is appreciated!!!
See code below:
Main.cpp:
#include "button.hpp"
void pushedA() { printf("Pushed A\n") ; }
void pushedB() { printf("Pushed B\n") ; }
void pushedC() { printf("Pushed C\n") ; }
int main() {
printf("Start\n") ;
Button a(27,pushedA) ;
Button b(25,pushedB) ;
Button c(23,pushedC) ;
printf("Execute\n") ;
// So in the interruptStore we have functionpointers to class members. Now execute!
for (auto interruptRoutine : interruptStore) {
interruptRoutine() ;
}
return 0 ;
}
Button.hpp:
#ifndef BUTTON_H
#define BUTTON_H
#include <vector>
#include <functional>
/* Helper routines to simulate Arduino "attachInterrupt" */
extern std::vector<std::function<void(void)>>interruptStore ;
#define RISING 1
// Same function definition as the Arduino definition, but on the Arduino this doesn't seem to work :-(
inline void attachInterrupt(uint8_t pin, std::function<void(void)> intRoutine, int mode) { interruptStore.push_back(intRoutine) ; }
inline void detachInterrupt(uint8_t pin) { ; }
/**************************************************/
class Button {
public:
Button(uint8_t pin, void (*callbackFunction)()) ;
~Button() ;
private:
inline void pressed() ;
uint32_t _pin ;
void (*_func)() ;
uint32_t _debounce ;
static std::vector<std::function<void(void)>>_lambdas ;
} ;
#endif
Button.cpp:
#include "button.hpp"
std::vector<std::function<void(void)>>interruptStore ;
Button::Button(uint8_t pin, void (*callbackFunction)()) {
_func = callbackFunction ;
auto pressedFunction = [this]() {this->pressed() ;} ;
_lambdas.push_back(pressedFunction) ;
attachInterrupt(pin,_lambdas.back(),RISING) ;
_pin = pin ;
}
Button::~Button() {
detachInterrupt(_pin) ;
}
inline void Button::pressed() {
// if (millis()-_debounce>60) { // Here we would normally handle the debounce
printf("Button on pin %d pressed\n",(int)_pin) ;
_func() ;
// _debounce = millis() ;
// }
}
std::vector<std::function<void(void)>>Button::_lambdas ; // We need to define a static here, declaration is done in button.hpp
or on Github: https://github.com/MatersM/Arduino-Button
ps. I couldn't believe it either but it really works on a Mac: enter image description here
Output on terminal, to show it works:
Launching: '/Users/mmaters/Documents/code/BUTTON-TEST/output/main'
Working directory: '/Users/mmaters/Documents/code/BUTTON-TEST'
1 arguments:
argv[0] = '/Users/mmaters/Documents/code/BUTTON-TEST/output/main'
Start
Execute
Button on pin 27 pressed
Pushed A
Button on pin 25 pressed
Pushed B
Button on pin 23 pressed
Pushed C
Process exited with status 0
logout
Saving session...
...copying shared history...
...saving history...truncating history files...
...completed.
[Proces voltooid]
Link to working code: code on godbolt