Wireless Joystick Hookup Guide

Pages
Contributors: Alex the Giant
Favorited Favorite 0

Introduction

The Wireless Joystick Kit provides an easy way to control your next XBee project. Before the Wireless Joystick, radio controlled projects used hobby RC transmitters, the same that are used for RC cars, boats, and planes. The problem with these transmitters is many aren’t customizable, and the ones that are, tend to be too expensive for many of us. The Wireless Joystick aims to bring a custom wireless solution for those that want to control their project the way they want to, not the way they’re forced to.

SparkFun Wireless Joystick Kit

KIT-14051
34.95
2

Suggested Reading

Before getting started, you may find the following links useful:

Kit Overview

Let’s go over the Wireless Joystick Kit in detail. The kit comes with the following:

The following is not provided in the kit and will need to be purchased separately.

Picking the right battery depends on the use, but we recommend using at least a 400 mAh battery. If you’ve never used Xbee before, it’s also recommended to use a Series 1 XBee, or check out our XBee buying guide.

Wireless Joystick Board Overview

The Wireless Joystick board comes with the following:

  • SAMD21 Microcontroller
  • Adjustable battery charger (500mA max charge rate)
  • MAX17043G LiPo fuel gauge
  • Programmable LED connected to D13
  • Two trigger buttons
  • Room for 2 Thumb Joysticks, or 1 Thumb Joystick and 4 push buttons

Hardware Hookup

The first step is to solder in the right angle tactile buttons. These are meant to inserted from the bottom the board and soldered to the top layer of PCB. This will look like the image below.

Bottom view close up of trigger buttons

The way the rest of the board is soldered really depends on what it will be used for. There are few different configurations for the joystick(s) and buttons. Read on to see he different configurations.

Dual Joysticks

In this configuration, the Wireless Joystick uses both joysticks and is perfect for tank steering robots. Tank steering maps the vertical position of the left and right joysticks to the speed and direction of the left and right motors of a robot. After soldering in the joysticks, the board should look like this:

Dual Joysticks

Single Joystick

In this configuration, the Wireless Joystick uses a single joystick on the left and 4 of our 12mm momentary pushbuttons on the right. This setup is similar to what older console game consoles used. After soldering in the joystick and switches, the Wireless Joystick board will look like the image below.

Gamepad configuration

A quick tip about soldering the joystick(s): Sometimes during shipping, the pins on the joystick can be bend slightly. Before trying to insert the pins to the board, make sure all of the pins look straight and run parallel with the others. When soldering the joysticks, start with the pins on the vertical and horizontal potentiometers, and use a pair of tweezers to motivate the pins into place and work my way to the select button.

Mounting the battery

To secure the battery to the board, we recommend using a small piece of foam double sided tape. We’ve found the easiest place to put the battery is under the right joystick. Before mounting the battery, make sure to trim the the joystick solder joints to avoid puncturing the battery!

trimmed solder joints

Battery taped

Battery mounted

Setting the Battery Charge Rate

By default, the charge rate is set to the maximum rate of 500mA. If you’re battery is larger than 500mAh, you can skip ahead to the Arduino Examples section. If you’re using less than a 500mAh battery, you should solder in the appropriate resistor that we’ll determine below.

The life of a lithium battery is dependent on a few factors: number of charge/discharge cycles, charge/discharge rate, battery temperature, as well as a few others. When charging a lithium battery, it’s recommended not to exceed a 1C charge rate. For example, if you have a 400mAh battery, your current to charge the battery should not exceed 400mA. To change the rate, change the solder jumper so that the middle and R_PROG pads are shorted and solder in the appropriate resistor. To calculate the right resistor, use the equation below:

R_PROG = 1000 / I_PROG

R_PROG = Resistor value in kohms

I_PROG = Desired current value in mA

To charge the battery, simply plug in the micro USB cable, and move the switch to the OFF position. If your charge rate is below 200mA, the board should charge without issue regardless of the power switch position. Faster charge rates may require the switch to be off to cut current to everything but the charging circuit if charging from a computer’s USB port or small USB chargers.

Arduino Examples

Now that the hardware is all set, lets look at some software examples. Before we get started though, make sure you have both the Arduino SAMD and the Sparkfun SAMD board definitions installed. If you need some help with this, check out the SAMD21 Mini/Dev Breakout Hookup Guide .

Tank Steering Motor Controller Example

For our first example, let’s try controlling a robot using tank steering. To use tank steering, you’ll need to solder in both joysticks. This examples uses the following parts.

To connect everything, start by soldering the “+” pin of the male deans connector to the motor driver pin that says “MAX 11V” as well as to the 5V pin on the XBee Explorer. Connect the “-” pin of the male deans connector to “GND” on both the motor driver, and XBee Explorer. Next we’ll connect the “DOUT” pin of the explorer to the “RX” pin of the motor driver. Solder the motors to the B1/B2 and A1/A2, but be sure the solder the second motor opposite of the first, so that they’ll both be spinning in the same direction. The wheels attach to the motors with a friction fit, so carefully push those onto the motor’s D-shaft. Finally, attach the XBees to the Wireless Joystick, as well as the to the Xbee Explorer.

Adding the Code to the Wireless Joystick

Now that we have everything wired up and soldered together, let’s put some code on the Wireless Joystick! To use this example, copy the code below to the Arduino IDE. Make sure you select the SparkFun SAMD21 Dev Breakout as your board.

language:c
/* Wireless Joystick Tank Steering Robot Example
 * by: Alex Wende
 * SparkFun Electronics
 * date: 9/28/16
 * 
 * license: Creative Commons Attribution-ShareAlike 4.0 (CC BY-SA 4.0)
 * Do whatever you'd like with this code, use it for any purpose.
 * Please attribute and keep this license.
 * 
 * This is example code for the Wireless Joystick to control a robot
 * using XBee. Plug the first Xbee into the Wireless Joystick board,
 * and connect the second to the SparkFun Serial Motor Driver.
 * 
 * Moving the left and right joystick up and down will change the
 * speed and direction of motor 0 and motor 1. The left trigger will
 * reduce the maximum speed by 5%, while the right trigger button
 * will increase the maximum speed by 5%.
 * 
 * Connections to the motor driver is as follows:
 * XBee - Motor Driver
 *   5V - VCC
 *  GND - GND
 * DOUT - RX
 * 
 * Power the motor driver with no higher than 11V!
 */

#define L_TRIG 6        // Pin used for left trigger
#define R_TRIG 3        // Pin used for right trigger
#define L_JOYSTICK A3   // Pin used for left joystick
#define R_JOYSTICK A0   // Pin used for right joystick

int8_t speedLevel = 20; //Maximum speed (%) = speedLevel * 5 (units are percent)

void setup() {  
  Serial1.begin(9600); // Start serial communication with XBee at 9600 baud
  delay(10);

  Serial1.print("W7001\r\n"); // Set the bit in enable register 0x70 to 0x01

  pinMode(L_TRIG,INPUT_PULLUP); // Enable pullup resistor for left trigger
  pinMode(R_TRIG,INPUT_PULLUP); // Enable pullup resistor for right trigger
}

void loop() {
  int16_t leftStick, rightStick;    // We'll store the the analog joystick values here
  char buf0[10],buf1[10]; // character buffers used to set motor speeds

  // Reduce top speed
  if(digitalRead(L_TRIG) == 0)
  {
    speedLevel -= 2;
    if(speedLevel < 2) speedLevel = 2;
    while(digitalRead(L_TRIG) == 0)
    {
      delay(2);
    }
  }
  // Increase top speed
  if(digitalRead(R_TRIG) == 0)
  {
    speedLevel += 2;
    if(speedLevel > 20) speedLevel = 20;
    while(digitalRead(R_TRIG) == 0)
    {
      delay(2);
    }
  }

  // Read joysticks
  // Convert analog value range to motor speeds (in %)
  leftStick = (5-(analogRead(L_JOYSTICK)/93))*speedLevel;
  rightStick = (5-(analogRead(R_JOYSTICK)/93))*speedLevel;

  // Build motor 0 buffer
  if(leftStick > 0)
  {
    sprintf(buf0,"M0F%d\r\n",leftStick);
  }
  else
  {
    sprintf(buf0,"M0R%d\r\n",abs(leftStick));
  }

  // Build motor 1 buffer
  if(rightStick > 0)
  {
    sprintf(buf1,"M1F%d\r\n",rightStick);
  }
  else
  {
    sprintf(buf1,"M1R%d\r\n",abs(rightStick));
  }

  // Send motor speeds
  delay(5);
  Serial1.print(buf0);
  delay(5);
  Serial1.print(buf1);
}

