Cast/converting function pointer?

I'm trying to attachInterrupt in a class. I want to attach a non-static member function in the class.

Hall::Hall() {
  //constructor
  void (Hall::*myISR)() = NULL; // declared pointer
  myISR = &Hall::rpm;
  attachInterrupt(digitalPinToInterrupt(14), myISR, RISING); //ERROR - cannot convert 'void (Hall::*)()' to 'void (*)() for argument '2' to 'void attachInterrupt(uint8_t, void (*)(), int)'
}

void Hall::rpm() {
  count++;
}

The problem is attachInterrupt is excepting a function signature of void ()() but I'm passing is a function of signature void (Hall::)()....

Can I convert 'myISR' pointer to void (*)()?

mistergreen:
The problem is attachInterrupt is excepting a function signature of void ()() but I'm passing is a function of signature void (Hall::)()....

example:

// CLASS DEFINITION

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

  private:
    double
      _timeConstant = 60000000.0;
    uint32_t
      _lastMicros = 0UL,
      _interval = 60000000UL;
    void(*ISR_callback)();
};

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 < 1000000UL) // has rotated in the last second
  {
    return _timeConstant / _interval;
  }
  else
  {
    return 0;
  }   
}

double FanSpeed::getHertz()
{
  if (micros() - _lastMicros < 1000000UL) // has rotated in the last second
  {
    return getSpeed() * 60.0;
  }
  else
  {
    return 0;
  }   
}

// PROGRAM START

FanSpeed* fan1;
uint8_t fan1pin = 2;

FanSpeed* fan2;
uint8_t fan2pin = 3;

void setup()
{
  Serial.begin(9600);
  pinMode(fan1pin, INPUT_PULLUP);
  fan1 = new FanSpeed();
  fan1->setup(fan1pin, [](){fan1->handleInterrupt();}, FALLING);
  fan2 = new FanSpeed();
  fan2->setup(fan2pin, [](){fan2->handleInterrupt();}, FALLING);
}

void loop()
{
  static uint32_t lastMillis = 0;
  if (millis() - lastMillis > 1000UL)
  {
    char message[64] = "";
    sprintf(message, "Fan1 Speed:%4i RPM\t%4i Hz", uint32_t(floor(fan1->getSpeed() + 0.5)), uint32_t(floor(fan1->getHertz() + 0.5)));
    Serial.println(message);
    sprintf(message, "Fan2 Speed:%4i RPM\t%4i Hz", uint32_t(floor(fan2->getSpeed() + 0.5)), uint32_t(floor(fan2->getHertz() + 0.5)));
    Serial.println(message);
    lastMillis = millis();
  }
}

Thanks, I'll look into it.
What's the '[]' in fan1->setup(fan1pin, {fan1->handleInterrupt();}, FALLING) ?
Undefined function?

mistergreen:
Thanks, I'll look into it.
What's the '[]' in fan1->setup(fan1pin, {fan1->handleInterrupt();}, FALLING) ?
Undefined function?

Anonymous function... a lambda. the lambda in that example shares the same function signature as expected in the ISR: void(*)(void)

I see. So calling Fanspeed::setup() in the class would never yield a void(*)(void) signature. You'd get a void(Fanspeed::*)(void)

You'd have to call outside the class? I've tried calling Fanspeed::setup() in the class but the lambda would complain.

mistergreen:
I've tried calling Fanspeed::setup() in the class but the lambda would complain.

I don't understand what you mean by "calling Fanspeed::setup()" in the class...

The example works. setup() passes the lambda (a function pointer) to the class instance.

Oh, it works thanks. I'm saying if I do this, it doesn't. Notice the constructor I made.

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

  private:
    double
      _timeConstant = 60000000.0;
    uint32_t
      _lastMicros = 0UL,
      _interval = 60000000UL;
    void(*ISR_callback)();
};

FanSpeed::FanSpeed() 
{
   //****************Constructor****************
   setup(14, []() { handleInterrupt(); } // error: 'this' was not captured for this lambda function
   // I tried this too
   //setup(14, [this]() { handleInterrupt(); } 
}

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;
}

I'm saying if I do this, it doesn't.

So, don't do that. That code is missing setup() and loop(), so it won't even compile. Any talk about it working, or not, is silly.

FanSpeed::FanSpeed() 
{
   //****************Constructor****************
   setup(14, []() { handleInterrupt(); } // error: 'this' was not captured for this lambda function
   // I tried this too
   //setup(14, [this]() { handleInterrupt(); } 
}

you are going in a circular direction, trying to get the this pointer dragged into the lambda...

again, setup of the ISR takes a pointer to a function of signature void(*)(void), there is no room for any class instance pointer in there!

essentially what my example does is create a function outside of any class and that function then operates on a class/instance... look at Pin2 example here, using my first example above:

// PROGRAM START

void handlePin2(void);

FanSpeed* fan1;
uint8_t fan1pin = 2;

FanSpeed* fan2;
uint8_t fan2pin = 3;

void setup()
{
  Serial.begin(9600);
  pinMode(fan1pin, INPUT_PULLUP);
  fan1 = new FanSpeed();
  fan1->setup(fan1pin, [](){fan1->handleInterrupt();}, FALLING);
  fan2 = new FanSpeed();
  fan2->setup(fan2pin, handlePin2, FALLING);
}

void loop()
{
  static uint32_t lastMillis = 0;
  if (millis() - lastMillis > 1000UL)
  {
    char message[64] = "";
    sprintf(message, "Fan1 Speed:%4i RPM\t%4i Hz", uint32_t(floor(fan1->getSpeed() + 0.5)), uint32_t(floor(fan1->getHertz() + 0.5)));
    Serial.println(message);
    sprintf(message, "Fan2 Speed:%4i RPM\t%4i Hz", uint32_t(floor(fan2->getSpeed() + 0.5)), uint32_t(floor(fan2->getHertz() + 0.5)));
    Serial.println(message);
    lastMillis = millis();
  }
}

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

mistergreen:
I'm trying to attachInterrupt in a class. I want to attach a non-static member function in the class.

This again :slight_smile:

You can't do this. The way that member functions work is that a reference to the instance is passed as a hidden parameter to the function. Interrupts don't have data bundled with them in this way - they save the current status of the chip on the stack and jump to an address, no parameters and whatnot.

You need a plain vanilla function with no arguments. That can be a global, or it can be a static class function. If you want the interrupt to execute against some instance, then you will have to store a reference to that instance somewhere and use it in your ISR.

-- EDIT --

you know, the API could provide this. After all - the interrupt can't be calling the ISR directly because regular C functions have a regular RET on the end of them, not a RETI. The bit than manages attachInterrupt could easily be built to keep a single void pointer.