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 ...
)
Have fun!
ec2021