Servos creep after switching control

Hi everyone,

I have an Arduino Uno R3 attached to an analog joystick with X,Y control and a button, which controls 3 parallax continuous rotation servos. I am trying to make a program where one servo can be controlled, then when I click the next is controlled instead, and so on. When I am controlling the first servo it works fine, but once I click the first servo starts rotating very slowly, but I can still control the second. Once I click again, the first two move very slowly, but I can control the third. If I click again, it goes back to the beginning where none of them are moving besides the first when I toggle the joystick. I can't wrap my head around why this is, anyone have any idea? Here is my code:

#include <Servo.h> 
 
int servoPin = 10;
int joyY;
int joyX;
int mjoyX;
int mjoyY;
int joyClick;
int cservo = 1;
int prec = 0;
 
Servo servo2; 
Servo servo1;
Servo servo3;
 
void setup() 
{ 
  servo1.attach(9);
  servo2.attach(10);
  servo3.attach(11); 
  Serial.begin(9600);
  servo1.writeMicroseconds(1500);
  servo2.writeMicroseconds(1500);
  servo3.writeMicroseconds(1500);
} 
 
 
void loop() 
{ 
    joyY = analogRead(2); 
    joyClick = analogRead(0);
     mjoyY = map(joyY,0,1023,-70,68); 
   joyX = analogRead(1);
     mjoyX = map(joyX,0,1023,-70,68);
     Serial.println(joyClick); 
     if (joyClick == 0) {
       prec = 1;
     } else {
       if (prec == 1) {
         prec=0;
         if(cservo < 3) cservo++;
         else cservo = 1;
       }
     }
    if((joyY > 200) || (joyY < 300) || (joyX > 600) || (joyX < 450)) {
      if (cservo == 1) servo1.writeMicroseconds(1500+mjoyY+mjoyX); 
      else if (cservo == 2) servo2.writeMicroseconds(1500+mjoyY+mjoyX); 
      else servo3.writeMicroseconds(1500+mjoyY+mjoyX); 
      
      
    } 
    if (cservo == 1) {
      servo3.writeMicroseconds(1500);
      servo2.writeMicroseconds(1500);
    } else if (cservo == 2) { 
      servo3.writeMicroseconds(1500);
      servo1.writeMicroseconds(1500);
    } else if (cservo == 3) {
      servo2.writeMicroseconds(1500);
      servo1.writeMicroseconds(1500);
    }
      delay(20);
    
}
  servo1.writeMicroseconds(1500);
  servo2.writeMicroseconds(1500);
  servo3.writeMicroseconds(1500);

Have you verified that 1500 is the stopping point for your servos? That may need to be increased or decreased slightly.

I would not think it necessary to write to every servo on every pass through loop. Only one servo needs new value on every pass.

Well I adjusted the screws to calibrate them. They use PWM and I setup by sending the 1500ms at the start, which doesn't cause any of them to move initially. I didn't realize I don't need to send the 1500 on each run, I thought with the PWM you have to send 1500 with a 20ms space then 1500 again to have no movement. Then slightly higher or lower to make it move (1700 or 1300 for example). What's strange is that when cservo=1 everything seems to work normally, its when cservo>1 that things go awry

Very interesting.

When you say after you click, the first servo moves slowly. Do you mean it moves according to joystick-positions, or is it just rotating at a certain speed?

