2

I am trying to create a class (Arm) that controls an arm. The arms have 2 motors, each with an encoder, so I pass 4 integers for the pins and I am trying to pass 2 pointers (one for each encoder object).

the .ino file is:

#include "Arm.h"
#include <Encoder.h>

//motor driver pins
#define ShoulderUP 7
#define ShoulderDW 8

#define ElbowUP 9
#define ElbowDW 10

#define encoderShoulderPinA 20
#define encoderShoulderPinB 21

#define encoderElbowPinA 22
#define encoderElbowPinB 23

//make encoder objects
Encoder ElbowEncoder(encoderElbowPinA, encoderElbowPinB);
Encoder ShoulderEncoder(encoderShoulderPinA, encoderShoulderPinB);

//make arm object
Arm Arm1(ShoulderUP, ShoulderDW, ElbowUP, ElbowDW, ElbowEncoder, ShoulderEncoder);

void setup() {
  Serial.begin(9600);
}

void loop() {

  Arm1.ShoulderMoveTime(1, 255, 1000);
}

the .h file is:

#ifndef Arm_h
#define Arm_h

#include <Arduino.h>
#include <Encoder.h>

class Arm {

public:
  Arm(int ShoulderUpPin, int ShoulderDownPin, int ElbowUpPin, int ElbowDownPin, Encoder *ElbowEncoder, Encoder *ShoulderEncoder);

  //pins connected to your encoder:
  //   Best Performance: both pins have interrupt capability
  //   Good Performance: only the first pin has interrupt capability
  //   Low Performance:  neither pin has interrupt capability

  void ShoulderMoveTime(int dir, int speed, int time);
  //dir = 0 means up, dir = 1 means down; speed between 0-255
  void ElbowMoveTime(int dir, int speed, int time);
  //dir = 0 means up, dir = 1 means down; speed between 0-255

  //Moves arm to poition given
  void ArmMovePosition(int shoulderPos, int elbowPos);

  // calibrates current arm position to zero
  void ZeroArm();

  //returns position of the arm
  void getArmPosition();

private:
  Encoder *ElbowEncoder;
  Encoder *ShoulderEncoder;
  int _ShoulderUpPin;
  int _ShoulderDownPin;
  int _ElbowUpPin;
  int _ElbowDownPin;
};

#endif

and the .cpp file is:

#include "Arduino.h"
#include "Encoder.h"
#include "Arm.h"

Arm::Arm(int ShoulderUpPin, int ShoulderDownPin, int ElbowUpPin, int ElbowDownPin, Encoder *ElbowEncoder, Encoder *ShoulderEncoder) {

  pinMode(ShoulderUpPin, OUTPUT);
  pinMode(ShoulderDownPin, OUTPUT);
  pinMode(ElbowUpPin, OUTPUT);
  pinMode(ElbowDownPin, OUTPUT);

  _ShoulderUpPin = ShoulderUpPin;
  _ShoulderDownPin = ShoulderDownPin;
  _ElbowUpPin = ElbowUpPin;
  _ElbowDownPin = ElbowDownPin;
  //_ShoulderEncoderPinA = ShoulderEncoderPinA;
  //_ShoulderEncoderPinB = ShoulderEncoderPinB;
  //_ElbowEncoderPinA = ElbowEncoderPinA;
 // _ElbowEncoderPinB = ElbowEncoderPinB;
  ElbowEncoder = *ElbowEncoder;
  ShoulderEncoder = *ShoulderEncoder;
  //ElbowEncoder(_ElbowEncoderPinA, _ElbowEncoderPinB);
  //ShoulderEncoder(_ShoulderEncoderPinA, _ShoulderEncoderPinB);
}

void Arm::ShoulderMoveTime(int dir, int speed, int time) {
...
}

void Arm::ElbowMoveTime(int dir, int speed, int time) {
 ...
}

void Arm::ArmMovePosition(int shoulderPos, int elbowPos) {
  int ShoulderinitialPos = ShoulderEncoder.read();
  int ElbowinitialPos = ElbowEncoder.read();

  //TODO:I think this will only move one joint at a time

  //Move shoulder-------------------------------------------------------------------------------------
  if (shoulderPos > ShoulderinitialPos) {  //check to see which direction arm needs to move

    while (shoulderPos > ShoulderEncoder.read()) {  //while the target position is greater than the motor's actual position, move the shoulder up
      digitalWrite(_ShoulderUpPin, HIGH);           //THIS MAY BE REVERSED
    }
    digitalWrite(_ShoulderUpPin, LOW);  //stop the arm from moving up

  } else if (shoulderPos < ShoulderinitialPos) {

    while (shoulderPos < ShoulderEncoder.read()) {
      digitalWrite(_ShoulderDownPin, HIGH);
    }
    digitalWrite(_ShoulderDownPin, LOW);

  } else {
    Serial.print("Error: invalid shoulder target position");
  }

  //Move elbow-------------------------------------------------------------------------------------
  if (elbowPos > ElbowinitialPos) {  //check to see which direction arm needs to move

    while (elbowPos > ElbowEncoder.read()) {  //while the target position is greater than the motor's actual position, move the elbow up
      digitalWrite(_ElbowUpPin, HIGH);        //THIS MAY BE REVERSED
    }
    digitalWrite(_ElbowUpPin, LOW);  //stop the arm from moving up

  } else if (elbowPos < ElbowinitialPos) {

    while (elbowPos < ElbowEncoder.read()) {
      digitalWrite(_ElbowDownPin, HIGH);
    }
    digitalWrite(_ElbowDownPin, LOW);

  } else {
    Serial.print("Error: invalid elbow target position");
  }
}

