How to pass an object or reference to a function?

Hello again.

I'm stumped. I need a function to operate a Servo without knowing which Servo it's operating. I thought I could pass the Servo by reference, but the compiler goes nuts when I try. Here's what I have:

Servo myServo1;
Servo myServo2;
...

I tried this for my function:

void calibrate (Servo* theServo)
{ theServo->Write(100); }

// I called the function with:

calibrate &myServo1);

That didn't work.

I saw one example online that said I should define my function as:

void calibrate (Servo &theServo)
{ theServo.Write(100); }
// I called the function with:

calibrate (myServo1);

That didn't work either. FWIW, the syntax seems odd to me.

When I instantiate a Servo with Servo myServo; isn't myServo a real object, not a pointer to an object in memory? Given the syntax myServo.doSomething(), I assume it's a real object. So if I want to pass it by reference, not pass a copy of the object, shouldn't I pass its address with &myServo?

As I said at the top, I'm stumped (again).

Will someone please show me how to pass a reference to a Servo object to a function.

Thanks in advance.

--- Mike

Mike,
are you trying to do something liek below:

#include <stdio.h>
#include <stdlib.h>

int *iptr;

int iA, iB ;

void show_value (int *ptr) ;

int main()
{
    printf("Hello world!\n");
    iA = 10 ;
    iB = 20 ;

    show_value(iB);


    return 0;
}
void show_value( int *ptr) {


    printf("A or B ? - : %d",ptr);

}

This compiles OK for me:

#include <Servo.h>
Servo myServo1;
Servo myServo2;
void calibrate (Servo &theServo)
{
  theServo.write(100);
}
void setup ()
{
  calibrate (myServo1);
}  // end of setup

void loop ()
{
}  // end of loop

Thanks. It works for me as well. Interestingly, That's the syntax I found online, that didn't work. I'll inspect my full sketch thoroughly to see where I might have gone wrong.

--- Mike

Delta_G:
This compiles as well. Untested, so I don't know if it works but it should. What was the code (the whole code) and error messages you got?

The code is pretty long and complex, and the error messages started by flagging line numbers early in the sketch that had no relation to the stated error.

Now that I know two syntaxes that work, I'll try one or both, and see if I can track down any remaining problems in my sketch.

Thank you.

--- Mike

MikeDodd:
Thanks. It works for me as well. Interestingly, That's the syntax I found online, that didn't work. I'll inspect my full sketch thoroughly to see where I might have gone wrong.

You had:

{ theServo.Write(100); }

That is a capitalization error (should be write). I fixed that without comment.

... that didn't work ...

It always helps to post the error message. Changing thing often changes the error message. You can't just say I changed it and "it still didn't work". Maybe this time it just said you don't have a Write function defined.

My mistake when typing the code here; the actual sketch had it lowercase.

Bad news. The IDE won't compile a sketch in which I have a function with a reference or pointer to my class, but it compiles fine with a reference or pointer to a built-in class.

I wrote a simple SemaphoreServo class that wraps a Servo. It has an attach() method and a write() method. The SemaphoreServo's write() method flashes the LED on pin 13 to prove it is the one being called, and not Servo.write(). If I call theServo.write() directly from within showServo(), the LED flashes when theServo is a SemaphoreServo, and not a Servo. As expected.

In my sketch I duplicated certain lines, changed one from Servo to SemaphoreServo, and tried to compile. I changed the variable declarations and the showServo() function declaration.

I will post the full code below, but first here are the key lines:

 Servo myServo1; // Standard Arduino Servo  // v1 works in all variations
// SemaphoreServo myServo1;  // My SemaphoreServo  // v2 Will not compile
//...
  void showServo(Servo &theServo) // v.1a works
//  void showServo(SemaphoreServo &theServo) // v.2a will not compile
{
//...
  theServo.write(i);  // v.1a works with Servo, v.2a will not compile
}
//...
}
// In sketch loop():
 showServo(myServo1);  // works with Servo, won't compile with SemaphoreServer
// showServo(&myServo1); // works with Servo, won't compile with SemaphoreServer
  delay (300);

Essentially the code compiles and runs correctly with a Servo, but won't even compile with a SemaphoreServer.

Here is the complete code set up for Servo:

#include <Servo.h>

