Skip to the content.

Smart Assistive Three-Jointed Robotic Arm

This project is a proof-of-concept three-jointed robotic arm inspired by assistive technology for people with limited mobility. I assembled and wired the arm, programmed four servo motors, developed an alternative laptop-based control system after identifying a faulty joystick, and added an ultrasonic distance sensor with a seven-segment display. The project taught me that engineering is not simply about following instructions; it involves testing individual systems, identifying failures, developing solutions, and adapting when the original plan does not work.

Alice S. Nord Anglia International School Dubai Biomedical, Electrical, and Mechanical Engineering Incoming Year 11

Smart Assistive Three-Jointed Robotic Arm

Final Milestone

For my final milestone, I completed two modifications to the original robotic-arm project. I added an HC-SR04 ultrasonic distance sensor and a single-digit seven-segment display. The sensor measures the distance between the claw and a nearby object, while the display shows the measured distance from zero to nine centimetres.

The seven-segment display is connected through a 74HC595 shift register. This integrated circuit simplified the breadboard wiring because it allowed the Arduino to control the display segments using only three digital pins.

A major challenge was combining the sensor and display code with the existing robotic-arm program without interfering with the servo motors or laptop controls. To solve this, I tested the sensor and display independently before integrating them with the main program one component at a time. This method allowed me to identify errors more efficiently and confirm that each subsystem worked correctly before moving forward.

My main triumph was successfully operating the arm controls, ultrasonic sensor, and seven-segment display together as one complete system. The final design uses one joystick to control the base and shoulder, while a Python interface on my laptop controls the elbow and claw. The ultrasonic sensor continuously measures distance, and the display presents the result while the arm is operating.

By the end of BlueStamp Engineering, I had assembled my first robot, wired and tested several electronic systems, programmed an Arduino, created a Python control interface, and learned how to troubleshoot both hardware and software problems systematically.

In the future, I would improve the cable management, replace the faulty joystick, make the arm’s movements smoother, and explore more advanced assistive features, such as automated movement routines or object recognition.

Second Milestone

For my second milestone, I completed and tested the main robotic-arm system. Four servo motors control the base rotation, shoulder, elbow, and claw. The original design used two joystick modules, with each joystick controlling two servo motors.

During testing, the elbow and claw motors began twitching and responding inconsistently. The main challenge was determining whether the problem came from the motors, wiring, Arduino signals, code, or joystick.

To isolate the issue, I swapped the motors between the two joystick circuits, checked the electrical connections, and compared how the motors responded. The motors connected to the working joystick operated correctly, while the motors connected to the other joystick began twitching. This confirmed that the motors and wiring were functioning correctly and that the second joystick had a physical fault.

Instead of allowing the faulty joystick to stop the project, I developed an alternative laptop-based control system. I created a Python interface in Thonny with buttons for controlling the elbow and claw. When a button is held, Python sends a movement command to the Arduino. When the button is released, Python sends a stop command.

My main triumph during this milestone was restoring reliable control of the complete robotic arm despite the hardware failure. The working joystick controlled the base and shoulder, while the laptop interface successfully controlled the elbow and claw.

Before my final milestone, I still needed to add the ultrasonic sensor and seven-segment display, combine their code with the robotic-arm program, and test the entire project as one integrated system.

First Milestone

For my first milestone, I assembled the physical structure of the three-jointed robotic arm and began connecting its mechanical and electrical systems.

The arm uses four servo motors. One servo rotates the base, one moves the shoulder, one moves the elbow, and one opens and closes the claw. Although the arm has three main joints, the claw requires an additional servo motor.

I connected the servo motors and joystick modules to an Arduino-compatible microcontroller. The Arduino reads the analogue values produced by the joysticks and converts those readings into changes in the servo angles.

One of my first challenges was correctly positioning the servo horns and preventing the motors from attempting to move beyond the arm’s physical limits. To solve this, I tested each servo separately and moved the motors to a neutral position of approximately 90 degrees before attaching or repositioning the servo horns.

This calibration process was an important early triumph because it allowed the mechanical structure to move more predictably and reduced the risk of damaging the servos or frame.

I also had to organise a large number of wires and understand how the mechanical components, electrical connections, and Arduino code worked together. By the end of the milestone, I had completed the physical base of the robot and established a working foundation for testing the full joystick-control system.

Circuit Schematic

Robotic Arm Circuit Diagram

The completed system contains the following components:

Main Pin Connections

Component Arduino Connection
Base servo Digital pin D4
Shoulder servo Digital pin D5
Elbow servo Digital pin D6
Claw servo Digital pin D7
Working joystick X-axis Analogue pin A0
Working joystick Y-axis Analogue pin A1
74HC595 data pin Digital pin D1
74HC595 latch pin Digital pin D2
74HC595 clock pin Digital pin D3
Ultrasonic sensor trigger Digital pin D8
Ultrasonic sensor echo Digital pin D9
Ultrasonic sensor power 5V and GND

Code

My project uses two connected programs. The Arduino program controls the four servo motors, reads the working joystick, receives serial commands from the laptop, measures distance using the ultrasonic sensor, and updates the seven-segment display. The Python program creates the laptop interface used to control the elbow and claw after I identified a physical fault in the second joystick.

