Robotic arm

Robotic arms are crucial in modern technology, with numerous applications in industries, medicine, production and education. Learning to build a robotic arm offers a deeper understanding of mechanics and electronics, as well as an introduction to programming and automation.

The robot is constructed from 3 mm HDF. You can find the .dxf and LightBurn files for laser cutting it here:

https://drive.google.com/drive/folders/1fXoBAaNmHokGctbiL1LUL7Q45WkVXJl5?usp=sharing

You can see in the engraved parts how to assembly the robot. They are assembled with strips and screws.

The components you'll need are:

  • 1 x Keyestudio 328 PLUS microcontroller
  • 1 x USB C Cable
  • 3 x 9g Servos
  • 1 x Joystick
  • 3 x female-male jumper wires
  • 5 x female-female jumper wires
  • 5 x Strips 2,5mm
  • 10 x Nylon Snap Rivets, 3mm
  • 6 x M2x3mm screws
  • 1 x M10 nut
  • (9V battery + connector )

To assemble the components, follow this diagram:

The Arduino code is available [here].

#include <Servo.h>

// Define servos
Servo baseServo;    // Base rotation servo  
Servo armServo;     // Arm movement servo  
Servo gripperServo; // Gripper servo

// Define joystick pins
const int joystickXPin = A0; // Joystick X-axis  
const int joystickYPin = A1; // Joystick Y-axis  
const int joystickButtonPin = 3; // Joystick button

// Define servo pins
const int baseServoPin = 9;  
const int armServoPin = 10;  
const int gripperServoPin = 11;

// Initial positions of servos
int basePos = 90;  
int armPos = 90;  
int gripperPos = 90; // Closed position

// Target positions of servos
int gripperTargetPos = 90; // Closed position

// Button state
int buttonState = 0;  
int lastButtonState = 0;  
bool gripperClosed = true; // Gripper initially closed

// Dead zone threshold for joystick
const int deadZone = 30;

// Function to gradually move the servo
void moveServoSmoothly(Servo &servo, int &currentPos, int targetPos, int stepSize) {  
  if (currentPos < targetPos) {
    currentPos += stepSize;
    if (currentPos > targetPos) currentPos = targetPos; // Ensure it doesn't overshoot
  } else if (currentPos > targetPos) {
    currentPos -= stepSize;
    if (currentPos < targetPos) currentPos = targetPos; // Ensure it doesn't overshoot
  }
  servo.write(currentPos);
}

void setup() {  
  // Attach the servos to their pins
  baseServo.attach(baseServoPin);
  armServo.attach(armServoPin);
  gripperServo.attach(gripperServoPin);

  // Set joystick button pin as input
  pinMode(joystickButtonPin, INPUT_PULLUP);

  // Move servos to initial positions
  baseServo.write(basePos);
  armServo.write(armPos);
  gripperServo.write(gripperPos);

  // Start serial communication for debugging (optional)
  Serial.begin(9600);
}

void loop() {  
  // Read joystick values
  int joystickX = analogRead(joystickXPin);
  int joystickY = analogRead(joystickYPin);
  buttonState = digitalRead(joystickButtonPin);



  // Map joystick values to servo position adjustments
  int baseMove = map(joystickY, 0, 1023, -5, 5); // Adjust this range for desired sensitivity
  int armMove = map(joystickX, 0, 1023, -5, 5);  // Adjust this range for desired sensitivity

  // Apply dead zone for joystick
  if (abs(joystickX - 512) < deadZone) {
    armMove = 0;
  }
  if (abs(joystickY - 512) < deadZone) {
    baseMove = 0;
  }

  // Adjust servo positions
  basePos += baseMove;
  armPos += armMove;

  // Constrain positions to valid servo range (0-180)
  basePos = constrain(basePos, 0, 180);
  armPos = constrain(armPos, 0, 90);

  // Write positions to servos
  baseServo.write(basePos);
  armServo.write(armPos);

  // Check if button state has changed
  if (buttonState != lastButtonState) {
    if (buttonState == LOW) {
      // Toggle gripper target position
      if (gripperClosed) {
        gripperTargetPos = 0; // Open position
      } else {
        gripperTargetPos = 90; // Closed position
      }
      gripperClosed = !gripperClosed;
    }
    // Debounce delay
    delay(50);
  }

  // Gradually move gripper to target position with a faster speed
  moveServoSmoothly(gripperServo, gripperPos, gripperTargetPos, 5); // Adjust step size for speed

  // Save the current button state for next comparison
  lastButtonState = buttonState;

  // Small delay to stabilize readings
  delay(20);

   Serial.print(" | Base Pos: ");
  Serial.print(basePos);
  Serial.print(" | Arm Pos: ");
  Serial.print(armPos);
  Serial.print(" | Gripper Pos: ");
  Serial.println(gripperPos);
}