Search code examples
c++functionpass-by-referenceinterrupt

How to attach a class function as an ISR callback


I am trying to attach a class function as an ISR callback but I am getting this error:

lib/classA/classA.cpp: In member function 'uint8_t MethodA::Init()':
lib/classA/classA.cpp:32:71: error: invalid use of non-static member function 'void MethodA::FlowCounter()'
   attachInterrupt( digitalPinToInterrupt(PIN_B), FlowCounter, FALLING );
                                                                       ^
lib/classA/classA.cpp:12:16: note: declared here
 void IRAM_ATTR MethodA::FlowCounter() {
                ^~~~~~~
*** [.pio\build\featheresp32\lib71a\classA\classA.cpp.o] Error 1

How do I correctly reference the FlowCounter function in the attachInterrupt call?

This is the main.cpp file:

#include <Arduino.h>
#include "classA.h"

MethodA myMethod( 5, 6 );

void setup() {
  myMethod.Init(); 
}

void loop() {
}

This is the classA.h file:

#pragma once

#include <Arduino.h>

class MethodA {

  public:

    MethodA( uint8_t _PIN_A, uint8_t _PIN_B );

    uint32_t stopCount;
    static const uint32_t stepCount;

    uint8_t Init();
    void Update();
    void StartCycle();
    
  private:

    volatile uint32_t flowCount;
    uint8_t pumpDone;

    void IRAM_ATTR FlowCounter();
    uint8_t PIN_A;
    uint8_t PIN_B;
 
};

And the classA.cpp file:

#include <Arduino.h>

#include "classA.h"

const uint32_t MethodA::stepCount = 10;

MethodA::MethodA( uint8_t _PIN_A, uint8_t _PIN_B ) {
    PIN_A = _PIN_A;
    PIN_B = _PIN_B;
}

void IRAM_ATTR MethodA::FlowCounter() {

    flowCount++;

    if ( flowCount >= stopCount ) {
        digitalWrite( PIN_A, LOW );
        pumpDone = true;
    }

    return;
}

uint8_t MethodA::Init() {
  
  pinMode( PIN_A, OUTPUT );
  
  digitalWrite( PIN_B, LOW );

  pinMode( PIN_B, INPUT_PULLUP );
  
  attachInterrupt( digitalPinToInterrupt(PIN_B), FlowCounter, FALLING );

  return true;
}

void MethodA::Update() {

    if ( pumpDone ) {
        Serial.print( "Cycle complete.");
    }

    return;
}

void MethodA::StartCycle() {

    stopCount = flowCount + stepCount;
    digitalWrite( PIN_A, HIGH );
    pumpDone = false;
    return;
}

I tried making the ISR function static by static void IRAM_ATTR FlowCounter(); but that made the compiler scream about the variable inside the function. I tried referencing it by MethodA::FlowCounter but that yielded the same error. I tried using a pointer &FlowCounter and &Method::FlowCounter to no avail. So I am missing something fundamental about cpp programming (I more familiar with the relative ease of Python).


Solution

  • From the IRAM_ATTR I'm assuming you're working with ESP8266 (or ESP32). The Arduino ESP8266 core has an attachInterrupt() overload that accepts a std::function, which can be produced by a std::bind() from an object and a member function. You are calling the wrong overload that is meant for just a function pointer to a static function.

    See

    So you just have to change this to use it:

    #include <FunctionalInterrupt.h>
    
    uint8_t MethodA::Init() {
      pinMode( PIN_A, OUTPUT );
      digitalWrite( PIN_B, LOW );
      pinMode( PIN_B, INPUT_PULLUP );
      attachInterrupt(
        digitalPinToInterrupt(PIN_B),
        std::bind(&MethodA::FlowCounter, this),
        FALLING);
      return true;
    }
    

    Edit: Added include for FunctionalInterrupt.h.