Arduino Control Code


#include <Servo.h>

// =====================================================
// SERVOS
// =====================================================

Servo baseServo;      // D4
Servo shoulderServo;  // D5
Servo elbowServo;     // D6
Servo clawServo;      // D7

int basePos = 90;
int shoulderPos = 90;
int elbowPos = 90;
int clawPos = 90;

// Base, shoulder, and elbow movement speed
const int stepSize = 2;

// Claw movement speed
const int clawStepSize = 5;


// =====================================================
// 74HC595 + 7-SEGMENT DISPLAY
// =====================================================

const int dataPin = 1;   // Yellow wire → D1
const int latchPin = 2;  // Blue wire → D2
const int clockPin = 3;  // Orange wire → D3

// Display patterns for numbers 0 to 9
const byte digits[10] = {
  B11111100,  // 0
  B01100000,  // 1
  B11011010,  // 2
  B11110010,  // 3
  B01100110,  // 4
  B10110110,  // 5
  B10111110,  // 6
  B11100000,  // 7
  B11111110,  // 8
  B11100110   // 9
};


// =====================================================
// ULTRASONIC SENSOR
// =====================================================

const int trigPin = 8;
const int echoPin = 9;


// =====================================================
// LAPTOP CONTROL
// =====================================================

// S means stopped
char currentCommand = 'S';


// =====================================================
// TIMING
// =====================================================

unsigned long lastMovementTime = 0;
unsigned long lastSensorTime = 0;

const unsigned long movementInterval = 20;
const unsigned long sensorInterval = 100;


// =====================================================
// DISPLAY FUNCTION
// =====================================================

void showDigit(int number) {

  number = constrain(number, 0, 9);

  digitalWrite(latchPin, LOW);

  shiftOut(
    dataPin,
    clockPin,
    LSBFIRST,
    digits[number]
  );

  digitalWrite(latchPin, HIGH);
}


// =====================================================
// SENSOR FUNCTION
// =====================================================

float measureDistance() {

  // Make sure TRIG begins LOW
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);

  // Send a 10-microsecond ultrasonic pulse
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);

  // Measure the returning signal
  unsigned long duration =
    pulseIn(echoPin, HIGH, 15000);

  // No returning pulse detected
  if (duration == 0) {
    return -1;
  }

  // Convert time into centimetres
  return duration * 0.0343 / 2.0;
}


// =====================================================
// SETUP
// =====================================================

void setup() {

  // Servo pins
  baseServo.attach(4);
  shoulderServo.attach(5);
  elbowServo.attach(6);
  clawServo.attach(7);

  // Starting servo positions
  baseServo.write(basePos);
  shoulderServo.write(shoulderPos);
  elbowServo.write(elbowPos);
  clawServo.write(clawPos);

  // Display pins
  pinMode(dataPin, OUTPUT);
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);

  // Sensor pins
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);

  // Begin laptop communication
  Serial.begin(9600);

  /*
    D1 is normally the Arduino Nano TX pin.

    The system only needs to receive commands from
    Thonny through D0. It does not need to send messages
    back to the laptop.

    This disables the outgoing transmitter so D1 can
    continue working as the display data pin.
  */
#if defined(UCSR0B) && defined(TXEN0)
  UCSR0B &= ~_BV(TXEN0);
#endif

  // Start the display at 9
  showDigit(9);
}


// =====================================================
// MAIN LOOP
// =====================================================

void loop() {

  unsigned long currentTime = millis();


  // ===================================================
  // READ LAPTOP COMMANDS
  // ===================================================

  while (Serial.available() > 0) {
    currentCommand = Serial.read();
  }


  // ===================================================
  // MOVE ROBOT EVERY 20 MILLISECONDS
  // ===================================================

  if (currentTime - lastMovementTime >= movementInterval) {

    lastMovementTime = currentTime;

    // Read joystick
    int valA0 = analogRead(A0);
    int valA1 = analogRead(A1);


    // -----------------------------------------------
    // BASE — JOYSTICK UP/DOWN
    // -----------------------------------------------

    if (valA0 < 300) {
      basePos -= stepSize;
    }

    if (valA0 > 700) {
      basePos += stepSize;
    }


    // -----------------------------------------------
    // SHOULDER — JOYSTICK LEFT/RIGHT
    // -----------------------------------------------

    if (valA1 < 300) {
      shoulderPos -= stepSize;
    }

    if (valA1 > 700) {
      shoulderPos += stepSize;
    }


    // -----------------------------------------------
    // ELBOW — LAPTOP BUTTONS
    // -----------------------------------------------

    if (currentCommand == 'E') {
      elbowPos += stepSize;
    }

    if (currentCommand == 'e') {
      elbowPos -= stepSize;
    }


    // -----------------------------------------------
    // CLAW — LAPTOP BUTTONS
    // -----------------------------------------------

    if (currentCommand == 'C') {
      clawPos += clawStepSize;
    }

    if (currentCommand == 'c') {
      clawPos -= clawStepSize;
    }


    // Prevent the motors from exceeding their limits
    basePos = constrain(basePos, 0, 180);
    shoulderPos = constrain(shoulderPos, 0, 180);
    elbowPos = constrain(elbowPos, 0, 180);
    clawPos = constrain(clawPos, 0, 180);


    // Update servo positions
    baseServo.write(basePos);
    shoulderServo.write(shoulderPos);
    elbowServo.write(elbowPos);
    clawServo.write(clawPos);
  }


  // ===================================================
  // MEASURE DISTANCE AND UPDATE DISPLAY
  // ===================================================

  if (currentTime - lastSensorTime >= sensorInterval) {

    lastSensorTime = currentTime;

    float distance = measureDistance();
    int displayNumber;

    if (distance < 0) {
      // No returning signal
      displayNumber = 9;
    }
    else if (distance <= 2.0) {
      // Extremely close
      displayNumber = 0;
    }
    else if (distance >= 9.0) {
      // 9 represents 9 centimetres or farther
      displayNumber = 9;
    }
    else {
      // Show the nearest whole centimetre
      displayNumber = round(distance);
    }

    showDigit(displayNumber);
  }
}
  