And by the way, what is the point of this if-function?
if((joyY > 200) || (joyY < 300) || (joyX > 600) || (joyX < 450)) {
If JoyY is higher than 200 OR lower than 300, which means whatever JoyY is, this is true.
I am a rookie, so just curious. :slight_smile:
Is it supposed to be opposite tho? Lower than 200 or higher than 300?

Lower than 200 or higher than 300?

Looks to me like it was supposed to be greater than 200 and less than 300, not or.

I thought with the PWM you have to send 1500 with a 20ms space then 1500 again to have no movement.

The servo library manages that.

I can't see anything in the code which would cause the behaviour you describe, and I wonder whether it may be somehow related to the external circuit e.g. the load from some servos affecting the supply voltage to others and causing the 'zero' point to drift fractionally.

This part of the code looks a bit dodgy:

if((joyY > 200) || (joyY < 300) || (joyX > 600) || (joyX < 450))

It's not obvious what situation those four conditions represent or why the logic for the X and Y axis is different.
When this condition isn't met, should you be zeroing the current servo, or just leave it wherever it was last time?

The code to detect and handle button clicks looks as if it should work but it far more than it needs to be. See the StateChangeDetection example.

The code handling the three servos could be made a lot simpler by putting all three servos in an array and using cservo as an index into the array (noting that array index values start at zero not one). If you change the servo numbering to be one-based you can also clean up the incrementing logic using the modulo operator:

cservo = (cservo+1) % 3;

(Preferably using a constant servo count instead of the literal three.)

I would also park the 'previous' servo when the current servo changed and not re-write 1500 to the other two servos every time through loop. These changes would simplify and roughly halve the size of your sketch. But they probably wouldn't fix this problem, because I can't spot anything in the code which would cause it.

If you have a voltmeter it would be worth checking the servo supply voltages as you use the system to see whether the problem coincides with any of the voltages changing.

You might do some testing with the below test code which probably can be modified to use writeMicroseconds. I've noticed that joystick pots are not very precise and can have varying outputs for any particular position. The below code is setup for viewing the joystick/servo output using the serial monitor. You might use a dead band of something like 1495 to 1505 or greater to help keep the servos from creeping when the joystick is neutral. Using the servo attach/detach functions might also be a creep solution.

//zoomkat multi pot/servo test 3-23-13
//includes dead band for testing and limit servo hunting
//view output using the serial monitor

#include <Servo.h> 
Servo myservo1;  //declare servos
Servo myservo2;
Servo myservo3;
Servo myservo4;
Servo myservo5;

int potpin1 = 0;  //analog input pin A0
int potpin2 = 1;
int potpin3 = 2;
int potpin4 = 3;
int potpin5 = 4;

int newval1, oldval1;  //pot input values
int newval2, oldval2;
int newval3, oldval3;
int newval4, oldval4;
int newval5, oldval5;

void setup() 
{
  Serial.begin(9600);  
  myservo1.attach(2);  
  myservo2.attach(3);
  myservo3.attach(4);
  myservo4.attach(5);
  myservo5.attach(6);
  Serial.println("testing multi pot servo");  
}

void loop()
{ 
  newval1 = analogRead(potpin1);           
  newval1 = map(newval1, 0, 1023, 0, 179); 
  if (newval1 < (oldval1-2) || newval1 > (oldval1+2)){ //dead band 
    myservo1.write(newval1); //position the servo
    Serial.print("1- ");
    Serial.println(newval1); //print the new value for testing 
    oldval1=newval1; //set the current old value
  }

  newval2 = analogRead(potpin2);
  newval2 = map(newval2, 0, 1023, 0, 179);
  if (newval2 < (oldval2-2) || newval2 > (oldval2+2)){  
    myservo2.write(newval2);
    Serial.print("2- ");    
    Serial.println(newval2);
    oldval2=newval2;
  }

  newval3 = analogRead(potpin3);           
  newval3 = map(newval3, 0, 1023, 0, 179); 
  if (newval1 < (oldval1-2) || newval3 > (oldval3+2)){  
    myservo1.write(newval3);
    Serial.print("3- ");
    Serial.println(newval3);
    oldval3=newval3;
  }

  newval4 = analogRead(potpin4);           
  newval4 = map(newval4, 0, 1023, 0, 179); 
  if (newval1 < (oldval1-2) || newval4 > (oldval4+2)){  
    myservo1.write(newval4);
    Serial.print("4- ");
    Serial.println(newval4);
    oldval4=newval4;
  }

  newval5 = analogRead(potpin5);           
  newval5 = map(newval5, 0, 1023, 0, 179); 
  if (newval1 < (oldval1-2) || newval5 > (oldval5+2)){  
    myservo1.write(newval5);
    Serial.print("5- ");
    Serial.println(newval5);
    oldval5=newval5;
  } 
  delay(50);  //to slow loop for testing
}