Delay in void function breaks servo

Hello, I'm making a project where I'll be using a couple servos so I decided to make a void function to make the code easier to read cause each moving servo will need their own "for loop" to move if I want to change the speeds.

I'm using servotimer2 cause in the main code I'll be using the timer one library.

#include <ServoTimer2.h>

#define S1Pin 12

ServoTimer2 S1;

int inpos1;
void move(ServoTimer2 obj, int inpos, int pos);

void move(ServoTimer2 obj, int inpos, int pos) {
  int i = inpos;
  if (pos >= inpos) {
    for (i = inpos; i <= pos; i += 1) {
      obj.write(i);
    }
  } else { //so it could move back when needed
    for (i; i >= pos; i -= 30) {
      obj.write(i);
    }
  }
  inpos = pos;
}

void setup() {
  S1.attach(S1Pin);
  S1.write(750);  //750 is 0 in servotimer2 library
  inpos1 = 750;
  delay(2000);
}

void loop() {
  move(S1, inpos1, 1500);
  delay(1000);
  move(S1, inpos1, 750);
  delay(1000);
}

This code works but when I add a delay in the for loop to adjust the servo speed, the servo gets all jittery and messed up.

void move(ServoTimer2 obj, int inpos, int pos) {
  int i = inpos;
  if (pos >= inpos) {
    for (i = inpos; i <= pos; i += 1) {
      obj.write(i);
      delay(20);
    }
  } else {
    for (i; i >= pos; i -= 30) {
      obj.write(i);
      delay(20);
    }
  }
  inpos = pos;
}

Anyone have any idea how I could fix this?

I don't know for sure, but try replacing delay with an equivalent millis() loop/while.

instead of

try with

void move(ServoTimer2 & obj, int inpos, int pos) {
//                    ^__ note the & here 

This means you pas the ServoTimer2 parameter by reference and not by copy.

Using a reference(& ) in function parameters avoids copying the object. The function calls are routed directly onto the original object and not to the copy (and copying objects relying on underlying hardware features like timers is risky business)

I built your project in the simulator where it functions perfectly.

I did not alter the code in #1.

Therefore I must ask how you have your servo wired and powered. At this point I do not think it is a software issue. The code is vanilla hand-made delay()-based sweeping, there is no obvious reason why it should not work IRL.

I not that the delay exactly matches the update frequency attainable by any servo control software, 50 Hz.

Try a longer delay, maybe 100 milliseconds. Yes, your servo will crawl. I just am not in the lab and your observation would be informative.

Never mind, I had to actually add the dealy that already makes the servo crawl, and it does indeed make it crawl. Perfectly.

So wiring and power?

a7

passing by copy could be an issue.

I have not looked at the details of the library, so may be it does not matter in this case (as your test seems to indicate).

For complex types (esp. objects depending on timers, ISR, ...) it's better to pass your instance always - not a copy.

@pendekarw - what do you get if you run the servo sweep code example on offer in the IDE?


It always struck me as an odd language design choice, to pass structures by value.

And the trick when you really do want or need to pass an array by value, which otherwise it wouldn't be, to wrap it in a struct. I think I may have done that never.

It's easy enough to avoid the copying, but it isn't immediately obvious that you need to do.

I think here it would show up as a problem in the simulation; changing it as @J-M-L certainly can't hurt.

I didn't try it after not having experienced any improper functioning.

If only the idea and not the manner in which that was shown was presented, I would have passed the address in the 20th century manner, viz:

void move(ServoTimer2 *obj, int inpos, int pos) {

which would have meant adjust the code in the called function as well as in the function calls.

a7

You want to control the speed of servos on the fly? Here you go.

-jim lee

Sorry for the late reply as @J-M-L said I tried adding the & symbol but it still didn't work. I'm not sure how @alto777 managed to make it work but I took a video to help clarify my problem.
How it's supposed to run
using the same code but added a couple angles to show the problem better.

#include <ServoTimer2.h>

#define S1Pin 8

ServoTimer2 S1;

int inpos1;
void move(ServoTimer2 &obj, int inpos, int pos);

void move(ServoTimer2 &obj, int inpos, int pos) {
  int i = inpos;
  if (pos >= inpos) {
    for (i; i <= pos; i += 5) {
      obj.write(i);
    }
  } else {
    for (i; i >= pos; i -= 5) {
      obj.write(i);
    }
  }
  inpos = pos;
}

void setup() {
  S1.attach(S1Pin);
  S1.write(750);  //750 is 0 in servotimer2 library
  inpos1 = 750;
  delay(2000);
}

void loop() {
  move(S1, inpos1, 1125); // 45
  delay(1000);
  move(S1, inpos1, 1500); // 90
  delay(1000);
  move(S1, inpos1, 1125); // 45
  delay(1000);
  move(S1, inpos1, 750); // 0
  delay(1000);
}

And how it ran with the added delay. I only changed this part

void move(ServoTimer2 &obj, int inpos, int pos) {
  int i = inpos;
  if (pos >= inpos) {
    for (i; i <= pos; i += 5) {
      obj.write(i);
      delay(15);
    }
  } else {
    for (i; i >= pos; i -= 5) {
      obj.write(i);
      delay(15);
    }
  }
  inpos = pos;
}

I've been trying to figure this out for a while. Worst case scenario I could just use multiple for loops for each movement since it works if I didn't put it in it's own void function. But I just don't understand why this fails.

without any delays isn't the above code the equivalent of just moving to pos -- obj.write (pos);

I'll check but I did not alter your code.

The simulator is a cheap SG90 servo. Do you have one? It may function differently to what appears to be a spendy and beefy unit.

You do not appear to have a power issue. And your original description was misleading! That's not a servo going crazy. trust me.

I'll look closer later - the videos are very helpful.

a7

Yes, it also updates the global. edited! It does not... THX @david_2018

The loop operates so ast a regular servo is traveling toward a fast moving target that reaches its goal pos long before the servo has moved a fraction of the way.

There is a mystery in the actual servo.

@pendekarw please if you have not state the make and model of you servo, and to be thorough say or remind us of how you are powering it, and tell us the Arduino board type you are unsing.

L8R

a7

You need to pass inpos to the function by reference. As it is, you are not storing the new position in inpos at the end of the function, you are storing pos in a local copy of inpos.

1 Like

This is the exact Servo I'm using. It's powered by a 5v 2~3A powersupply. I'm not sure the problem is with the servo as I tried this exact setup on Wokwi and it had the same jerking motion as real life.

But the code works when there's no delay on the loop the only difference is the 15ms delay after each "for loop"

And I've also tried using 2 arduino boards with the same result.
An elegoo uno R3
And Arduino UNO rev3

do you see the servo moving in 1 deg steps or simply from inpos to pos when move() is called?

Post a link to that please.

I'll look when I can, but I think Injsed your code unmodified, so.

a7

Without the delay it's too fast to actually see so I'm not sure but if I didn't put the for loop in it's own function the delay does have an effect on the servo speed

I don't have a Wokwi account so i need to make a new one, I'll post it here when I can

I can no longer save my wokwi version, but here's code I have been working with.

There are two new variables. One that lets you say whether or not to delay between steps, the other whether or not to fix the code per @david_2018 in #12.

/* shows error of shadowing the global
ervo move (from 750) to 1500
servo move (from 750) to 750
servo move (from 750) to 1500


no shadow. inpos1 properly updated, inpos matter fixed in one way it could be:

servo move (from 750) to 1500
servo move (from 1500) to 750
servo move (from 750) to 1500

In addition, I finally saw that the second for loop takes giant steps. This may be your intent;

  } else { //so it could move back when needed
    for (i; i >= pos; i -= 30) {

The sketch. I'll try to see why I can't save it, but the diagram is just a servo on the servo pin:

# include "ServoTimer2.h"

# define S1Pin 12

const bool DELAY = true;  // do or do not
const bool SHADOW = false; // fix or do not @david_2018 #12

ServoTimer2 S1;

int inpos1;
void move(ServoTimer2 obj, int inpos, int pos);

void move(ServoTimer2 obj, int inpos, int pos) {
    
  Serial.print("servo move (from ");
  Serial.print(inpos);
  Serial.print(") to ");  
  Serial.println(pos);

  int i = inpos;
  if (pos >= inpos) {
    for (i = inpos; i <= pos; i += 1) {
      if (DELAY) delay(20);
      obj.write(i);
    }
  } else { //so it could move back when needed
    for (i; i >= pos; i -= 30) {
      if (DELAY) delay(20);
      obj.write(i);
    }
  }

// fix @david_2018
  if (SHADOW)
    inpos = pos;
  else
    inpos1 = pos;
}


void setup() {
  Serial.begin(115200);
  Serial.println("Jello Whirled!\n");

  S1.attach(S1Pin);
  S1.write(750);  //750 is 0 in servotimer2 library
  inpos1 = 750;
  delay(2000);
}

void loop() {
  move(S1, inpos1, 1500);
  delay(1000);
  move(S1, inpos1, 750);
  delay(1000);
}

I am surprised @gcjr has resisted posting his servo mover function what keeps its own track of where the servo be. Stripped out of his usually complicated exmaple sketch it could be useful, although at this point perhaps more of a distraction. But it really is the way to do the hole "I'm here and need to go there" thing.

a7