Pages: [1]   Go Down
Author Topic: Passing class-member function to another class  (Read 429 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Newbie
*
Karma: 0
Posts: 6
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
#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); };
};
Logged

Dubai, UAE
Offline Offline
Edison Member
*
Karma: 21
Posts: 1670
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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


Logged


North Queensland, Australia
Online Online
Edison Member
*
Karma: 52
Posts: 1774
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Logged


Dubai, UAE
Offline Offline
Edison Member
*
Karma: 21
Posts: 1670
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged


Offline Offline
Newbie
*
Karma: 0
Posts: 6
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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
Logged

Seattle, WA
Offline Offline
God Member
*****
Karma: 7
Posts: 673
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Logged


Dubai, UAE
Offline Offline
Edison Member
*
Karma: 21
Posts: 1670
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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




   
Logged


Seattle, WA
Offline Offline
God Member
*****
Karma: 7
Posts: 673
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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


Logged


Pages: [1]   Go Up
Jump to: