AccelStepper looping w/ limited accelerations

I am working on a project that has a 4x3 array of stepper motors (28byj-48), running off an ArduinoMega (setup pic).

I’m trying to program the motors to step in various patterns (causing the black faceplates to move up/down), often having multiple motors moving at the same time.

I’ve found that I am not able to get the same acceleration performance as I start including more motors within my AccelStepper .run() loop. I’m suspecting that there is something wrong with how I am defining the loop, or maybe this is a known limitation (although my searches have not made this obvious).

When only running a single motor (Accel = 1500; MaxSpeed=2500), I am able to move through the full stroke in ~3.1s. In the below video you can also hear the motor step with acceleration as expected.

Video of single motor operation:https://photos.app.goo.gl/rqEbi17NCxwd5LLP6

When running 4 motors at the same time, with the same settings, it takes ~4s to cover the same distance, and the motor also does not sound like it is going through the same acceleration.

Video of 4 motor operation: https://photos.app.goo.gl/JdWfr4vQtDobkKEe6

Appreciate any advice! I don’t have much programming experience, although I suspect that is already clear. :slight_smile:

[Code attached due to length]

12_Motor_Development_RevA.zip (5.23 KB)

Please post the complete program as a single piece. There is a too great risk that I will make a mistake when joining your snippets.

...R

[Removed the code here and attached to the original post]

Please post the complete program as a single piece.

Do you not understand what the word in red means?

Read the stickies at the top of the forum. They explain what to do if your code exceeds the 9500 character limit. Splitting it into multiple posts simply proves that you haven't read the stickies, or think that they don't apply to you.

Well, they do.

PaulS:
Do you not understand what the word in red means?

Read the stickies at the top of the forum. They explain what to do if your code exceeds the 9500 character limit. Splitting it into multiple posts simply proves that you haven’t read the stickies, or think that they don’t apply to you.

Well, they do.

A little harsh, but I can understand where your frustration is coming from. Apologies.

I did read through the stickies here (several times now), and I don’t see any commentary on the character limit. But I’m sure it’s there and will be pointed out to me with enthusiasm.

I did some searching on the Googs, and the best reference thread I could find (no sticky) suggested to simply attach a .zip with the sketch. I’ve done that here now.

12_Motor_Development_RevA.zip (5.23 KB)

Attaching the file was correct. Embedding it in a zip file was not.

Gah!

... I knew I should of RAR'd it.

Please just add your .ino file as an attachment if the code is too long to include in a Post. And in your next Reply. People rarely bother to re-read earlier posts.

It is generally easier to help if you write a short program that illustrates the problem and leaves out other irrelevant stuff.

...R

Robin2:
Please just add your .ino file as an attachment if the code is too long to include in a Post. And in your next Reply. People rarely bother to re-read earlier posts.

It is generally easier to help if you write a short program that illustrates the problem and leaves out other irrelevant stuff.

…R

Thanks for the advice. I cut down the program enough now to fit below, and to do only the two functions discussed in the original post (Step-x1 or Step-x4).

Note that the x4 runs through all twelve motors (in groups of 4) while the x1 just runs on the first motor.

The focus here is that when running in groups of 4, I’m not getting the same acceleration response from my .run() loop.

Much thanks for all the patience.

#include <AccelStepper.h>
#include<Wire.h>
#define FULLSTEP 4
#define HALFSTEP 8
#define motor1_Pin2 2 
#define motor1_Pin1 3      
#define motor1_Pin4 4      
#define motor1_Pin3 5     
#define motor2_Pin2 6      
#define motor2_Pin1 7      
#define motor2_Pin4 8      
#define motor2_Pin3 9 
#define motor3_Pin1 10      
#define motor3_Pin2 11     
#define motor3_Pin3 12     
#define motor3_Pin4 13 
#define motor4_Pin1 14      
#define motor4_Pin2 15     
#define motor4_Pin3 16     
#define motor4_Pin4 17 
#define motor5_Pin1 47      
#define motor5_Pin2 49      
#define motor5_Pin3 51      
#define motor5_Pin4 53 
#define motor6_Pin1 22     
#define motor6_Pin2 24      
#define motor6_Pin3 26      
#define motor6_Pin4 28 
#define motor7_Pin1 23      
#define motor7_Pin2 25      
#define motor7_Pin3 27      
#define motor7_Pin4 29 
#define motor8_Pin1 30      
#define motor8_Pin2 32      
#define motor8_Pin3 34      
#define motor8_Pin4 36 
#define motor9_Pin1 31      
#define motor9_Pin2 33      
#define motor9_Pin3 35      
#define motor9_Pin4 37 
#define motor10_Pin1 38      
#define motor10_Pin2 40      
#define motor10_Pin3 42      
#define motor10_Pin4 44 
#define motor11_Pin1 39      
#define motor11_Pin2 41     
#define motor11_Pin3 43      
#define motor11_Pin4 45 
#define motor12_Pin2 46      
#define motor12_Pin1 48      
#define motor12_Pin4 50      
#define motor12_Pin3 52 

char user_input;
int user_input2;
int user_input3;
int x;
int y;
int z;
int StepperNumber;
const int NumberOfSteppers = 12;
int MaxSpeed = 2500;
int Accel = 1300;
int Speed = 10000;
int RunOut = -2048*1.8;
long MoveTo;
int SlaveGo = 1;
int MasterReceive;

AccelStepper stepper1(HALFSTEP, motor1_Pin1, motor1_Pin3, motor1_Pin2, motor1_Pin4);
AccelStepper stepper2(HALFSTEP, motor2_Pin1, motor2_Pin3, motor2_Pin2, motor2_Pin4);
AccelStepper stepper3(HALFSTEP, motor3_Pin1, motor3_Pin3, motor3_Pin2, motor3_Pin4);
AccelStepper stepper4(HALFSTEP, motor4_Pin1, motor4_Pin3, motor4_Pin2, motor4_Pin4);
AccelStepper stepper5(HALFSTEP, motor5_Pin1, motor5_Pin3, motor5_Pin2, motor5_Pin4);
AccelStepper stepper6(HALFSTEP, motor6_Pin1, motor6_Pin3, motor6_Pin2, motor6_Pin4);
AccelStepper stepper7(HALFSTEP, motor7_Pin1, motor7_Pin3, motor7_Pin2, motor7_Pin4);
AccelStepper stepper8(HALFSTEP, motor8_Pin1, motor8_Pin3, motor8_Pin2, motor8_Pin4);
AccelStepper stepper9(HALFSTEP, motor9_Pin1, motor9_Pin3, motor9_Pin2, motor9_Pin4);
AccelStepper stepper10(HALFSTEP, motor10_Pin1, motor10_Pin3, motor10_Pin2, motor10_Pin4);
AccelStepper stepper11(HALFSTEP, motor11_Pin1, motor11_Pin3, motor11_Pin2, motor11_Pin4);
AccelStepper stepper12(HALFSTEP, motor12_Pin1, motor12_Pin3, motor12_Pin2, motor12_Pin4);

AccelStepper* stepperPtrArray[NumberOfSteppers]={&stepper1,&stepper2,&stepper3,&stepper4,&stepper5,
&stepper6,&stepper7,&stepper8,&stepper9,&stepper10,&stepper11,&stepper12};

void setup() {
  Serial.println();
  Serial.begin(9600); //Open Serial connection for debugging
  Serial.println("Begin motor control");
  Serial.println();
  Serial.println("Enter number for control option:");
  Serial.println("1. Move single motor");
  Serial.println("2. Move 4x motor");
  Serial.println();

  stepper1.setMaxSpeed(MaxSpeed);stepper1.setAcceleration(Accel);stepper1.setSpeed(Speed);
  stepper2.setMaxSpeed(MaxSpeed);stepper2.setAcceleration(Accel);stepper2.setSpeed(Speed);
  stepper3.setMaxSpeed(MaxSpeed);stepper3.setAcceleration(Accel);stepper3.setSpeed(Speed);
  stepper4.setMaxSpeed(MaxSpeed);stepper4.setAcceleration(Accel);stepper4.setSpeed(Speed);
  stepper5.setMaxSpeed(MaxSpeed);stepper5.setAcceleration(Accel);stepper5.setSpeed(Speed);
  stepper6.setMaxSpeed(MaxSpeed);stepper6.setAcceleration(Accel);stepper6.setSpeed(Speed);
  stepper7.setMaxSpeed(MaxSpeed);stepper7.setAcceleration(Accel);stepper7.setSpeed(Speed);
  stepper8.setMaxSpeed(MaxSpeed);stepper8.setAcceleration(Accel);stepper8.setSpeed(Speed);
  stepper9.setMaxSpeed(MaxSpeed);stepper9.setAcceleration(Accel);stepper9.setSpeed(Speed);
  stepper10.setMaxSpeed(MaxSpeed);stepper10.setAcceleration(Accel);stepper10.setSpeed(Speed);
  stepper11.setMaxSpeed(MaxSpeed);stepper11.setAcceleration(Accel);stepper11.setSpeed(Speed);
  stepper12.setMaxSpeed(MaxSpeed);stepper12.setAcceleration(Accel);stepper12.setSpeed(Speed);
}

void loop() {
  
while(Serial.available()){
      user_input = Serial.read(); //Read user input and trigger appropriate function
      if (user_input =='1')
        {
         Stepx1();
        }
       else if(user_input =='2')
        {
         Stepx4();
        }
      else
        {
         Serial.println("Invalid option entered.");
        } 
}
}

void Stepx1()
{
float STArray;
float FTArray;

StepperNumber = 0; 
stepperPtrArray[StepperNumber]->setCurrentPosition(0);
stepperPtrArray[StepperNumber]->moveTo(RunOut);

STArray=millis();
  while (abs(stepperPtrArray[StepperNumber]->distanceToGo()) > 0)
      {
        stepperPtrArray[StepperNumber]->run();
      }
    
FTArray=millis();
stepperPtrArray[StepperNumber]->disableOutputs();
  
  Serial.println(FTArray-STArray);
}

void Stepx4()
{
 
 int StepperGroups[][4] = {
 {0, 1, 2, 3},
 {4, 5, 6, 7},
 {8, 9, 10, 11}};

int ArrayRow = (sizeof(StepperGroups)/10)%10 + 1;
int ArrayCol = (sizeof(StepperGroups))%10;
float STArray[12];
float FTArray[12];
  
Serial.println("Moving all steppers forward");
   
  for (StepperNumber = 0; StepperNumber < NumberOfSteppers; StepperNumber++)
    {
      stepperPtrArray[StepperNumber]->setCurrentPosition(0);
      stepperPtrArray[StepperNumber]->moveTo(RunOut);
    }
    
for (x=0; x<ArrayRow; x++)
 {
  STArray[x]=millis();
    while (abs(stepperPtrArray[(StepperGroups[x][1])]->distanceToGo()) > 0)
       {
        for (z=0; z<ArrayCol; z++)
          {
          stepperPtrArray[(StepperGroups[x][z])]->run();
          }
       }
      
    FTArray[x]=millis();
    DisableOutputs();
  }
  
for(int i = 0; i < 3; i++)
  {
    Serial.print("Motor");Serial.print(i);Serial.print(":");
    Serial.println(FTArray[i]-STArray[i]);
  }
}

void DisableOutputs()
  {
       for (StepperNumber = 0; StepperNumber < NumberOfSteppers; StepperNumber++)
    {
      stepperPtrArray[StepperNumber]->disableOutputs();
    }
  }

Any time you have numbers in your variable names, it's time to learn about arrays

MorganS:
Any time you have numbers in your variable names, it’s time to learn about arrays

Are you suggesting that my loop that’s running through the motors (in an array) is not the appropriate use of arrays? Or just pointing out that I could use arrays more than I have?

Is this feedback relevant to my topic, or intended as general coding advice? Sorry if I’m not capturing your intent correctly.

Sorry, I didn't read past setup() on my phone. You could have used the array there too.

The whole loop() function is inside if(Serial.available()) The steppers can't step any faster than arriving Serial characters. You need to call run() MUCH faster than that. Note that Serial is so extremely slow that while() has the same effect as if() in this example.

MorganS:
Sorry, I didn't read past setup() on my phone. You could have used the array there too.

