Issues with implementing classes/making a library!

Hello!

I have a few questions regarding Arduino libraries. I've been trying to implement my own library into my code, and I've been getting these errors:

sketch_sep24a.cpp.o: In function __static_initialization_and_destruction_0': D:\Users\Arduino\arduino-1.0.5/sketch_sep24a.ino:58: undefined reference to PumpControl::PumpControl(int)'
sketch_sep24a.cpp.o: In function loadSDConfig()': D:\Users\Arduino\arduino-1.0.5/sketch_sep24a.ino:193: undefined reference to PumpControl::setTimers(int, int, int)'

I've build the library as the Arduino reference dictates:

#ifndef PumpControl_h
#define PumpControl_h

#include "Arduino.h"

class PumpControl {
  public:
	// On which pin the relay for the pump is connected.
    PumpControl(int relayPin);
	// Starting the relay on the defined pin.
	void startRelay();
	// Check of the parameters are within the configuration bounds. 
	boolean checkPumpConfig(int pumpOnPerMins, int delayPumpSec, int delayPumpMins);
	// Sets the parameters as the objects configuration 
	void setTimers(int pumpOnPerMins, int delayPumpMins, int delayPumpSec);
	// Main function to control the pump, needs the current time given as hour, minutes and seconds.
    void controlPump(int hour, int mins, int secs);
	// Getter for mainPumpTimer
	int getMainPumpTimer();
	// Setter for mainPumpTimer
	void setMainPumpTimer(int mins);
  private:
    int _relayPin;
    int nextPumpM;
    int nextPumpS;
    int mainPumpTimer;
    boolean pumpOnce;
    boolean setMPTimerOnce; 
};

#endif

And:

#include "Arduino.h"
#include "PumpControl.h"

PumpControl::PumpControl(int relayPin){
  _relayPin = relayPin;
}
void PumpControl::startRelay(){
  pinMode(_relayPin, OUTPUT);
}
void PumpControl::setTimers(int pumpOnPerMins, int delayPumpMins, int delayPumpSec){
  if(checkPumpConfig(pumpOnPerMins, delayPumpMins, delayPumpSec)){
    this.pumpOnPerMins = pumpOnPerMins;
    this.delayPumpMins = delayPumpMins;
    this.delayPumpSec = delayPumpSec;
  }
}
boolean PumpControl::checkPumpConfig(int pumpOnPerMins, int delayPumpSec, int delayPumpMins){
  //Check pump config
  if( delayPumpMins <= 0 || delayPumpMins > 59 ){
    if(delayPumpSec > 0){
	  return false;
	}
  } else if( delayPumpSec <= 0 || delayPumpSec > 59 ){
    if(delayPumpMins > 0){
	  return false;
	}
  } else if( pumpOnPerMins < 1 || pumpOnPerMins > 59 ){
    return false;
  } else {
    return true;
  }
} etc. etc.

And the main code looks like this:

#include <PumpControl.h>
PumpControl pumpOne(2); 
void setup(){
  pumpOne.startRelay();
  pumpOne.setMainPumpTimer(mins());
}
void loadSDConfig(){
  File config;
  char cfgarray[5];
  int pumpOnPerMins;
  int delayPumpMins;
  int delayPumpSec;
  
  // SD card initiation 
  Serial.print("Initializing SD card...");
  pinMode(53, OUTPUT);

  if(!SD.begin(4)){
    Serial.println("initialization failed");
    // no point in carrying on, so do nothing forevermore:
    for(;;)
      ;
    return;
  }
  Serial.println("initialization complete");
  if(SD.exists("config.ini")) {
    Serial.println("Loading configuration,....");
  }
  else {
    Serial.println("Configuration not present on sd card.");
  }
  // open the config file
  config = SD.open("config.ini");
  
  // if the file is available
  if (config) {
    for(int i = 0; config.available(); i++) {
      cfgarray[i] = config.read(); // data saved to config array
    }
    config.close(); // closed the SDCard file
  }  
  // if the file isn't open, pop up an error:
  else {
    Serial.println("Error opening configuration file.");
  } 
  // Load config data into code
  sysMode = cfgarray[0] - 48;
  sensorAmount = cfgarray[1] - 48;
  update2Server = cfgarray[2] - 48;
  pumpOnPerMins = ((cfgarray[3] - 48) * 10) + cfgarray[4] - 48; // pump on every 1st decimal + 2d decimal mins 
  delayPumpMins = ((cfgarray[5] - 48) * 10) + cfgarray[6] - 48; // minutes delay
  delayPumpSec = ((cfgarray[7] - 48) * 10) + cfgarray[8] - 48; // seconds delay 
  //Setting the pump timers for pumpOne Object
  pumpOne.setTimers(pumpOnPerMins, delayPumpMins, delayPumpSec);
 }

