stepper and servo simultaneous run and acceleration

Hello everyone! I am new to this community and the arduino platform. I am trying to run a stepper motor QSH4218-35-10-027 simultaneous with a servor motor Modelcraft MC-1811 on an Arduino UNO with an Adafruit Motor Shield v.2.3. (Motor/Stepper/Servo Shield (Version 2) | Adafruit Shield Compatibility Guide | Adafruit Learning System)

Stepper motor data: 200Steps, 1A phase current, rated voltage 5.3V, (http://www.trinamic.com/products/motors/motors-stepper/qmot-qsh4218)

A short overview about my project: I have a big spool with e.g. 500 meters of electric cable on it and an emtpy spool. My goal is it to uncoil a defined length of the big spool to the small spool. The stepper is connected to the small spool. The servo is placed between big and small spool and moves from 80° to 100° (start position 90°).

Process:

Step1: Stepper motor accelerate the small spool from 0 to 100.

Step2: After the small spool turned 1 rotation the servo starts. The servo starts at 90° and moves to 100° and than back to 80° (1° steps) and so on. I need this movement to avoid that the cable uncoils at the same place.

Step3: Shortly before the stepper reaches his target position it should deccelerate and stop at the target position.

Step4: Stepper and servo motor stop their movement.

Sketch:

#include <Wire.h>
#include <Adafruit_MotorShield.h>
#include "utility/Adafruit_PWMServoDriver.h"
#include <Servo.h>
#include <Stepper.h>
#include <AccelStepper.h>


float Circumference= 0.2827;  // in meters
float CableLength = 3;  // for example 3 meters
long NumberRotation;  


//Servo 
const int SERVO_PIN = 9;
int SRV_pos;
boolean SRV_moveForward;
long SRV_nextWakeUp;
  
Servo myservo;


Adafruit_MotorShield AFMS = Adafruit_MotorShield(); 
Adafruit_MotorShield AFMStop(0x60); // Rightmost jumper closed

Adafruit_StepperMotor *myStepper1 = AFMStop.getStepper(200, 1);


void forwardstep1() {  
 myStepper1->onestep(FORWARD, DOUBLE);
}

AccelStepper stepper1(forwardstep1, 0);

void setup() {

  Serial.begin(9600);   
  Serial.println("Stepper: Start");
  
  AFMStop.begin(); 

  NumberRotation= 200*(FiberLength/Circumference);   
  
  stepper1.setMaxSpeed(100.0);
  stepper1.setAcceleration(10.0);
  stepper1.moveTo(NumberRotation);
  Serial.println(NumberRotation/200);

   
  myservo.attach(SERVO_PIN); 
  SRV_pos = 90; 
  SRV_moveForward = true;
  SRV_nextWakeUp = millis();

}
void loop() 
 {
  if(stepper1.distanceToGo() == 0)
    stepper1.moveTo(stepper1.currentPosition());
 
    stepper1.run();
}

  long currentTime = millis();
  long nextWakeUpSRV = SRV_doStep(currentTime);

/***Servo***/
long SRV_doStep(long currentMillis) {  
  if(currentMillis>SRV_nextWakeUp) {
    if(SRV_moveForward) {     
     SRV_pos += 1;   
      if(SRV_pos>100){
        SRV_moveForward = false;
        SRV_pos = 100;
      }
    } else {
      SRV_pos -= 1;   
       if(SRV_pos<80) {
        SRV_moveForward = true;
        SRV_pos = 80;
      }
    }
    myservo.write(SRV_pos);
    SRV_nextWakeUp = currentMillis + 100;
    Serial.println(SRV_pos);  
  }
  return SRV_nextWakeUp;
}

Problem:
If I run this sketch without any acceleration and decceleration task both motors are moving simultaneous.

But if I try to run the sketch with the acceleration and decceleration task than the servo allways stays at his start position 90° and does not move. The stepper rotates without any problem.

Did I do a mistake in my sketch?

Is it possible to run this process with a stepper and a servo motor or is it better to use 2 stepper motors.

I tried to run 2 stepper motors at 5V (external power supply) last week. It worked but I had a strong heat development and a rough and harsh noice! It does not matter if I use 2 or 1 stepper.

If I run the sketch without the motor shield (stepper connected to Arduino Uno breadboard) than the stepper runs smooth and without a strong heat development.

Thanks a lot for your help.

  if(stepper1.distanceToGo() == 0)
    stepper1.moveTo(stepper1.currentPosition());

If we are where we need to be, move to where we are. Does that make ANY sense?

  long nextWakeUpSRV = SRV_doStep(currentTime);

This code will be called one time, before the servo has been attached. Why?

I am working the first time with an Arduino UNO and do not have much experience in programming.

if(stepper1.distanceToGo() == 0)
    stepper1.moveTo(stepper1.currentPosition());

I found this code in the Adafruit Motor Shield V2 Accel_MultiStepper.ino example. I understood it in this way: If distance is 0 then move to target position which would be 2122 Steps.

  long nextWakeUpSRV = SRV_doStep(currentTime);
This code will be called one time, before the servo has been attached. Why?

Have not I attached the servo in the void setup() ?

I understood it in this way: If distance is 0 then move to target position which would be 2122 Steps.

The first part is right. If the number of steps that still need to be taken is zero, do something.

The do something part is not. The code does say to move to another position. But, what position? Move to where we are now. Well, that certainly won't take long, or require a moving van and boatload of moving boxes.

Have not I attached the servo in the void setup() ?

You have. When is setup() called? When is that function call made?

The only code in loop() is

void loop()
 {
  if(stepper1.distanceToGo() == 0)
    stepper1.moveTo(stepper1.currentPosition());
 
    stepper1.run();
}

and there is nothing there to require the servo to move.

You have a servo function called SRV_doStep() but you never call it.

...R
Stepper Motor Basics

Hi PaulS and Robin2,

thanks for your help. I played with the sketch and I think that now I understand what you mean.
With:

stepper1.moveTo(NumberRotation);

I already told the stepper to which traget position it has to move. So I do not need this code. Could you explain me why this code is used in Accel_MultiStepper.ino (AccelStepper/MultiStepper.pde at master · adafruit/AccelStepper · GitHub)
and when this code could be useful.

I did some research and I have found this example: Overview | Multi-tasking the Arduino - Part 1 | Adafruit Learning System

Based on this sketch I have modified my sketch to:

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

  float Circumference= 0.2827;  
  float FiberLength= 2;  //in m
  float NumberRotation;  
  

class Sweeper
{
  //Class Member Varialbes
  //These are initialized at startup
  Servo servo;              // the servo
  int pos =90;              // current servo position
  
  // These maintain the current state 
  int increment;        // increment to move for each interval
  int  updateInterval;      // interval between updates
  unsigned long lastUpdate; // last update of position
  
  // Constructor - creates a Flasher 
  // and initializes the member variables and state 
public: 
  Sweeper(int interval)
  {
    updateInterval = interval;
    increment = 1;
  }
  
  void Attach(int pin)
  {
    servo.attach(pin);
  }
  
  void Detach()
  {
    servo.detach();
  }
  
  void Update()
  {
    if((millis() - lastUpdate) > updateInterval)
    {
      lastUpdate = millis();
      pos += increment;
      servo.write(pos);
      Serial.println(pos);
      if ((pos >= 110) || (pos <= 70)) // end of sweep     
      {
        // reverse direction
        increment = -increment;
      }
     }
   }
};

Sweeper sweeper1(50);

AccelStepper stepper2(4, 8, 9, 10, 11); 

void setup() 
{ 
  Serial.begin(9600);
  sweeper1.Attach(12);
  
  NumberRotation= 200*(FiberLength/Circumference);
  stepper2.setMaxSpeed(200.0);
  stepper2.setAcceleration(20.0);
  stepper2.move(NumberRotation);

  Serial.println(NumberRotation/200);
} 
 
 
void loop() 
{
  {
    stepper2.run();
  }

  if(digitalRead(2) == HIGH)
  {
     sweeper1.Update();
  } 
}

Now it is possible that both motors are running simultaneously.

My problem now is that I do not know how to connect both motors together for example: that the servo motor starts 4secs after the stepper did (or the servo starts after the stepper did 200 steps).

Is it possible to read the steps that the stepper is doing?

Is it possible to read the steps that the stepper is doing?

You can read the current position, using currentPosition(). The value will be relative to the last position you told it to go to.

My problem now is that I do not know how to connect both motors together for example: that the servo motor starts 4secs after the stepper did

You need to explain EXACTLY what you want to happen, without any handwaving.

Should the servo do something (what?) 4 seconds after the stepper was to to march? Or should it do something 4 seconds after the stepper gets to the commanded position?

I would like to the following process:

Project overview: I have a big spool1 with for example 500 meters of electric cable on it and an emtpy spool2. My goal is it to uncoil a defined length of the spool1 to the spool2. The stepper is connected to the spool2 (empty spool). The servo is placed between spool1 and spool2 and moves from 80° to 100° (start position 90°).

Process:

Step1: Stepper motor accelerates the small spool from and runs afterwards with constant speed.

Step2: After the stepper turned 1 rotation the servo should start to run. The servo starts at 90° and moves to 100° and than back to 80° (1° steps) and so on. I need this movement to avoid that the cable uncoils at the same place.

Step3: Shortly before the stepper reaches his target position it should deccelerate and stop at the target position.

Step4: At the point when the stepper reaches the target position both motors should stop.

So far the stepper accelerates, runs with const. speed, deccelerates and stops at the target position.

The stepper needs 200steps or 5sec to turn 1 hole ratition. I thought to use an if-function which is linked to the number of steps that the stepper already did to start the servo motor.
Something like:

if(StepperSteps>200)
{
sweeper1.Upadte();
}

How do I use read currentPosition()?

Step1: Stepper motor accelerates the small spool from and runs afterwards with constant speed.

That is NOT a step that you can implement. You need to define how many steps are required to accelerate to the desired speed, or measure how long it takes to accelerate to the desired speed, and then calculate how many steps were taken in that time (or ask the stepper).

Step2: After the stepper turned 1 rotation the servo should start to run. The servo starts at 90° and moves to 100° and than back to 80° (1° steps) and so on. I need this movement to avoid that the cable uncoils at the same place.

Servos are stupid. They will do only what you tell them to do. They do not, in particular, "start to run".

Notice, too, that this step seems to be completely independent of step 1. Step 1 says that the stepper needs to accelerate to a constant speed. Step 2 says that the stepper should step a number of steps needed to accomplish one complete revolution.

Step 1 doesn't really sound like something you need to be concerned about, except that it requires that you use a library that manages acceleration and constant speed for a device for which those are foreign concepts. A stepper steps. Nothing more. If the result of the stepping is to look like the stepper accelerated and then ran at constant speed, that is strictly a matter of timing of the steps.

So, you know (or need to get your calculator out and calculate) the number of steps needed to spin the windup drum the required amount to roll up x inches/feet/meters/angstroms/light years of cable. You tell the stepper to take that number of steps, with acceleration and maximum speed.

Periodically, you call currentPosition(), to see if the stepper has stepped enough to complete one revolution. If it has, you start making the servo move. How much it needs to move, in which direction, is a mystery that you need to solve.

Hi PaulS,

could you please send me an example code for the currentPosition() function so that I know how to use it.

Thanks

Jack29:
Hi PaulS,

could you please send me an example code for the currentPosition() function so that I know how to use it.

Thanks

   int pos = stepper1.currentPosition();

@Jack29, I don't intend in any way to be unkind - do you understand the code you posted in Reply #5 ?

If not don't waste your time with it.

As fas as I can see (and correct me if I am wrong) all you need is code that works like this

void loop() {
    myStepper.runToPosition(stepperPos); // this blocks until the movement is complete
    myServo.write(180);
    delay(200);
    myServo.write((0);
    delay(200);
    stepperPos = stepperPos + nnn; // add the amount required for the next move
}

I may have some of the fine details wrong - such as the servo angles and the servo positions.

...R

@Robin2, don´t worry you were not unkind. I would not say that I understand the code in Reply#5 to 100%.

source: A clean sweep | Multi-tasking the Arduino - Part 1 | Adafruit Learning System

class Sweeper
{
  //Class Member Varialbes
  //These are initialized at startup
  Servo servo;              // the servo
  int pos =90;              // current servo position
  
  // These maintain the current state 
  int increment;        // increment to move for each interval
  int  updateInterval;      // interval between updates
  unsigned long lastUpdate; // last update of position
  
public: 
  Sweeper(int interval)
  {
    updateInterval = interval;
    increment = 1;
  }
  
  void Attach(int pin)
  {
    servo.attach(pin);
  }
  
  void Detach()
  {
    servo.detach();
  }
  
  void Update()
  {
    if((millis() - lastUpdate) > updateInterval)
    {
      lastUpdate = millis();
      pos += increment;
      servo.write(pos);
      Serial.println(pos);
      if ((pos >= 110) || (pos <= 70)) // end of sweep     
      {
        // reverse direction
        increment = -increment;
      }
     }
   }
};

As far as I understand the code right than I have declared the class Sweeper. I could save Sweeper as Sweeper.h and use it like the Servo.h library. If I want to call a function of Sweeper.h in a new sketch than I can call it with sweeper.xxx();

With the currentPosition()-function it was possible to linke both motors together. Now the servo starts after the stepper did 200 steps. Afterthat both motors are running simultaneously. The servo stops at that point when the stepper has to run 10 more steps.

This is my new sketch:

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

  float Circumference= 0.2827;  
  float FiberLength= 2;  //in m
  float NumberRotation;  
  

class Sweeper
{
  //Class Member Varialbes
  //These are initialized at startup
  Servo servo;              // the servo
  int pos =90;              // current servo position
  
  // These maintain the current state 
  int increment;        // increment to move for each interval
  int  updateInterval;      // interval between updates
  unsigned long lastUpdate; // last update of position
  

public: 
  Sweeper(int interval)
  {
    updateInterval = interval;
    increment = 1;
  }
  
  void Attach(int pin)
  {
    servo.attach(pin);
  }
  
  void Detach()
  {
    servo.detach();
  }
  
  void Update()
  {
    if((millis() - lastUpdate) > updateInterval)
    {
      lastUpdate = millis();
      pos += increment;
      servo.write(pos);
      Serial.println(pos);
      if ((pos >= 100) || (pos <= 80)) // end of sweep     
      {
        // reverse direction
        increment = -increment;
      }
     }
   }
};

Sweeper sweeper1(100);

AccelStepper stepper2(4, 8, 9, 10, 11); 

void setup() 
{ 
  Serial.begin(9600);
  sweeper1.Attach(12);
  
  NumberRotation= 200*(FiberLength/Circumference);
  stepper2.setMaxSpeed(200.0);
  stepper2.setAcceleration(20.0);
  stepper2.move(NumberRotation);

  Serial.println(NumberRotation/200);
} 
  
void loop() 
{
   long pos3 = stepper.distanceToGo();
   Serial.pirntln(pos3);

   stepper2.run();

   long pos2 = stepper2.currentPosition();  

  if(pos2 >= 200)
  {
     sweeper1.Update();
  
     if(pos3 <=10)
     {
      sweeper1.Detach();
     } 
}

It is maybe not the easiest way but it does what I need. My next step will be to connect the stepper to a spool and check if the parameters like speed, acceleration and so on are perfect or not.

There is one point that I don´t understand. If I use this Code 1:

void Update()
  {
      pos += increment;
      servo.write(pos);
      //Serial.println(pos);
      if ((pos >= 100) || (pos <= 80)) // end of sweep     
      {
        // reverse direction
        increment = -increment;
      }
   }

Sweeper sweeper1(100);

instead of this Code 2:

void Update()
  {
    if((millis() - lastUpdate) > updateInterval)
    {
      lastUpdate = millis();
      pos += increment;
      servo.write(pos);
      //Serial.println(pos);
      if ((pos >= 100) || (pos <= 80)) // end of sweep     
      {
        // reverse direction
        increment = -increment;
      }
    }
  }

Sweeper sweeper1(100);

the servo is running much faster under Code1 than it does under Code2.

An other point is also that the number 100 in Sweeper sweeper1(100); has an influence on the speed of the servo motor.

Is this the time in milliseconds that the servo needs to move for 1 step?

Can somebody explain me the difference between Code1 and Code2?

Thanks
Jack

the servo is running much faster under Code1 than it does under Code2.

Of course it does. The Update() function in code 1 moves the servo every time it is called. The Update() function in code 2 moves the servo only if sufficient time has passed since the servo was last moved. Unless there is significant delay between calls to the two different Update() functions (longer than updateInterval), code 1 will always be faster. Code 2 can be as fast as code 1, but it can never be faster.

Jack29:
@Robin2, don´t worry you were not unkind. I would not say that I understand the code in Reply#5 to 100%.

You did not comment on whether the code I suggested in Reply #11 captures your requirement ?

...R

@PaulS, so in other words: the speed of the servo in Code1 depends on the speed of the data rate and in case of Code2 the servo moves after a sufficient time has passed, which would be 1 millisecond ?

Do you know what the number 100 in " Sweeper sweeper1(100) " stands for?

Is this the time in milliseconds that the servo needs to move for 1 step?

@Robin2, your code in reply#11 comes close to my requirement but the problem is the delay() function. As far as I understand it right the delay() function stops any other process besides the one that was running. So 2 processes at one time are not possible.

Correct me if I am wrong but in your code the stepper and servo motor would never run simultaneously, right? The program would run one function after an other and not at the same time.

Jack29:
@Robin2, your code in reply#11 comes close to my requirement but the problem is the delay() function. As far as I understand it right the delay() function stops any other process besides the one that was running. So 2 processes at one time are not possible.

I just put the delay() in because I was too lazy to write a longer example that uses millis() for timing. It does not invalidate the simple concept in my few lines of code.

What 2 processes do you want to run at the same time?

The impression I have is that you have two activities that need to run in sequence

  • Move, then stop.
  • Cut

...R

@Robin2, attached you will find a drawing of my process. It is like you said: move to target position, stop at target position, both motors stand still, cut cable from big spool.

The time which you see in the drwaing was measured with a stop watch. So it is not to 100% accurate. In my code I have linked the stepper and servo through the steps that the stepper has been run.

Do you know at which step type (single, double, microstep, interleave) the stepper is running if you do not use a Motor Shield?

Is it possible to change the step type?

Thanks Jack

Stepper_Servo Porcess.PNG

Thanks for the timing diagram that makes things clear. (But why not post it as text).

I had forgotten that the purpose of the servo was to guide the moving cable onto the drum. I was mixing your project up with another Thread.

If this was my project I would make my own simple acceleration function and when it got to the desired speed I would then start the servo moving.

Imagine having a function with code like the servo sweep example (but using millis() for timing) and then imagine having an IF clause at the start of the function - like

void (sweepServo() {
  if (coilAtSpeed == true) {
      // code to sweep servo
  }
}

Then think of starting the stepper with coilAtSpeed = false. Now increase the stepper speed and at some point you reach a speed at which it is appropriate to change coilAtSpeed to true.

Later, after N steps it will be time to set coilAtSpeed back to false and the servo will stop.

The code in loop() could be as simple as

void loop() {
   readButtons(); // to start or stop the process ?
   moveStepper();
   moveServo();
}

I think the code needed for the moveStepper() function will be more complex. When you get that figured out it should be easy to trigger the servo.

Do you know at which step type (single, double, microstep, interleave) the stepper is running if you do not use a Motor Shield?

You have to use some sort of driver between the Arduino and the stepper, and that will determine the default microstep situation. The Pololu A4988 defaults to full steps. The BigEasydriver defaults to microsteps. Both can easily be changed.

...R