Pointer to Member Function

I'm not sure how to use pointers to member functions.

I am declaring it in the example class like this:

void (FanSpeed::*ISR_callback)()  = &FanSpeed::handleInterrupt;

where handleInterrupt() is a member function that I am trying to initialize an ISR like this (doesn't compile):

attachInterrupt(digitalPinToInterrupt(irq_pin), this->ISR_callback, value);

I get this error:

FanClassTest.ino: In member function 'void FanSpeed::setup(uint8_t, int)':
FanClassTest.ino:25:76: error: cannot convert 'void (FanSpeed::*)()' to 'void (*)()' for argument '2' to 'void attachInterrupt(uint8_t, void (*)(), int)'
Error compiling.

Full Code:

//
// CLASS DEFINITION
//

class FanSpeed {

  public:
    FanSpeed() {};
    void
      setup(uint8_t irq_pin, int value),
      handleInterrupt(void);
    double
      getSpeed();
    void (FanSpeed::*ISR_callback)()  = &FanSpeed::handleInterrupt;  //<<<<<<< member function pointer

  private:
    uint32_t
      _lastMicros = 0UL,
      _interval = 60000000UL;
    //
};

void FanSpeed::setup(uint8_t irq_pin, int value)
{
  attachInterrupt(digitalPinToInterrupt(irq_pin), this->ISR_callback, value);  // trying to pass a pointer to the member function to attachInturrupt
}

inline void FanSpeed::handleInterrupt(void)
{
  uint32_t nowMicros = micros();
  _interval = nowMicros - _lastMicros;
  _lastMicros = nowMicros;
}

double FanSpeed::getSpeed()
{
  if (micros() - _lastMicros < 4294967296UL) // has rotated in the last ~70 seconds
  {
    return 60000000.0 / _interval;
  }
  else
  {
    return 0;
  }   
}

//
// PROGRAM START
//

FanSpeed* fan1;
uint8_t fan1pin = 2;

void setup()
{
  Serial.begin(115200);
  pinMode(fan1pin, INPUT_PULLUP);
  fan1 = new FanSpeed();
  fan1-> setup(fan1pin, FALLING);
}

void loop()
{
  static uint32_t lastMillis = 0;
  if (millis() - lastMillis > 1000UL)
  {
    Serial.print(F("Fan one speed = ")); Serial.print(floor(fan1->getSpeed() + 0.5), 0);Serial.println(F(" RPM"));
    Serial.print('\r');
    lastMillis = millis();
  }
}

Interrupt handlers can't be member functions. The question you have to ask yourself is which instance of the class do you want to use? How do you tell the ISR that? You can't and that's why ISR can't be a member function unless it is a static member.

Easy, you can't, unless the function is declared static. If you need to call a member function as part of an ISR you have to either make that function static (in which case, you better not ever create more than one instance of the object), or set the ISR to a non-member function that CALLs the member function through it's object.

Regards,
Ray L.

RayLivingston:
Easy, you can't, unless the function is declared static. If you need to call a member function as part of an ISR you have to either make that function static (in which case, you better not ever create more than one instance of the object), or set the ISR to a non-member function that CALLs the member function through it's object.

Regards,
Ray L.

I see...

I had this, but wanted not to have to mess about with the creation of the function prototypes:

//
// CLASS DEFINITION
//

class FanSpeed {

  public:
    void
      setup(uint8_t irq_pin, void (*ISR_callback)(void), int value),
      handleInterrupt(void);
    double
      getSpeed();

  private:
    uint32_t
      _lastMicros = 0UL,
      _interval = 60000000UL;
};

void FanSpeed::setup(uint8_t irq_pin, void (*ISR_callback)(void), int value)
{
  attachInterrupt(digitalPinToInterrupt(irq_pin), ISR_callback, value);
}

inline void FanSpeed::handleInterrupt(void)
{
  uint32_t nowMicros = micros();
  _interval = nowMicros - _lastMicros;
  _lastMicros = nowMicros;
}

double FanSpeed::getSpeed()
{
  if (micros() - _lastMicros < 4294967296UL) // has rotated in the last ~70 seconds
  {
    return 60000000.0 / _interval;
  }
  else
  {
    return 0;
  }   
}

//
// PROGRAM START
//

FanSpeed* fan1;
uint8_t fan1pin = 2;
FanSpeed* fan2;
uint8_t fan2pin = 3;

// functon prototypes

void fan1InterruptHandler(void)
{
  fan1->handleInterrupt();
}

void fan2InterruptHandler(void)
{
  fan2->handleInterrupt();
}

void setup()
{
  Serial.begin(115200);
  pinMode(fan1pin, INPUT_PULLUP);
  pinMode(fan2pin, INPUT_PULLUP);
  fan1 = new FanSpeed();
  fan1-> setup(fan1pin, fan1InterruptHandler, FALLING);
  fan2 = new FanSpeed();
  fan2-> setup(fan2pin, fan2InterruptHandler, FALLING);
}

void loop()
{
  static uint32_t lastMillis = 0;
  if (millis() - lastMillis > 1000UL)
  {
    Serial.print(F("Fan one speed = ")); Serial.print(floor(fan1->getSpeed() + 0.5), 0);Serial.println(F(" RPM"));
    Serial.print(F("Fan two speed = ")); Serial.print(floor(fan2->getSpeed() + 0.5), 0);Serial.println(F(" RPM"));
    Serial.print('\r');
    lastMillis = millis();
  }
}

Yup, that's how you gotta do it. Especially if you want to have more than one instance.

yeah, @PaulS taught me that way when I was learning callbacks, but I was wondering if it could be brought along further.

Thanks!

Calling an ISR from a class