1

I'm building a robot arm which is quite complicated, so I wrote a class with inheritance to control different servos without having to write too much code. The classes look as follows (some stuff is left out):

In servoPart.h:

#include <Servo.h>

#ifndef SERVO_PART_H
#define SERVO_PART_H

 class ServoPart {
 protected:
      virtual void doJob() = 0;
 private:
      Servo servo;
 public:
      ServoPart(int pin, int minPWM, int maxPWM) {
           servo.attach(pin, minPWM, maxPWM);
      };

      void setAngle(int angle) {
           servo.write(angle);
      };

      int getPosition() {
          return servo.read();
      };
 }
#endif

And in base.h:

#include "servoPart.h"

#ifndef BASE_H
#define BASE_H

class Base : public ServoPart {
private:
    void doJob() {/* implementation */};
public:
    Base(int pin, int stepsize = 5) : ServoPart(pin, 771, 2193) {
    };
};
#endif

And in main.cpp:

#include <Arduino.h>
#include "base.h"

#define SERVO_BASE 9

Base base(SERVO_BASE);

void setup() {
    Serial.begin(9600);
    delay(500);
    base.setAngle(80);
    Serial.println(base.getPosition());    // <-- prints 80
    delay(500);
    base.setAngle(110);
    Serial.println(base.getPosition());    // <-- prints 110
}

void loop() {

}

The servo seems to be set to 80/110, however, nothing moves. If I create the servo object in the main.cpp and use servo.write() there, the servo moves, meaning the problem is not the servo or the connection/circuit. Is it possible that the fault lays in my way of initializing the base? Using Base base(SERVO_BASE) before the setup function?

matthesinator
  • 136
  • 1
  • 8
  • where do you call the constructor to that private Servo named servo? calling attach is not enough. – Abel May 12 '21 at 02:39
  • @Abel in the Base constructor: `Base(int pin, int stepsize) : ServoPart(pin, 771, 2193) { }`. Is that not correct? – matthesinator May 12 '21 at 07:52
  • You were asked when you call the constructor of Servo servo, and not about the call of ServoPart constructor. – Klaus May 12 '21 at 09:28
  • Oh, thanks for the clarification! That's in ServoPart class, in the private section: `Servo servo;` and in the constructor `servo.attach(pin);`. At least that's how I do it when using the servo class in the main.cpp. Edit: If you take a look at the Arduino IDE Servo example, they do it the same way. Create a servo with `Servo myservo;` and then call `attach` to activate it – matthesinator May 12 '21 at 09:40

2 Answers2

2

I guess I'll clarify. In C++ if you do not have a specified constructor, members get their default constructors called.

If the following code is not present

ServoPart(int pin, int minPWM, int maxPWM) {
           servo.attach(pin, minPWM, maxPWM);
      }

it is the equivalent of

ServoPart():servo() {}

The missing step is the default constructor for the Servo. Solution that allows you to use the constructor is

ServoPart(int pin, int minPWM, int maxPWM):servo() {
           servo.attach(pin, minPWM, maxPWM);
      }

Abel
  • 437
  • 4
  • 7
  • Thanks for the response, but the problem was simply that I needed to call the attach function separately from the ServoPart constructor. The initialization `Servo servo;` was enough – matthesinator May 12 '21 at 13:25
  • and this is why people get confused. When the same steps are executed in the same order, they will produce the same result. There are many ways to tell the compiler to do the same thing. Sometimes the lack of code results in more instructions being executed. – Abel May 13 '21 at 02:09
0

I found the solution here: When calling servo.attach() in the constructor, the order of variable initialization is messed up. Creating an init() function to be called in setup() solves this problem.

matthesinator
  • 136
  • 1
  • 8