Using Joysticks

This section is about direct control of the joints via two joysticks. Here, the control signals from the user are converted directly into movements of the robot arm. If you are not interested in direct control via joysticks, you can simply skip this section.

Joystick Test

It makes sense to test each joystick before installation and determine the available range of values. Typically, the joysticks provide values between 0 and 1024 (10 bit resolution).

Just connect as follows:

Arduino      Joystick
+5V               +5V
GND               GND
A0                VRx
A1                VRy

 
/* RobotArm
 * Joystick Test
 * Can be used to check which signals (range) the joystick delivers
 * Stefan Hager 2022
 */
  
void setup() 
{
   Serial.begin(9600); 
}

void loop() {
  int xPos = analogRead(A0);  // read analog input pin A0
  int yPos = analogRead(A1);

  Serial.print("X - Axis: ");
  Serial.print(xPos);
  Serial.print("      Y - Axis: ");
  Serial.println(yPos);
     
  delay(1000);
}     

Controlling 1 Servowith a Joystick

Pinout

Arduino           Device
A0                J1 VRx
9                 servo
GND               Battery GND (Important!!!)
 

/* RobotArm
 * Joystick Servo Control Test
 * Control a servo via joystick
 * Servo uses Pin 8
 * Jostick is on A0
 * Stefan Hager 2022
 */

#include <Servo.h>

Servo myServo;  

int position = 90; // current position of the servo
int speed = 2; // change in position = speed of the servo

void setup() 
{
   Serial.begin(9600); 
   myServo.attach(8);  // connect Servo with PIN 8
   myServo.write(position);
}

void loop() 
{
  int joy_input = analogRead(A1);  // read analog input pin A0

  if(joy_input < 400)
  {
    position = position - speed;
    if(position < 0)
    { 
      position = 0;
    }
  }
  if(joy_input > 700)
    {
    position = position + speed;
    if(position > 180)
    { 
      position = 180;
    }
  }
  myServo.write(position);
  Serial.println(position);
  delay(20);

}

Full Robotarm Control with 2 Joysticks

The following program allows direct control of all axes with two joysticks. In addition, the program outputs the current position of the servos after each movement. Thus you can use these positions later for the programming of automated sequences.

Move the arm over several positions to the desired target position. At each intermediate position, note the values for the individual joints. Later, you can use the program from step 3 to move the arm to specific target positions.

The Complete Setup

Pinout

Arduino           Device
A0                J1 VRx
A1                J1 VRy
A2                J2 VRx
A3                J2 VRy
9                 Base servo
10                Shoulder servo
11                Elbow servo
12                Gripper servo 
GND               Battery GND (Important!!!)

STRATEGY 1: Joystick sets speed

In this strategy, the joystick is used to set the speed for the servo forward or backward. The speed is held as long as the joystick is moved forward or backward in one position. As soon as the joystick is released, the servo remains in the current position.

To keep the implementation simple, a very simple strategy is followed: All values of Joystick that are between 400 and 700 are evaluated as neutral (no User - input). Values above 700 are considered as a movement in one direction, values below 400 as a movement in the other direction. Accordingly, the servo is moved by "speed" in one direction or the other direction.

 
/* RobotArm
 * Full Joystick Control
 * Stefan Hager 2022
 */

#include <Servo.h>

// any joystick value between LOWER_LIMIT and UPPER_LIMIT is seen as neutral position
#define LOWER_LIMIT 400
#define UPPER_LIMIT 700


// limits 
#define LOW_POS 10
#define HIGH_POS 170


// use this define to control speed of the servo movement

// create all required objects from class Servo
Servo base;  
Servo shoulder;
Servo elbow;
Servo gripper;

int speed = 2; // change in position = speed of the servo, start with 2, 1 is too low
int outputcounter = 0; // control how often you want to see the servo positions

// ***** make sure that you use the REAL start position! *****
int basePos = 90;
int shoulderPos = 90;
int elbowPos = 90;
int gripperPos = 90;
  
  
void setup() 
{
  // opens serial port, sets data rate to 9600 bps
  Serial.begin(9600); 
  // check if the servos are connected as follows:
  base.attach(9);  
  shoulder.attach(10);  
  elbow.attach(11);  
  gripper.attach(12);  

  // set the servos to start positions
  Serial.println("Moving servos into base position ... "); 
  base.write(basePos); 
  delay(1000);
  shoulder.write(shoulderPos); 
  delay(1000);
  elbow.write(elbowPos); 
  delay(1000);
  gripper.write(gripperPos); 

  Serial.println("Robotarm is ready ... "); 
}

void loop() 
{
     // check each joystick input and move according joint
     moveBase();
     moveShoulder();
     moveElbow();
     moveGripper();
     delay(20);
     printPositions();
}


// printPositions: prints position of each joint in following oder:
// B: base | S: shoulder | E: elbow | G: gripper

void printPositions()
{
  outputcounter++;
  if(outputcounter < 15)
  {
    return;
  }
  Serial.print("B: ");
  Serial.print(basePos);
  Serial.print(" | S: ");
  Serial.print(shoulderPos);
  Serial.print(" | E: ");
  Serial.print(elbowPos);
  Serial.print(" | G: ");
  Serial.println(gripperPos);
  outputcounter = 0;
}

