How do I put objects of a class I created inside of an array?

Okay, so I’m creating a hexapod with arduino and two servo drivers. there are 18 servos driving the legs. I want to do this really well, so I’m trying to use classes so i can say something like left_front.goToPosition(x,y,z); but I can’t get my code to verify… I want to control each servo in an object (no idea at all if that’s what you call it), and then control each leg (group of 3 servos) in its own object. I thought I should use an array of legs and servos, but this is very new to me. Thanks in advance!

#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>

Adafruit_PWMServoDriver pwm_0 = Adafruit_PWMServoDriver(0x40);
Adafruit_PWMServoDriver pwm_1 = Adafruit_PWMServoDriver(0x41);
Adafruit_PWMServoDriver drivers[2] = {pwm_0, pwm_1};

/*
 * 0 | 3
 * 1 | 4
 * 2 | 5
 */
//int servos[6];
int minim = 250;
int maxim = 750;
sErVo servos[18] = {
  sErVo(0, 0, minim, maxim),
  sErVo(1,0,minim, maxim),
  sErVo(2,0,minim, maxim),
  
  sErVo(4,0,minim, maxim),
  sErVo(5,0,minim, maxim),
  sErVo(6,0,minim, maxim),
  
  sErVo(8,0,minim, maxim),
  sErVo(9,0,minim, maxim),
  sErVo(10,0,minim, maxim),
  
  sErVo(0,1,minim, maxim),
  sErVo(1,1,minim, maxim),
  sErVo(2,1,minim, maxim),
  
  sErVo(4,1,minim, maxim),
  sErVo(5,1,minim, maxim),
  sErVo(6,1,minim, maxim),
  
  sErVo(8,1,minim, maxim),
  sErVo(9,1,minim, maxim),
  sErVo(10,1,minim, maxim),
};

void setup() {
  // put your setup code here, to run once:
  pwm_0.begin();
  pwm_1.begin();
}

void loop() {
  // put your main code here, to run repeatedly:

}

double degToPulse(double deg, double servoMin, double servoMax){
  double pulseLen = map(deg, 0, 180, servoMin, servoMax);
  return pulseLen;
}
void writeToServo(int driverNum, int servoNum, double angle, double servoMin, double servoMax){
  drivers[driverNum].setPWM(servoNum, driverNum, degToPulse(angle, servoMin, servoMax));
  
}
class sErVo 
{
  int driverNum;
  int servoNum;
  long servoMin;
  long servoMax;
  
public:
sErVo(int n, int d, long minimum, long maximum){
  servoNum = n;
  driverNum = d;
  servoMin = minimum;
  servoMax = maximum;
}

void setAngle(long angle){
    writeToServo(driverNum, servoNum, angle, servoMin, servoMax);
  }
};

class leggy
{
  
  int leggyNum;
  float Z_Offset;
  float coxaLength;
  float femurLength;
  float tibiaLength;
  sErVo coxaServo;
  sErVo femurServo;
  sErVo tibiaServo;

  long gama;
  long alpha;
  long beta;

  //long L_1;
  long len;
  
  
public:
//takes pre-initialized sErVo objects.
leggy(int servo_indexes[3], int legNum, float coxaLen, float femurLen, float tibiaLen, float z_off){
  Z_Offset = z_off;
  coxaServo = servos[servo_indexes[0]];
  femurServo = servos[servo_indexes[1]];
  tibiaServo = servos[servo_indexes[2]];
  leggyNum = legNum;
  coxaLength = coxaLen;
  femurLength = femurLen;
  tibiaLength = tibiaLen;
}

//sets leg to point relative to the leg itself, not the robot. 
//that should come from another class
//y is a magnitude, the distance from the bot, not a location. so 
//the left and right of the bot have +y coordinates.
void setLegToPoint(float x, float y, float z){
  //math to set gama alpha and beta here
  gama = atan(x/y);
  len = sqrt(sq(Z_Offset) + sq(y - coxaLength));
  long alpha_1 = acos(Z_Offset/len);
  long alpha_2 = acos((sq(tibiaLength) - sq(femurLength) - sq(len))/(-2*femurLen*len));
  alpha = alpha_1 + alpha_2;
  beta = acos((sq(len) - sq(tibiaLength) - sq(femurLength))/(-2*tibiaLength*femurLength));
  setAngles(gama, alpha, beta);
}
void setAngles(long gama, long alpha, long beta){
  coxaServo.setAngle(gama);
  femurServo.setAngle(alpha);
  tibiaServo.setAngle(beta);
}
};

try putting the class definition before you declare the array, e.g. this should compile

#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>

Adafruit_PWMServoDriver pwm_0 = Adafruit_PWMServoDriver(0x40);
Adafruit_PWMServoDriver pwm_1 = Adafruit_PWMServoDriver(0x41);
Adafruit_PWMServoDriver drivers[2] = {pwm_0, pwm_1};

void writeToServo(int driverNum, int servoNum, double angle, double servoMin, double servoMax){
  drivers[driverNum].setPWM(servoNum, driverNum, degToPulse(angle, servoMin, servoMax));
 
}

class sErVo
{
  int driverNum;
  int servoNum;
  long servoMin;
  long servoMax;
 
public:
sErVo(int n, int d, long minimum, long maximum){
  servoNum = n;
  driverNum = d;
  servoMin = minimum;
  servoMax = maximum;
}

void setAngle(long angle){
    writeToServo(driverNum, servoNum, angle, servoMin, servoMax);
  }
};

/*
 * 0 | 3
 * 1 | 4
 * 2 | 5
 */
//int servos[6];
int minim = 250;
int maxim = 750;

sErVo servos[18] = {
  sErVo(0, 0, minim, maxim),
  sErVo(1,0,minim, maxim),
  sErVo(2,0,minim, maxim),
 
  sErVo(4,0,minim, maxim),
  sErVo(5,0,minim, maxim),
  sErVo(6,0,minim, maxim),
 
  sErVo(8,0,minim, maxim),
  sErVo(9,0,minim, maxim),
  sErVo(10,0,minim, maxim),
 
  sErVo(0,1,minim, maxim),
  sErVo(1,1,minim, maxim),
  sErVo(2,1,minim, maxim),
 
  sErVo(4,1,minim, maxim),
  sErVo(5,1,minim, maxim),
  sErVo(6,1,minim, maxim),
 
  sErVo(8,1,minim, maxim),
  sErVo(9,1,minim, maxim),
  sErVo(10,1,minim, maxim),
};

void setup() {
  // put your setup code here, to run once:
  pwm_0.begin();
  pwm_1.begin();
}

void loop() {
  // put your main code here, to run repeatedly:

}

double degToPulse(double deg, double servoMin, double servoMax){
  double pulseLen = map(deg, 0, 180, servoMin, servoMax);
  return pulseLen;
}

note I removed class leggy for this test

Thanks for the reply! That worked, but what about the leggy class? I changed the code to have the leggy class after the new sErVo class location.

#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>

Adafruit_PWMServoDriver pwm_0 = Adafruit_PWMServoDriver(0x40);
Adafruit_PWMServoDriver pwm_1 = Adafruit_PWMServoDriver(0x41);
Adafruit_PWMServoDriver drivers[2] = {pwm_0, pwm_1};

void writeToServo(int driverNum, int servoNum, double angle, double servoMin, double servoMax){
  drivers[driverNum].setPWM(servoNum, driverNum, degToPulse(angle, servoMin, servoMax));
  
}
class sErVo 
{
  int driverNum;
  int servoNum;
  long servoMin;
  long servoMax;
  
public:
sErVo(int n, int d, long minimum, long maximum){
  servoNum = n;
  driverNum = d;
  servoMin = minimum;
  servoMax = maximum;
}

void setAngle(long angle){
    writeToServo(driverNum, servoNum, angle, servoMin, servoMax);
  }
};

class leggy
{
  
  int leggyNum;
  float Z_Offset;
  float coxaLength;
  float femurLength;
  float tibiaLength;
  sErVo coxaServo;
  sErVo femurServo;
  sErVo tibiaServo;

  long gama;
  long alpha;
  long beta;

  //long L_1;
  long len;
  
  
public:
//takes pre-initialized sErVo objects.
leggy(sErVo coxa, sErVo femur, sErVo tibia, int legNum, float coxaLen, float femurLen, float tibiaLen, float z_off){
  Z_Offset = z_off;
  coxaServo = coxa;//servos[0];//servo_indexes[0]];
  femurServo = femur;//servos[1];//servo_indexes[1]];
  tibiaServo = sibia;//servos[2];//servo_indexes[2]];
  leggyNum = legNum;
  coxaLength = coxaLen;
  femurLength = femurLen;
  tibiaLength = tibiaLen;
}

//sets leg to point relative to the leg itself, not the robot. 
//that should come from another class
//y is a magnitude, the distance from the bot, not a location. so 
//the left and right of the bot have +y coordinates.
void setLegToPoint(float x, float y, float z){
  //math to set gama alpha and beta here
  gama = atan(x/y);
  len = sqrt(sq(Z_Offset) + sq(y - coxaLength));
  long alpha_1 = acos(Z_Offset/len);
  long alpha_2 = acos((sq(tibiaLength) - sq(femurLength) - sq(len))/(-2*femurLen*len));
  alpha = alpha_1 + alpha_2;
  beta = acos((sq(len) - sq(tibiaLength) - sq(femurLength))/(-2*tibiaLength*femurLength));
  setAngles(gama, alpha, beta);
}
void setAngles(long gama, long alpha, long beta){
  coxaServo.setAngle(gama);
  femurServo.setAngle(alpha);
  tibiaServo.setAngle(beta);
}
};

