Joystick Library for Arduino

In my previous post, I briefly mentioned that I had released a joystick library for Arduino.  I thought I oughtta expand on that a bit, especially since I use it quite a bit these days.  I’m the CTO of the family landscaping business.  One of the services we provide is snow removal.  As such, we have multiple snowplows.  All of the plows have hand-held controllers that control blade movement (raise, lower, pan left, pan right).  One of the controllers blew up, so we ordered a new one.  The new one blew up too.  So we got copies of the schematics and went over it with our mechanic looking for wiring faults that may be causing the failure.  We couldn’t find anything.  We then manually manipulated the plow by shorting wires in the wiring harness and in all our testing did not encounter any problems.  We had the last controller replaced under warranty.  After a couple of weeks, the replacement blew up too.  So I tore it apart and discovered that it was basically just a microcontroller that read button inputs and then triggered one or more solid-state relays to control the plow.  The problem was, I could not find any protection circuitry on the outputs.  So, when the magnetic fields collapse for the pump motor or solenoids, there is some reverse-biased current that flows back into the system.  Without any suppression circuitry (ie. “clamping” diode) and no reset-able fuses or anything, it eventually damages the board.  Each of these controllers was about $486.00 cost to us.  So eventually, we decided we could build our own better controller, and we could do it significantly cheaper using an Arduino-based solution.

So that is exactly what I did.  All the other controllers used a D-pad arrangement; Soft-membrane buttons for each direction.  I opted for a thumbstick because I felt it would be easier to water-proof (not necessarily true as it turns out) and would be easier to manipulate and potentially allow for finer-grained control of the plow.  So I went with Arduino Uno R3, a Sparkfun ProtoShield , and a SparkFun Thumb Joystick which I soldered to the protoshield.  The thumbstick is essentially just a couple of potentiometers (one for each axis) that act as voltage dividers.  I needed a way to read the analog values for both pots (axis) and then normalize the values to range from -100 -> 0 -> +100.  Naturally, I went looking for an Arduino library that could do this work for me.  I found a library that *almost* met my needs.  The problem with it was it did not process events immediately. Instead it would only emit a single event after processing all the possibilities.  So I decided to write my own library instead.  My library emits *all* events as soon as they are detected.

You can use it as described below.  The first thing you’ll need to do, is download and install the library.  Next, wire up a thumbstick to an Arduino like so:

joystick

 

(Note: You can use whatever analog inputs you want, I just used A0 and A1 as an example. However, you MUST use analog inputs.)

Now, you can calibrate it and start getting readings.  It is important to note that you should not touch the joystick while the sketch is starting up. The calibration process takes current readings from the stick and computes new center, stroke, and threshold values based on these readings.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include "Arduino.h"
#include "JoystickLib.h"
 
Joystick stick(A0, A1);
 
void setup() {
   Serial.begin(9600);
   while(!Serial) {
      delay(10);
   }
 
   // Calibrate the joystick. For this to be effective, the joystick should
   // be resting at its center position.
   stick.calibrate();
}
 
void loop() {
   // Both axis values should be:
   // Center = 0
   // Farthest left (X) or farthest down (Y) = -100
   // Farthest right (X) or farthest up (Y) = 100
   // Or any value in between. Make sure you call loop() first.
   stick.loop();
 
   // Print the X-axis value.
   Serial.print(F("X = "));
   Serial.printlin(stick.getX());
 
   // Print the Y-axis value.
   Serial.print(F("Y = "));
   Serial.println(stick.getY());
   delay(500); 
}

You can also test what direction the joystick is in like so:

 

1
2
3
4
5
6
7
if (stick.isUp()) {
   Serial.println(F("Stick is up"));
}
 
if (stick.isDown()) {
   Serial.println(F("Stick is down."));
}

Or you can handle events. These events include: when the joystick moves in any direction including diagonals and does so in +1 or -1 increments (in other words, a movement of only +1 on the Y-axis would be considered an “up” movement) and when the joystick goes neutral (returns to zero on both axis). The method signature for the event handler callbacks do not currently pass any event objects, so it only needs to be a void:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
void printPosition() {
   Serial.print(F("X: "));
   Serial.println(stick.getX());
   Serial.print(F("Y: "));
   Serial.println(stick.getY());
   Serial.println();
}
 
void onUpHandler() {
   Serial.println(F("Direction: Up"));
   printPosition();
}
 
void onDownHandler() {
   Serial.println(F("Direction: Down"));
   printPosition();
}
 
void setup() {
   Serial.begin(9600);
   stick.calibrate();
   stick.onUp(onUpHandler);
   stick.onDown(onDownHandler);
}
 
void loop() {
   stick.loop();
   delay(300);  // Delay not necessary.  Just added to make it easier to read the output.  
}

Given the sensitive nature of the events, it may be best to determine a threshold value and then compare the axis value against it before reacting to it:

 

1
2
3
4
5
6
7
8
const int AXIS_THRESHOLD = 30;
 
void onUpHandler() {
   if (stick.getY() > AXIS_THRESHOLD) {
      // Do stuff only if the Y-axis is greater than 30.
      // This way we know the movement was more deliberate.
   }
}

For my application, I used event handlers and forced cardinal direction movement only, then fired one or more SSRs based on which direction the user was trying to move the plow. These SSRs, in turn, triggered solenoid(s) and the pump motor (except when lowering the blade) to allow the hydraulic fluid to move the arms.  You can increase or decrease the sample rate by using delay() inside your loop() method.  In the future, this will be option in the library itself.

Have fun and happy hacking!

Facebooktwittergoogle_plusredditpinterestlinkedinby feather
Wednesday, December 2nd, 2015 Arduino, C/C++, Programming

Leave a Reply

You must be logged in to post a comment.