Plug in the battery to power the motor driver and receiving XBee and turn on the Wireless Joystick. Moving the left stick should move the left motor, and the right stick should move the right motor. If your left stick is controlling the right motor (or visa versa), swap the pin values for the L_JOYSTICK and R_JOYSTICK at the top of the sketch.

You can slow down the speed of the motors by pressing the left trigger button, and speed up the motor by pressing the right trigger button.

USB Example

In order to program a microcontroller from a computer, many microcontrollers like the ATMega328, require another IC to bridge USB to the microcontroller’s UART. Other microcontrollers, like the SAMD21 used on the Wireless Joystick, come with native USB, which means there isn’t any need for the bridge IC. Having native USB allows us to program the microcontroller and imitate USB devices like keyboards, mice, and gaming joysticks.

In this example, we’ll program the Wireless Joystick to help us play a classic game, Asteroids. This example can use either the dual joystick or the single joystick configuration. Let’s first upload the code below to our board by copying and pasting it into the Arduino IDE. Make sure you select the SparkFun SAMD21 Dev Breakout as your board. After the code has finished transferring to board, go back to the webpage that has the game on it. Click start and try it out!

language:c
/* Not So Wireless Wireless Joystick USB Example
 * by: Alex Wende
 * SparkFun Electronics
 * date: 9/28/16
 * 
 * license: Creative Commons Attribution-ShareAlike 4.0 (CC BY-SA 4.0)
 * Do whatever you'd like with this code, use it for any purpose.
 * Please attribute and keep this license.
 * 
 * This example sends ASCII arrow key characters over USB when the left
 * joystick is moved or a space character when right trigger button is pressed.
 */

#include "Keyboard.h"

#define H_JOYSTICK    A2
#define V_JOYSTICK    A3
#define R_TRIGGER     3

void setup() {
  pinMode(R_TRIGGER, INPUT_PULLUP);
  Keyboard.begin();
}

void loop() {
  uint16_t hStick = analogRead(H_JOYSTICK);
  uint16_t vStick = analogRead(V_JOYSTICK);

  if(hStick > 766) Keyboard.press(KEY_LEFT_ARROW);
  else if(hStick < 255) Keyboard.press(KEY_RIGHT_ARROW);
  else{
    Keyboard.release(KEY_RIGHT_ARROW);
    Keyboard.release(KEY_LEFT_ARROW);
  }

  if(vStick > 766) Keyboard.press(KEY_UP_ARROW);
  else{
    Keyboard.release(KEY_UP_ARROW);
  }

  if(digitalRead(R_TRIGGER) == LOW){
    Keyboard.press(' ');
  }
  else{
    Keyboard.release(' ');
  }
}

You can find a free version of Asteroids here. The controls are pretty simple, you can use the left joystick to rotate your rocketship left and right as well as to accelerate forward. To destroy the asteroids, you press the right trigger button.

Battery Monitoring Example

In this example we’ll program the Wireless Joystick to print out information about our battery, such as the remaining charge and the current battery voltage. This example also makes use of the programmable LED to indicate when our battery is running low and it’s time to recharge. To use this example, copy the code below to the Arduino IDE. Make sure you select the SparkFun SAMD21 Dev Breakout as your board.

