2x SG90 servo, with millis(), speed issue

Purpose is to drive 2 servo's in sweep modus but at differing speeds, same min and max angle settings.
No matter what value is given to the intervals (speed settings) for the two servo's, they keep sweeping synchronous. But the one set at the slower speed does not make the full left and right angles, so although its speed is lower, they both go back and forth at the same time.

What could be wrong?

Edit: I think solved: I used the same incr variable for both servo's. The bool incr must be split up in a bool incr1 and bool incr2.

Edit 2: no not solved yet.

/* Sweep

  Sweep without delay and with assymetric back/forth speed

  http://www.arduino.cc/en/Tutorial/Sweep
*/

#include <Servo.h>

Servo servo1;  // create servo object to control a servo
Servo servo2;
unsigned long previousMillis1a = 0;
unsigned long previousMillis1b = 0;
unsigned long previousMillis2a = 0;
unsigned long previousMillis2b = 0;
unsigned long currentMillis;
int interVal1a = 20;
int interVal1b = 20;
int interVal2a = 20;
int interVal2b = 20;
int minPos = 20;
int maxPos = 160;
int pos;    // variable to store the servo position
bool incr1 = true; //  increasing angle or decreasing angle servo 1
bool incr2 = true; //  increasing angle or decreasing angle servo 2


void setup() {
  pos = minPos;       // reset servo to minimal angle
  servo1.attach(9);  // attaches the servo on pin 9 to the servo object
  servo1.write(pos);              // tell servo to go to position in variable 'pos'
  servo2.attach(10);  // attaches the servo on pin 10 to the servo object
  servo2.write(pos);
}

void loop() {
  currentMillis = millis();
  servo_1();
  servo_2();
  servo_3();  // not yet used
}

void servo_1() {
  if (incr1 == true && currentMillis - previousMillis1a >= interVal1a) {
    pos = pos + 1;
    servo1.write(pos);              // tell servo to go to position in variable 'pos'
    previousMillis1a = currentMillis;
    if (pos == maxPos) {
      incr1 = false;
    };
  }
  if (incr1 == false && currentMillis - previousMillis1b >= interVal1b) {
    pos = pos - 1;
    servo1.write(pos);              // tell servo to go to position in variable 'pos'
    previousMillis1b = currentMillis;
    if (pos == minPos) {
      incr1 = true;
    };
  }
}

void servo_2() {
  if (incr2 == true && currentMillis - previousMillis2a >= interVal2a) {
    pos = pos + 1;
    servo2.write(pos);              // tell servo to go to position in variable 'pos'
    previousMillis2a = currentMillis;
    if (pos == maxPos) {
      incr2 = false;
    };
  }
  if (incr2 == false && currentMillis - previousMillis2b >= interVal2b) {
    pos = pos - 1;
    servo2.write(pos);              // tell servo to go to position in variable 'pos'
    previousMillis2b = currentMillis;
    if (pos == minPos) {
      incr2 = true;
    };
  }
}
void servo_3() {
  // not yet used
}

Doesn't each servo need its own position variable and why are all the intervals the same ?

Sorry, my mistake when posting the code: indeed the intervals must be different, but even with interVal1a and interVal1b both set at 100 the issue remains.

What do you mean by "its own position variabele"... oh I see: pos must become pos1 and pos2...

Thank you UKHeliBob, solved!

/* Sweep

  Sweep without delay and with assymetric back/forth speed

  http://www.arduino.cc/en/Tutorial/Sweep
*/

#include <Servo.h>

Servo servo1;  // create servo object to control a servo
Servo servo2;
unsigned long previousMillis1a = 0;
unsigned long previousMillis1b = 0;
unsigned long previousMillis2a = 0;
unsigned long previousMillis2b = 0;
unsigned long currentMillis;
int interVal1a = 100;
int interVal1b = 100;
int interVal2a = 20;
int interVal2b = 20;
int minPos = 20;
int maxPos = 160;
int pos1;    // variable to store the servo position
int pos2;
bool incr1 = true; //  increasing angle or decreasing angle servo 1
bool incr2 = true; //  increasing angle or decreasing angle servo 2


void setup() {
  pos1 = minPos;       // reset servo to minimal angle
  pos2 = minPos;       // reset servo to minimal angle
  servo1.attach(9);  // attaches the servo on pin 9 to the servo object
  servo1.write(pos1);              // tell servo to go to position in variable 'pos'
  servo2.attach(10);  // attaches the servo on pin 10 to the servo object
  servo2.write(pos2);
}

void loop() {
  currentMillis = millis();
  servo_1();
  servo_2();
  servo_3();  // not yet used
}

void servo_1() {
  if (incr1 == true && currentMillis - previousMillis1a >= interVal1a) {
    pos1 = pos1 + 1;
    servo1.write(pos1);              // tell servo to go to position in variable 'pos'
    previousMillis1a = currentMillis;
    if (pos1 == maxPos) {
      incr1 = false;
    };
  }
  if (incr1 == false && currentMillis - previousMillis1b >= interVal1b) {
    pos1 = pos1 - 1;
    servo1.write(pos1);              // tell servo to go to position in variable 'pos'
    previousMillis1b = currentMillis;
    if (pos1 == minPos) {
      incr1 = true;
    };
  }
}

