Can Servo Obects be Named, Stored in Struct, and Called By Name?

It looks like the answer is no but thought I'd ask. I'm using the basic Servo.h library, so this question may be moot when the PCA9685 driver arrives, it uses an entirely different library, but learning more about these as I go.

Think of the joints in a skeleton. I would like to name them backbone, shoulder, elbow, etc. This way I can easily identify mods I need to make to each servo's properties (speed, direction, etc.) on the fly. I'm aware the easiest way would be to just use arrays, but I'm hoping I don't have to fiddle about mapping which index is the shoulder, which is elbow, etc.

My hope is to be able to do something like moveServo(backbone, 0, 180, 10) where 0 is start, 180 is end, 10 is speed. I'll need to be able to dynamically change these on the fly.

This is not working code but my muddled thinking-through of setting up the structures for easier access. The first version just uses a string for "name" (and obviously doesn't work)

typedef struct {
    String servo_name;
    char pin_num;
    int speed;
} servo_props;

/* object name, pin number, speed in MS*/
servo_props props[] = {
  { "backbone", 9, 15 },
  { "shoulder", 10, 15},
  { "elbow", 11, 15 } 
};

I suppose this would do if it's the only way, but I can't find anything on the typedef for Servo objects.


Servo backbone;
Servo shoulder;
Servo elbow;

typedef struct {
    [typedef?] servo_object; // What typedef to use here? Couldn't figure it out.
    char pin_num;
    int speed;
} servo_props;

/* object name, pin number, speed in MS*/
servo_props props[] = {
  { backbone, 9, 15 },
  { shoulder, 10, 15},
  { elbow, 11, 15 } 
};

I've read up on pointers and object references, don't seem like they'd work for what I'm trying to do.

What's a good way to approach this, should I just stop trying to be smarter than I am and use arrays? :smiley:

How about using this approach

#include <Servo.h>

struct servoData
{
    int pin;
    Servo servo;
};

servoData shoulder;

void setup()
{
    Serial.begin(115200);
    shoulder.pin = 2;
    shoulder.servo.attach(shoulder.pin);
}

void loop()
{
    moveJoint(shoulder.servo, 35);
    delay(1000);
    moveJoint(shoulder.servo, 125);
    delay(1000);
}

void moveJoint(Servo name, int angle)
{
    name.write(angle);
}

First of all you can name your servos according to their name. IMO that fits a lot of use cases. Next you can have a name list or char tree with pointers or references to the servos. Fixed names can be looked up once and the servo object is stored with the string.

Character input for servo names can be used to search that list or tree with each received character. With well formed names a few characters are sufficient to find the right servo.

This looks great, I didn't know you could attach before instantiating the object. I just have to multiply it by 8 or 10. :smiley:

Look more carefully. The instance is created before attaching the pin

You don't need a struct to do that

Declare an array of Servos, with 8 levels and an enum with 8 names, then you can simply refer to each servo by the name in the enum

Like this

#include <Servo.h>

enum names
{
    arm,
    shoulder,
    elbow
};

const byte pinNums[] = { 2, 4, 7 };

Servo joints[3];

void setup()
{
    Serial.begin(115200);
    joints[arm].attach(pinNums[arm]);
    joints[shoulder].attach(pinNums[shoulder]);
    joints[elbow].attach(pinNums[elbow]);
}

void loop()
{
    moveJoint(arm, 35);
    delay(1000);
    moveJoint(arm, 125);
    delay(1000);
}

void moveJoint(int number, int angle)
{
    joints[number].write(angle);
}

Be careful with creating a proliferation of code - it may well be necessary/desirable to access your servos by name but it may lead to loads of code with the necessity to refer to the individual parts directly.

Will you have a left shoulder & elbow? Then it'll be tempting to indulge in some copy and paste coding, with commensurate risks of extra bugs.

Consider, if possible, using higher level constructs that perform an arm action perhaps and hide all the details of what the servos are doing. You can use the same thing for multiple arms and debugging will be easier.

I agree. Naming the joints is only the start

Taking things further, each joint could be an object and so could each limb. Each object could have properties defining the current position for joints and perhaps shape for each limb.

It would also be possible to introduce reverse kinematics into the project so that the actual details of joint/limb movement is transparent but I suspect that @rocknbil is not quite ready for that yet

In fact, I would go as far as to say that creating a proliferation of code then changing to an OOP approach would be a good introduction to the advantages of OOP

This is awesome, I love the OOP approach! May be over the top for this project, but will play with it!

Thanks and yeah, I saw that after I fiddled with it a bit. :smiley:

This is exactly what I'm trying to avoid.

Correct, this is what I'm trying to move toward, also controlling the speed of the joints individually.

Correct, I am a die hard OOP fan in web apps, coded OOP PHP for about a decade (linear before that,) but there comes a point where you have to just get it done. I started off using structs more to organize the code and will try the enum approach, that looks pretty clean as well.

I have modified the code you originally and have a basic circuit working, but it's still a bit hinky and being early in the game I will mod it all today for review. Thank you all for your help, it's great to head this off before I build a barely working bowl of spaghetti! :smiley: