Help with a 2 Servo, 4 button Project. What am I missing?!?

Im making a turret using 2 MG996R servos and testing it in TinkerCAD. When I give inputs to the servos using the buttons, the servos jump (surge?). This especially happens when i havent given an input after a minute or more. Code listed below and screenshot of my schematics. Ive done searches and tried a few things with adding components, but no luck.

Also, after I cycle both servos a few times, the right servo doesnt work... making me think its a power issue? When i disconnect the left servo, the right seems to work no problem

My power supply for tinkercad is listed at 6V @10A. IRL but project is 6V @ 2000mAh

Cap is 1000 UF
Resistor is 1000ohms

#include <Servo.h>

Servo myservoH;
Servo myservoV;

int angle = 90;  // initial angle  for servo
int angleStep = 2;

#define LEFT A1
#define RIGHT A0
#define UP A2
#define DOWN A3
void setup() {

  Serial.begin(9600);  //  setup serial
  myservoH.attach(10);
  myservoV.attach(5);  // attaches the servo on pin 9 to the servo object
  pinMode(LEFT, INPUT_PULLUP);
  pinMode(RIGHT, INPUT_PULLUP);
  pinMode(UP, INPUT_PULLUP);
  pinMode(DOWN, INPUT_PULLUP);
  myservoH.write(angle);  // send servo to the middle at 90 degrees
  myservoV.write(angle);  // send servo to the middle at 90 degrees
}

void loop() {

  while (digitalRead(RIGHT) == LOW) {

    if (angle > 0 && angle <= 180) {
      angle = angle - angleStep;
      if (angle < 0) {
        angle = 0;
      } else {
        myservoH.write(angle);  // move the servo to desired angle
        Serial.print("Moved H to: ");
        Serial.print(angle);  // print the angle
        Serial.println(" degree");
      }
    }

    delay(40);  // waits for the servo to get there
  }             // while
                // Servo button demo by Robojax.com

  while (digitalRead(LEFT) == LOW) {

    // Servo button demo by Robojax.com
    if (angle >= 0 && angle <= 180) {
      angle = angle + angleStep;
      if (angle > 180) {
        angle = 180;
      } else {
        myservoH.write(angle);  // move the servo to desired angle
        Serial.print("Moved H to: ");
        Serial.print(angle);  // print the angle
        Serial.println(" degree");
      }
    }

    delay(40);  // waits for the servo to get there
  }             //


  while (digitalRead(DOWN) == LOW) {

    if (angle > 20 && angle <= 130) {
      angle = angle - angleStep;
      if (angle < 20) {
        angle = 20;
      } else {
        myservoV.write(angle);  // move the servo to desired angle
        Serial.print("Moved V to: ");
        Serial.print(angle);  // print the angle
        Serial.println(" degree");
      }
    }

    delay(40);  // waits for the servo to get there
  }             // while
                // Servo button demo by Robojax.com

  while (digitalRead(UP) == LOW) {

    // Servo button demo by Robojax.com
    if (angle >= 20 && angle <= 130) {
      angle = angle + angleStep;
      if (angle > 130) {
        angle = 130;
      } else {
        myservoV.write(angle);  // move the servo to desired angle
        Serial.print("Moved V to: ");
        Serial.print(angle);  // print the angle
        Serial.println(" degree");
      }
    }

    delay(40);  // waits for the servo to get there
  }             //
}

Debounce your buttons.

You can also add logic that checks when a button is pressed then released, and only when it’s released to then trigger your action.

If you run into noise issues look at using one of these boards:
Overview | Adafruit 16-Channel PWM/Servo HAT & Bonnet for Raspberry Pi | Adafruit Learning System

I used it on my initial predator build. I will have to dig for the final code base piece.

/*
 * Example 6
 * Nunchuck control for four servos and two button inputs
 * Honus 2007
 * This allows the use of a Wii nunchuck as an input device and is modified/extended from the original code
 * by Tod E. Kurt and Windmeadow Labs
 *2007 Tod E. Kurt, http://todbot.com/blog/
 *The Wii Nunchuck reading code is taken from Windmeadow Labs, http://www.windmeadow.com/node/42
 * ArduinoNunchuk.cpp - Improved Wii Nunchuk library for Arduino
 *
 * Copyright 2011-2013 Gabriel Bianconi, http://www.gabrielbianconi.com/
 *
 * Project URL: http://www.gabrielbianconi.com/projects/arduinonunchuk/
 *
 * Based on the following resources:
 *   http://www.windmeadow.com/node/42
 *   http://todbot.com/blog/2008/02/18/wiichuck-wii-nunchuck-adapter-available/
 *   http://wiibrew.org/wiki/Wiimote/Extension_Controllers
 *   http://www.gammon.com.au/blink
 *
 */
// www.facebook.com/ArduinoCenter
// https://blog.underc0de.org/arduino-wii-nunchuck-servo-motores/
// Original Code base credited to Undercode
// Code adapted from Sean Maio Crybabyfx setup
//https://github.com/outcry27/crybabyFX
// Updated by knoxvilles_joker 2017
// http://facebook.com/knoxvillesjoker
// more instructions documented at
// http://alienslegacy.com
 

#include <Wire.h>
#include "ArduinoNunchuk.h"
#include <Servo.h>

//Creates the objects to control the servos
ArduinoNunchuk nunchuk = ArduinoNunchuk();

const int ledPin1 = 13;       // Control pin for LED 1
const byte ledPin2 = 12;       // Control pin for LED 2
const unsigned long ledPin2interval = 500;
unsigned long ledPin2timer;
const int servoPin1 = 9;      // Control pin for servo motor
const int servoPin2 = 11;      // Control pin for servo motor
const int servoPin3 = 10;      // Control pin for servo motor
const int servoPin4 = 6;      // Control pin for servo motor
// sound pins for firing sounds
const int firePin1 = 4;
const int firePin2 = 5;
const int firePin3 = 16;
const int startSoundPin = 17;
 int pulseWidth1 = 0;    // Amount to pulse the servo 1
 int pulseWidth2 = 0;    // Amount to pulse the servo 2
 int pulseWidth3 = 0;    // Amount to pulse the servo 3
 int pulseWidth4 = 0;    // Amount to pulse the servo 4
 int refreshTime = 20;  // the time in millisecs needed in between pulses
//Initializes the variables
int xjoystick;
int yjoystick;
int xtilt;
int ytilt;
long lastPulse1;
long lastPulse2;
long lastPulse3;
long lastPulse4;
int minPulse = 700;   // minimum pulse width
int loop_cnt=0;

void setup() {
  // put your setup code here, to run once:
// sound pins are setup before initialization of serial interfaces.
  // initialize the audio pins
  pinMode(startSoundPin, OUTPUT);
  pinMode(firePin1, OUTPUT);
  pinMode(firePin2, OUTPUT);
  pinMode(firePin3, OUTPUT);
  // set up the audio trigger pins to give a path to GND when set to OUTPUT
  analogWrite(startSoundPin, LOW);
  analogWrite(firePin1, LOW);
  analogWrite(firePin2, LOW);
  analogWrite(firePin3, LOW);

  pinMode(servoPin1, OUTPUT);  // Set servo pin as an output pin
  pinMode(servoPin2, OUTPUT);  // Set servo pin as an output pin
  pinMode(servoPin3, OUTPUT);  // Set servo pin as an output pin
  pinMode(servoPin4, OUTPUT);  // Set servo pin as an output pin
 
  pulseWidth1 = minPulse;      // Set the motor position to the minimum
  pulseWidth2 = minPulse;      // Set the motor position to the minimum
  pulseWidth3 = minPulse;      // Set the motor position to the minimum
  pulseWidth4 = minPulse;      // Set the motor position to the minimum
  pinMode(ledPin1, OUTPUT);  // sets the LED pin as output
  pinMode(ledPin2, OUTPUT);
  ledPin2timer = millis ();
  digitalWrite(ledPin1, LOW); // sets the LED pin LOW (turns it off)
  digitalWrite(ledPin2, LOW);

Serial.begin(19200);
 Serial.print("loading sound card init\n");
  delay(1000); 
  Serial.print("card initialized");
   //give the audio board time to power up.
  // Otherwise bootup sound will be called before audio
  // board is ready.
  // this plays an initialization sound.
  digitalWrite(startSoundPin, HIGH);
  //Serial.print("#00\n");
  delay(300);
  digitalWrite(startSoundPin, LOW);
  delay(300);
    //Initializes nunchuck and servos
  nunchuk.init();

}