// SemaphoreServo class
//
//***********************************
// The class definition.
//
class SemaphoreServo
{
  private:
    Servo _theServo;

  public:
    SemaphoreServo();
    void attach(int sPin);
    void write(int newPos);
};

//***********************************
// The class implementation.
//
SemaphoreServo::SemaphoreServo()  // Default constructor
{
  pinMode(13, OUTPUT);  // Debug - For LED flashing
}  

//Attach the specified control pin.
void SemaphoreServo::attach(int sPin)
{
  pinMode(sPin, OUTPUT);
  _theServo.attach(sPin);
}

 // Move servo to a specific position.
 //
void SemaphoreServo::write(int newPos)
{
  _theServo.write(newPos);
  digitalWrite(13, HIGH);
  delay(20);
  digitalWrite(13, LOW);
  delay(20);
}
// ===== End SemaphoreServo class =====


// ===== Begin sketch. =====
//
 Servo myServo1; // Standard Arduino Servo  // v1 works in all variations
// SemaphoreServo myServo1;  // My SemaphoreServo  // v2 Will not compile

// ===== showServo - Tests passing object reference to function. =====

  void showServo(Servo &theServo) // v.1a works
//  void showServo(SemaphoreServo &theServo) // v.2a will not compile

//  void showServo(Servo *theServo)  // v.1b works
//  void showServo(SemaphoreServo *theServo)  // v.2b will not compile

{
  for (int i = 0; i < 180; i+= 10)
  {
  theServo.write(i);  // v.1a works with Servo, v.2a will not compile
//  theServo->write(i); // v.1b works with Servo, v.2b will not compile
  delay(300);
  } // End for() loop
} // End showServo()

// ===== Sketch setup() =====
void setup ()
{
  pinMode(9, OUTPUT);
  myServo1.attach(9);
}

// ===== Sketch loop() =====
void loop()
{
 showServo(myServo1);  // works with Servo, won't compile with SemaphoreServer
// showServo(&myServo1); // works with Servo, won't compile with SemaphoreServer
  delay (300);
}

Here is the same code set up for SemaphoreServo that won't compile:

#include <Servo.h>

// SemaphoreServo class
//
//***********************************
// The class definition.
//
class SemaphoreServo
{
  private:
    Servo _theServo;

  public:
    SemaphoreServo();
    void attach(int sPin);
    void write(int newPos);
};

//***********************************
// The class implementation.
//
SemaphoreServo::SemaphoreServo()  // Default constructor
{
  pinMode(13, OUTPUT);  // Debug - For LED flashing
}  

//Attach the specified control pin.
void SemaphoreServo::attach(int sPin)
{
  pinMode(sPin, OUTPUT);
  _theServo.attach(sPin);
}

 // Move servo to a specific position.
 //
void SemaphoreServo::write(int newPos)
{
  _theServo.write(newPos);
  digitalWrite(13, HIGH);
  delay(20);
  digitalWrite(13, LOW);
  delay(20);
}
// ===== End SemaphoreServo class =====


// ===== Begin sketch. =====
//
// Servo myServo1; // Standard Arduino Servo  // v1 works in all variations
 SemaphoreServo myServo1;  // My SemaphoreServo  // v2 Will not compile

// ===== showServo - Tests passing object reference to function. =====

//  void showServo(Servo &theServo) // v.1a works
  void showServo(SemaphoreServo &theServo) // v.2a will not compile

//  void showServo(Servo *theServo)  // v.1b works
//  void showServo(SemaphoreServo *theServo)  // v.2b will not compile

{
  for (int i = 0; i < 180; i+= 10)
  {
  theServo.write(i);  // v.1a works with Servo, v.2a will not compile
//  theServo->write(i); // v.1b works with Servo, v.2b will not compile
  delay(300);
  } // End for() loop
} // End showServo()

// ===== Sketch setup() =====
void setup ()
{
  pinMode(9, OUTPUT);
  myServo1.attach(9);
}

// ===== Sketch loop() =====
void loop()
{
 showServo(myServo1);  // works with Servo, won't compile with SemaphoreServer
// showServo(&myServo1); // works with Servo, won't compile with SemaphoreServer
  delay (300);
}

Here are the errors from compiling the second listing:

Arduino: 1.6.5 (Windows 8.1), Board: "Arduino/Genuino Uno"

