Go Down

Topic: Passing class-member function to another class (Read 594 times) previous topic - next topic

roys1

Hi,
I'm creating a project to turn on & off my home's heat tapes (to melt snow on the roof to prevent ice dams) daily using the TimeAlarms.h library.
I'd like to create a HeatTape class that manages everything internally, including passing the onTickHandler (which is a function called by alarmRepeat). I'd like to define the onTickHandler within the HeatTape class because it uses a variable set within the class.
In my code below, I'd like to pass the HeatTapeClass::turnOn and HeatTapeClass::TurnOff functions to Alarm.alarmRepeat, but can not seem to do so. I've tried many variations including &HeatTape::turnOn without success.

Any suggestions are appreciated.
Thanks!

Code: [Select]

#include <Time.h>
#include <TimeAlarms.h>
class HeatTapeClass {

  public:
 
    // Properties
    char name[10];
    byte powerPin;
    byte programPin;
    byte hourOnLocation;
    byte minuteOnLocation;
    byte hourOffLocation;
    byte minuteOffLocation;
    AlarmId startAlarm;
    AlarmId endAlarm;

    // Methods
    void init(char *theName,
              byte thePowerPin,
              byte theProgramPin,
              byte theHourOnLocation,
              byte theMinuteOnLocation,
              byte theHourOffLocation,
              byte theMinuteOffLocation
              ) {
               
      strcpy(name, theName);
      powerPin = thePowerPin;
      programPin = theProgramPin;
     
      hourOnLocation = theHourOnLocation;
      minuteOnLocation = theMinuteOnLocation;
      hourOffLocation = theHourOffLocation;
      minuteOffLocation = theMinuteOffLocation;
     
      startAlarm = Alarm.alarmRepeat( getHourOn(),
                                      getMinuteOn(),
                                      00, // seconds
                                      turnOn // <----- how do I pass the HeatTapeClass::turnOn function here?
                                      );

      endAlarm = Alarm.alarmRepeat(   getHourOff(),
                                      getMinuteOff(),
                                      00, // seconds
                                      turnOff
                                      );
    };

    boolean isOn() {
      return digitalRead(powerPin);
    };
   
    void turnOn() {
      digitalWrite(powerPin, HIGH);
    };

    void turnOff() {
      digitalWrite(powerPin, LOW);
    };

    void toggleOnOff() {
      digitalWrite(powerPin, !(isOn()));
    };

    boolean isProgramEnabled() {
      return digitalRead(programPin);
    };
   
    void enableProgram() {
      Alarm.enable(startAlarm);
      Alarm.enable(endAlarm);
      digitalWrite(progamPin, HIGH);
    };
   
    void disableProgram() {
      Alarm.disable(startAlarm);
      Alarm.disable(endAlarm);
      digitalWrite(programPin, LOW);
    };

    byte getHourOn()    { return EEPROM.read(hourOnLocation); };
    byte getMinuteOn()  { return EEPROM.read(minuteOnLocation); };
    byte getHourOff()   { return EEPROM.read(hourOffLocation); };
    byte getMinuteOff() { return EEPROM.read(minuteOffLocation); };
   
    void setHourOn(byte value)    { EEPROM.write(hourOnLocation, value); };
    void setMinuteOn(byte value)  { EEPROM.write(minuteOnLocation, value); };
    void setHourOff(byte value)   { EEPROM.write(hourOffLocation, value); };
    void setMinuteOff(byte value) { EEPROM.write(minuteOffLocation, value); };
};

DuaneB

Hi,
   From very distant memory, you can only pass a static function as a callback function, static functions are associated with the class, not any individual instance (object) of the class so you will not have access to your object variables, only status variables.

   As all your doing is turning pins on and off, I might suggest that you take off your perfectly valid but may be out of context 'object orientated' hat and put on your microcontroller 'how can i get this done in 2k memory' hat and have another look at what your trying to do.

   Just a suggestion and there is nothing wrong with trying to do it in objects other than in this case your are inventing problems for yourself - i.e. how do I use half a dozen layers of complexity to change a pin.

Duane B

rcarduino.blogspot.com


Read this
http://rcarduino.blogspot.com/2012/04/servo-problems-with-arduino-part-1.html
then watch this
http://rcarduino.blogspot.com/2012/04/servo-problems-part-2-demonstration.html