void ledPin2toggle ()
  {
   if (digitalRead (ledPin2) == LOW)
      digitalWrite (ledPin2, HIGH);
   else
      digitalWrite (ledPin2, LOW);

  // remember when we toggled it
  ledPin2timer = millis (); 
  }  // end of toggleGreenLED

void loop() {
  // put your main code here, to run repeatedly:
  checkNunchuck1();
  updateServo1();   // update servo 1 position
  checkNunchuck2();
  updateServo2();   // update servo 2 position
  checkNunchuck3();
  updateServo3();   // update servo 3 position
  checkNunchuck4();
  updateServo4();   // update servo 4 position

   if(nunchuk.zButton == 1)  {    // light the LED if z button is pressed
    digitalWrite(ledPin1, HIGH);
    digitalWrite(firePin1, HIGH);
  //  Serial.print("#3\n");
    //  3 = 1
    delay(300);
    digitalWrite(ledPin1,LOW);
    digitalWrite(firePin1, LOW);
    delay(300);
   }

    if (nunchuk.cButton == 1)  {
      if ( (millis () - ledPin2timer) >= ledPin2interval) {
     ledPin2toggle ();
      }
    digitalWrite(firePin2, HIGH);
//  Serial.print("#4\n");
//  4 = 4
//    delay(300);
   
    digitalWrite(firePin2, LOW);
//    delay(300);
   }



    delay(1);        // this is here to give a known time per loop

 //Guardamos los valores que nos manda el Nunchuk en las variables
  xjoystick = nunchuk.analogX;
  xjoystick = constrain(xjoystick, 26, 226);
  xjoystick = map(xjoystick, 26, 226, 0, 180);
 
  yjoystick = nunchuk.analogY;
  yjoystick = constrain(yjoystick, 26, 226);
  yjoystick = map(yjoystick, 26, 226, 180, 0);
 
  xtilt = nunchuk.accelX;
  xtilt = constrain(xtilt, 320, 720);
  xtilt = map(xtilt, 320, 720, 180, 0);
 
  ytilt = nunchuk.accelY;
  ytilt = constrain(ytilt, 320, 720);
  ytilt = map(ytilt, 320, 720, 0, 180);
// This prints the serial status of the nunchuck.
  Serial.print ("Joystick X: ");
  Serial.print (xjoystick, DEC);
  Serial.print ("\t");
 
  Serial.print ("Joystick Y: ");
  Serial.print (yjoystick, DEC);
  Serial.print ("\t");
 
  Serial.print ("X: ");
  Serial.print (xtilt, DEC);
  Serial.print ("\t");
 
  Serial.print ("Y: ");
  Serial.print (ytilt, DEC);
  Serial.print ("\t");
 
  nunchuk.update();
 
  if (nunchuk.cButton == 1) {
    Serial.print("--C--  ");
  }
 
  if (nunchuk.zButton == 1) {
    Serial.print("--Z--  ");
  }
 
  if (nunchuk.cButton == 1 && nunchuk.zButton == 1) {
    Serial.print("--Z-C--");
  }


 
  Serial.print ("\r\n");


}

void checkNunchuck1()
{
  if( loop_cnt > 100 ) {  // loop()s is every 1msec, this is every 100msec
   


    float tilt = xjoystick;    // x-axis, in this case ranges from ~70 - ~185
    tilt = (tilt - 70) * 1.5;        // convert to angle in degrees, roughly
    pulseWidth1 = (tilt * 9) + minPulse; // convert angle to microseconds

//   servoPin1.write(xjoystick);
   
    loop_cnt = 0;  // reset for
  }
  loop_cnt++;
 
}

// called every loop().
// uses global variables servoPin, pulsewidth, lastPulse, & refreshTime
void updateServo1()
{
  // pulse the servo again if rhe refresh time (20 ms) have passed:
  if (millis() - lastPulse1 >= refreshTime) {
    digitalWrite(servoPin1, HIGH);    // Turn the motor on
    delayMicroseconds(pulseWidth1);   // Length of the pulse sets the motor position
    analogWrite(servoPin1, LOW);    // Turn the motor off
    lastPulse1 = millis();            // save the time of the last pulse
  }
}

