Built-in project · Arduino Uno R3

Robot arm with joysticks on an Arduino Uno

A four-servo desktop arm on four Uno pins, steered by two joysticks with the Servo library: base and shoulder on one stick, elbow and claw on the other. The whole build, an Arduino Uno and 3 more parts, runs here in your browser on the firmware below; open it in the editor to change the wiring or the code and run it again.

Teaching? Assign it to your class in one click.

Intermediate About 30 minutes to build and run. Runs in your browser. Free, and no account needed.

The parts retail for about $55. On Mokxi they're free.

The circuit runs here once the page has loaded.

What you'll learn

  • Driving several servos with the Servo library
  • Reading joysticks as analog voltages
  • Rate control versus position control
  • A dead band to ignore sensor noise
  • Clamping a value to a safe range
  • Difficulty: Intermediate
  • Time: About 30 minutes
  • Board: Arduino Uno R3
  • Parts: 6

How it works

Four servos make up the arm: base, shoulder, elbow and claw. They sit on pins 3, 5, 6 and 9. Two thumb sticks feed four analog inputs, A0 to A3, one axis per joint.

The sticks act like speed controls, not position controls. Push a stick and its joint keeps turning while you hold it. Push harder and it turns faster, up to 3 degrees per pass. Let go and the joint stays where it is.

A dead band around the middle ignores small readings, so a stick that does not center perfectly will not creep the arm. Each joint also has limits, so the arm cannot fold into its own base.

Parts list

6 parts, plus the jumper wires. They retail for about $55; on Mokxi they're free.

Wiring table

Every connection, pin by pin, read from the circuit itself. Each row joins the pin on the left to the pin on the right, by a jumper wire or a breadboard strip.

Part and pinConnects to
Arduino Uno R3 pin GNDGround
Robot arm, 4 servos pin GRIPArduino Uno R3 pin 9
Robot arm, 4 servos pin ELBOWArduino Uno R3 pin 6
Robot arm, 4 servos pin SHOULDERArduino Uno R3 pin 5
Robot arm, 4 servos pin BASEArduino Uno R3 pin 3
Thumb stick (1) pin VCCArduino Uno R3 pin 5V
Thumb stick (1) pin VRXArduino Uno R3 pin A0
Thumb stick (1) pin VRYArduino Uno R3 pin A1
Thumb stick (2) pin VRXArduino Uno R3 pin A2
Thumb stick (2) pin VRYArduino Uno R3 pin A3
Robot arm, 4 servos pin VCC5 V
Robot arm, 4 servos pin GNDGround
Thumb stick (1) pin GNDGround
Thumb stick (2) pin GNDGround
Thumb stick (2) pin VCC5 V

The code

The firmware exactly as the editor opens it. Change a line there and press Run: it compiles in the browser.

sketch.ino · Arduino Uno R3
// Robot arm with two joysticks: a four-servo desktop arm driven straight
// from the Uno with the Servo library.
//
//   arm      BASE pin 3, SHOULDER pin 5, ELBOW pin 6, GRIP pin 9
//            VCC and GND from a separate 5 V supply, GND joined to the Uno's
//   left     joystick VRX A0 (base), VRY A1 (shoulder)
//   right    joystick VRX A2 (elbow), VRY A3 (claw)
//
// The sticks are rate controls, the way the kit sketches do it: push a stick
// and its joint keeps moving while you hold it, faster the further you push;
// let go and the stick springs back to the middle and the joint stays put.
// A small dead band around the middle stops a stick that does not center
// perfectly from creeping the arm.
//
// Four SG90s moving at once can pull more than an amp, which is more than an
// Uno's 5 V pin gives from USB. That is why the arm has its own supply on the
// bench and here, with the grounds joined so the signals mean something.

#include <Servo.h>

Servo base, shoulder, elbow, claw;
Servo *joints[4] = {&base, &shoulder, &elbow, &claw};
const int SERVO_PINS[4] = {3, 5, 6, 9};
const int STICKS[4] = {A0, A1, A2, A3};

// Where each joint starts, and how far it may go. The claw shuts at 20 and
// is wide open at 120; the shoulder and elbow stop short of the ends so the
// arm does not fold into its own base.
int angle[4] = {90, 90, 90, 70};
const int LOW_END[4] = {0, 40, 40, 20};
const int HIGH_END[4] = {180, 160, 160, 120};

const int DEAD_BAND = 60;  // of 1023, either side of the middle

void setup() {
  for (int i = 0; i < 4; i++) {
    joints[i]->attach(SERVO_PINS[i]);
    joints[i]->write(angle[i]);
  }
  Serial.begin(115200);
  Serial.println("robot arm: push a stick to move a joint");
}