void Arm::ZeroArm() {
  ShoulderEncoder.write(0);
  ElbowEncoder.write(0);
  Serial.println("Arm has been tared");
}

void Arm::getArmPosition() {
  Serial.print("Shoulder Position: ");
  Serial.print(ShoulderEncoder.read());
  Serial.print("Elbow Position: ");
  Serial.println(ElbowEncoder.read());
}

Unfortunately, there appears to be some sort of problem with passing the Encoder object. I am getting the error:

Compilation error: no matching function for call to 'Arm::Arm(int, int, int, int, Encoder&, Encoder&)'

I am definitely a noob when it comes to coding, so thanks in advance for what is probably a simple problem.

4
  • What happens when you pass the Encoder object as pointer via &ElbowEncoder?
    – chrisl
    Mar 15, 2023 at 20:30
  • I get "Compilation error: no match for 'operator=' (operand types are 'Encoder' and 'Encoder*')" Mar 15, 2023 at 22:17
  • please copy all of the error message and paste it into your question ... do not put it into a comment like you just did ... also, no pictures of text ... format the text as code
    – jsotola
    Mar 15, 2023 at 22:49
  • 1
    you can use reference. member and parameter as Encoder& type and in constructor initialization list you set the value of the member from the value of the parameter. MyClass::MyClass(Encoder& _encoder) : encoder(_encoder) {}
    – Juraj
    Mar 16, 2023 at 4:58

1 Answer 1

1

You're jumping backwards and forwards between pointers and the objects themselves. Change it to pass pointers to the arm constructor and consistently use pointers (or pass a reference and always use that, just be consistent). This would be something like:

    // main code, pass a pointer rather than the object itself
    Arm Arm1(ShoulderUP, ShoulderDW, ElbowUP, ElbowDW, &ElbowEncoder, &ShoulderEncoder);

    // header
    public:
      Arm(int ShoulderUpPin, int ShoulderDownPin, int ElbowUpPin, int ElbowDownPin, Encoder *ElbowEncoder, Encoder *ShoulderEncoder);


    // cpp
    Arm::Arm(int ShoulderUpPin, int ShoulderDownPin, int ElbowUpPin, int ElbowDownPin, Encoder *ElbowEncoder, Encoder *ShoulderEncoder) {

      pinMode(ShoulderUpPin, OUTPUT);
      pinMode(ShoulderDownPin, OUTPUT);
      pinMode(ElbowUpPin, OUTPUT);
      pinMode(ElbowDownPin, OUTPUT);

      _ShoulderUpPin = ShoulderUpPin;
      _ShoulderDownPin = ShoulderDownPin;
      _ElbowUpPin = ElbowUpPin;
      _ElbowDownPin = ElbowDownPin;
      this.ElbowEncoder = ElbowEncoder; // use 'this.' to indicate the class private variable since it's the same name as the passed parameter.
      this.ShoulderEncoder = ShoulderEncoder;
    }

And then within your class, since ElbowEncoder is a pointer rather than the encoder itself, use ElbowEncoder->method() rather than ElbowEncoder.method()

Personally, I'd restructure it a little. Why create the encoders at the top level when they are only used within the arm class? Create them in the arm class instead:

    //in .h
    public:
      Arm(int ShoulderUpPin, int ShoulderDownPin, int ShoulderEncoderPinA,int ShoulderEncoderPinB,
          int ElbowUpPin, int ElbowDownPin, int ElbowEncoderPinA, int ElbowEncoderPinB);

    private:
      Encoder ShoulderEncoder;
      Encoder ElbowEncoder;


    // in .cpp
    Arm::Arm(int ShoulderUpPin, int ShoulderDownPin, int ShoulderEncoderPinA,int ShoulderEncoderPinB,
             int ElbowUpPin, int ElbowDownPin, int ElbowEncoderPinA, int ElbowEncoderPinB) :
                 ShoulderEncoder(ShoulderEncoderPinA,ShoulderEncoderPinB),
                 ElbowEncoder(ElbowEncoderPinA,ElbowEncoderPinB) {
       // constructor code
    }

This will create the encoders as private to the arm as soon as you call the arm constructor. Especially handy if you want to have more than one arm.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.