Build options changed, rebuilding all

Using library Servo in folder: C:\Program Files (x86)\Arduino\libraries\Servo 



C:\Program Files (x86)\Arduino\hardware\tools\avr/bin/avr-g++ -c -g -Os -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -MMD -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=10605 -DARDUINO_AVR_UNO -DARDUINO_ARCH_AVR -IC:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino -IC:\Program Files (x86)\Arduino\hardware\arduino\avr\variants\standard -IC:\Program Files (x86)\Arduino\libraries\Servo\src C:\Users\Mike\AppData\Local\Temp\build6128207164092041553.tmp\sketch_servo_ref3.cpp -o C:\Users\Mike\AppData\Local\Temp\build6128207164092041553.tmp\sketch_servo_ref3.cpp.o 

sketch_servo_ref3.ino:9:16: error: variable or field 'showServo' declared void
sketch_servo_ref3.ino:9:16: error: 'SemaphoreServo' was not declared in this scope
sketch_servo_ref3.ino:9:32: error: 'theServo' was not declared in this scope
variable or field 'showServo' declared void

I will greatly appreciate any tips on where I might be going wrong. To me, it seems that like everything should work. All I'm changing is the theServo declaration and the showServo() declaration.

Thanks to all who put in the effort to read my code.

--- Mike

Here is the same code set up for SemaphoreServo that won't compile:

I copied and pasted your code. I moved the SemaphoreServo class definition into a new file, SemaphoreServo.h. I moved the SemaphoreServo class implementation into a new file, SemaphoreServo.cpp. I added the necessary #include statements to the SemaphoreServo.h and SemaphoreServo.cpp files, and I got just one error:

Binary sketch size: 2,728 bytes (of a 30,720 byte maximum)

PaulS:
I copied and pasted your code. I moved the SemaphoreServo class definition into a new file, SemaphoreServo.h. I moved the SemaphoreServo class implementation into a new file, SemaphoreServo.cpp. I added the necessary #include statements to the SemaphoreServo.h and SemaphoreServo.cpp files, and I got just one error:
"Binary sketch size: 2,728 bytes (of a 30,720 byte maximum)"

I assume that "error" means the class compiled correctly, yes?

Did you compile the sketch, or just the .cpp file?

I started with .h and .cpp files, and tried to make a library. The compiler gave similar errors, and would not compile a simple sketch that #included the .h file.

Where did you save the .h and .cpp files? In the same directory as the sketch, or in the libraries directory?

I'll give it a try, saving the class files in the sketch directory.

--- Mike

Following PaulS' info about saving my class in .h and .cpp files, I did the same thing, saving the files in the sketch directory. It worked, although my #include statements needed the full path to the files. E.g., #include <C:/Users/Mike/Documents/Arduino/sketch_servo_ref4/SemaphoreServo.h> With just #include <SemaphoreServo.h> I got these compiler errors:

Arduino: 1.6.5 (Windows 8.1), Board: "Arduino/Genuino Uno"

sketch_servo_ref4.ino: In function 'void showServo(SemaphoreServo*)':
sketch_servo_ref4:21: error: 'class SemaphoreServo' has no member named 'write'
sketch_servo_ref4.ino: In function 'void setup()':
sketch_servo_ref4:30: error: 'class SemaphoreServo' has no member named 'attach'
'class SemaphoreServo' has no member named 'write'

I will create a SemaphoreServo library directory, and copy the .h and .cpp files there, then see what happens. That's what I had originally, and the compiler didn't like it. Maybe there was a coding error in the full class. If this bare-bones class compiles as a library, then I know to examine my full class code.

If the compiler doesn't like the bare-bones class as a library, I don't mind placing the .h and .cpp files in the sketch directory.

A hearty thanks again for those who helped put me on the right track.

--- Mike

My mistake when typing the code here; the actual sketch had it lowercase.

Please don't do that. It's annoying to debug code with obvious errors, and then get told "oh but I just retyped it, the actual code is different". Post the actual code. You have Copy and Paste on computers for a reason.

1 Like

Understand. I apologize to you and the group.

I can almost lay this topic to rest.

I have successfully put my SemaphoreServer in a library, and have successfully passed a pointer to a function, which successfully called a method. I will live with the requirement to #include Servo.h in a sketch that uses SemaphoreServer.

Today I figured how to incorporate public constants in SemaphoreServer. Previously I had some kind of error that prevented compilation with constants, so I wrote getter methods. But I discovered my error, and now SemaphoreServer has public constants.

Almost. The IDE compiler chokes when I attempt to use a constant value as an argument to an object method.

My SemaphoreServo.h file:

#ifndef SemaphoreServo_h
#define SemaphoreServo_h

#include <arduino.h>
#include <Servo.h>

//***********************************
// SemaphoreServo class definition.
//
class SemaphoreServo
{
  private:
    Servo _theServo;
    int _currentPos;

  public:
  	static const int SPEED_SLOW;
    SemaphoreServo();
		int getSpeedSlow();
		int getSpeedMedium();
		int getSpeedFast();
		int getSpeedInstant();
    void attach(int sPin);
		int xxx(int newPos, int speed);
	  void moveTo(int newPos, int speed);
};
#endif

And this is my SemaphoreServo.cpp file:

#include <SemaphoreServo.h>

//***********************************
// SemaphoreServo class implementation.
//
SemaphoreServo::SemaphoreServo()  // Default constructor
{
  pinMode(13, OUTPUT);  // Debug - For LED flashing
  _currentPos = 90;
}

static const int SPEED_SLOW = 20;

int SemaphoreServo::getSpeedSlow() {return 20;}
int SemaphoreServo::getSpeedMedium() {return 10;}
int SemaphoreServo::getSpeedFast() {return 5;}
int SemaphoreServo::getSpeedInstant() {return 0;}

//Attach the specified control pin.
//
void SemaphoreServo::attach(int sPin)
{
  pinMode(sPin, OUTPUT);
  _theServo.attach(sPin);
}

 // Move servo to a specific position.
 //
void SemaphoreServo::moveTo(int newPos, int speed)
{
	int delta = newPos - _currentPos;
  int moveIncrement = (delta >= 0 ? 1 : -1);  // ALWAYS make moveIncrement 1 or -1

  while(_currentPos != newPos) // Relies on moveIncrement being 1 or -1, never larger
  {
    digitalWrite(13, HIGH);
    if(speed > 0)
    	delay(speed);
    digitalWrite(13, LOW);
    if(speed > 0)
    		delay(speed);

    _currentPos += moveIncrement;
    _theServo.write(_currentPos);
  }	// End while(_currentPos != newPos)
}	// End moveTo() method

The two important lines in the .cpp file are:

static const int SPEED_SLOW = 20;
int SemaphoreServo::getSpeedSlow() {return 20;}

They both do the same thing -- return an int value of 20.

Here is my sketch.

#include <arduino.h>
#include <SemaphoreServo.h>
#include <Servo.h>  // Needed for compiler only -- No Servo is used in the sketch

// ===== Begin sketch. =====
//
 SemaphoreServo myServo1;

// ===== showServo - Tests passing object reference to function. =====
//
void showServo(SemaphoreServo *theServo)
{
  int j = theServo->SPEED_SLOW; // OK if line 18 commented. Fails if line 18 UNcommented.

  for (int i = 0; i < 180; i+= 40)
  {
    theServo->moveTo(i, theServo->getSpeedSlow());
//    theServo->moveTo(i, j); // Uncomment to cause compiler to fail @ line 13.
    delay(400);
  } // End for() loop
} // End showServo()

// ===== Sketch setup() =====
void setup ()
{
  myServo1.attach(9);
}

// ===== Sketch loop() =====
void loop()
{

  showServo(&myServo1);
  delay (300);
}

Notice line 13: int j = theServo->SPEED_SLOW; which gets the constant value. Also notice lines 17: theServo->moveTo(i, theServo->getSpeedSlow()); and line 18: theServo->moveTo(i, j); Both call the SemaphoreServo's moveTo() method. Line 17 calls a getter, and always works. Line 18 uses the constant value stored in local variable j.

If line 18 is uncommented, the compiler failes, pointing to line 13!

Arduino: 1.6.5 (Windows 8.1), Board: "Arduino/Genuino Uno"

