Arduino library 路 Communication

The Arduino Joystick Library: make a USB game controller

Matthew Heironimus鈥檚 Arduino Joystick Library makes a Leonardo, Micro or Pro Micro show up on the computer as a game controller, with no driver. It is what most DIY fight sticks, hit boxes, button boxes, flight panels, pedals and sim-racing shifters are built on, because the ATmega32U4 can be a USB device itself.

Built in on Mokxi: #include <Joystick.h> and it builds, with nothing to install. Free, and no account needed.

What Arduino Joystick Library does

You describe the controller you want in the Joystick_ constructor: how many buttons (up to 32), how many hat switches (up to 2), and which axes exist, from X and Y through the rudder, throttle, brake and steering. The library builds the USB descriptor to match, so the computer鈥檚 game controller settings show exactly those inputs.

Each axis takes values in a range you set with setXAxisRange() and the like, 0 to 1023 by default to match analogRead(). By default every change is sent at once; begin(false) holds the changes until sendState(), which sends one report per pass of loop() however many buttons changed.

Library Manager nameArduino Joystick Library
AuthorMatthew Heironimus
Include#include <Joystick.h>
BoardsArduino Leonardo, SparkFun Pro Micro, Arduino Micro
In the simulatorRuns, with a gap the note below names.

Install it

It builds on Arduino Leonardo, SparkFun Pro Micro, Arduino Micro.

In Mokxi it is built in: #include <Joystick.h> and the sketch compiles, with no download, no zip and no Library Manager.

On a real board, install "Arduino Joystick Library" by Matthew Heironimus from the Arduino IDE鈥檚 Library Manager (Sketch, Include Library, Manage Libraries), or add Arduino Joystick Library to lib_deps in PlatformIO. Mokxi follows the calls of release 2, so a sketch written here builds there too.

What to know in the simulator: There is no computer on the other end: the reports are shown on Serial.

Arduino Leonardo 路 SparkFun Pro Micro 路 Arduino Micro

The calls you will use

In the order a sketch meets them.

Joystick_ Joystick;

A controller with every input the library offers: 32 buttons, 2 hats and all the axes. Most sketches pass arguments to ask only for what they have.

Joystick.begin()

Starts the controller. begin(false) turns off automatic sending so the sketch calls sendState() itself.

Joystick.setButton(0, value)

Sets button 0 (counted from zero) to pressed (1) or released (0).

for (int i = 0; i < 8; i++) {
  Joystick.setButton(i, digitalRead(2 + i) == LOW);
}

Joystick.setXAxis(analogRead(A0))

Sets the X axis within its range; setYAxis(), setZAxis(), setThrottle() and the rest work the same way.

Joystick.setXAxisRange(0, 1023)

Sets the values that mean full left and full right, for a sensor that does not read 0 to 1023.

Joystick.setHatSwitch(0, 90)

Sets a hat (the d-pad) in degrees: 0 up, 90 right, 180 down, 270 left, -1 released.

Joystick.pressButton(3)

Presses one button; releaseButton(3) lets it go.

Joystick.sendState()

Sends the current state of everything in one report, used with begin(false).

Example: Dance pad on a Pro Micro

On the SparkFun Pro Micro. It runs in the browser exactly as printed.

sketch.ino 路 SparkFun Pro Micro
// Dance pad on a Pro Micro: four FSR panels, a lamp for each, and a USB
// gamepad report through the Arduino Joystick library's API (Joystick.h).
//
// The panels. Each arrow has a force sensing resistor (FSR) under it: thin,
// cheap and with no moving parts, which is why so many DIY pads and the open
// firmware written for them use FSRs. Here each FSR runs from its analog pin
// to ground, with the chip's internal pull-up as the top half of the
// divider. Standing on the panel drops the FSR's resistance and pulls the pin
// down, so the sketch turns the reading over: pressure = 1023 - reading.
//
//   left A3   down A2   up A1   right A0      lamps on pins 2, 3, 4 and 5
//
// The internal pull-up is somewhere between 20k and 50k and differs from chip
// to chip, so thresholds tuned on one board can be off on another. Many pad
// builds use a fixed resistor instead (a 10k from each pin to 5 V does the
// same job); set USE_INTERNAL_PULLUP to false when you add them.
//
// In Mokxi: drag a pressure pad's force up to stand on that panel. The Serial
// Monitor shows each step and the pressure that set it off.
//
// Two thresholds per panel. A panel turns on when its pressure rises past
// PRESS and off only when it falls below PRESS minus RELEASE_GAP. With one
// threshold, a foot resting near the line would flicker the arrow on and off
// many times a second (in the game, a burst of steps). The gap is the cure.
//
// Not simulated: the USB connection to a PC. Mokxi models this chip's USB as
// a serial pipe, so each report Joystick.h would send prints as a [Joystick]
// line instead. Flash it to a real Pro Micro and the pad is a gamepad with
// four buttons, which a rhythm game such as StepMania can map to the arrows.

