Protocol for avoiding pointers

Hi @ehnmc ,

guess there is a copy&paste error in this switch/case. The motor numbers in case 3 and 4 should quite likely be motor3 and motor4 ...

If I am not mistaken:

  • The data structure for all motors is identical, only their values can be different.
  • The function/methods to control the motors are identical.

This would imply that your application could be implemented with a motor class and an array of motor objects.

Actually struct and class are very similar in C++. It's only the way variables are handled as public and private. Really a small step from your struct to a class.

Good luck!
ec2021

P.S.: Here is an example sketch that uses a struct with integrated functions/methods to handle a set of motors:

Sketch
/*
   Forum: https://forum.arduino.cc/t/protocol-for-avoiding-pointers/1394145
   Wokwi: https://wokwi.com/projects/435993176508959745

  2025/07/10
  ec2021

    Corrected version
    unsigned long steps to float
    MICROSTEP_SIZE to float

  2025/07/11
  ec2021

*/

#define NUM_ELEMENTS(_ArrayName) (sizeof(_ArrayName) / sizeof(_ArrayName[0]))

constexpr int CLOCKWISE {1};
constexpr int COUNTERCLOCKWISE {0};

constexpr long MOTOR_REV {0};
constexpr long DRIVER_MODE {0};

//constexpr long MICROSTEP_SIZE {1 / 8};
// Corrected 2025/07/11
constexpr float MICROSTEP_SIZE {1.0 / 8};

struct MotorData {
  int stepPin;
  int dirPin;
  int direction;
  int rpm;
};

//******************************************************************************
struct MotorClass {
  MotorData data;
  long speed;
  bool run;
  // unsigned long steps = 0;
  // Corrected 2025/07/11
  float steps = 0;
  void init(MotorData mData) {
    data.stepPin = mData.stepPin;
    data.dirPin = mData.dirPin;
    data.direction = mData.direction;
    data.rpm = mData.rpm;
    speed = 10000;  // ((60000000L) / ((long)rpm * ((long)MOTOR_REV * (long)DRIVER_MODE))) / 2;
    run = false;
    pinMode(data.stepPin, OUTPUT);
    pinMode(data.dirPin, OUTPUT);
  }
  void setClockWise(boolean cw) {
    if (cw) {
      data.direction = CLOCKWISE;
    } else {
      data.direction = COUNTERCLOCKWISE;
    }
    digitalWrite(data.dirPin, data.direction); //Set the direction.
  }
  void toggleDirection() {
    setClockWise(data.direction == COUNTERCLOCKWISE);
  }
  void setRun(bool shall_run) {
    run = shall_run;
    if (!run) {
      digitalWrite(data.stepPin, LOW); //Pulse OFF.
    }
  }
  void handle() {
    if (run) {
      digitalWrite(data.stepPin, HIGH); //Pulse ON.
      delayMicroseconds(speed); //Period halftime.
      digitalWrite(data.stepPin, LOW); //Pulse OFF.
      delayMicroseconds(speed); //Period halftime.
      steps += MICROSTEP_SIZE; //Add 1/8 step (motor driver step mode.)
    }
  }
};
//******************************************************************************

// stepPin, dirPin, direction, rpm
MotorData motorData[] = {
  {12, 11, 1, 360},
  {10,  9, 1, 360},
  { 8,  7, 1, 360},
  { 6,  5, 1, 360}
};

constexpr int nMotor = NUM_ELEMENTS(motorData);
MotorClass motor[nMotor];

void setup() {
  Serial.begin(115200);
  for (int i = 0; i < nMotor; i++) {
    motor[i].init(motorData[i]);
  }
}

void loop() {
  doSomethingAll(2);
  for (int i = 0; i < nMotor; i++) {
    motor[i].handle();
  }
}


void doSomethingAll(unsigned long seconds) {
  static unsigned long lastChange = 0;
  static int activeMotor = 0;
  static boolean isFirstCall = true;
  if (millis() - lastChange > seconds * 1000UL || isFirstCall) {
    lastChange = millis();
    if (isFirstCall) {
      isFirstCall = false;
    } else {
      motor[activeMotor].setRun(false);
      activeMotor += 1;
      if (activeMotor >= nMotor) {
        activeMotor = 0;
      }
    }
    motor[activeMotor].toggleDirection();
    motor[activeMotor].setRun(true);
  }
}


Feel free to check it out on Wokwi: https://wokwi.com/projects/435993176508959745

Still there may be room for improvement, just a quick shot ...

And here the changes that change the struct to a class:

Class
//******************************************************************************
class MotorClass {
  private:
    MotorData data;
    long speed;
    bool run;
    // unsigned long steps = 0;
    // Corrected 2025/07/11
    float steps = 0;
  public:
    void init(MotorData mData) {
      data.stepPin = mData.stepPin;
      data.dirPin = mData.dirPin;
      data.direction = mData.direction;
      data.rpm = mData.rpm;
      speed = 10000;  // ((60000000L) / ((long)rpm * ((long)MOTOR_REV * (long)DRIVER_MODE))) / 2;
      run = false;
      pinMode(data.stepPin, OUTPUT);
      pinMode(data.dirPin, OUTPUT);
    }
    void setClockWise(boolean cw) {
      if (cw) {
        data.direction = CLOCKWISE;
      } else {
        data.direction = COUNTERCLOCKWISE;
      }
      digitalWrite(data.dirPin, data.direction); //Set the direction.
    }
    void toggleDirection() {
      setClockWise(data.direction == COUNTERCLOCKWISE);
    }
    void setRun(bool shall_run) {
      run = shall_run;
      if (!run) {
        digitalWrite(data.stepPin, LOW); //Pulse OFF.
      }
    }
    void handle() {
      if (run) {
        digitalWrite(data.stepPin, HIGH); //Pulse ON.
        delayMicroseconds(speed); //Period halftime.
        digitalWrite(data.stepPin, LOW); //Pulse OFF.
        delayMicroseconds(speed); //Period halftime.
        steps += MICROSTEP_SIZE; //Add 1/8 step (motor driver step mode.)
      }
    }
};
//******************************************************************************`

For your purposes you had to add the data for

constexpr long MOTOR_REV {0};
constexpr long DRIVER_MODE {0};

and to change the line

 speed = 10000;  // ((60000000L) / ((long)rpm * ((long)MOTOR_REV * (long)DRIVER_MODE))) / 2;

to use your algorithm.

You can check out the class version on https://wokwi.com/projects/435998337444397057

(Haven't tested all of it ... :wink: )

Have fun!
ec2021

1 Like