Python Laptop Controller

The Python controller was run in Thonny. Holding a button sends a movement command to the Arduino, while releasing the button sends S to stop the movement.


import tkinter as tk
import serial
import time

# Arduino USB port
PORT = "/dev/cu.usbserial-10"

arduino = serial.Serial(PORT, 9600, timeout=1)
time.sleep(2)


def send(command):
    arduino.write(command.encode())


def button_press(command):
    send(command)


def button_release():
    send("S")


def close_program():
    try:
        send("S")
        time.sleep(0.05)
        arduino.close()
    finally:
        window.destroy()


window = tk.Tk()
window.title("Robotic Arm Controller")
window.geometry("400x300")

title = tk.Label(
    window,
    text="ELBOW + CLAW CONTROL",
    font=("Arial", 18, "bold")
)
title.pack(pady=15)


# =====================================================
# ELBOW
# =====================================================

tk.Label(
    window,
    text="ELBOW",
    font=("Arial", 14, "bold")
).pack()

elbow_frame = tk.Frame(window)
elbow_frame.pack(pady=5)


elbow_up = tk.Button(
    elbow_frame,
    text="▲",
    font=("Arial", 20),
    width=5,
    height=2
)

elbow_up.grid(row=0, column=0)

elbow_up.bind(
    "<ButtonPress-1>",
    lambda event: button_press("E")
)

elbow_up.bind(
    "<ButtonRelease-1>",
    lambda event: button_release()
)


elbow_down = tk.Button(
    elbow_frame,
    text="▼",
    font=("Arial", 20),
    width=5,
    height=2
)

elbow_down.grid(row=1, column=0)

elbow_down.bind(
    "<ButtonPress-1>",
    lambda event: button_press("e")
)

elbow_down.bind(
    "<ButtonRelease-1>",
    lambda event: button_release()
)


# =====================================================
# CLAW
# =====================================================

tk.Label(
    window,
    text="CLAW",
    font=("Arial", 14, "bold")
).pack(pady=(15, 0))

claw_frame = tk.Frame(window)
claw_frame.pack(pady=5)


claw_left = tk.Button(
    claw_frame,
    text="◀",
    font=("Arial", 20),
    width=5,
    height=2
)

claw_left.grid(row=0, column=0)

claw_left.bind(
    "<ButtonPress-1>",
    lambda event: button_press("C")
)

claw_left.bind(
    "<ButtonRelease-1>",
    lambda event: button_release()
)


claw_right = tk.Button(
    claw_frame,
    text="▶",
    font=("Arial", 20),
    width=5,
    height=2
)

claw_right.grid(row=0, column=1)

claw_right.bind(
    "<ButtonPress-1>",
    lambda event: button_press("c")
)

claw_right.bind(
    "<ButtonRelease-1>",
    lambda event: button_release()
)


window.protocol("WM_DELETE_WINDOW", close_program)

window.mainloop()
  

The USB port listed in the Python program was the port assigned to the Arduino on my laptop. The port name may need to be changed if the program is run on another computer.

Bill of Materials

Part Note Price Link
Robot Arm Kit Included the three-jointed robotic-arm frame, claw, Arduino-compatible microcontroller, four servo motors, two joystick modules, mounting components, screws, and the main wiring used to construct and control the arm. $45.00 Product Link
Electronics Kit Included the breadboard, jumper wires, resistors, 74HC595 shift register, single-digit seven-segment display, and other circuit components used to create the display modification. $14.00 Product Link
9V Barrel-Jack Cable Used to connect the power source to the microcontroller. $6.00 Product Link
9V Batteries Used as a portable power source during testing. $12.37 Product Link
Precision Screwdriver Kit Used to assemble and adjust the robotic-arm frame, servo horns, and mounting components. $5.94 Product Link
Digital Multimeter Used to test voltage, continuity, and electrical connections while troubleshooting the circuit. $11.00 Product Link
HC-SR04 Ultrasonic Sensor Purchased separately and used to measure the distance between the claw and nearby objects. Not recorded Not available

Resources