Varying RPM of 2 Stepper Motors Controlled by Stepper Drivers

const int stepPin = 2;//only works on this pin right now
const int dirPin = 3;
const int actPin = 4;//not used
const float motorAngle = 1.8;
const float stepSize = 0.03125;//full=1, half=0.5, quarter=0.25, etc...

void stepperRotate(float rotation, float rpm);

void setup() 
{
  pinMode(stepPin, OUTPUT);
  pinMode(dirPin, OUTPUT);
  Serial.begin(9600);
}

void loop() 
{
// simple rotation forward then backward:
  stepperRotate(1, 100);//rotations, RPM
  delay(1000);
  stepperRotate(-1, 100);//rotations, RPM
  delay(1000);

// acceleration
  for (int i = 200; i <= 500; i = i + 10) 
 { 
    stepperRotate(1, i); //rotations, RPM
  }
//decelerate 
  for (int i = 500; i >= 200; i = i - 10) 
 {
    stepperRotate(1, i); //rotations, RPM
  }

}

void stepperRotate(float rotation, float rpm) 
{
  if (rotation > 0) 
 {
    digitalWrite(dirPin, HIGH);
  }
  else {
    digitalWrite(dirPin, LOW);
    rotation = rotation * -1;
  }

  // first figure out how many steps in one rotation, so a motor with 1.8deg per step, equals 360/1.8 = 200 steps/rotation
  // then if you're doing half step, that will double, so divide by 0.5, gives 400steps per rotation
  float stepsPerRotation = (360.00 / motorAngle) / stepSize;

  //now we have the steps per rotation, multiply by the rotations for this command to get total steps
  float totalSteps = rotation * stepsPerRotation;
  //Serial.println(totalSteps); // debug

  //tricky part here - what is the ON/OFF time of the step pin to get the desired RPM?
  // First, what is the seconds per revolution? then we can figure out seconds per step
  // RPM (rotation per minute) needs to be converted to MPR, so 1/RPM, then seconds per rotation is 60seconds/RPM
  // that gives us Seconds per Rotation, but how many seconds per step? well, we just divide that by the number of steps per rotation
  // so now we're at 60/RPM/stepsPerRotation
  // this is seconds, but we're going to use microSeconds, so let's multiply by 1 Million (1E6)
  // then, we want a 50% duty cycle, half time ON, half time OFF, so divide this value by 2, then we end up with:
  unsigned long stepPeriodmicroSec = ((60.0000 / (rpm * stepsPerRotation)) * 1E6 / 2.0000) - 5;

  //what's up with the -5 at the end?  well, in the for loop, we have to compensate for the i++ and i<x check, so 5us is subracted to speed it up a little

  for (unsigned long i = 0; i < totalSteps; i++) {
    PORTD |= (1 << 2);
    delayMicroseconds(stepPeriodmicroSec);
    PORTD &= ~(1 << 2);
    delayMicroseconds(stepPeriodmicroSec);
  }
}
#include <Stepper.h> 

const double step_mode = .125;   // 1: full step, 0.5: half step, 0.125: one-eigth step
const double motor_deg = 1.8;    // number of degrees in one step
const double spr = 360/motor_deg/step_mode;  // spr: (steps / revolution)
const double wheel_diameter = 3.54331;   // radius in inches
const double dpr = PI*wheel_diameter;   // dpr: (distance in inches / revolution);
const double rpm1 = 60;
const double rpm2 = 120;

Stepper left_stepper(spr, 2, 3); //stepper(spr, dir, step)
Stepper right_stepper(spr, 9, 8);  

unsigned long stepPeriodmicroSec = ((60.0000 / (rpm1 * spr)) * 1E6 / 2.0000) - 5; //used to get RPM
unsigned long stepPeriodmicroSec2 = ((60.0000 / (rpm2 * spr)) * 1E6 / 2.0000) - 5; //used to get RPM

void setup() 
{
}

