Interrupt in class, ESP32

Hi everyone,

I habe a problem with an interrupt in a class. I tried several variants to write the code but i always get errors.
I use a ESP32 and I want to get a interrupt when pin 36 changes its state. Below is my actual code and i get the error: invalid conversion from 'void ()(void)' to 'void (*)()' on line "attachInterrupt(...)".
IDE is version 1.8.13

Has anyone an idea how i can do it?

Greetings

Frank

class Test
{
	public:
	Test();
	void Init();
	private:
	static void IRAM_ATTR Interrupt_static(void *pInterrupt);
	int x;
};

Test::Test()
{
}

void Test::Init()
{
	pinMode(36,INPUT_PULLUP);
	int t=digitalPinToInterrupt(36);
	attachInterrupt(t,&Test::Interrupt_static,CHANGE);
}

void IRAM_ATTR Test::Interrupt_static(void *pInterrupt)
{
	Test* self = static_cast<Test*>(pInterrupt);
	self->x++;
}

shouldn't the function passed in attachInterrupt() have a void argument?

One thing. GPIO_NUM_36 does not have pullup resistors. All GPIO pins one a ESP32's portB do not have pullups, GPIO & RTC GPIO - ESP32 - — ESP-IDF Programming Guide latest documentation. To use pullups on pin 36 will require the use of the ESP32's RTC API.

Good point, the only problem then is how can I access the variable (x) in the class...
error: invalid use of member 'Test::x' in static member function

When I declare x as static the Linker says undefined reference to `Test::x

The missing pullup is no problem, the signal comes out of an HC14. But Thanks!

interrupts are not executed with arguments. they are simply a function pointer located in an interrupt vector. that function can be hardcoded with variables

not sure what the overhead is accessing a class member within the interrupt

Good luck.

That is exactly my problem. I was also confused with the void* in the interrupt routine but I found it on the web... but it seems not to be working...

isn't it "void(*)(void)", not "void*"? -- a function-ptr (the "*") having no argument (the "(void)" and not returning anything

maybe you should get this working without an interrupt

The method attachInterrupt() will not allow you to specify an argument for the ISR. If you want to pass a pointer to an object to the ISR, you must use a different approach which includes gpio_isr_handler_add, here is an example.

You need a separate definition for it:

int Test::x = 0;

Try this:

#include "Arduino.h"
#include "FunctionalInterrupt.h"

class TestClass {
public:
	TestClass(uint8_t pin) {
		attachInterrupt(digitalPinToInterrupt(pin), std::bind(&TestClass::classIsr, this), RISING);
	}

private:
	uint32_t count = 0;
	void classIsr() {
		count++;
	}
};

TestClass testObject(5);

void setup() {
}

void loop() {
}

It seems to work. Many Thanks! I will continue testing...

what does the "bind" do?

It's one of those "Modern C++" black magic things. My hand-waiving explanation is that the signature of class member functions include a hidden 'this' parameter so that the function can know which instance to operate on.
std::bind(&TestClass::classIsr, this)
Creates an object of the std::bind class that specifies ("binds") the function pointer and instance pointer.

The "FunctionalInterrupt.h" header includes an overload of attachInterrupt() that accepts a std::function<> --- yet more "Modern C++" black magic. The std::function constructor has an overload that accepts a std::bind.

https://en.cppreference.com/w/cpp/utility/functional/bind
https://en.cppreference.com/w/cpp/utility/functional/function

Would a lambda not be the same as using bind?

Not sure. Try it and see if it compiles / works.

@gfvalvo : Good example of why people hate C++ :-/

A lambda would work for encapsulation but the OP wants a contextual and conditional variable so the lambda would have to have access to a outer/global variable. However, even though it's "private", it's the same thing with the class (a function pointer could work too). Since it's all about the intention of usage, realistically, a single global variable makes this simpler than me typing this out :-/

NOTE: don't forcefully bind/tie/use a ISR with a class as there's a lot of overhead that just doesn't make sense (a vtable to boot). An ISR isn't/doesn't have polymorphism as every ISR is functionally independent and identical.

getting ... just curious

C:\stuff\SW\Arduino_Others\Tst\Tst.ino: In constructor 'TestClass::TestClass(uint8_t)':
Tst:28:52: error: 'bind' is not a member of 'std'
attachInterrupt(digitalPinToInterrupt(pin), std::bind(&TestClass::classIsr, this), RISING);

I'm not aware of any C++ police that would force you to use this technique ... or even use C++ for that matter.

There's no vtable or polymorphism required for the code I posted. The overload of attachInterrupt simply stores a std::function object that is invoked when the interrupt occurs and is processed by the initial ISR that's vectored based on the specific interrupt. I doubt there's much more processing overhead than the regular attachInterrupt technique that stores a function pointer.