The whole loop() function is inside if(Serial.available()) The steppers can't step any faster than arriving Serial characters. You need to call run() MUCH faster than that. Note that Serial is so extremely slow that while() has the same effect as if() in this example.

Interesting, so even though I'm pointing to a function outside the main loop() when I run the motors (where I'm not checking for serial comm inputs), it will still be limited by the serial communication speed in the main loop()?

Have a read of Planning and implementing an Arduino program

MorganS:
Have a read of Planning and implementing an Arduino program

I did a simple test by cutting out the serial communication and just called my subroutine directly, and there is no difference in the stepping performance with the four motors. Given my motor stepping loops are in separate functions, this seems logical to me that they would not have been impacted by the if(serial.available()) in the main loop().

Looking at my looping for multiple motors, I’m not sure how I could do it more efficiently… So I’m suspecting this may be either a limitation of the AccelStepper library or in how I’m using it (likely the latter).

while (abs(stepperPtrArray[(StepperGroups[x][1])]->distanceToGo()) > 0)
       {
        for (z=0; z<ArrayCol; z++)
          {
          stepperPtrArray[(StepperGroups[x][z])]->run();
          }
        }

Awesome thread regardless, thanks for sharing! I’ll be digging into that.

marshallworrall:
Thanks for the advice. I cut down the program enough now to fit below, and to do only the two functions discussed in the original post (Step-x1 or Step-x4).

Either I have completely missed what your project is about or this is absurdly complicated.

I would approach this by creating an array of stepper motors. I’m not sure what is the syntax for doing that with the AccelStepper library but I know it can be done.

Pretend it’s something like this

AccelStepper myStepperArray[12] = { {HALFSTEP, motor1_Pin1, motor1_Pin3, motor1_Pin2, motor1_Pin4},
                                                             {HALFSTEP, motor2_Pin1, motor2_Pin3, motor2_Pin2, motor2_Pin4},

                                                           }

Then in loop() I would run them all with a few lines like this

for (byte n = 0; n < numStepper, n++) {
    myStepperArray[n].run()
}

and then elsewhere in the program there could be code that sets the number of steps for any or all motors.

With multiple motors you do run the possibility that the AccelStepper library cannot produce steps fast enough to run them at the speed you would like.

…R

marshallworrall:
I did a simple test by cutting out the serial communication and just called my subroutine directly, and there is no difference in the stepping performance with the four motors. Given my motor stepping loops are in separate functions, this seems logical to me that they would not have been impacted by the if(serial.available()) in the main loop().

Show your new code.

The original code would never call those functions without serial input so they were not independent.

If you think the number of motors is important, try 3 or 2.

Robin2:
Either I have completely missed what your project is about or this is absurdly complicated.

The problem here has been in my ability to communicate my question clearly… a lesson learned.

I have further simplified this sketch to look at only 4 motors. Then tested it with varying number of motors stepping at my target speeds.

I have found that when running two motors in parallel, they seem to follow the expecting acceleration curve. However, once I include 3 or more motors in the same stepping loop, they run at almost a constant speed (maybe a very brief moment of acceleration in the initial steps).

Robin2:
With multiple motors you do run the possibility that the AccelStepper library cannot produce steps fast enough to run them at the speed you would like.

This seems to be the source of my problem, and I assume would scale (i.e. the motors run slower) if I were to include, say 10 motors into one loop…

I see a few threads out there on exploring alternative paths with faster controllers or alternative libraries. Looks like I have some research to do. Thanks again for your time.

#include <AccelStepper.h>
#define HALFSTEP 8
#define motor1_Pin2 2 
#define motor1_Pin1 3      
#define motor1_Pin4 4      
#define motor1_Pin3 5     
#define motor2_Pin2 6      
#define motor2_Pin1 7      
#define motor2_Pin4 8      
#define motor2_Pin3 9 
#define motor3_Pin1 10      
#define motor3_Pin2 11     
#define motor3_Pin3 12     
#define motor3_Pin4 13 
#define motor4_Pin1 14      
#define motor4_Pin2 15     
#define motor4_Pin3 16     
#define motor4_Pin4 17 