language:c
/* Wireless Joystick battery monitoring Example Code
  by: Jim Lindblom and modified by Alex Wende
  SparkFun Electronics
  date: 9/28/16

  license: Creative Commons Attribution-ShareAlike 4.0 (CC BY-SA 4.0)
  Do whatever you'd like with this code, use it for any purpose.
  Please attribute and keep this license.

  This is example code for the MAX17043G chip on the Wireless Joystick.
  The MAX17043G+U is a compact, low-cost 1S LiPo fuel gauge.
  The SAMD21 talks with the MAX17043 over an I2C (two-wire) interface,
  so we'll use the Wire.h library to talk with it.

  It's a silly example. It reads the battery voltage, and its percentage
  full and prints it out over SerialUSB. You probably wouldn't care about
  the battery voltage if you had the Wireless Joystick connected via USB.
  But this code does show you how to configure the MAX17043G, and how to
  read and manipulate the voltage values.
*/
#include <Wire.h>

#define MAX17043_ADDRESS 0x36  // R/W =~ 0x6D/0x6C

// Pin definitions
int alertPin = 7;  // This is the alert interrupt pin, connected to pin 7 on the Wireless Joystick

// Global Variables
float batVoltage;
float batPercentage;
int alertStatus;

void setup()
{
  pinMode(alertPin, INPUT_PULLUP);

  SerialUSB.begin(9600);  // Start hardware SerialUSB
  delay(500);
  SerialUSB.println("Hello World");

  Wire.begin();  // Start I2C
  configMAX17043(32);  // Configure the MAX17043's alert percentage
  qsMAX17043();  // restart fuel-gauge calculations
}

void loop()
{
  batPercentage = percentMAX17043();
  batVoltage = (float) vcellMAX17043() * 1/800;  // vcell reports battery in 1.25mV increments
  alertStatus = digitalRead(alertPin);

  SerialUSB.print(batPercentage, 2);  // Print the battery percentage
  SerialUSB.println(" %");
  SerialUSB.print(batVoltage, 2);  // print battery voltage
  SerialUSB.println(" V");
  SerialUSB.print("Alert Status = ");
  SerialUSB.println(alertStatus, DEC);
  SerialUSB.println();
  delay(1000);
}

/*
vcellMAX17043() returns a 12-bit ADC reading of the battery voltage,
as reported by the MAX17043's VCELL register.
This does not return a voltage value. To convert this to a voltage,
multiply by 5 and divide by 4096.
*/
unsigned int vcellMAX17043()
{
  unsigned int vcell;

  vcell = i2cRead16(0x02);
  vcell = vcell >> 4;  // last 4 bits of vcell are nothing

  return vcell;
}

/*
percentMAX17043() returns a float value of the battery percentage
reported from the SOC register of the MAX17043.
*/
float percentMAX17043()
{
  unsigned int soc;
  float percent;

  soc = i2cRead16(0x04);  // Read SOC register of MAX17043
  percent = (byte) (soc >> 8);  // High byte of SOC is percentage
  percent += ((float)((byte)soc))/256;  // Low byte is 1/256%

  return percent;
}

/* 
configMAX17043(byte percent) configures the config register of
the MAX170143, specifically the alert threshold therein. Pass a 
value between 1 and 32 to set the alert threshold to a value between
1 and 32%. Any other values will set the threshold to 32%.
*/
void configMAX17043(byte percent)
{
  if ((percent >= 32)||(percent == 0))  // Anything 32 or greater will set to 32%
    i2cWrite16(0x9700, 0x0C);
  else
  {
    byte percentBits = 32 - percent;
    i2cWrite16((0x9700 | percentBits), 0x0C);
  }
}

/* 
qsMAX17043() issues a quick-start command to the MAX17043.
A quick start allows the MAX17043 to restart fuel-gauge calculations
in the same manner as initial power-up of the IC. If an application's
power-up sequence is very noisy, such that excess error is introduced
into the IC's first guess of SOC, the Arduino can issue a quick-start
to reduce the error.
*/
void qsMAX17043()
{
  i2cWrite16(0x4000, 0x06);  // Write a 0x4000 to the MODE register
}

/* 
i2cRead16(unsigned char address) reads a 16-bit value beginning
at the 8-bit address, and continuing to the next address. A 16-bit
value is returned.
*/
unsigned int i2cRead16(unsigned char address)
{
  int data = 0;

  Wire.beginTransmission(MAX17043_ADDRESS);
  Wire.write(address);
  Wire.endTransmission();

  Wire.requestFrom(MAX17043_ADDRESS, 2);
  while (Wire.available() < 2)
    ;
  data = ((int) Wire.read()) << 8;
  data |= Wire.read();

  return data;
}

