Learn

DIY fight stick on an Arduino Pro Micro

Intermediate

Or see every lesson.

  • 261parts on the bench
  • 31boards running now
  • 1.00xreal time, on every board
Pro Micro: a lever and six buttonslive0.000 s 0.00x
Press Run, then use the arrow keys
Arrow keys work the lever, Z and X are buttons 5 and 6, and the arcade buttons are clicks. Watch the Serial Monitor.

A fight stick is one of the most rewarding things to build with a microcontroller. The electronics are simple: a lever that is four switches, a row of arcade buttons that are one switch each, and a small board that reads them and tells the console or PC what is held. The Pro Micro is a favorite for it because its ATmega32U4 has USB built into the chip, so the same board that reads your buttons can show up on the computer as a game controller.

The circuit above is a stick on a Pro Micro in Mokxi: a four-way lever on pins 2 to 5, four arcade buttons on pins 6 to 9, and two more buttons on pins 1 and 0. Press Run, move the lever with the arrow keys, and click the buttons. The Serial Monitor prints the direction in numpad notation and the buttons held, one line every time something changes.

One thing up front, because it matters: Mokxi does not simulate the USB game controller part. The simulated Pro Micro models its USB as a serial connection to the Serial Monitor and nothing more, so the sketch prints the report it would send instead of sending it. What you can test here is everything before that point: the wiring, the pull-ups, the debounce, the direction logic and the button map. When that is right, you flash the same logic to a real board and add the USB library on the bench.

Why a Pro Micro

Most Arduino boards talk to the computer through a separate USB-to-serial chip, so to the PC they are always a serial port. The ATmega32U4 on the Pro Micro, the Leonardo and the Micro is different: the USB device is on the chip itself, and the sketch decides what the board is. That is why the Arduino Keyboard library and the community Arduino Joystick library work on these boards and not on an Uno. The Joystick library’s own documentation says it needs a 32U4 based board and will not work on an Uno or a Mega.

The Pro Micro also has enough pins. The one Mokxi models, the 5 volt, 16 MHz SparkFun board, brings out 18 input and output pins, and a stick needs four for the lever and one per button. Eight buttons plus a lever is twelve pins, with room left for a start or select button and a mode switch.

It has one surprise for newcomers: there is no LED on pin 13. The two lamps on the board are the RX and TX lights, and they light when their pin goes LOW. The example sketch uses the RX lamp to show that a button is held, so there is feedback on the board itself.

Wiring the lever and the buttons

Every switch on a stick is wired the same way. One leg goes to ground, the other goes to a pin, and the pin’s internal pull-up holds it HIGH until the switch pulls it LOW. So a pressed button reads LOW, and no resistors are needed anywhere. In a real case the grounds are usually one wire daisy-chained from button to button, and that single chain goes back to a GND pin on the board.

A lever is four microswitches, one per direction, around a shaft. Many levers come with a five-wire harness: one ground and one wire each for up, down, left and right. This example uses the console pad’s D-pad in its place, which brings two extra buttons along; its rocker setting is on, which behaves like a lever’s gate. The Pico fight stick template uses Mokxi’s Sanwa-style lever part instead, five-pin harness and all.

Arcade button microswitches often have three tabs: common, normally open and normally closed. Use common and normally open. The normally closed tab is closed when the button is up, which reads backwards with a pull-up. The pull-up page explains why the pin needs one at all.

Debounce without adding lag

Metal contacts bounce. When a switch closes, it opens and closes again a few times in the first milliseconds before it settles. A sketch that reads fast enough sees every one of those edges as a separate press. The arcade buttons in the example have their bounce setting on, so you can see it happen.

The usual Arduino debounce waits until a pin has been steady for a while before believing it. That works, but on a controller it adds the wait to every single press. The example does it the other way around: a change counts the moment it arrives, and the input then ignores its pin for a short lockout, 5 milliseconds in the sketch. The bounce falls inside the lockout and is thrown away, and the press itself is not delayed at all.

We ran it both ways on the simulated stick. With the lockout set to zero, one press and release of a bouncing button printed six lines: press, release, press, and the same again on the way up. With the lockout at 5 milliseconds it printed two, and the press was reported in the same millisecond as before. Gamepad firmware such as GP2040-CE has a debounce setting for the same reason.

The lockout, from the example sketch
bool down = digitalRead(PINS[i]) == LOW;
if (down != held[i] && now - changedAt[i] >= LOCK_MS) {
  held[i] = down;       // accept the change at once
  changedAt[i] = now;   // then ignore this pin for LOCK_MS
}

Numpad notation and a clean direction

Fighting game players write directions as the digits on a keyboard’s number pad: 5 is neutral, 6 is forward for a character facing right, 2 is down, 3 is down and forward, 8 is up, and so on. The example reports the lever the same way, which makes the Serial Monitor readable for anyone who has read a combo list.

The sketch turns the four switches into two axes, left minus right and up minus down, and the direction is 5 plus the horizontal axis plus three times the vertical. If both switches of a pair are ever closed together, the axis comes out as zero, so the direction falls back to neutral. A lever’s gate cannot make that state, but a leverless controller can, and the SOCD cleaning page covers the rules for it.

From the simulator to a real stick

When the Serial Monitor shows the right direction and buttons for every input, the logic is done. Then hand the same state to a HID library where the example calls report(). Mokxi ships the Arduino Joystick library’s API as Joystick.h, and Keyboard.h, for the Pro Micro, Leonardo and Micro: a setButton() call per button and a hat switch or two axes for the lever, followed by sendState(). In the simulator each report is a [Joystick] line on the Serial Monitor; flashed to a real board, the same sketch is a USB game controller.

That last step is the one Mokxi cannot check for you: whether the console or PC accepts the board, and which input mode it expects, depends on the library and the host. Many builders skip writing firmware entirely and flash ready-made gamepad firmware instead. Either way, the wiring you proved here is the wiring you solder.

Questions

Can an Arduino Uno be a fight stick?

Not directly. The Uno’s main chip has no USB of its own, so the Keyboard and Joystick libraries do not run on it. Use a board with an ATmega32U4, such as a Pro Micro, Leonardo or Micro, or an RP2040 board such as a Pico.

Do arcade buttons need resistors?

No. Wire one leg to ground and the other to a pin with INPUT_PULLUP. The chip’s internal pull-up holds the pin HIGH, and a press pulls it LOW.

Does Mokxi simulate the USB controller?

No. The simulated Pro Micro models its USB as a serial connection only, so the example prints its report to the Serial Monitor. You test the wiring and the logic here, then add the USB library and flash a real board.

How long should the debounce be?

Long enough to cover the bounce of your switches and no longer. The example uses a 5 millisecond lockout that starts after the press is accepted, so it does not delay the press.

Build this for real

Open the editor, change a value and watch the number move with it. Nothing to install, and no account needed.