int x;
int y;
int z;
int StepperNumber;
const int NumberOfSteppers = 4;
int MaxSpeed = 2500;
int Accel = 1300;
int Speed = 1000;
int RunOut = -2048*1.8;
long MoveTo;

AccelStepper stepper1(HALFSTEP, motor1_Pin1, motor1_Pin3, motor1_Pin2, motor1_Pin4);
AccelStepper stepper2(HALFSTEP, motor2_Pin1, motor2_Pin3, motor2_Pin2, motor2_Pin4);
AccelStepper stepper3(HALFSTEP, motor3_Pin1, motor3_Pin3, motor3_Pin2, motor3_Pin4);
AccelStepper stepper4(HALFSTEP, motor4_Pin1, motor4_Pin3, motor4_Pin2, motor4_Pin4);

AccelStepper* stepperPtrArray[NumberOfSteppers]={&stepper1,&stepper2,&stepper3,&stepper4};

void setup() {

  stepper1.setMaxSpeed(MaxSpeed);stepper1.setAcceleration(Accel);stepper1.setSpeed(Speed);
  stepper2.setMaxSpeed(MaxSpeed);stepper2.setAcceleration(Accel);stepper2.setSpeed(Speed);
  stepper3.setMaxSpeed(MaxSpeed);stepper3.setAcceleration(Accel);stepper3.setSpeed(Speed);
  stepper4.setMaxSpeed(MaxSpeed);stepper4.setAcceleration(Accel);stepper4.setSpeed(Speed);
}

void loop() {
Stepx1();
Stepx4();
}

void Stepx1()
{

StepperNumber = 0; 
stepperPtrArray[StepperNumber]->setCurrentPosition(0);
stepperPtrArray[StepperNumber]->moveTo(RunOut);

  while (abs(stepperPtrArray[StepperNumber]->distanceToGo()) > 0)
      {
        stepperPtrArray[StepperNumber]->run();
      }

stepperPtrArray[StepperNumber]->disableOutputs();
}

void Stepx4()
{
   
  for (StepperNumber = 0; StepperNumber < 4; StepperNumber++)
    {
      stepperPtrArray[StepperNumber]->setCurrentPosition(0);
      stepperPtrArray[StepperNumber]->moveTo(RunOut);
    }
    
  while (abs(stepperPtrArray[0]->distanceToGo()) > 0)
       {
          stepperPtrArray[0]->run();
          stepperPtrArray[1]->run();
          stepperPtrArray[2]->run();
          stepperPtrArray[3]->run();
       }
       
    DisableOutputs();
  }


void DisableOutputs()
  {
       for (StepperNumber = 0; StepperNumber < NumberOfSteppers; StepperNumber++)
    {
      stepperPtrArray[StepperNumber]->disableOutputs();
    }
  }

Because you are using 28BYJ stepper motors the Arduino has to deal with the business of applying the coil current in the correct sequence. In contrast, if you use a bipolar stepper motor that works with a stepper driver like an A4988 all the Arduino has to do is send a signal every time it needs a step performed. The A4988 takes care of the "housekeeping".

As a separate comment, you may get better performance by not using the AccelStepper library. You could create a simple function that causes a step to happen each time it is called and then write your own acceleration code similar to the code in this link.

Another thought is to use something like an Attiny as a quasi-stepper-driver for one (or at most two) of the stepper motors - still using the ULN2003 as tha actual drivers. Then the Arduino could ask for a step to be taken merely by asserting an I/O pin between it and an Attiny The Attiny would do the "housekeeping"

...R

This part is slow???

  while (abs(stepperPtrArray[0]->distanceToGo()) > 0)

{
          stepperPtrArray[0]->run();
          stepperPtrArray[1]->run();
          stepperPtrArray[2]->run();
          stepperPtrArray[3]->run();
      }

I would not have expected that at all, even with the slightly-more-work of stepping 4 individual outputs. That should run at many thousands of steps per second, divided among the 4 motors. I find it hard to believe that 4000/second is too fast. That gives a 16MHz Arduino 4000 instructions in between each step. You can't do a lot with 4000 instructions but it should be enough. AccelStepper is pretty efficient.

I'm thinking there's something else going on. Maybe power supply or wiring?