Arduino Projects

How 2-Axis Joystick Works & Interface with Arduino + Processing

When you hear the word Thumb Joystick, the first thing that comes to mind is the game controllers. They are mainly used for playing games, although in DIY Electronics, there are a lot of fun things you can do with it. Like controlling a robot/a rover, controlling the movement of camera; these are just the tip of the iceberg.

Hardware Overview

This is a joystick very similar to the ‘analog’ joysticks on PS2 (PlayStation 2) controllers. It is a self-centering spring loaded joystick, meaning when you release the joystick it will center itself. It also contains a comfortable cup-type knob/cap which gives the feel of a thumb-stick.

PS2 Joystick Module 2 Potentiometers Internal Structure

The goal of the joystick is to communicate motion in 2D (2-axis) to an Arduino. This is achieved by housing two independent 10K potentiometers (one per axis). These potentiometers are used as dual adjustable voltage dividers, providing 2-Axis analog input in a control stick form.

The potentiometers are the two blue boxes on the sides of the joystick. If you move the joystick while watching the center shaft of each potentiometer, you’ll see that each of the potentiometers pick up movement in only one direction. We will discuss how they actually work, a little later.

PS2 Joystick Module Push Button Switch Internal Structure

This joystick also contains a switch which activates when you push down on the cap. The switch is the small black box on the rear of the joystick. If you push down on the cap, you can see a lever pushing down on the head of the switch. The lever works no matter what position the joystick is in.

How PS2 2-axis thumb joystick module works?

The basic idea of a joystick is to translate the stick’s position on two axes — the X-axis (left to right) and the Y-axis (up and down) into electronic information an Arduino can process. This can be little tricky, but thanks to the design of the joystick consisting of two potentiometers and a Gimbal Mechanism.

Gimbal Mechanism

2-Axis Joystick Working Gimbal Mechanism

When you rotate the joystick, the thumb handle moves a narrow rod that sits in two rotatable slotted shafts (Gimbal). One of the shafts allows motion in the X-axis (left and right) while the other allows motion in the Y-axis (up and down). Tilting the stick forward and backward pivots the Y-axis shaft from side to side. Tilting it left to right pivots the X-axis shaft. When you move the stick diagonally, it pivots both shafts.

Potentiometer Working In Joystick Module

A potentiometer is connected to each joystick shaft that interprets the position of the rod as analog readings. Moving the slotted shafts rotates the contact arm of the potentiometer. In other words, if you push the stick all the way forward, it will turn the potentiometer contact arm to one end of the track, and if you pull it back toward you, it will turn the contact arm the other way.

Reading analog values from Joystick

In order to read the joystick’s physical position, we need to measure the change in resistance of a potentiometer. This change can be read by an Arduino analog pin using ADC.

As the Arduino board has an ADC resolution of 10 bits, the values on each analog channel (axis) can vary from 0 to 1023. So, if the stick is moved on X axis from one end to the other, the X values will change from 0 to 1023 and similar thing happens when moved along the Y axis. When the joystick stays in its center position the value is around 512.

The graphic below shows the X and Y directions and also gives an indication of how the outputs will respond when the joystick is pushed in various directions.

PS2 Joystick Module Movement Analog Values on Arduino

NOTE

In order to put this thumb joystick to use, you are going to want to understand which direction is X and which direction is Y. You will also need to decipher the direction it is being pushed in either the X or the Y direction.

Thumb Joystick Module Pinout

Let’s have a look at the pinout of PS2 2-axis Thumb Joystick module.

Pinout PS2 Joystick Module

GND is the Ground Pin which we connect the GND pin on the Arduino.

VCC supplies power for the module. You can connect it to 5V output from your Arduino.

VRx gives readout of the joystick in the horizontal direction (X-coordinate) i.e. how far left and right the joystick is pushed.

VRy gives readout of the joystick in the vertical direction (Y-coordinate) i.e. how far up and down the joystick is pushed.

SW is the output from the pushbutton. It’s normally open, meaning the digital readout from the SW pin will be HIGH. When the button is pushed, it will connect to GND, giving output LOW.

Wiring – Connecting Thumb Joystick Module to Arduino UNO

Now that we know everything about the module it is time to put them to use!

As we know, in order to determine the X and Y coordinates of the joystick, we need to connect both analog outputs from the joystick to analog pins on the Arduino. For our Arduino board, we connect VRx to analog pin A0 of the Arduino and VRy to analog pin A1 of the Arduino.

To read whether the joystick knob has been pressed down, we connect the SW pin of the joystick to digital pin D8 of the Arduino.

Other than this, the joystick just simply needs power. Its VCC pin is connected to the 5V terminal of the Arduino and its GND pin is connected to the GND terminal of the Arduino.

Arduino Wiring Fritzing Connections with PS2 2-axis Joystick Module
Wiring PS2 2-axis Joystick Module with Arduino UNO

That’s it. Now you are ready to show your Joystick manipulation skills.

Arduino Code

The program is very simple. We will read the measurement from two analog inputs and one digital input. Then we will display the result on serial monitor.

// Arduino pin numbers
const int SW_pin = 8; // digital pin connected to switch output
const int X_pin = 0; // analog pin connected to X output
const int Y_pin = 1; // analog pin connected to Y output

void setup() {
  pinMode(SW_pin, INPUT);
  digitalWrite(SW_pin, HIGH);
  Serial.begin(9600);
}

void loop() {
  Serial.print("Switch:  ");
  Serial.print(digitalRead(SW_pin));
  Serial.print(" | ");
  Serial.print("X-axis: ");
  Serial.print(analogRead(X_pin));
  Serial.print(" | ");
  Serial.print("Y-axis: ");
  Serial.print(analogRead(Y_pin));
  Serial.println(" | ");
  delay(200);
}

The sketch starts by initializing connections of Joystick module on the Arduino. The SW pin is connected to Arduino Pin#8 while the VRx and VRy pins are connected to Analog pin #0 and #1.

// Arduino pin numbers
const int SW_pin = 8; // digital pin connected to switch output
const int X_pin = 0; // analog pin connected to X output
const int Y_pin = 1; // analog pin connected to Y output

In setup() function: We initialize the SW pin as an input and keep it HIGH. This is because as long as the SW pin is HIGH, we know that the button is not pressed. We also start the serial communication.

  pinMode(SW_pin, INPUT);
  digitalWrite(SW_pin, HIGH);
  Serial.begin(9600);

In loop() function: We merely read the value of SW pin using digitalRead() function, VRx & VRy pin using analogRead() and display on serial monitor.

  Serial.print("Switch:  ");
  Serial.print(digitalRead(SW_pin));
  Serial.print(" | ");
  Serial.print("X-axis: ");
  Serial.print(analogRead(X_pin));
  Serial.print(" | ");
  Serial.print("Y-axis: ");
  Serial.print(analogRead(Y_pin));
  Serial.println(" | ");
  delay(200);

If everything is fine, you should see below output on serial monitor.

PS2 Joystick Module Arduino Sketch Output on serial window
Joystick Module Output On Serial Monitor

Arduino Project

Animating Joystick Movements In Processing IDE

Let’s create a quick Arduino project to demonstrate how a simple 2-Axis Joystick module can be used for controlling animations in Processing IDE. First we will program our Arduino to spit values of x-axis, y-axis and button state on serial port. We will receive these values coming from serial port in Processing IDE. These values can then be used to animate joystick position. Great! Right?

This is how the output looks like.

Arduino Project - Animating Joystick in Precessing IDE
Joystick movement animation in Precessing IDE

Of course this project could be extended to animate characters, surveillance projects or controlling unmanned vehicles.

Arduino Code

To start with, we need to program our Arduino to spit values of x-axis, y-axis and button state on serial port. The program is quite same as above except the values we print on serial monitor are comma-separated. Why comma-separated? Because this is a popular format for transferring data from one application to another. In Processing IDE we can split incoming values by ‘comma’ character and get our data back.

Upload following sketch to your Arduino.

int xValue = 0 ; // read value of the X axis	
int yValue = 0 ; // read value of the Y axis	
int bValue = 0 ; // value of the button reading	

void setup()	
{	
	Serial.begin(9600) ; // Open the serial port
	pinMode(8,INPUT) ; // Configure Pin 2 as input
	digitalWrite(8,HIGH);	
}	

void loop()	
{	
	// Read analog port values A0 and A1	
	xValue = analogRead(A0);	
	yValue = analogRead(A1);	

	// Read the logic value on pin 2	
	bValue = digitalRead(8);	

	// We display our data separated by a comma	
	Serial.print(xValue,DEC);
	Serial.print(",");
	Serial.print(yValue,DEC);
	Serial.print(",");
	Serial.print(!bValue);

	// We end with a newline character to facilitate subsequent analysis	
	Serial.print("\n");

	// Small delay before the next measurement	
	delay(10);	
}