Rcarduino.blogspot.com

pYro_65

Objects are fine to use, micro controllers are no reason not to take advantage of the C++ offerings.
This topic may help you, http://www.parashift.com/c++-faq-lite/pointers-to-members.html


DuaneB

Hi,
   Quoting from the link provided by the previous poster -

Quote

[33.2] How do I pass a pointer-to-member-function to a signal handler, X event callback, system call that starts a thread/task, etc?

Don't.


As I mentioned in my original response, an object orientated approach is valid, but in a micro controller you are much closer to the hardware and sometimes it good to acknowledge this and enjoy the differences.

If you want to continue with the OO approach, I would suggest that you revisit your objects so that they are less tightly coupled - i.e. should the alarms be inside the heat tape object - could you achieve what you want more easily if they were outside ?

Duane B.
Read this
http://rcarduino.blogspot.com/2012/04/servo-problems-with-arduino-part-1.html
then watch this
http://rcarduino.blogspot.com/2012/04/servo-problems-part-2-demonstration.html

Rcarduino.blogspot.com

roys1

Hi,

Thanks for the responses. I was hoping for a "just put a -> after the variable and it'll work" type of answer. DuaneB: Your comments are interesting because I'm basically rewriting my original working sketch (which also includes buttons, LED displays, a web server and thermostat control) to make it object oriented. One of the first things that occurred to me is that, although the OO code is more understandable, extensible and compact from a coding perspective, I'm using more RAM. I'm not sure about more Flash but it wouldn't surprise me. pYro_65: the paradshift link greatly increased my understanding of this subject - Thanks!

-Roy


In my code below, I'd like to pass the HeatTapeClass::turnOn and HeatTapeClass::TurnOff functions to Alarm.alarmRepeat, but can not seem to do so. I've tried many variations including &HeatTape::turnOn without success.


The canonical way to do this is to create an interface, and then pass a pointer to that interface.

Code: [Select]

class OnOffInterface
{
public:
  virtual void turnOn(void) =0;
  virtual void turnOff(void) =0;
};

class HeatTapeClass: public OnOffInterface
{
public:
  virtual void turnOn(void)
  {
    Serial.println("I'm on :)");
  }
  virtual void turnOff(void);
  {
    Serial.println("I'm off :(");
  }
};

class AlarmClass
{
public:
  void alarmRepeat(OnOffInterface* p)
  {
    p->turnOn();
    delay(1000);
    p->turnOff();
  }
}

void setup(void)
{
  HeatTapeClass myHeatTape;
  AlarmClass Alarm;

  Alarm.alarmRepeat(&myHeatTape);
}


DuaneB

Hi,
   In a purely OO environment, the approach suggested above would work very nicely however while many Arduino libraries implement classes, they tend to be implemented to work with C rather than a pure 00 approach. The Alarm class expects a C function and will not work with interfaces however if you really want to use an OO approach, you could rewrite the Alarm library to work with interfaces, the previous poster has provided a good template.

   Then again, how much new code and reinventing of existing code do you really want to do to toggle a pin ?

Duane B

rcarduino.blogspot.com




   
Read this
http://rcarduino.blogspot.com/2012/04/servo-problems-with-arduino-part-1.html
then watch this
http://rcarduino.blogspot.com/2012/04/servo-problems-part-2-demonstration.html

Rcarduino.blogspot.com

Ah, yes, indeed.  If you're working with someone else's code, there's almost zero chance it would use this approach.   :smiley-mr-green:  Didn't realize Alarm was an existing class.

In that case, you have no choice but to use a static method.  I'd do something like this

Code: [Select]

class HeatTapeClass
{
private:
  static HeatTapeClass* currentInstance;
public:
  HeatTapeClass(void): currentInstance(this)
  {
  }
  void turnOn(void)
  {
    Serial.println("I'm on! :)");
  }
  static void turnOnCurrent(void)
  {
    currentInstance->turnOn();
  } 
};

HeatTapeClass* HeatTapeClass::currentInstance;

void setup(void)
{
  HeatTapeClass myHeatTape;
 
  Alarm.alarmRepeat( getHourOn(),
                                      getMinuteOn(),
                                      00, // seconds
                                      HeatTapeClass::turnOnCurrent
                                      );
}



Go Up