/*
 * 0 | 3
 * 1 | 4
 * 2 | 5
 */
//int servos[6];
int minim = 250;
int maxim = 750;
sErVo servos[18] = {
  sErVo(0, 0, minim, maxim),
  sErVo(1,0,minim, maxim),
  sErVo(2,0,minim, maxim),
  
  sErVo(4,0,minim, maxim),
  sErVo(5,0,minim, maxim),
  sErVo(6,0,minim, maxim),
  
  sErVo(8,0,minim, maxim),
  sErVo(9,0,minim, maxim),
  sErVo(10,0,minim, maxim),
  
  sErVo(0,1,minim, maxim),
  sErVo(1,1,minim, maxim),
  sErVo(2,1,minim, maxim),
  
  sErVo(4,1,minim, maxim),
  sErVo(5,1,minim, maxim),
  sErVo(6,1,minim, maxim),
  
  sErVo(8,1,minim, maxim),
  sErVo(9,1,minim, maxim),
  sErVo(10,1,minim, maxim),
};

void setup() {
  // put your setup code here, to run once:
  pwm_0.begin();
  pwm_1.begin();
}

void loop() {
  // put your main code here, to run repeatedly:

}

double degToPulse(double deg, double servoMin, double servoMax){
  double pulseLen = map(deg, 0, 180, servoMin, servoMax);
  return pulseLen;
}

this gives me an error:

no matching function for call to 'sErVo::sErVo()'

why can’t I use an object as a parameter?

fourth-decent:
[/code]
this gives me an error:

no matching function for call to 'sErVo::sErVo()'

why can't I use an object as a parameter?

Well, that's a bit complicated.

You have three instances of Servo inside Leggy. That means that Leggy contains three little 12-byte chunks of memory. You want to initialise it from your array of servo, which is an entirely separate array of 12-byte chunks of memory. If you accomplished this, you'd have three sets of pairs of servo objects laying about - one in your array, one in your object, that woudln't talk to one another but that would each be pointing at the same sets of pins.

There are two ways to fix this: the easy way and the hard way. I suggest you use the easy way.

Leggy should not hold the SeRvO ovject inside it. The object should reside in your array, and leggy should hold a reference to that object. This is very easy to do.