void loop() 
{
   for(int x=0; x<=3200; x++)  //rpm is only set to whichever stepPeriodmicroSec is higher
    {
      PORTD |= (1 << PD2);
      delayMicroseconds(stepPeriodmicroSec);
      PORTD &= ~(1 << PD2); 
      delayMicroseconds(stepPeriodmicroSec); 
      PORTB |= (1 << PB0); 
      delayMicroseconds(stepPeriodmicroSec2);
      PORTB &= ~(1 << PB0);
      delayMicroseconds(stepPeriodmicroSec2); 
    }
    delay(1000);
}

The first code is an example from a video to control only 1 stepper motor. I tried to manipulate (snippet of it in second code) it to run 2 motors with different set rpms. In my for loop, it only runs both at the rpm of whichever is the lower set rpm. Using separate for loops with the integrated Blinkwithoutdelay example runs both at whichever is the higher rpm as seen in the 3rd example

#include <Stepper.h> //used to decrlare stepper objects and .step() function

const double step_mode = .125;   // 1: full step, 0.5: half step, 0.125: one-eigth step
const double motor_deg = 1.8;    // number of degrees in one step
const double spr = 360/motor_deg/step_mode;  // spr: (steps / revolution)
const double wheel_diameter = 3.54331;   // radius in inches
const double dpr = PI*wheel_diameter;   // dpr: (distance in inches / revolution);
const double rpm1 = 120;
const double rpm2 = 200;

Stepper left_stepper(spr, 2, 3); //stepper(spr, dir, step)
Stepper right_stepper(spr, 9, 8);  

unsigned long stepPeriodmicroSec = ((60.0000 / (rpm1 * spr)) * 1E6 / 2.0000) - 5; //used to get RPM
unsigned long stepPeriodmicroSec2 = ((60.0000 / (rpm2 * spr)) * 1E6 / 2.0000) - 5; //used to get RPM

unsigned long previousMillis1 = 0;
unsigned long previousMillis2 = 0;

const long interval_left_stepper = 1;           // interval at which to blink (milliseconds)
const long interval_right_stepper = 1;           // interval at which to blink (milliseconds)

void setup() {
}

void loop() {

  unsigned long currentMillis1 = millis();
  unsigned long currentMillis2 = millis();

  if (currentMillis - previousMillis >= interval_left_stepper) 
  {
    previousMillis1 = currentMillis1;
    
    PORTD |= (1 << PD2);
    delayMicroseconds(stepPeriodmicroSec);
    PORTD &= ~(1 << PD2); 
    delayMicroseconds(stepPeriodmicroSec); 
  }
  if (currentMillis - previousMillis >= interval_right_stepper) 
  {
    previousMillis2 = currentMillis2;
    
    PORTB |= (1 << PB0); 
    delayMicroseconds(stepPeriodmicroSec2);
    PORTB &= ~(1 << PB0);
    delayMicroseconds(stepPeriodmicroSec2); 
  }
}

You are not going to be able to get two motors working independently using delay() or delayMicroseconds(). Have a look at how millis() and micros() are used for non-blocking timing in the second example in this Simple Stepper Code

However the simplest solution may be to use the AccelStepper library and then you won't need any of the PORTD etc stuff.

...R
Stepper Motor Basics

@Robin2. From what I can tell from your 2nd example in simple stepper code, instead of having a delay between step high and low, you put nothing inbetween and set them in their own defined function. Adding a second stepper would, as you suggested, require me to use the AccelStepper library for running 2 steppers with varying rpms and directions over time. But the problem with that library, is that their function calls are also blocking. I need the steppers to run a predetermined course while having space for my main loop to read stuff from my sensors.

SkarnerFTW:
Adding a second stepper would, as you suggested, require me to use the AccelStepper library for running 2 steppers with varying rpms and directions over time. But the problem with that library, is that their function calls are also blocking.

You are wrong here on two counts.

Adapting my code to work with two or more motors would be straightforward. I have something similar driving three motors for a small CNC lathe which also requires the movements to be closely coordinated.

The standard AccelStepper .run() function is non-blocking. There are also blocking functions for those who need them.

...R

#include <AccelStepper.h>
#include <Servo.h>
#include <Adafruit_TCS34725.h>

AccelStepper Left_Stepper(1, 2, 3);
AccelStepper Right_Stepper(1, 4, 5);