#include <Joystick.h>

// ---------------------------------------------------------------------------
// Make it yours
// ---------------------------------------------------------------------------

const uint8_t PANEL_PINS[4] = {A3, A2, A1, A0};
const uint8_t LAMP_PINS[4] = {2, 3, 4, 5};
const char *const PANEL_NAMES[4] = {"LEFT", "DOWN", "UP", "RIGHT"};

// The pressure (0 to 1023) at which each panel counts as stepped on. Lower is
// more sensitive. Tune each panel by itself: the FSRs, the panel's weight and
// how it sits all differ, which is why pads keep one number per arrow.
int PRESS[4] = {600, 600, 600, 600};
// How far below PRESS a panel has to fall to let go.
const int RELEASE_GAP = 80;

// False when each panel has its own pull-up resistor to 5 V.
const bool USE_INTERNAL_PULLUP = true;

// Readings averaged per panel per pass, to steady a noisy sensor.
const uint8_t SAMPLES = 4;

// Lamps: true lights a panel while it is stepped on, false leaves them off.
const bool LAMPS_FOLLOW_STEPS = true;

// Print every panel's pressure twice a second, to find PRESS values.
const bool SHOW_PRESSURE = false;

// ---------------------------------------------------------------------------

Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID, JOYSTICK_TYPE_GAMEPAD, 4, 0,
                   false, false, false, false, false, false,
                   false, false, false, false, false);

bool down[4];
unsigned long lastShown = 0;

int pressure(uint8_t p) {
  long sum = 0;
  for (uint8_t s = 0; s < SAMPLES; s++) {
    sum += analogRead(PANEL_PINS[p]);
  }
  return 1023 - (int)(sum / SAMPLES);
}

void setup() {
  for (uint8_t p = 0; p < 4; p++) {
    pinMode(PANEL_PINS[p], USE_INTERNAL_PULLUP ? INPUT_PULLUP : INPUT);
    pinMode(LAMP_PINS[p], OUTPUT);
    digitalWrite(LAMP_PINS[p], LOW);
  }
  Serial.begin(115200);
  Joystick.begin(false);
  Serial.println("Dance pad ready: step on a panel.");
  Joystick.sendState();
}

void loop() {
  bool changed = false;
  int force[4];
  for (uint8_t p = 0; p < 4; p++) {
    force[p] = pressure(p);
    bool now = down[p] ? force[p] >= PRESS[p] - RELEASE_GAP : force[p] >= PRESS[p];
    if (now != down[p]) {
      down[p] = now;
      changed = true;
      Serial.print(PANEL_NAMES[p]);
      Serial.print(now ? " step (" : " lift (");
      Serial.print(force[p]);
      Serial.println(")");
      Joystick.setButton(p, now);
      digitalWrite(LAMP_PINS[p], (now && LAMPS_FOLLOW_STEPS) ? HIGH : LOW);
    }
  }
  if (changed) {
    Joystick.sendState();
  }
  if (SHOW_PRESSURE && millis() - lastShown >= 500) {
    lastShown = millis();
    for (uint8_t p = 0; p < 4; p++) {
      Serial.print(PANEL_NAMES[p]);
      Serial.print(' ');
      Serial.print(force[p]);
      Serial.print(p < 3 ? "  " : "\n");
    }
  }
}

Open it in the editor, wired and ready to run 路 Dance pad on a Pro Micro: the project page

Common errors and how to fix them

'Joystick_' does not name a type

The library is not installed or the include is missing. Install "Joystick" by Matthew Heironimus (it is on GitHub rather than in every Library Manager list) and put #include <Joystick.h> at the top.

The controller shows up but buttons light up the wrong numbers

Buttons count from 0 in the sketch and usually from 1 in the computer鈥檚 controller settings, so button 0 shows as button 1. Map them in the game rather than renumbering the wiring.

Joystick.h will not build for an Uno or Nano

Only boards whose chip is its own USB device can be a game controller. Use a Leonardo, Micro or Pro Micro (ATmega32U4).

Inputs feel laggy when many buttons change at once

With automatic sending on, every setButton() sends a report. Call Joystick.begin(false), set everything, then call sendState() once per loop().

Try Arduino Joystick Library without installing anything

Open the example, change the sketch, and keep your version in a free account.