/*
i2cWrite16(unsigned int data, unsigned char address) writes 16 bits
of data beginning at an 8-bit address, and continuing to the next.
*/
void i2cWrite16(unsigned int data, unsigned char address)
{
  Wire.beginTransmission(MAX17043_ADDRESS);
  Wire.write(address);
  Wire.write((byte)((data >> 8) & 0x00FF));
  Wire.write((byte)(data & 0x00FF));
  Wire.endTransmission();
}

Using the Extra GPIO

You may have noticed that the SPI, I2C, and other GPIO pins have been broken out. We wanted to breakout the unused pins to allow for any customization that you may want. In this final example, we’ll use the OLED screen to display battery information from the MAX17043 fuel gauge.

Before we look at the code, let’s wire up the OLED Breakout. You’ll need seven wires to connect the OLED Breakout to the Wireless Joystick, their connections are:

Wireless Joystick - OLED Breakout

  • 3.3V - 3V3
  • GND - GND
  • MOSI - SDI
  • SCK - SCK
  • D10 - CS
  • D11 - RST
  • D12 - D/C

Where you place the OLED screen is up to you, but I personally like to use foam doubled sided tape to mount the display on the top of the board by the USB connector.

OLED Example

To use the code below, you’ll want to download the MicroOLED Arduino library first. To download the libary, click the button below, or grab the latest version from our GitHub repository. For more information on how to use the libary, visit the Micro OLED Breakout Hookup Guide.

Download the Arduino Library!

After installing the library, copy the code below to the Arduino IDE. Make sure you select the SparkFun SAMD21 Dev Breakout as your board.

language:c
/* GPIO Example For the Wireless Joystick
 * by: Alex Wende
 * SparkFun Electronics
 * date: 9/28/16
 * 
 * license: Creative Commons Attribution-ShareAlike 4.0 (CC BY-SA 4.0)
 * Do whatever you'd like with this code, use it for any purpose.
 * Please attribute and keep this license.
 * 
 * This example the SparkFun OLED Breakout (LCD-13003) to display
 * the battery's voltage and remaining charge.
 * 
 * Connections:
 * Wireless Joystick - OLED
 * 3.3V - 3V3
 *  GND - GND
 * MOSI - SDI
 *  SCK - SCK
 *  D12 - D/C
 *  D11 - RST
 *  D10 - CS
 */

#include <SPI.h>
#include <Wire.h>
#include <SFE_MicroOLED.h>

#define PIN_RESET 11  // Connect RST to pin 9 (req. for SPI and I2C)
#define PIN_DC    12  // Connect DC to pin 8 (required for SPI)
#define PIN_CS    10 // Connect CS to pin 10 (required for SPI)
#define DC_JUMPER 0

#define MAX17043_ADDRESS 0x36

// Pin definitions
int alertPin = 7;  // This is the alert interrupt pin, connected to pin 7 on the Wireless Joystick
int ledPin = 13;   // This is the pin the led is connected to

// Global Variables
float batVoltage;
float batPercentage;
int alertStatus;

MicroOLED oled(PIN_RESET, PIN_DC, PIN_CS);

void setup()
{  
  oled.begin();  // Start OLED
  Wire.begin();  // Start I2C

  pinMode(alertPin, INPUT_PULLUP);  // Enable pullup resistor
  pinMode(ledPin, OUTPUT);
  configMAX17043(32);  // Configure the MAX17043's alert percentage
  qsMAX17043();  // restart fuel-gauge calculations

  oled.setFontType(0);  // Set the text to small (10 columns, 6 rows worth of characters)
}

void loop()
{
  batPercentage = percentMAX17043();  // Get battery percentage
  batVoltage = (float) vcellMAX17043() * 1/800;  // vcell reports battery in 1.25mV increments
  alertStatus = digitalRead(alertPin);

  oled.clear(PAGE); // clears the screen
  oled.setCursor(0,0);  // move cursor to top left corner
  oled.print(batPercentage, 2);
  oled.println(" %\n");

  oled.print(batVoltage, 2);
  oled.println(" V\n\n");

  oled.print("VBAT : ");
  if(alertStatus == LOW){
    digitalWrite(ledPin, HIGH);
    oled.print("LOW");
  }
  else{
    digitalWrite(ledPin, LOW);
    oled.print("OK");
  }
  oled.display();
  delay(10);  
}