void servo_2() {
  if (incr2 == true && currentMillis - previousMillis2a >= interVal2a) {
    pos2 = pos2 + 1;
    servo2.write(pos2);              // tell servo to go to position in variable 'pos'
    previousMillis2a = currentMillis;
    if (pos2 == maxPos) {
      incr2 = false;
    };
  }
  if (incr2 == false && currentMillis - previousMillis2b >= interVal2b) {
    pos2 = pos2 - 1;
    servo2.write(pos2);              // tell servo to go to position in variable 'pos'
    previousMillis2b = currentMillis;
    if (pos2 == minPos) {
      incr2 = true;
    };
  }
}
void servo_3() {
  // not yet used
}

There is the VarSpeedServo library that may be of interest.

Instead of writing a function and adding several new global variables for each new servo, encapsulating the behavior and the variables into an object class allows more servos to be added almost trivially:

/*
    Sweeper: Sweep a servo without delay and with assymetric back/forth speed
*/

#include <Servo.h>

class Sweeper : public Servo
{
  public:
    Sweeper(unsigned _incrStepMillis, unsigned _decrStepMillis, int _initialPos,
            int _minPos = 0, int _maxPos = 180, boolean _incr = true)
    {
      minPos = _minPos;
      maxPos = _maxPos;
      currentPos = _initialPos;
      incr = _incr;
      incrStepMillis = _incrStepMillis;
      decrStepMillis = _decrStepMillis;
    };

    void update()
    {
      if (incr)
      {
        if (millis() - previousMillis >= incrStepMillis)
        {
          previousMillis += incrStepMillis;
          currentPos++;
          write(currentPos);
          if (currentPos >= maxPos)
          {
            incr = false;
            currentPos = maxPos;
          }
        }
      }
      else // incr == false
      {
        if (millis() - previousMillis >= decrStepMillis)
        {
          previousMillis += decrStepMillis;
          currentPos--;
          write(currentPos);
          if (currentPos <= minPos)
          {
            incr = true;
            currentPos = minPos;
          }
        }
      }
    }

  private:
    int minPos;
    int maxPos;
    int currentPos;
    boolean incr;
    unsigned incrStepMillis;
    unsigned decrStepMillis;
    unsigned long previousMillis = 0;
};

// Arguments and defaults:
// unsigned incrStepMillis;  // Milliseconds between increment steps
// unsigned decrStepMillis;  // Milliseconds between decrement steps
// int initialPos;           // Initial position (should be between minPos and maxPos)
// int minPos = 0;           // Default sweep range is 0 to 180
// int maxPos = 180
// boolean incr = true;      // Default start direction is 'increment'

Sweeper sweeper1(100, 100, 20, 20, 160);  // create sweeper object to control a servo
Sweeper sweeper2(20, 20, 20, 20, 160);

void setup()
{
  sweeper1.attach(9);  // attaches the servo on pin 9 to the sweeper object
  sweeper2.attach(10);  // attaches the servo on pin 10 to the sweeper object
}

void loop()
{
  sweeper1.update();
  sweeper2.update();
}

@johnwasser
Hi John,

I assume your code is a good example on how to program an object-class.

I have some questions about the details:
class Sweeper : public Servo

"class" reserved word for defining a class

"Sweeper" name of the object

"public" ???

Servo "use (=inherit) the servo-object given through #include <servo.h> ?

Sweeper(unsigned _incrStepMillis, unsigned _decrStepMillis, int _initialPos,
            int _minPos = 0, int _maxPos = 180, boolean _incr = true)

define the Ssweeper-objects constructor-parameters

int _minPos = 0, int _maxPos = 180, boolean _incr = true

if parameters are missing like in an instantion like this
Sweeper mySweeper1(100, 100, 20) //leaving out these parameters: , 20, 160,true);

set default values?

Assign constructor-values to variables that are local to the object

      minPos = _minPos;
      maxPos = _maxPos;
      currentPos = _initialPos;
      incr = _incr;
      incrStepMillis = _incrStepMillis;
      decrStepMillis = _decrStepMillis;
    void update()
    { ...

code for function "update" callable with objectname.functionname

As the Sweeper-object inherits everything from object Servo.h
all functions of object Servo.h can be used

void setup()
{
  sweeper1.attach(9);  // attaches the servo on pin 9 to the sweeper object
  sweeper2.attach(10);  // attaches the servo on pin 10 to the sweeper object

inside the update-function

        if (millis() - previousMillis >= incrStepMillis)
        {
          previousMillis += incrStepMillis;
          currentPos++;
          write(currentPos);

is it enough to use the function's name "write(currentPos);
without mentioning the objects-name
servo.write() or Sweeper.write() ??

best regards Stefan

The "class Sweeper : public Servo" means that Sweeper is a kind of Servo. We also say "Sweeper is a sub-class derived from the base class Servo". The 'public' means that you can treat a Sweeper object as you would a Servo object. Without it, setup() would get an error trying to call sweeper1.attach().

Yes, the "= value" parts set defaults if the parameter is not passed. This is just normal function declaration stuff. You can only leave out trailing values that have a default.

This sets the two speeds and the initial position, leaving the min and max positions to default to 0 and 180 and the initial direction default to 'increment'. You have to supply at least three arguments (they have no defaults) and can optionally supply the other three in order.

You can't mention the servo object's name because it has no name. The Sweeper class doesn't have a its own 'write()' function so calls to write() go to the Servo::write() inherited from Servo.

Hi John,

thank you very much for explaining it.

best regards Stefan