Does anyone know why i keep getting these "undefined reference to" errors?

Thanks for any tips! :cold_sweat:

I've build the library as the Arduino reference dictates:

The library reference page does not suggest random indenting in the header file. It does not suggest a complete lack of white space between lines.

} etc. etc.

This will certainly generate a compiler error.

Reading, and if necessary, posting ALL of the output in the IDE's lower window would be good.

You did not say where you put the .h and .cpp files.

    this.pumpOnPerMins = pumpOnPerMins;
    this.delayPumpMins = delayPumpMins;
    this.delayPumpSec = delayPumpSec;

The member this is a pointer. You can't use . notation with pointers.

The PumpControl class fails to compile. You can't then expect the linker to find stuff in PumpControl.o, which wasn't created. That is what the undefined reference messages are saying.

Thank you for the reply PaulS,

The library reference page does not suggest random indenting in the header file. It does not suggest a complete lack of white space between lines.

I've read this tutorial and copied how to make the .h and .cpp files: http://arduino.cc/en/Hacking/LibraryTutorial

The complete compiler error is pretty much the same error again and again, but here's the complete errorlist:

Autoponics_alpha0_1.cpp.o: In function `__static_initialization_and_destruction_0':
D:Arduino\arduino-1.0.5/Autoponics_alpha0_1.ino:58: undefined reference to `PumpControl::PumpControl(int)'
Autoponics_alpha0_1.cpp.o: In function `loadSDConfig()':
D:\Arduino\arduino-1.0.5/Autoponics_alpha0_1.ino:193: undefined reference to `PumpControl::setTimers(int, int, int)'
Autoponics_alpha0_1.cpp.o: In function `displayControl()':
D:\Arduino\arduino-1.0.5/Autoponics_alpha0_1.ino:662: undefined reference to `PumpControl::getMainPumpTimer()'
Autoponics_alpha0_1.cpp.o: In function `systemControl(int, int, int, int)':
D:\Arduino\arduino-1.0.5/Autoponics_alpha0_1.ino:596: undefined reference to `PumpControl::controlPump(int, int, int)'
D:\Arduino\arduino-1.0.5/Autoponics_alpha0_1.ino:602: undefined reference to `PumpControl::controlPump(int, int, int)'
D:\Arduino\arduino-1.0.5/Autoponics_alpha0_1.ino:611: undefined reference to `PumpControl::controlPump(int, int, int)'
Autoponics_alpha0_1.cpp.o: In function `setup':
D:\Arduino\arduino-1.0.5/Autoponics_alpha0_1.ino:82: undefined reference to `PumpControl::startRelay()'
D:\Arduino\arduino-1.0.5/Autoponics_alpha0_1.ino:83: undefined reference to `PumpControl::setMainPumpTimer(int)'

The .h and .cpp files are located inside the PumpControl folder that resides libraries folder, which is inside the Arduino folder.

I also changed the setTimers method, like:

     _pumpOnPerMins = pumpOnPerMins;
    _delayPumpMins = delayPumpMins;
    _delayPumpSec = delayPumpSec;

Any idea's why the PumpControl class still fails to compile?

Any idea's why the PumpControl class still fails to compile?

If you post the real code, ALL of it, I can try to build it, and see what errors I get.

Here is the complete code:

http://pastebin.com/y2SYLJ6s

Plus the two library files:
.h:

/*
  PumpControl lib for Autoponics
*/
#ifndef PumpControl_h
#define PumpControl_h

#include "Arduino.h"

class PumpControl {
  public:
	// On which pin the relay for the pump is connected.
    PumpControl(int relayPin);
	// Starting the relay on the defined pin.
	void startRelay();
	// Check of the parameters are within the configuration bounds. 
	boolean checkPumpConfig(int pumpOnPerMins, int delayPumpSec, int delayPumpMins);
	// Sets the parameters as the objects configuration 
	void setTimers(int pumpOnPerMins, int delayPumpMins, int delayPumpSec);
	// Main function to control the pump, needs the current time given as hour, minutes and seconds.
    void controlPump(int hour, int mins, int secs);
	// Getter for mainPumpTimer
	int getMainPumpTimer();
	// Setter for mainPumpTimer
	void setMainPumpTimer(int mins);
  private:
    int _relayPin;
    int _nextPumpM;
    int _nextPumpS;
    int _mainPumpTimer;
    boolean pumpOnce;
    boolean setMPTimerOnce; 
};

#endif

And the .ccp:

#include "Arduino.h"
#include "PumpControl.h"

PumpControl::PumpControl(int relayPin){
  _relayPin = relayPin;
}
void PumpControl::startRelay(){
  pinMode(_relayPin, OUTPUT);
}
void PumpControl::setTimers(int pumpOnPerMins, int delayPumpMins, int delayPumpSec){
  if(checkPumpConfig(pumpOnPerMins, delayPumpMins, delayPumpSec)){
    _pumpOnPerMins = pumpOnPerMins;
    _delayPumpMins = delayPumpMins;
    _delayPumpSec = delayPumpSec;
  }
}
boolean PumpControl::checkPumpConfig(int pumpOnPerMins, int delayPumpSec, int delayPumpMins){
  //Check pump config
  if( delayPumpMins <= 0 || delayPumpMins > 59 ){
    if(delayPumpSec > 0){
	  return false;
	}
  } else if( delayPumpSec <= 0 || delayPumpSec > 59 ){
    if(delayPumpMins > 0){
	  return false;
	}
  } else if( pumpOnPerMins < 1 || pumpOnPerMins > 59 ){
    return false;
  } else {
    return true;
  }
}
void PumpControl::controlPump(int hour, int mins, int secs){
  if( hour >= 7 && hour <= 23 ){  
    
    // Pump ON
    if( mainPumpTimer == mins && pumpOnce == false ){
      digitalWrite(_relayPin, HIGH);
      pumpOnce = true;
      Serial.print("Pumping water into terrace.  @");
      Serial.println(mins);
      
	  nextPumpM = mins + _delayPumpMins;
      nextPumpS = secs + _delayPumpSec;
      if( nextPumpS >= 60 ){
        nextPumpM = nextPumpM + 1;
	    nextPumpS = nextPumpS - 60;
      }
      if( nextPumpM >= 60 ){
        nextPumpM = nextPumpM - 60;
      }
    }
  }
  // Pump OFF
  if( mins >= nextPumpM && secs >= nextPumpS && pumpOnce == true ){
    digitalWrite(_relayPin, LOW);
    setMPTimerOnce = false;
    pumpOnce = false;
    Serial.print("Stopping waterflow to terrace.  @");
    Serial.println(mins);
	
	
  }
  
  // mainPumpTimer init + check
  if( pumpOnce == false && setMPTimerOnce == false){
    mainPumpTimer = mins + _pumpOnPerMins;
    setMPTimerOnce = true;  
  }
  if( mainPumpTimer >= 60 ){
    mainPumpTimer = mainPumpTimer - 60;
  }
}
int PumpControl::getMainPumpTimer(){
  return mainPumpTimer;
}  
void PumpControl::setMainPumpTimer(int mins){
  mainPumpTimer = mins;
}

Thank you for your time! :open_mouth:

That sketch is ridiculously complicated. Build a sketch that just creates an instance of PumpControl and calls a few functions. The PumpControl class won't even compile. Trying to solve the linker issues before the code compiles is silly.

I was going to copy and paste the errors, but there are way too many of them.

What method are you using to compile the code?

I know, i only recently learned how to work with classes and thus my code was just a huge pile of code.

This is also why i only posted a small portion of the code in the original post:

#include <SD.h>
#include <PumpControl.h>

PumpControl pumpOne(2); 
void setup(){
  pumpOne.startRelay();
  pumpOne.setMainPumpTimer(5);
}
void loadSDConfig(){
  File config;
  char cfgarray[5];
  int pumpOnPerMins;
  int delayPumpMins;
  int delayPumpSec;
  
  // SD card initiation 
  Serial.print("Initializing SD card...");
  pinMode(53, OUTPUT);

  if(!SD.begin(4)){
    Serial.println("initialization failed");
    // no point in carrying on, so do nothing forevermore:
    for(;;)
      ;
    return;
  }
  Serial.println("initialization complete");
  if(SD.exists("config.ini")) {
    Serial.println("Loading configuration,....");
  }
  else {
    Serial.println("Configuration not present on sd card.");
  }
  // open the config file
  config = SD.open("config.ini");
  
  // if the file is available
  if (config) {
    for(int i = 0; config.available(); i++) {
      cfgarray[i] = config.read(); // data saved to config array
    }
    config.close(); // closed the SDCard file
  }  
  // if the file isn't open, pop up an error:
  else {
    Serial.println("Error opening configuration file.");
  } 
  // Load config data into code
  //sysMode = cfgarray[0] - 48;
  //sensorAmount = cfgarray[1] - 48;
  //update2Server = cfgarray[2] - 48;
  pumpOnPerMins = ((cfgarray[3] - 48) * 10) + cfgarray[4] - 48; // pump on every 1st decimal + 2d decimal mins 
  delayPumpMins = ((cfgarray[5] - 48) * 10) + cfgarray[6] - 48; // minutes delay
  delayPumpSec = ((cfgarray[7] - 48) * 10) + cfgarray[8] - 48; // seconds delay 
  //Setting the pump timers for pumpOne Object
  pumpOne.setTimers(pumpOnPerMins, delayPumpMins, delayPumpSec);
 }

I am using the Arduino compiler to compile my code.

What have you done about the errors in the PumpControl source file? You are trying to use member variables that you have not defined in the header file.

I know, i only recently learned how to work with classes..

Sorry if this sounds harsh but I am not so sure you have.

Take this for instance.

void PumpControl::setTimers(int pumpOnPerMins, int delayPumpMins, int delayPumpSec){
  if(checkPumpConfig(pumpOnPerMins, delayPumpMins, delayPumpSec)){
    _pumpOnPerMins = pumpOnPerMins;
    _delayPumpMins = delayPumpMins;
    _delayPumpSec = delayPumpSec;
  }
}


boolean PumpControl::checkPumpConfig(int pumpOnPerMins, int delayPumpSec, int delayPumpMins){
  //Check pump config
  if( delayPumpMins <= 0 || delayPumpMins > 59 ){
    if(delayPumpSec > 0){
	  return false;
	}
  } else if( delayPumpSec <= 0 || delayPumpSec > 59 ){
    if(delayPumpMins > 0){
	  return false;
	}
  } else if( pumpOnPerMins < 1 || pumpOnPerMins > 59 ){
    return false;
  } else {
    return true;
  }
}

First. You need to add the variables to the class definition if you want to assign values to them.

Second. Implementing a class allows you to think in terms of objects,
having properties, methods and behaviours. That is not what you are doing.

Below, I have implemented your function in a more object oriented fashion, using a class.

class pumpControl {
  protected: 
    int delayPumpSecs;
    int delayPumpMins;
    int pumpOnPerMins;
 
  private:
    bool error_;
  
  public:
    pumpControl(void);
    inline bool Error(void) { return error_; }
    inline int getDelayPumpSecs(void) { return delayPumpSecs; }
    void setDelayPumpSecs(int);
    inline int getDelayPumpMins(void) { return delayPumpMins; }
    void setDelayPumpMins(int);
    inline int getPumpOnPerMinute(void) { return pumpOnPerMins; }
    void setPumpOnPerMinute(int);

  private:
    void checkSetting_(void);
};

//initialise properties in the constructor
pumpControl::pumpControl(void) {
  delayPumpSecs =0;
  delayPumpMins = 0;
  pumpOnPerMins = 0;
  error_ = true;
}

//do not allow properties to hold invalid values
void pumpControl::setDelayPumpSecs(int value) {
  if ((value > 0) && (value < 60)) delayPumpSecs = value;
  checkSetting_();
}
  
void pumpControl::setDelayPumpMins(int value) {
  if ((value > 0) && (value < 60)) delayPumpMins = value;
  checkSetting_();
}

void pumpControl::setPumpOnPerMinute(int value) {
  if ((value > 0) && (value < 60)) pumpOnPerMins = value;
  checkSetting_();
} 

void pumpControl::checkSetting_(void) {
  error_ = (delayPumpSecs != 0) && (delayPumpMins != 0) && (pumpOnPerMins !=0);  
}

pumpControl pump;

void setup(void) {
  pump.setDelayPumpSecs(10);
  pump.setDelayPumpMins(20);
  pump.setPumpOnPerMinute(60);
 
  if (pump.Error()) Serial.println("Oops");

  pump.setPumpOnPerMinute(59);
  
  if (!pump.Error()) Serial.println("That's better"):  
}

void loop(void) {

  do {
     // something with pump
  } while (!pump.Error());

}

The highlights.
The interesting variables were declared protected, which allows a derived class to manipulate them if (when) a future need arises.
The variables can only be manipulated outside the class scope using the get and set methods, hence they are properties.
The set methods reject invalid values, so the properties remain valid once they are set.
The Error function only returns false when all other properties are valid, so it too is a property.
The error_ flag is private, so a derived class can not mess with it.
The checkSetting_ method is private, because it updates the error_ flag.
Attempting to set any property which can be set causes the error_ flag to be evaluated.

Thus the pumpControl class describes an object which always remains valid.
The Error property is true or the other properties are all valid.

You might think it is a lot of code for three variables but importantly, the code is clear and much easier to write and maintain.

[edit] cleaned up a wayward brace and a confusing declaration in the code example [/edit]

class pumpControl {
  protected: 
    int delayPumpSecs;
    int delayPumpMins;
    int pumpOnPerMins;

I'm curious why you chose protected: here. Protected means that classes that inherit from this class can override these variables. Does that really make sense?

PaulS:
I'm curious why you chose protected: here. Protected means that classes that inherit from this class can override these variables.

No it doesn't!

  • Protected is an access specifier. A derived class may access protected members but can not access private members.
  • Overriding pertains to visibility rather than access. An overridden member is obscured, by an overriding substitute in some derived class.
  • The access specifier (always) remains intact. A derived class may cast away an override but it can not cast away a private access specifier.

Does that really make sense?

Emphatically, yes.

Consider a specialPumpControl class derived from my pumpControl class. The specialPumpControl IsA pumpControl. For the IsA relationship statement to be true, a specialPumpControl must have the same fundamental attributes as a pumpControl. These elemental attributes should be declared protected. If they were declared private, a derived class is forced to override them, which creates duplication, unnecessary complexity and unnecessary work.

I would be interested to read the justification for making those three variables anything but protected.

I would be interested to read the justification for making those three variables anything but protected.

I only ever consider protected when I expect to derive from a class. Since this is OPs first class, defining a class that can be the basis for a derived class hardly makes sense.

PaulS:
I only ever consider protected when I expect to derive from a class.

You may not expect to now but may find reason to in the future. Some other developer may find reason to. I particularly dislike paranoid class definitions, because they make assumptions about the future and poor assumptions about other people!

Over use of private variable declaration is a futile gesture anyhow. It serves only to irritate downstream developers; forcing them to mess with the original class, creating new versions of source code, or write reams of duplicate functionality to work around the paranoia. The very problems OOP is supposed to solve.

Since this is OPs first class, defining a class that can be the basis for a derived class hardly makes sense.

Every class can be the basis of a derived class; is an inescapable feature of the language. Therefore, it makes perfect sense for every class to be created as a good parent. Understanding the IsA relationship is so fundamental to understanding OOP, I think it needs to be encouraged right from the start.

When you come down to it, I think a private variable declaration is the most difficult to justify. Public variable declaration is just lazy, of course. Which leaves protected as the goto option.

What does not make sense is the OP or anyone else, trying to cram poorly structured code into a class using trial and error. A recipe for frustration, I believe.

MattS-UK:
I would be interested to read the justification for making those three variables anything but protected.

I don't know a lot about OO design, but I'm trying to learn, and this is a topic that has confused me. Assuming that variables have access methods (set() and get()), why would a derived class need to read/write the variables directly? Maybe because the set() method has some range-checking or other secondary behavior that isn't valid for the derived class? Hmm... I guess I could see that.

MattS-UK:

I know, i only recently learned how to work with classes..

Sorry if this sounds harsh but I am not so sure you have.

Correction, i only recently starting learning OO programming in Java*
Bare with me here.

Anyway, thank you all for the tips! I should've given more information on my current work:

The code i'm currently using is code written before i started learning OO Programming, my goal here is to implement several classes/libraries to shorten the current code ( 700 lines of code ) and find way to easily add more pumps.

So, I've changed the code based on your tips MattS and checked for compile errors with Visual studio:

.h file:

/*
  PumpControl lib for Autoponics
*/
#ifndef PumpControl_h
#define PumpControl_h

#include "Arduino.h"

class PumpControl {

  private:
    bool error_;
	int relayPin;
    int nextPumpMins;
    int nextPumpSecs;
	int pumpOnPerMins;
	int delayPumpSecs;
	int delayPumpMins;
    int mainPumpTimer;
    bool pumpOnce;
    bool setMPTimerOnce; 
	int hour;
	int mins;
	int secs;
	
  public:
	// On which pin the relay for the pump is connected.
    PumpControl(int relayPin);
	// Starting the relay on the defined pin.
	void startRelay(void);
	// Setters the configuration parameters.
	void setDelayPumpSecs(int newDelayPumpSec);
	void setDelayPumpMins(int newDelayPumpMins);
	void setPumpOnPerMinute(int newPumpOnPerMins);
	// Getter for the error_ variable.
	inline bool errorInConfig(void) { return error_; }
	// Main function to control the pump, needs the current time given as hour, minutes and seconds.
    void controlPump(int hour, int mins, int secs);
	// Getter for mainPumpTimer
	int getMainPumpTimer(void) { return mainPumpTimer; }
	// Setter for mainPumpTimer
	void setMainPumpTimer(int mins);
	
  private:
    // Check of the parameters are within the configuration bounds. 
    void checkSetting_(void);
};

#endif

And the .ccp file:

/*
  #include "Arduino.h"
#include "PumpControl.h"

PumpControl::PumpControl(int newRelayPin){
  relayPin = newRelayPin;
}
void PumpControl::startRelay(){
  pinMode(relayPin, OUTPUT);
}
void PumpControl::setDelayPumpSecs(int newDelayPumpSecs){
  if( delayPumpSecs >= 0 && delayPumpSecs < 60 ){
    delayPumpSecs = newDelayPumpSecs;
  }
  checkSetting_();
}
void PumpControl::setDelayPumpMins(int newDelayPumpMins){
  if( delayPumpMins >= 0 && delayPumpMins < 60 ){
    delayPumpMins = newDelayPumpMins;
  }
  checkSetting_();
}
void PumpControl::setPumpOnPerMinute(int newPumpOnPerMins){
  if( pumpOnPerMins > 1 && pumpOnPerMins < 60 ){
    pumpOnPerMins = newPumpOnPerMins;
  }
  checkSetting_();
}
void PumpControl::checkSetting_(void) {
  error_ = (delayPumpSecs != 0) && (delayPumpMins != 0) && (pumpOnPerMins !=0);
}
void PumpControl::controlPump(int hour, int mins, int secs){
  if( hour >= 7 && hour <= 23 ){  
    
    // Pump ON
    if( mainPumpTimer == mins && pumpOnce == false ){
      digitalWrite(relayPin, HIGH);
      pumpOnce = true;
      Serial.print("Pumping water into terrace.  @");
      Serial.println(mins);
      // Set time variables for when pump should go OFF.
	  nextPumpMins = mins + delayPumpMins;
      nextPumpSecs = secs + delayPumpSecs;
	  // Check if they are within limits
      if( nextPumpSecs >= 60 ){
        nextPumpMins += 1;
	    nextPumpSecs -= 60;
      }
      if( nextPumpMins >= 60 ){
        nextPumpMins -= 60;
      }
    }
  }
  // Pump OFF
  if( mins >= nextPumpMins && secs >= nextPumpSecs && pumpOnce == true ){
    digitalWrite(relayPin, LOW);
    setMPTimerOnce = false;
    pumpOnce = false;
    Serial.print("Stopping waterflow to terrace.  @");
    Serial.println(mins);
  }
  
  // Set mainPumpTimer for when pump should go ON.
  if( pumpOnce == false && setMPTimerOnce == false){
    mainPumpTimer = mins + pumpOnPerMins;
    setMPTimerOnce = true;  
  }
  // Check if mainPumpTimer is within bounds
  if( mainPumpTimer >= 60 ){
    mainPumpTimer = mainPumpTimer - 60;
  }
}
int PumpControl::getMainPumpTimer(){
  return mainPumpTimer;
}  
void PumpControl::setMainPumpTimer(int mins){
  mainPumpTimer = mins;
}

But i still seem to be getting these "undefined reference to" errors!

PaulS:
The PumpControl class won't even compile. Trying to solve the linker issues before the code compiles is silly.

So what are these linker issues you speak about? :slight_smile:

joshuabardwell:
Assuming that variables have access methods (set() and get()), why would a derived class need to read/write the variables directly?

Although there may be some member variables which are exposed through get/set methods, in general classes will typically also have internal data which is not accessible outside of that class.

The problem comes when a future developer wants to produce a more specialised class which is based on your class - although they can read your code and see that the internal data exists, and may understand how it's used and have a sensible reason for wanting to access it, if the data has been marked private then their code can't access it.

I copied the sketch from Reply #6 into the IDE, and the code from the last reply into the appropriate files in the PumpControl directory. When I try to compile, I get a bunch of errors. After changing the set timers call to the three methods that seem to be appropriate, I get:

C:\Users\pjs9486\Documents\Arduino\libraries\PumpControl\PumpControl .cpp:72: error: redefinition of 'int PumpControl::getMainPumpTimer()'
C:\Users\pjs9486\Documents\Arduino\libraries\PumpControl\/PumpControl.h:37: error: 'int PumpControl::getMainPumpTimer()' previously defined here

Don't you bother compiling before posting?

After deleting the duplicate definition, and adding the missing loop() function (doing nothing), I get:

Binary sketch size: 5,880 bytes (of a 30,720 byte maximum)

PaulS:
Don't you bother compiling before posting?

This is what i did, using the latest .h and .ccp:

#include <PumpControl.h>

PumpControl onePump(2);

void setup(){
  onePump.startRelay();
  onePump.setDelayPumpSecs(30);
  onePump.setDelayPumpMins(1);
  onePump.setPumpOnPerMinute(5);
  onePump.setMainPumpTimer(1);
}
void loop(){
  do{
    int i;
    i++;
    onePump.controlPump(1, 2, i);
    delay(1000);
  } while ( !onePump.errorInConfig() );
}

Getting these errors:

_1_Autoponics_library_tester.cpp.o: In function __static_initialization_and_destruction_0': D:\Users\Jaime\Dropbox\Arduino\arduino-1.0.5/_1_Autoponics_library_tester.ino:3: undefined reference to PumpControl::PumpControl(int)'
_1_Autoponics_library_tester.cpp.o: In function loop': D:\Users\Jaime\Dropbox\Arduino\arduino-1.0.5/_1_Autoponics_library_tester.ino:16: undefined reference to PumpControl::controlPump(int, int, int)'
_1_Autoponics_library_tester.cpp.o: In function setup': D:\Users\Jaime\Dropbox\Arduino\arduino-1.0.5/_1_Autoponics_library_tester.ino:6: undefined reference to PumpControl::startRelay()'
D:\Users\Jaime\Dropbox\Arduino\arduino-1.0.5/_1_Autoponics_library_tester.ino:7: undefined reference to PumpControl::setDelayPumpSecs(int)' D:\Users\Jaime\Dropbox\Arduino\arduino-1.0.5/_1_Autoponics_library_tester.ino:8: undefined reference to PumpControl::setDelayPumpMins(int)'
D:\Users\Jaime\Dropbox\Arduino\arduino-1.0.5/_1_Autoponics_library_tester.ino:9: undefined reference to PumpControl::setPumpOnPerMinute(int)' D:\Users\Jaime\Dropbox\Arduino\arduino-1.0.5/_1_Autoponics_library_tester.ino:10: undefined reference to PumpControl::setMainPumpTimer(int)'

I also checked both the .h and .ccp files using Visual Studio, getting no errors there.

So yes, I do bother compiling and checking my stuff before I post.

Anyone has any idea why I keep getting these errors? :sweat_smile:

Anyone has any idea why I keep getting these errors?

Those are all linker errors. The linker is invoked after the compiler gets done. I can't get the code to compile, so I'd expect to get linker errors.

The only thing I can suggest at this point is that you zip all the files and attach to your post. This makes it easier to try to replicate your problem AND assures that we are operating on the same code.

I'd also suggest that you use File + Preferences. Enable verbose mode for compiling, but NOT for linking. Post ALL of the output that you then get when verifying.

At a minimum, that will prove that the code does at least compile for you. If it does, indeed, compile for you, I'll shut about about compilation issues.