One of the main objectives of robotics is to articulate objects. And to do this, we can also act on the articulation of objects via user action directly on the system. In this article, we’ll look at how to program the Arduino board to drive servo motors with a joystick.

Prerequisites: Controlling a servomotor with Arduino, Using a joystick with Arduino

Hardware

  • Computer
  • Arduino Nano board
  • USB cable to connect Arduino board to PC
  • 4x female cables
  • 2x servomotor SG90 9G
  • 1x Modulo KYO-23 joystick

Connection diagram

The servomotors are supplied with voltage across the brown wires of the common ground (GND) and the red wires of the voltage source (5V). Here, they are connected directly to the Arduino Nano’s 5V voltage source, and are driven by PWM signals sent to the orange wires (terminal 5 and terminal 6). The joystick, also powered by a 5V voltage and common ground (GND), returns two analog values that we’ll connect to the microcontroller’s analog inputs. It also returns a digital value which we connect to pin 2 of the microcontroller.

Cablage-Tuto-Joystick-Servo Controlling servomotors with an Arduino joystick

Explanations and Code

We’re going to program the Arduino Nano board so that the Joystick directs the direction of rotation of the motors. To do this, we’ll interpret our 2 servo-motor connection as 2 motorized wheels whose direction depends on 2 analog values returned by the Joystick. These two values correspond to the Joystick’s YX coordinates, each ranging from 0 to 1023. To understand the logic of our program, we can represent it with a diagram on a YX plane:

Plan-pour-Joystick-1 Controlling servomotors with an Arduino joystick

Here, we can see that the Joystick coordinates correspond to system states, which in turn correspond to directives for controlling our servo-motors.

This joystick has a built-in pushbutton, which we’ll simply program to display a message when pressed. To control the servomotors, we’ll use the ServoLib.h library (which can be found at the end of this article).

// headers for library
#include <Arduino.h> //only used on PlatformIO IDE on VSCode. Not needed on Arduino IDE.
#include <ServoLib.h>

// function's prototypes used
void printing_results(int x, int y);
void motor_joystick_controlled(int xValue,int yValue);
void MotorAvancer();
void MotorReculer();
void MotorDroite();
void MotorGauche();
void MotorAvancerDroite();
void MotorAvancerGauche();
void MotorReculerDroite();
void MotorReculerGauche();
void MotorStop();

// declaration of pins used
#define joyX A1
#define joyY A2
const int joyBtn  = 2;

// Motors definition
ServoLib motorG;
const int motorGPin = 5;
const int motorGZero = 1500;
ServoLib motorD;
const int motorDPin = 6;
const int motorDZero = 1400;

// Variables
int Power = 400; //Motor velocity

// initialization
void setup() 
{ 
  Serial.begin(9600); // open to communicate via serial port
  motorG.associePin(motorGPin);// pins assignment ...
  motorD.associePin(motorDPin);// ... for right and left motors
  pinMode(joyBtn,INPUT_PULLUP); // button initialization (TOR = 0 or 1)
  MotorStop(); // stop motors as initial state
}
 
// main loop 
void loop() 
{
  int xValue = analogRead(joyX); // read joystick ...
  int yValue = analogRead(joyY); // ... position
  printing_results(xValue, yValue); // display of Joystick coordinates
  motor_joystick_controlled(xValue,yValue); // definition of the state of the motors 
                                            //according to the coordinates of the joystick
}

// function to display the position of the joystick as well as the state of the button if pressed
void  printing_results(int x, int y) // only used for debugging/monitoring the response of the joystick
{ Serial.print(x);
  Serial.print("\t");
  Serial.println(y);
  if (!digitalRead(joyBtn)) 
  {Serial.println(F("Joy Button pressed"));}//if the button is pressed, display "Joy Button pressed"
}

// motor status montoring function based on XY coordinates
void motor_joystick_controlled(int xValue,int yValue)
{
  if(639<xValue && 382<yValue && yValue<639)
  { MotorAvancer();
    Serial.println(F("avancer"));} //only used for debugging/monitoring the state of the motors

  else if(xValue<382 && 382<yValue && yValue<639)
  { MotorReculer();
    Serial.println(F("reculer"));} //only used for debugging/monitoring the state of the motors

  else if(382<xValue && xValue<639 && 639<yValue)
  { MotorDroite();
    Serial.println(F("tourner vers la droite"));} //only used for debugging/monitoring the state of the motors

  else if(382<xValue && xValue<639 && yValue<382)
  { MotorGauche();
    Serial.println(F("tourner vers la gauche"));} //only used for debugging/monitoring the state of the motors
    
  else if(639<xValue && 639<yValue)
  { MotorAvancerDroite();
    Serial.println(F("avancer et tourner a droite"));} //only used for debugging/monitoring the state of the motors

  else if(639<xValue && yValue<382)
  { MotorAvancerGauche();
    Serial.println(F("avancer et tourner a gauche"));}//only used for debugging/monitoring the state of the motors
    
  else if(xValue<382 && 639<yValue)
  { MotorReculerDroite();
    Serial.println(F("reculer et tourner à droite"));}//only used for debugging/monitoring the state of the motors

  else if(xValue<382 && yValue<382)
  { MotorReculerGauche();
    Serial.println(F("reculer et tourner a gauche"));}//only used for debugging/monitoring the state of the motors

  else
  { MotorStop();
    Serial.println(F("arret totale"));}//only used for debugging/monitoring the state of the motors
}