void loop() {
  bool moved = false;
  for (int i = 0; i < 4; i++) {
    int offset = analogRead(STICKS[i]) - 512;
    if (offset > DEAD_BAND || offset < -DEAD_BAND) {
      // Up to 3 degrees a pass at full deflection.
      angle[i] += offset / 170;
      if (angle[i] < LOW_END[i]) angle[i] = LOW_END[i];
      if (angle[i] > HIGH_END[i]) angle[i] = HIGH_END[i];
      moved = true;
    }
    joints[i]->write(angle[i]);
  }
  if (moved) {
    Serial.print("base ");
    Serial.print(angle[0]);
    Serial.print("  shoulder ");
    Serial.print(angle[1]);
    Serial.print("  elbow ");
    Serial.print(angle[2]);
    Serial.print("  claw ");
    Serial.println(angle[3]);
  }
  delay(20);
}

Code walkthrough

The key functions of the sketch, and what each one does.

setup sketch.ino

Attaches each of the four servos to its pin from SERVO_PINS and writes its starting angle right away, so the arm begins in a known pose. The claw starts at 70, the other joints at 90. It also opens the serial port at 115200 and prints a short hint.

void setup() {
  for (int i = 0; i < 4; i++) {
    joints[i]->attach(SERVO_PINS[i]);
    joints[i]->write(angle[i]);
  }
  Serial.begin(115200);
  Serial.println("robot arm: push a stick to move a joint");
}

loop sketch.ino

For each joint it reads the stick and subtracts 512 to get an offset from center. If the offset is outside DEAD_BAND, it adds offset / 170 to the angle, which is up to 3 degrees per pass, then clamps the angle between LOW_END and HIGH_END. Every pass writes the angle to the servo, then waits 20 ms. It prints the four angles only when something moved.

void loop() {
  bool moved = false;
  for (int i = 0; i < 4; i++) {
    int offset = analogRead(STICKS[i]) - 512;
    if (offset > DEAD_BAND || offset < -DEAD_BAND) {
      // Up to 3 degrees a pass at full deflection.
      angle[i] += offset / 170;
      if (angle[i] < LOW_END[i]) angle[i] = LOW_END[i];
      if (angle[i] > HIGH_END[i]) angle[i] = HIGH_END[i];
      moved = true;
    }
    joints[i]->write(angle[i]);
  }
  if (moved) {
    Serial.print("base ");
    Serial.print(angle[0]);
    Serial.print("  shoulder ");
    Serial.print(angle[1]);
    Serial.print("  elbow ");
    Serial.print(angle[2]);
    Serial.print("  claw ");
    Serial.println(angle[3]);
  }
  delay(20);
}

Troubleshooting

The usual mistakes with this circuit, and the fix for each.

  • The arm twitches or the board resets when several joints move This project
    Four servos can draw more than the Uno's 5 V pin gives from USB. Give the arm its own 5 V supply and join its ground to the Uno's ground.
  • A joint creeps slowly with the stick let go This project
    The stick does not rest exactly at 512. Raise DEAD_BAND a little.
  • A stick moves the wrong joint This project
    Check which analog pin each axis uses: A0 and A1 on the first stick, A2 and A3 on the second, matching the order in STICKS.
  • The arm jerks, resets the board or moves weakly when several joints move at once. Robot arm
    Four SG90 servos can pull an amp or more together. Power them from a separate 5 V supply with a 470 uF or larger capacitor. Join its ground to the board's ground.
  • A joint hits its end stop and buzzes, or the claw crushes. Robot arm
    Limit each servo to a safe angle range in code and move gradually in small steps. Find the open and closed angle of the claw by testing, and never command beyond them.
  • The axis reads about half scale at rest instead of exactly zero, and the values drift a little, so the cursor or motor creeps. Thumb stick
    Center is about half of the ADC range (about 512 on a 10 bit Arduino). Read it at startup and subtract that, then add a dead zone of a few counts.
  • The push button reads random values or always looks pressed. The switch pin is a plain contact to ground and has no pull-up on the stick. Thumb stick
    Set the switch pin to INPUT_PULLUP and treat LOW as pressed. Debounce it in code. Power the stick from the same voltage as the ADC reference.
  • Upload fails or nothing runs after wiring pins 0 and 1 to a module. Arduino Uno R3
    Pins 0 and 1 are the USB serial port. Move the module to other pins, or unplug it while uploading. Also check the right board is selected.

Change it and keep it

Open it in the editor, change the circuit or the code, and keep your version in a free account.