void checkNunchuck2()
{
  if( loop_cnt > 100 ) {  // loop()s is every 1msec, this is every 100msec
   
//    nunchuck_get_data();
//    nunchuck_print_data();

    float tilt = yjoystick;    // y-axis, in this case ranges from ~70 - ~185
    tilt = (tilt - 70) * 1.5;        // convert to angle in degrees, roughly
    pulseWidth2 = (tilt * 9) + minPulse; // convert angle to microseconds
   
    loop_cnt = 0;  // reset for
  }
  loop_cnt++;
 
}

// called every loop().
// uses global variables servoPin, pulsewidth, lastPulse, & refreshTime
void updateServo2()
{
  // pulse the servo again if rhe refresh time (20 ms) have passed:
  if (millis() - lastPulse2 >= refreshTime) {
    digitalWrite(servoPin2, HIGH);   // Turn the motor on
    delayMicroseconds(pulseWidth2);   // Length of the pulse sets the motor position
    analogWrite(servoPin2, LOW);    // Turn the motor off
    lastPulse2 = millis();            // save the time of the last pulse
  }
}

void checkNunchuck3()
{
  if( loop_cnt > 100 ) {  // loop()s is every 1msec, this is every 100msec
   
//    nunchuck_get_data();
//    nunchuck_print_data();

    float tilt = xtilt;    // x-axis, in this case ranges from ~70 - ~185
    tilt = (tilt - 70) * 1.5;        // convert to angle in degrees, roughly
    pulseWidth3 = (tilt * 9) + minPulse; // convert angle to microseconds
   
    loop_cnt = 0;  // reset for
  }
  loop_cnt++;
 
}

// called every loop().
// uses global variables servoPin, pulsewidth, lastPulse, & refreshTime
void updateServo3()
{
  // pulse the servo again if rhe refresh time (20 ms) have passed:
  if (millis() - lastPulse3 >= refreshTime) {
    digitalWrite(servoPin3, HIGH);   // Turn the motor on
    delayMicroseconds(pulseWidth3);   // Length of the pulse sets the motor position
    analogWrite(servoPin3, LOW);    // Turn the motor off
    lastPulse3 = millis();            // save the time of the last pulse
  }
}

void checkNunchuck4()
{
  if( loop_cnt > 100 ) {  // loop()s is every 1msec, this is every 100msec
   
//    nunchuck_get_data();
//    nunchuck_print_data();

    float tilt = ytilt;    // y-axis, in this case ranges from ~70 - ~185
    tilt = (tilt - 70) * 1.5;        // convert to angle in degrees, roughly
    pulseWidth4 = (tilt * 9) + minPulse; // convert angle to microseconds
   
    loop_cnt = 0;  // reset for
  }
  loop_cnt++;
 
}

// called every loop().
// uses global variables servoPin, pulsewidth, lastPulse, & refreshTime
void updateServo4()
{
  // pulse the servo again if rhe refresh time (20 ms) have passed:
  if (millis() - lastPulse4 >= refreshTime) {
    digitalWrite(servoPin4, HIGH);   // Turn the motor on
    delayMicroseconds(pulseWidth4);   // Length of the pulse sets the motor position
    analogWrite(servoPin4, LOW);    // Turn the motor off
    lastPulse4 = millis();            // save the time of the last pulse
  }
}

When I do this set up in TinkerCAD, it does the same thing IRL. The simulation (TinkerCAD) should have "perfect" buttons that dont need to be debounced... right? Im thinking it might be something else?

Im still fairly new to Arudino and coding.. so please excuse my ignorance and not knowing things...

It could just be the simulator itself, but adding the debounce won't hurt.

sounds like an r/c circuit, i see some big caps and resistors??

10 amps will do, not so sure about the 2 amps, but if you see this in sim as well then not the issue.

A 2Amp supply is not even enough for one MG996r (2.5Amp stall current at 6volt).

But this is a battery rating. So are you using a supply or a battery.
Leo..

thats just what the universal power adaptor had listed. Even at 10 Amps on TinkerCAD, its been having power issues... same schematic as the OP. Keep in mind that the simulated servos are SG90's probably... so less current than my MG996R's IRL

still seems like a wiring issue. The simulated SG90's aren't getting enough power even at 10Amp.. so maybe a code issue? Left servo interrupting the right servo? head scratching for sure...