Servo servo1;

unsigned long curMillis;
unsigned long prevStepMillis = 0;
unsigned long millisBetweenSteps = 1; // milliseconds

const int deg_angle = 1.8; //1.8 degrees per step
const int step_mode = 1; //1: full step, 0.5: half step, 0.125: one-eight step
const int spr = 360/deg_angle/step_mode; //spr: steps/rotation
const int pos = 0; //default servo position

void Forward(double rotations); 
void Reverse(double rotations);
void Left_Forward(double rotations);
void Right_Forward(double rotations);
void Turn_360(double rotations);

void setup() 
{ 
  Left_Stepper.setSpeed(400); // 400 steps/second: 120 rpm
  Right_Stepper.setSpeed(400); // 400 steps/second: 120 rpm
  servo1.attach(6);  
}
void loop() 
{  
  curMillis = millis();  
  Forward(1); //forward 1 rotation: forward 200 steps
  Left_Forward(0.25); //left wheel forward a quarter turn: forward 50 steps
  Right_Forward(0.25); //right wheel forward a quarter turn: forward 50 steps
  Turn_360(1); //turn around 180 degrees CCW: 2 180's makes a 360 degree turn CCW
  Reverse(1); //reverse 1 rotation: reverse 200 steps
  
  if(millis()==1000)
  {
    servo1.write(pos); delay(500);
  }
  if(millis()==2000)
  {
    servo1.write(pos); delay(500);
  }
}
void Forward(double rotations) //rotations: number of rotations forward
{
  if (curMillis - prevStepMillis >= millisBetweenSteps) //check if its time to move
  {
//  prevStepMillis += millisBetweenSteps;
    Left_Stepper.move(rotations*spr); //here it is 200 steps
    Right_Stepper.move(rotations*spr); //here it is 200 steps
    
    //didnt have a separate if statement for both since they're going straight the same distance/speed/acceleration
    if(Left_Stepper.distanceToGo()!=0 && Right_Stepper.distanceToGo()!=0)
    { 
      Left_Stepper.run();
      Right_Stepper.run();
    }  
  }
}
void Reverse(double rotations) //rotations: number of rotations in reverse
{
  if (curMillis - prevStepMillis >= millisBetweenSteps) //check if its time to move
  {
//  prevStepMillis += millisBetweenSteps;
    Left_Stepper.move(-rotations*spr); //here it is -200 steps or 1 rotation backwards
    Right_Stepper.move(-rotations*spr); //here it is -200 steps or 1 rotation backwards

    //didnt have a separate if statement for both since they're going straight the same distance/speed/acceleration
    if(Left_Stepper.distanceToGo()!=0 && Right_Stepper.distanceToGo()!=0) //move until number of steps have been made
    { 
      Left_Stepper.run();
      Right_Stepper.run();
    }  
  }
}
void Left_Forward(double rotations)
{
  if (curMillis - prevStepMillis >= millisBetweenSteps) //check if its time to move
  {
//  prevStepMillis += millisBetweenSteps; 
    Left_Stepper.move(rotations); //left wheel forward 50 steps

    if(Left_Stepper.distanceToGo()!=0) //move until number of steps have been made
    {
      Left_Stepper.run();
    }
  }
}
void Right_Forward(double rotations)
{
  if (curMillis - prevStepMillis >= millisBetweenSteps) //check if its time to move
  {
//  prevStepMillis += millisBetweenSteps; 
    Right_Stepper.move(rotations); //left wheel forward 50 steps

    if(Right_Stepper.distanceToGo()!=0) //move until number of steps have been made
    {
      Left_Stepper.run();
    }
  }
}
void Turn_360(double rotations) // 1 rotation = 360 degree turn CCW
{
  if (curMillis - prevStepMillis >= millisBetweenSteps) //check if its time to move
  {
//  prevStepMillis += millisBetweenSteps; 
    Left_Stepper.move(-rotations*spr); //left wheel in reverse 200 steps
    Right_Stepper.move(rotations*spr); //right wheel forward 200 steps

    //didnt have a separate if statement for both since they're going the same distance and acceleration
    if(Left_Stepper.distanceToGo()!=0 && Right_Stepper.distanceToGo()!=0) //move until number of steps have been made
    {
      Left_Stepper.run();
      Right_Stepper.run();
    }
  }
}