// control base - servo with A0
void moveBase()
{
  int a0_input = analogRead(A0);  // read analog input pin A0
  if(a0_input < LOWER_LIMIT)  
  {   
    basePos = basePos - speed;
    if(basePos < LOW_POS)
    { 
      basePos = LOW_POS;
    }
  }
  if(a0_input > UPPER_LIMIT)  
  {    
     basePos = basePos + speed;
     if(basePos > HIGH_POS)
     { 
        basePos = HIGH_POS;
     }
  }
  base.write(basePos); 
}

// control shoulder - servo with A1
void moveShoulder()
{
  int a1_input = analogRead(A1);  // read analog input pin A0
  if(a1_input < LOWER_LIMIT)  
  {   
    shoulderPos = shoulderPos - speed;
    if(shoulderPos < LOW_POS)
    { 
      shoulderPos = LOW_POS;
    }
  }
  if(a1_input > UPPER_LIMIT)  
  {    
     shoulderPos = shoulderPos + speed;
     if(shoulderPos > HIGH_POS)
     { 
        shoulderPos = HIGH_POS;
     }
  }
  shoulder.write(shoulderPos); 
}

// control elbow - servo with A2
void moveElbow()
{
  int a2_input = analogRead(A2);  // read analog input pin A0
  if(a2_input < LOWER_LIMIT)  
  {   
    elbowPos = elbowPos - speed;
    if(elbowPos < LOW_POS)
    { 
      elbowPos = LOW_POS;
    }
  }
  if(a2_input > UPPER_LIMIT)  
  {    
     elbowPos = elbowPos + speed;
     if(elbowPos > HIGH_POS)
     { 
        elbowPos = HIGH_POS;
     }
  }
  elbow.write(elbowPos); 
}

// control gripper - servo with A3
void moveGripper()
{
  int a3_input = analogRead(A3);  // read analog input pin A0
  if(a3_input < LOWER_LIMIT)  
  {   
    gripperPos = gripperPos - speed;
    if(gripperPos < LOW_POS)
    { 
      gripperPos = LOW_POS;
    }
  }
  if(a3_input > UPPER_LIMIT)  
  {    
     gripperPos = gripperPos + speed;
     if(gripperPos > HIGH_POS)
     { 
        gripperPos = HIGH_POS;
     }
  }
  gripper.write(gripperPos); 
}

STRATEGY 2: Joystick sets servo target angle

With this strategy, the current position of the joystick is converted directly into the target position of the servo. The analog values between 0 and 1023 are mapped to angles between 0 and 180. To avoid the extreme positions, only target angles between 20 and 160 degrees are used in my implementation.

Caution: when the joystick is released, the servos move back to the center position. With this variant, the position of the joystick must be kept constant in order to maintain a position over a longer period of time.

 
/* RobotArm
 * Direct Joystick Positioning
 * Stefan Hager 2023
 */

#include <Servo.h>

// limits for servo movement
#define LOW_POS 20
#define HIGH_POS 160

// create all required objects from class Servo
Servo base;  
Servo shoulder;
Servo elbow;
Servo gripper;

// define start postions for yout joints
int pos_base = 90;
int pos_shoulder = 45;
int pos_elbow = 45;
int pos_gripper = 10;


int outputcounter = 0;
int readBase, readShoulder , readElbow , readGripper;

// Also connect the joystick VCC to Arduino 5V, and joystick GND to Arduino GND.

void setup()
{
  // set up serial port for output
  Serial.begin(9600);
  
  base.attach(9); // Attach base servo to pin 9
  base.write(pos_base); // Initial base servo position: 90° angle
  delay(500);
  shoulder.attach(10); 
  shoulder.write(pos_shoulder); 
  delay(500);
  elbow.attach(11); 
  elbow.write(pos_elbow); 
  delay(500);
  gripper.attach(12);
  gripper.write(pos_gripper); 
  delay(500);
}


void loop()
{
  // read values from joysticks
  readBase = (readBase * 0.9) + analogRead(A0) * 0.1; // will be 0-1023
  readShoulder = (readShoulder * 0.9) + analogRead(A1) * 0.1;
  readElbow = (readElbow * 0.9) + analogRead(A2) * 0.1; 
  readGripper = (readGripper * 0.9) + analogRead(A3) * 0.1;

  // map input to target positions
  pos_base = map(readBase, 0, 1023, LOW_POS, HIGH_POS);
  base.write(pos_base);
  
  pos_shoulder = map(readShoulder, 0, 1023, HIGH_POS, LOW_POS);
  shoulder.write(pos_shoulder);
  
  pos_elbow = map(readElbow, 0, 1023, LOW_POS, HIGH_POS);
  elbow.write(pos_elbow);
    
  pos_gripper = map(readGripper, 0,1023, 0, 90); //gripper pos between 0 and 90 degrees
  gripper.write(pos_gripper);

  // print positions
  printPositions();
  // make it a bit slower
  delay(10);
}

// printPositions: prints position of each joint in following oder:
// B: base | S: shoulder | E: elbow | G: gripper

void printPositions()
{
  outputcounter++;
  if(outputcounter < 30)
  {
    return;
  }
  Serial.print("B: ");
  Serial.print(pos_base);
  Serial.print(" | S: ");
  Serial.print(pos_shoulder);
  Serial.print(" | E: ");
  Serial.print(pos_elbow);
  Serial.print(" | G: ");
  Serial.println(pos_gripper);
  outputcounter = 0;
}