nope in the code somewhere, UP and Down stop responding because angle is less than 20 or greater than 131. the left and right can set it outside these limits to 0 and 180.
i do see a slight jump in the servo if it sits, looks like it's figuring out where it is.

also curious why only one var angle? you can get a radical change on the other servo when its applied to it. for smooth servo up down left right, should have separate vars for each servo.

try this, sims good.

#include <Servo.h>

Servo myservoH;
Servo myservoV;

int angle = 90;  // initial angle  for servo
int angle2 = 90; // angle for servo 2
int angleStep = 2;

#define LEFT A1
#define RIGHT A0
#define UP A2
#define DOWN A3
void setup() {

  Serial.begin(9600);  //  setup serial
  myservoH.attach(10);
  myservoV.attach(5);  // attaches the servo on pin 9 to the servo object
  pinMode(LEFT, INPUT_PULLUP);
  pinMode(RIGHT, INPUT_PULLUP);
  pinMode(UP, INPUT_PULLUP);
  pinMode(DOWN, INPUT_PULLUP);
  myservoH.write(angle);  // send servo to the middle at 90 degrees
  myservoV.write(angle);  // send servo to the middle at 90 degrees
}

void loop() {

  while (digitalRead(RIGHT) == LOW) {

    if (angle > 0 && angle <= 180) {
      angle = angle - angleStep;
      if (angle < 0) {
        angle = 0;
      } else {
        myservoH.write(angle);  // move the servo to desired angle
        Serial.print("Moved H to: ");
        Serial.print(angle);  // print the angle
        Serial.println(" degree");
      }
    }

    delay(40);  // waits for the servo to get there
  }             // while
                // Servo button demo by Robojax.com

  while (digitalRead(LEFT) == LOW) {

    // Servo button demo by Robojax.com
    if (angle >= 0 && angle <= 180) {
      angle = angle + angleStep;
      if (angle > 180) {
        angle = 180;
      } else {
        myservoH.write(angle);  // move the servo to desired angle
        Serial.print("Moved H to: ");
        Serial.print(angle);  // print the angle
        Serial.println(" degree");
      }
    }

    delay(40);  // waits for the servo to get there
  }             //


  while (digitalRead(DOWN) == LOW) {

    if (angle2 > 20 && angle2 <= 130) {
      angle2 = angle2 - angleStep;
      if (angle2 < 20) {
        angle2 = 20;
      } else {
        myservoV.write(angle2);  // move the servo to desired angle
        Serial.print("Moved V to: ");
        Serial.print(angle2);  // print the angle
        Serial.println(" degree");
      }
    }

    delay(40);  // waits for the servo to get there
  }             // while
                // Servo button demo by Robojax.com

  while (digitalRead(UP) == LOW) {

    // Servo button demo by Robojax.com
    if (angle2 >= 20 && angle2 <= 130) {
      angle2 = angle2 + angleStep;
      if (angle2 > 130) {
        angle2 = 130;
      } else {
        myservoV.write(angle2);  // move the servo to desired angle
        Serial.print("Moved V to: ");
        Serial.print(angle2);  // print the angle
        Serial.println(" degree");
      }
    }

    delay(40);  // waits for the servo to get there
  }             //
}

Ohh you built it too?

Yes!! Finally someone sees (and can explain) what I'm getting!!!

you just needed to add angle2 is all, but yes, looked like this..

Seriously? That's all it was? And it works completely?!?...

yes, unless you wanted it to work all jerky, jerky..
think about what was happening..
left and right was adding and subtracting angle and applying it to one servo.
then you touch up and down and that value that left and right was at is slammed into the other servo, could swing hard from where its at.
you need to track the position of each servo separately, then yes, at least in the sim, the gremlins disappear..

This totally makes sense now that you have found the solution! With so many variables, I was wondering if it was a bunch of things! All it took was adding a 2! Thank you so so much!!!! Im so thankful!!

If you want easier control you may want to look at this servo shield built for arduino by adafruit:
Adafruit Motor/Stepper/Servo Shield for Arduino v2 Kit [v2.3] : ID 1438 : $19.95 : Adafruit Industries, Unique & fun DIY electronics and kits

It works and separates the power out so you can have a cleaner wiring setup. It fixed a lot of the servo jitter issues tooo.