sketch_servo_ref6.cpp.o: In function `showServo(SemaphoreServo*)':
C:\Program Files (x86)\Arduino/sketch_servo_ref6.ino:13: undefined reference to `SemaphoreServo::SPEED_SLOW'
C:\Program Files (x86)\Arduino/sketch_servo_ref6.ino:13: undefined reference to `SemaphoreServo::SPEED_SLOW'
collect2.exe: error: ld returned 1 exit status
Error compiling.

  This report would have more information with
  "Show verbose output during compilation"
  enabled in File > Preferences.

It also fails if I use the constant as an argument to the moveTo() method, as theServo->moveTo(i, theServo->SPEED_SLOW);

It gets worse. If I copy the value in j into a new variable int k = j;
, then use k with moveTo() theServo->moveTo(i, k);
the compiler still failes.

I have no idea what's going on. Turning on verbose compiler messages doesn't show any additional info about the error.

If I get the constant but never use it, everything is rosy. But what good is that?

I guess I'll stick with my getters, and forget about constants.

Maybe I'm doing something wrong, but I sure don't see it.

Thanks again for all the help.

---Mike

If line 18 is not present, then j i never used, so the compiler simply discards line 13, as it serves no useful purpose.

You have declared your constant as static, which means there is one, and only one, copy of it, and that copy is shared by ALL instances of the class. So, you cannot access it through an instance pointer or reference, but rather through the class itself. Try:

int j = SemaphoreServo::SPEED_SLOW;

Regards,
Ray L.

I think you have to use

static const int SemaphoreServo::SPEED_SLOW = 20;

in the cpp file.

The error message show the linker failing, not the compiler.

RayLivingston:
You have declared your constant as static, which means there is one, and only one, copy of it, and that copy is shared by ALL instances of the class.

I originally made it static because the compiler gave a warning about initializing non-static constants.

Now I can't replicate that warning, and the compiler gives errors when I try to make it static.

So, you cannot access it through an instance pointer or reference, but rather through the class itself. Try:

int j = SemaphoreServo::SPEED_SLOW;

Regards,
Ray L.

I made it non-static:

SemaphoreServo.h:
  	const int SPEED_SLOW;

SemaphoreServo.cpp:
const int SemaphoreServo::SPEED_SLOW = 20;

and got a passel of errors.

I don't know what's going on -- I'm throwing in the towel. I will use my getters instead of class constants. They are reliable, and they do exactly the same thing as constants. I suspect the compiler optimizes them to be as efficient as a constant.

Thanks for the help. I am restoring my SemaphoreServo class to the full-blown class it was before I stripped it down for testing.

Thanks again for all the information. It's been 25 years since I did serious C++ coding, and I know the standards have changed. I'll focus on understanding the Arduino, and try fancy C++ later.

Regards, Mike

and got a passel of errors.

What errors?

I'm throwing in the towel. I will use my getters instead of class constants.

Post code that demonstrates your problem, not a snippet.

RayLivingston:
You have declared your constant as static, which means there is one, and only one, copy of it, and that copy is shared by ALL instances of the class. So, you cannot access it through an instance pointer or reference, but rather through the class itself. Try:

int j = SemaphoreServo::SPEED_SLOW;

Regards,
Ray L.

I tried that successfully with the SemaphoreServo class defined in the sketch, but I couldn't get it to compile with SemaphoreServo in a library with .h and .cpp files.

I quit. I'll use getters instead of constants.

At this point, I have my SemaphoreServo class fleshed-out to almost its final design, and a fairly comprehensive test sketch works as intended. I still need to add code for an Ethernet server to control the servo. I expect that to be easy. My breadboard is plugged into an Ethernet shield, and I have a working sketch that accepts a connection from a C#.Net app on my PC, and echoes characters it receives.

Let's close this topic. I greatly appreciate all the helpful information and advice.

I'm sure the C++ language has changed a lot since I last seriously used it 25 years ago. I need to become familiar with Arduino, and then bone-up on doing fancy stuff with C++.

Thanks again.

Sincerely,

Mike

I tried that successfully with the SemaphoreServo class defined in the sketch, but I couldn't get it to compile with SemaphoreServo in a library with .h and .cpp files.

I quit. I'll use getters instead of constants.

This is ridiculous. You are going to have the overhead of a function call just to get a constant? Post your code, then we can advise. Post the error message. Don't just say "I got errors". Post them.