Simultaneous Stepper Motor and Servos

Hi,

I am using an Arduino Uno to control one stepper motor and multiple servos. The instructions are received in a serial message, which contains a device and an instruction (for example “servo2/180”).

I have then used the idea behind the “blink without delay” / “several things at the same time” example code to make a ServoController and a StepperController class which makes it seems like a motor and a servo are moving at the same time.

#include <Servo.h>
#include <Stepper.h>
#define STEPS_PER_MOTOR_REVOLUTION 32   
#define STEPS_PER_OUTPUT_REVOLUTION 32 * 64  //2048 

// The pin connections need to be 4 pins connected
// to Motor Driver In1, In2, In3, In4  and then the pins entered
// here in the sequence 1-3-2-4 for proper sequencing


Stepper small_stepper(STEPS_PER_MOTOR_REVOLUTION, 9, 11, 10, 12);

class ServoController
{
  Servo servo;              // a servo
  int pos;                  // current servo position
  int updateTarget;         // target servo position 
  int increment;            // increment to move for each interval
  int updateInterval;       // time interval between updates
  unsigned long lastUpdate; // last update of position
  
  public: 
  ServoController(int interval)
  {
    updateInterval = interval;
    increment = 10;
  }
  
  void Attach(int pin)
  {
    servo.attach(pin);
  }
  
  void Detach()
  {
    servo.detach();
  }

  void Update(int target)
  {
    updateTarget = target;
  }
  
  
  void Move()
  {
    if(pos != updateTarget)  // need to update
    {
      if((millis() - lastUpdate) > updateInterval)  // time to update
      {
        lastUpdate = millis();
        if (pos < updateTarget)
          pos += increment;
        else
          pos -= increment;
          
        servo.write(pos);
        Serial.println(pos);
      }
    }
    else
    {
      servo.detach();
    }
  }
};

class StepperController
{
  int pos;                  // current servo position
  int updateTarget;         // target servo position 
  int increment;            // increment to move for each interval
  int updateInterval;       // time interval between updates
  unsigned long lastUpdate; // last update of position
  
  public: 
  StepperController(int interval)
  {
    updateInterval = interval;
    increment = 1;
  }

  void Update(int target)
  {
    updateTarget += target;
  }
  
  void Move()
  {
    if(pos != updateTarget)  // need to update
    {
      if((millis() - lastUpdate) > updateInterval)  // time to update
      {
        lastUpdate = millis();
        if (pos < updateTarget)
          pos += increment;
        else
          pos -= increment;
          
        small_stepper.step(pos);
        Serial.println(pos);
      }
    }
    else
    {
      pos = 0;
      updateTarget = 0;
    }
  }
};

StepperController stepper(20);
ServoController servo2(20);
ServoController servo3(20);
 

void setup() {
   Serial.begin(9600);     // opens serial port, sets data rate to 9600 bps
   small_stepper.setSpeed(800);
}

void loop() {

    if (Serial.available() > 0) {
        char message[255] = {0};
        String device;
        int instruction = '0';
        
        Serial.readBytesUntil(10, message, 255);

        device = strtok(message, "/");
        
        instruction = atoi((strtok(NULL, "/")));

        if (device == "servo2")
        {
            servo2.Attach(2);
            servo2.Update(instruction);
        }
        if (device == "servo3")
        {
            servo3.Attach(3);
            servo3.Update(instruction);
        }
         
        if (device == "stepper")
            stepper.Update(instruction);
    }

    servo2.Move();
    servo3.Move();
    stepper.Move();
}

However, I have a problem which the Stepper Motor timing which messes everything up.

I am sending a serial message (inside the “Move()” functions in the classes) for debugging. Watching these messages roll in (which should be one every step, every 20ms), I noticed that the longer the stepper motor was turning for, the longer it took for each message to come through.

For example - if I send a message to move the Stepper 100 steps, the first debug messages come quickly - roughly every 20ms as you would expect. However, by the time it gets to the 100th debug message, it is taking around 500ms per step.

So the motor carries on turning longer than expected, and it slows down the servos which means they judder.

Any help would be much appreciated.

Thanks,
Joel

readBytesUntil() is a blocking function and may be part or all of the problem.

Have a look at the examples in Serial Input Basics - they are non-blocking.

If that was my project I would send 3 numbers each time - for example <123, 87, 1133> - being the positions for each servo and the stepper. If the data was the same as the previous time nothing would happen. But there would be no need to interpret whether an item was for a servo or a stepper.

…R

Hi Robin,

It turns out the problem with my stepper steps getting slower was a small coding problem on this line:

small_stepper.step(pos);

It should have been

small_stepper.step(interval);

which works fine.

I did then come up against the problem with the readBytesUntil() function that you mentioned. The link you sent helped greatly, so I now have it working.

Thanks for your help