Processing Code

Once the program is uploaded to Arduino, we can start animating Joystick position in Processing IDE. Keep your Arduino plugged and Run following Processing code.

import processing.serial.*; //import the Serial library
Serial myPort;

int x; // variable holding the value from A0
int y; // variable holding the value from A1
int b; // variable holding the value from digital pin 2
PFont f; // define the font variable
String portName;
String val;

void setup()
{
  size ( 512 , 512 ) ; // window size
  
  // we are opening the port
   myPort = new Serial(this, Serial.list()[0], 9600);
  myPort.bufferUntil('\n'); 
  
  // choose the font and size
  f = createFont("Arial", 16, true); // Arial, 16px, anti-aliasing
  textFont ( f, 16 ) ; // size 16px
}

// drawing loop
void draw()
{
  fill(0) ; // set the fill color to black
  clear() ; // clean the screen
  
  fill(255) ; // set the fill color to white
  
  if (b == 1) // check if the button is pressed
  {
    // draw a larger circle with specified coordinates
    ellipse(x/2,y/2, 50, 50);
  } 
  else
  {
    // we draw a circle with a certain coordinates
    ellipse(x/2,y/2, 25, 25);
  }
  
  // we display data
  text("AnalogX="+(1023-x)+" AnalogY="+(1023-y),10,20);
}


// data support from the serial port
void serialEvent( Serial myPort) 
{
  // read the data until the newline n appears
  val = myPort.readStringUntil('\n');
  
  if (val != null)
  {
        val = trim(val);
        
    // break up the decimal and new line reading
    int[] vals = int(splitTokens(val, ","));
    
    // we assign to variables
    x = vals[0];
    y = vals[1] ;
    b = vals[2];

  }
}

Let’s do a quick breakdown. First we need to import serial library for reading values coming on serial port.

import processing.serial.*; //import the Serial library
Serial myPort;

Next, the variables to hold x-axis, y-axis & button state values are declared.

int x; // variable holding the value from A0
int y; // variable holding the value from A1
int b; // variable holding the value from digital pin 2
PFont f; // define the font variable
String portName;
String val;

In Setup function, we need to create a window of size 512×512 to show our animation. Next, we open up an available serial port by passing parameter Serial.list()[0] . If this doesn’t work for you, change it to the port to which Arduino is connected. We also need to create a font for showing our values on the window along with the animation.

  size ( 512 , 512 ) ; // window size
  
  // we are opening the port
   myPort = new Serial(this, Serial.list()[0], 9600);
  myPort.bufferUntil('\n'); 
  
  // choose the font and size
  f = createFont("Arial", 16, true); // Arial, 16px, anti-aliasing
  textFont ( f, 16 ) ; // size 16px

In draw function, at first the background of the window is filled with black color. Then we select a white color for drawing a small circle that represents joystick’s position. Now, depending upon the button state we draw a small or big circle using if statement.

fill(0) ; // set the fill color to black
  clear() ; // clean the screen
  
  fill(255) ; // set the fill color to white
  
  if (b == 1) // check if the button is pressed
  {
    // draw a larger circle with specified coordinates
    ellipse(x/2,y/2, 50, 50);
  } 
  else
  {
    // we draw a circle with a certain coordinates
    ellipse(x/2,y/2, 25, 25);
  }

Next, we print x-axis and y-axis values on the top left corner of the window.

  // we display data
  text("AnalogX="+(1023-x)+" AnalogY="+(1023-y),10,20);

The serialEvent(Serial myPort) is a custom function which reads the string on serial port until the newline character appears. The string is then split by ‘comma’ character and assigned to respective variables.

void serialEvent( Serial myPort) 
{
  // read the data until the newline n appears
  val = myPort.readStringUntil('\n');
  
  if (val != null)
  {
        val = trim(val);
        
    // break up the decimal and new line reading
    int[] vals = int(splitTokens(val, ","));
    
    // we assign to variables
    x = vals[0];
    y = vals[1] ;
    b = vals[2];

  }
}

Problem with Analog Joysticks

There are a couple of small but important problems with the analog joystick system.

  • First of all, the crude analog-to-digital conversion process isn’t very accurate, since the system doesn’t have a true analog-to-digital converter. This compromises the joystick’s sensitivity somewhat.
  • Second, the microcontroller has to dedicate a lot of processing power to regularly “poll” the joystick system to determine the position of the stick. This takes a lot of power away from other operations.