// function for forward state
void MotorAvancer()
{ motorG.appliquerImpulsion(motorGZero + Power);
  motorD.appliquerImpulsion(motorDZero - Power);}

// function for backward state
void MotorReculer()
{ motorG.appliquerImpulsion(motorGZero - Power);
  motorD.appliquerImpulsion(motorDZero + Power);}

// function for turn right state
void MotorDroite()
{ motorG.appliquerImpulsion(motorGZero + Power);
  motorD.appliquerImpulsion(motorDZero + Power);}

// function for turn left state
void MotorGauche()
{ motorG.appliquerImpulsion(motorGZero - Power);
  motorD.appliquerImpulsion(motorDZero - Power);}

// function for turn right and forward state
void MotorAvancerDroite()
{ motorG.appliquerImpulsion(motorGZero + Power);
  motorD.appliquerImpulsion(motorDZero);}

// function for turn left and forward state
void MotorAvancerGauche()
{ motorG.appliquerImpulsion(motorGZero);
  motorD.appliquerImpulsion(motorDZero - Power);}

// function for turn right and backward state  
void MotorReculerDroite()
{ motorG.appliquerImpulsion(motorGZero - Power);
  motorD.appliquerImpulsion(motorDZero);}

// function for turn left and backward state  
void MotorReculerGauche()
{ motorG.appliquerImpulsion(motorGZero);
  motorD.appliquerImpulsion(motorDZero + Power);}

// function for stop state
void MotorStop()
{ digitalWrite(motorDPin,LOW);
  digitalWrite(motorGPin,LOW);}

Results

Once the code has been loaded into the microcontroller, you should see the values of X and Y evolve according to the position of the joystick, as well as the states of the corresponding motors and the message displayed when the button is pressed, on the serial port.

Sortie-Joystick-Servo-1 Controlling servomotors with an Arduino joystick

ServoLib.h library

To use this library, you need to create and place the following files in a folder created in the library directory of your programming software:

arduino-library-folder Controlling servomotors with an Arduino joystick
Pour Arduino IDE.

If you program in ¨Visual Studio Code with PlatformIO IDE, you will find your project library here: DocumentsPlatformIO\Projects\Servo_Joystick_Control\lib\ServoLib

image-11 Controlling servomotors with an Arduino joystick

ServoLib.cpp file :

//Librairie
#include <Servo.h>
//Déclaration des constantes
#define UPDATE_TIME 15
#define MAX_POS 180
#define MIN_POS 0
//Déclaration des paramètres
int servoPin = 9;
int pulse = 1500;
//Déclaration des variables
Servo myServo;  // création d'un objet Servo
// Sur la plupart des cartes, on peut créer jusqu'à douze objets
int pos=0;    // variable contenant la position du servomoteur
void setup() {
  myServo.attach(servoPin);
}
void loop() {
  for (pos = MIN_POS; pos <= MAX_POS; pos += 1) {
    myServo.write(pos);
    delay(UPDATE_TIME);
  }
  for (pos = MAX_POS; pos >= MIN_POS; pos -= 1) {
    myServo.write(pos);
    delay(UPDATE_TIME);
  }
}

ServoLib.h file :

#include <ServoLib.h>
ServoLib::ServoLib(){}
void ServoLib::associePin(int pin){
  servoPin=pin;
  pinMode(servoPin,OUTPUT);
}
void ServoLib::envoiePosition(int value){
  int pulse=0;
  if (value<MIN_POS)
    value=MIN_POS;
  else if (value>MAX_POS)
    value=MAX_POS;
  value=map(value,MIN_POS,MAX_POS,MIN_PULSE_WIDTH,MAX_PULSE_WIDTH);
  pulse=this->convertirAngleEnImpulsion(value);
  this->appliquerImpulsion(pulse);
}
void ServoLib::appliquerImpulsion(int pulse){
  digitalWrite(servoPin,HIGH);
  delayMicroseconds(pulse);
  digitalWrite(servoPin,LOW);
  delay(UPDATE_TIME);
}
int ServoLib::convertirAngleEnImpulsion(int ang){
  float a = 2500/180;
  float b = 500;
  return int(a*ang+b);
}

The keyword.txt file is optional. It is used to modify the color of function names in the program.

#######################################
# Syntax Coloring Map ServoLib
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
ServoLib  KEYWORD1  ServoLib
#######################################
# Methods and Functions (KEYWORD2)
#######################################
associePin KEYWORD2
envoiePosition  KEYWORD2
convertirAngleEnImpulsion KEYWORD2
appliquerImpulsion KEYWORDS2
#######################################
# Constants (LITERAL1)
#######################################
UPDATE_TIME LITERAL1
MIN_POS LITERAL1
MAX_POS LITERAL1
MIN_PULSE_WIDTH LITERAL1
MAX_PULSE_WIDTH LITERAL1

Applications

  • Create a remote control to drive your microcontroller
  • Use one or more joysticks to control a robot arm articulated by servomotors

References