Here I tried to implement the non-blocking accelstepper functions with your second example, and unfortunately I fried my Arduino so I can not tell if this works/makes sense until my new one comes. I have the self-defined functions run the “predetermined course”. I know the function runSpeedToPosition() blocks, but does my distanceToGo() do the same? Will my self-defined functions return only after my steppers have moved their required distances? I’m assuming my servo goes off of the millis() and not when the sketch passes through the servo code.

SkarnerFTW:
Here I tried to implement the non-blocking accelstepper functions with your second example,

You are mixing up two different concepts. My examples do not not use any library.

Study the AccelStepper examples.

When you are using the AccelStepper library the calls to .run() should be in loop() not in any function. The .run() function needs to be called all the time as often as possible - hundreds or thousands of times per second. For most of those calls nothing will happen but at the right moments the library will cause a step to happen.

...R

// MultiStepper.pde
// -*- mode: C++ -*-
// Use MultiStepper class to manage multiple steppers and make them all move to 
// the same position at the same time for linear 2d (or 3d) motion.
#include <AccelStepper.h>
#include <MultiStepper.h>
// EG X-Y position bed driven by 2 steppers
// Alas its not possible to build an array of these with different pins for each :-(
AccelStepper stepper1(AccelStepper::FULL4WIRE, 2, 3, 4, 5);
AccelStepper stepper2(AccelStepper::FULL4WIRE, 8, 9, 10, 11);
// Up to 10 steppers can be handled as a group by MultiStepper
MultiStepper steppers;
void setup() {
  Serial.begin(9600);
  // Configure each stepper
  stepper1.setMaxSpeed(100);
  stepper2.setMaxSpeed(100);
  // Then give them to MultiStepper to manage
  steppers.addStepper(stepper1);
  steppers.addStepper(stepper2);
}
void loop() {
  long positions[2]; // Array of desired stepper positions
  
  positions[0] = 1000;
  positions[1] = 50;
  steppers.moveTo(positions);
  steppers.runSpeedToPosition(); // Blocks until all are in position
  delay(1000);
  
  // Move to a different coordinate
  positions[0] = -100;
  positions[1] = 100;
  steppers.moveTo(positions);
  steppers.runSpeedToPosition(); // Blocks until all are in position
  delay(1000);
}

All the examples that involved 2 or more steppers either had blocking functions, or only moved to 1 position, then returned to its original position. This example from the AccelStepper named “MultiStepper” is the only thing close to my application, and I have a feeling that I have to calculate the time needed for my steppers to reach desired positions in order to move them to their next position without blocking, which would require the millis() part.

SkarnerFTW:
All the examples that involved 2 or more steppers either had blocking functions, or only moved to 1 position,

You are jumping all over the place and I can’t keep up with you.

If you are looking for an example that does exactly what you want you are obviously going to be disappointed.

And ALL of the things you have looked at - my examples, AccelStepper and MultiStepper can be used to do what you think they can’t do.

Let’s get back to basics. What are the steppers being used for?

…R

"Face palm". Just realized you asked what am I using them for, rather than what steppers are used for. Anyways, they are being used to drive 2 wheels with one caster wheel at the back. They must go through a path using dead reckoning, which will involve straight forward/reverse movements, some instances where only one wheel will move forward/reverse, and other instances where one goes forward and the other goes in reverse. This will be timed, so acceleration will be added later to get higher rpms.

For that application you probably need the two motors to be able to work in sync. In other words MotorL makes 100 steps in exactly the same time as MotorR makes 100 steps. The AccelStepper library cannot do that, but the MultiStepper variant can, HOWEVER, the MultiStepper library cannot do acceleration.

On the other hand acceleration is not that difficult to add and I posted a piece of code that shows a simple acceleration system. It is generally similar to my Simple Stepper Code

As I said earlier this code can be extended to work with several motors.

...R