/*
vcellMAX17043() returns a 12-bit ADC reading of the battery voltage,
as reported by the MAX17043's VCELL register.
This does not return a voltage value. To convert this to a voltage,
multiply by 5 and divide by 4096.
*/
unsigned int vcellMAX17043()
{
  unsigned int vcell;

  vcell = i2cRead16(0x02);
  vcell = vcell >> 4;  // last 4 bits of vcell are nothing

  return vcell;
}

/*
percentMAX17043() returns a float value of the battery percentage
reported from the SOC register of the MAX17043.
*/
float percentMAX17043()
{
  unsigned int soc;
  float percent;

  soc = i2cRead16(0x04);  // Read SOC register of MAX17043
  percent = (byte) (soc >> 8);  // High byte of SOC is percentage
  percent += ((float)((byte)soc))/256;  // Low byte is 1/256%

  return percent;
}

/* 
configMAX17043(byte percent) configures the config register of
the MAX170143, specifically the alert threshold therein. Pass a 
value between 1 and 32 to set the alert threshold to a value between
1 and 32%. Any other values will set the threshold to 32%.
*/
void configMAX17043(byte percent)
{
  if ((percent >= 32)||(percent == 0))  // Anything 32 or greater will set to 32%
    i2cWrite16(0x9700, 0x0C);
  else
  {
    byte percentBits = 32 - percent;
    i2cWrite16((0x9700 | percentBits), 0x0C);
  }
}

/* 
qsMAX17043() issues a quick-start command to the MAX17043.
A quick start allows the MAX17043 to restart fuel-gauge calculations
in the same manner as initial power-up of the IC. If an application's
power-up sequence is very noisy, such that excess error is introduced
into the IC's first guess of SOC, the Arduino can issue a quick-start
to reduce the error.
*/
void qsMAX17043()
{
  i2cWrite16(0x4000, 0x06);  // Write a 0x4000 to the MODE register
}

/* 
i2cRead16(unsigned char address) reads a 16-bit value beginning
at the 8-bit address, and continuing to the next address. A 16-bit
value is returned.
*/
unsigned int i2cRead16(unsigned char address)
{
  int data = 0;

  Wire.beginTransmission(MAX17043_ADDRESS);
  Wire.write(address);
  Wire.endTransmission();

  Wire.requestFrom(MAX17043_ADDRESS, 2);
  while (Wire.available() < 2)
    ;
  data = ((int) Wire.read()) << 8;
  data |= Wire.read();

  return data;
}

/*
i2cWrite16(unsigned int data, unsigned char address) writes 16 bits
of data beginning at an 8-bit address, and continuing to the next.
*/
void i2cWrite16(unsigned int data, unsigned char address)
{
  Wire.beginTransmission(MAX17043_ADDRESS);
  Wire.write(address);
  Wire.write((byte)((data >> 8) & 0x00FF));
  Wire.write((byte)(data & 0x00FF));
  Wire.endTransmission();
}

Resources and Going Further

Here are a few helpful links that might help answer any question you may still have regarding the Wireless Joystick:

Going Further

Now that you’ve got the Wireless Joystick working, what are you going to use it for? Need some inspiration, check out the tutorials below!

MiniMoto DRV8830 Hookup Guide

MiniMoto is an I2C-based low-voltage DC motor driver.

Hacking the MiP - ProMini Pack

How to attach the ProMini Pack and begin Hacking the MiP.

Experiment Guide for RedBot with Shadow Chassis

This Experiment Guide offers nine experiments to get you started with the SparkFun RedBot. This guide is designed for those familiar with our SparkFun Inventors Kit and want to take their robotics knowledge to the next level.

Building an Autonomous Vehicle: The Batmobile

Documenting a six-month project to race autonomous Power Wheels at the SparkFun Autonomous Vehicle Competition (AVC) in 2016.