attachInterrupt Pin Inside of Class

Hello dear community,

I am a newbie in the microcontroller scene. Recently, I have been trying to integrate attachInterrupt into a class which you can find here at my repository InputDigital/InputDigital.h at main · DngQucVng/InputDigital.

I looked all over the internet and found several solutions. Some require pointer, others require static ISR which come with their own disadvantages. The best method I could find is using the function “std::bind” but this could only worked for the ESP32 core with its <FunctionalInterrupt.h> and <WInterrupt.h> for the STM32duino core. I tested this way with an ESP32 and a STM32F1 Blue Pill, they worked wonderfully.

I wonder if there is an similar solution for the Arduino core since I tried it with an Uno and the error I got was "error: 'bind' is not a member of 'std’".

Thank you for reading.

This limitation comes from the compiler and standard library for AVR, not from attachInterrupt itself. On ESP32 and STM32, the cores added extended support for functional callbacks, but the AVR core never did, and with the 8-bit AVR resource constraints it’s unlikely to change.

On AVR, attachInterrupt can only accept a plain function pointer with the ISR signature void func(). It cannot accept non-static member functions, lambdas with captures, and as you noted std::function support is not there.

If you want to wrap interrupts in a class, you have to use one of the common workarounds like using a static function (which has the correct signature) inside the class as the ISR, and inside that function call back into a global pointer or reference to the instance (That static function does not know which object to call, so you keep a global table of object pointers and each interrupt number corresponds to an entry in that table. When the interrupt fires, attachInterrupt calls the static ISR. The static ISR looks up the correct object pointer from the table (using the interrupt number as the index) and then calls a normal instance method on that object.).

I’m not convinced we need to push OO to that level on small MCUs.

Why would you do that?

Do you want to attach an interrupt to an object or an object to an interrupt?

For examples of the technique described by @J-M-L, see:

My Post #25 Here.
My Post #6 Here.

BTW, if you are using one of those processors, I find using a lambda expression to be more straightforward than std::bind:

#include <FunctionalInterrupt.h>

class MyClass {
  public:
    MyClass(uint8_t p) : interruptPin(p) {}

    bool begin() {
      intNumber = static_cast<InterruptNumberType>(digitalPinToInterrupt(interruptPin));
      if (intNumber == NOT_AN_INTERRUPT) {
        return false;
      }
      auto isr {[this]() {
        this->memberISR();
      }};
      attachInterrupt(intNumber, isr, FALLING);
      return true;
    }

    ~MyClass() {
      if (intNumber != NOT_AN_INTERRUPT) {
        detachInterrupt(intNumber);
      }
    }

  private:
    using InterruptNumberType = decltype(NOT_AN_INTERRUPT);
    uint8_t interruptPin;
    InterruptNumberType intNumber {NOT_AN_INTERRUPT};

    void memberISR() {
      // Handle Interrupt Here
    }

};

MyClass myObject(3);

void setup() {
  auto result {myObject.begin()};
  assert(result && "myObject.begin FAILED");
}

void loop() {
}