class leggy
{
… 
  sErVo &coxaServo;
  sErVo &femurServo;
  sErVo &tibiaServo;
…
leggy(sErVo &coxa, sErVo &femur, sErVo &tibia, int legNum, float coxaLen, float femurLen, float tibiaLen, float z_off){

To do it the complicated way … I think I'll actually write that in a separate post, because what I have written here is a simple fix that will work.

you've got a lot going on in there...

Advice:

try to simplify your code to work with one object....get that working first, then add in all the other objects

To fix this class the complicated way requires an understanding of constructors and constructor syntax.

When any C++ object is instantiated, an appropriate constructor must be invoked. The reason for this is that it allows you to write your class in such a way that you can prove that no matter how it is used from outside, it's internals are always in a consistent state.

An object becomes instantiated at static setup time (if it's a global), during a function call (if it's a stack variable), during the call to new (if it's on the heap) or - and this is the important bit - during the time that a class that contains the instance gets instansiated, which is what we have going on here.

C++, for convenience, supplies a class with a no-argument do-nothing constructor if you don't provide a constructor of your own. But if you do, then it doesn't supply this. And that's the case for your sErVo class.

So, your Leggy class has a variable named tibiaServo, of type sErVo. At no place in your own leggy constructor is tibiaServo correctly constructed. Oh sure, if it had a no-arg constructor then that would be invoked (in principle). But it doesn't. Hence the error. You must invoke the (int, int, long, long) constructor that you have defined.

But when? The problem is that the constructor for tibiaServo must be invoked before any code in Leggy gets run, and that includes the code in the leggy constructor. A bit of a chicken-and-egg situation.

To fix this, C provides some syntax in constructor declarations allowing you to instantiate variables in an object with their appropriate constructors. You put the constructor calls after a colon but before the opening brace:

leggy(int coxa_n, int coxa_d, long coxa_min, long coxa_max, 
   int legNum, float coxaLen, float femurLen, float tibiaLen, float z_off) :
  coxaSevo(coxa_n, coxa_d, coxa_min, coxa_max),
{
 …

An important thing to note is that you can instantiate simple primitive variables this way, too. This give you a way to instantiate classes that have const variables:

class sErVo
{
  const int driverNum;
  const int servoNum;
  const long servoMin;
  const long servoMax;
 
public:
sErVo(int n, int d, long minimum, long maximum) :
  servoNum(n),
  driverNum(d)
  servoMin(minimum),
  servoMax)maximum)
{
}
…

Now obviously, this means you have to pass a swag of parameters to your leggy constructor. That's bad. One way to fix it is with arrays:

leggy(int n[], int d[], long min[], long max[], 
   int legNum, float coxaLen, float femurLen, float tibiaLen, float z_off) :
  coxaSevo(n[0], d[0], min[0], max[0]),
  femurSevo(n[1], d[1], min[1], max[1]),
  tibiaSevo(n[2], d[2], min[2], max[2])
{
 …

But this is bad. The four values should be together, not split across four different arrays.

The way you fix it is to supply SeRvO with a copy constructor and to invoke it in the declaration of the leggy constructor:

class sErVo {
…
public:
sErVo(int n, int d, long minimum, long maximum){
  servoNum = n;
  driverNum = d;
  servoMin = minimum;
  servoMax = maximum;
}

sErVo(const sErVo& s){
  servoNum = s.servoNum;
  driverNum = s.driverNum;
  servoMin = s.servoMin;
  servoMax = s.servoMax;
}
…
}

…
class leggy {

leggy(sErVo& coxa, sErVo& femur, sErVo& tibia,
   int legNum, float coxaLen, float femurLen, float tibiaLen, float z_off) :
  coxaSevo(coxa),
  femurSevo(femur),
  tibiaSevo(tibia)
{
 …

And that's your second and more complicated answer. The positive is that when leggy does suff with coxaServo, it does not have to use a layer of indirection via a reference. The negative is that it leaves two instances of sErVo laying about - one in the array, one in leggy - for each servo.

If this has helped you, them please for the love of God use TitleCase for class names and camelCase for variables. If you need a name that doesn't clash with Servo form the arduino library, use MyServo or LegServo or something, not sErVo. Your prof will thank you for not making his eyes bleed.

OP may find it easier to think of Objects and use inheritance:

#include<Servo.h>

const int minim = 250;
const int maxim = 750;

class Joint : public Servo { // Joint inherits from Servo
  public:
    Joint(int n, int d, long minimum, long maximum): servoNum(n), driverNum(d), servoMin(minimum), servoMax(maximum) {}
    int servoNum;
    long servoMin;
    long servoMax;
    int driverNum;
};

Joint leg1 [] = {
  {4, 0, minim, maxim},
  {5, 0, minim, maxim},
  {6, 0, minim, maxim},
};

class Leg {
  public:
    Leg(Joint* leg) { // Leg class takes an array of (3) Joints as a parameter
      joint = leg;
    }
    void begin() {
      for (int i = 0; i < 3; i++) {
        joint[i].attach(joint[i].servoNum);
      }
    }
    void bendFoot(int dest) {
      joint[0].write(dest);
    }
    void bendKnee(int dest) {
      joint[1].write(dest);
    }
    void bendWaist(int dest) {
      joint[2].write(dest);
    }
  private:
    Joint* joint;
};

Leg frontRight(leg1);

void setup() {
  frontRight.begin();
  frontRight.bendKnee(45);
  delay(1000);
  frontRight.bendFoot(0);
  delay(1000);
  frontRight.bendWaist(90);
}

void loop() {

}

BulldogLowell:
OP may find it easier to think of Objects and use inheritance:

class Joint : public Servo { // Joint inherits from Servo

public:
    Joint(int n, int d, long minimum, long maximum): servoNum(n), driverNum(d), servoMin(minimum), servoMax(maximum) {}

If the Servo class also has a constructor, then

class Joint : public Servo { // Joint inherits from Servo
  public:
    Joint(int n, int d, long minimum, long maximum, int servoArg1, int servoArg2): 
      Servo(servoArg1, servoArg2),
      servoNum(n),  
      driverNum(d), 
      servoMin(minimum), 
      servoMax(maximum) {}

if the 'n' is just a pin number, and the Servo will accept it as a constructor parameter, then the Joint class doesn't need to keep track of it at all - it can just be passed through.

PaulMurrayCbr:
If this has helped you, them please for the love of God use TitleCase for class names and camelCase for variables. If you need a name that doesn't clash with Servo form the arduino library, use MyServo or LegServo or something, not sErVo. Your prof will thank you for not making his eyes bleed.

This helped a ton. I will start using TitleCase for class names and camelCase for variables. It doesn't bother me, but I know it bothers others a lot. I didn't think that I'd have to put my code in a forum right off the bat, so readability by anyone wasn't on my mind... In hindsight, however, I can see that that's pretty dumb, especially since me in 5 years might want to be able to read it as well... lol. Thank you so much for your help, I got it to compile and now I can see if it actually works!