Arcade joystick lever

The lever in a fight stick or an arcade cabinet, Sanwa JLF style: a ball top on a shaft with four microswitches around its foot.

Aliases: arcade stick, Sanwa lever, JLF, 8-way joystick, digital joystick.

It is not the thumb stick in the starter kits. That one is two potentiometers and reads with analogRead; this one is four switches and reads with digitalRead. Push it up and the up switch closes; push it into a corner and two close, which is how a diagonal reaches the board.

Pins

The five-pin harness, left to right:

Pin What it does
GND The common: one leg of every switch. Wire it to ground.
UP Closed to GND while the lever is up, up-left or up-right.
DOWN Closed to GND while the lever is down or in a lower corner.
LEFT Closed to GND while the lever is left or in a left corner.
RIGHT Closed to GND while the lever is right or in a right corner.

On a real JLF the switch that closes for up sits at the bottom of the PCB, because the shaft pushes the far side. The harness is labeled by direction, and so are these pins.

Properties

gate is the restrictor plate under the washer:

  • square, what a JLF ships with: all eight directions.
  • octagon, the common swap: all eight directions, with grooves that guide the lever into the corners. Electrically the same as square.
  • 4way, a plus-shaped slot for maze games: no corners, so never two switches at once.

bounce, off by default, makes each switch chatter once as it closes or opens, inside 2 ms.

While it runs

Drag the ball toward a direction, or use the arrow keys or W A S D. Hold two keys for a diagonal. Let go and it springs back to center.

Reading it on an Arduino

Four pins with their pull-ups on, each LOW while its direction is held:

const int UP_PIN = 2, DOWN_PIN = 3, LEFT_PIN = 4, RIGHT_PIN = 5;

void setup() {
  pinMode(UP_PIN, INPUT_PULLUP);
  pinMode(DOWN_PIN, INPUT_PULLUP);
  pinMode(LEFT_PIN, INPUT_PULLUP);
  pinMode(RIGHT_PIN, INPUT_PULLUP);
}

void loop() {
  bool up = digitalRead(UP_PIN) == LOW;
  bool right = digitalRead(RIGHT_PIN) == LOW;
  // up and right together is the up-right diagonal
}

The Arcade lever template prints the direction in numpad notation, the way fighting game players write it: 5 is neutral, 8 is up, 6 is right, 9 is up-right.

What the model gets right

Four contacts and a common. A closed switch is a low-resistance contact from its pin to GND, passing current either way; an open one leaves its pin floating, so without a pull-up the pin reads nothing useful, as on the real harness.

The shaft cannot be on both sides. Up and down are never closed together, and neither are left and right. If both of an opposed pair are pushed at once (two keys held), the newer one wins, the way the shaft swings over. A fight stick firmware's SOCD cleaning still matters for a hitbox, where four separate buttons can all be down; it does not for a lever.

The 4-way plate. With the lever in one arm of the plus, pushing the other axis too does nothing: the only way into the other arm is back through the center.

Bounce, when it is on: one make, one rebound, one settle per switch that moves, the pattern a snap-action microswitch shows on a scope.

What it does not model

The spring's feel, the engage distance and the throw: the lever is either in a direction or not. Square and octagon gates differ only in the picture. The microswitch's bounce, when on, is the same fixed pattern every time.

Common mistakes

Forgetting INPUT_PULLUP: with nothing pulling the pins up, a released direction floats instead of reading HIGH.

Reading the lever as analog. analogRead on a switch pin gives 0 or 1023 and nothing in between; the thumb stick is the analog one.

See it in action

Arcade lever prints the numpad direction and lights an LED per direction on an Uno. Open it at /templates.