This is a work in progress. Trying to make a working arcade button to USB interface with the Nucleo F411RE.

Dependencies:   USBDevice mbed

This project was inspired by USB Joystick Device

The goal of this project is to build an simple interface that I will connect numerous Arcade buttons and joysticks to the Nucleo board, and the Nucleo will present itself as a USB Gamepad to Windows or Linux. The joysticks are Arcade joysticks that have four switches, up/down/left/right. It is not an analog joystick, just a set of switches. These will be connected to the Nucleo's pins configured as inputs.

The code will then continuously loop and test which buttons are closed, and transfer this data to the host via USBGamepad protocol.

Much thanks to Wim Huiskamp for his original project, documentation, and later reaching out to me to guide me to solving a last minute problem. Wim clued me into the fact that I needed a 1k5 pullup resistor between the 3v3 pin and the PA_12/D+ pin. Once I did this Windows detected the device as a Gamepad and registered it correctly! Yay!

Connecting USB cable to the board is as follows:

You will need a USB data cable (the one I used had a micro usb on one end and regular usb on the other). I cut off the micro USB end, and cut the insulation back about 30mm. This exposed four wires, Red, Black, White and Green. You will then either crimp some header connectors or solder directly to the Nucleo header pins as follows:

  • Green USB D+ to PA_12
  • White USB D- to PA_11
  • Red USB 5V to E5V (with jumper JP5 set to E5V)
  • Black USB GND to GND

As an extra debugging measure, you can connect both the ST/Link USB and the PA_12/11 USB to the Windows machine to run both at the same time, and you can see printf messages from within ST/Link.

We can verify the HID Vendor and Product IDs by looking at the Device Manager, and look for the HID Game Controller:

/media/uploads/thetazzbot/arcade_controller_hid.jpg

I used pid.codes to register my own Vendor_ID and Product_ID

If you go to the USB Game Controller control panel widget, you will see the new entry for Arcade Gamepad:

/media/uploads/thetazzbot/arcade_controller_controlpanel.jpg

And here we can see all 32 buttons:

/media/uploads/thetazzbot/arcade_controller_buttons.jpg

On the Nucleo board you may have difficulties depending on the revision. The board I am using is an STM32F411RE Revision C03, which has resistors and solder joints (bottom) to allow the use of the Crystal on the STLink board for USB purposes. After programming via STLink, remove the USB cable from the STLink, the jumper must be set to E5V to power the board from the PC's usb port. Plug the new cable into the PC.

When you're ready to install it in the arcade cabinet, or project, just remember to setup the jumper JP5 to E5V and you only need the single USB connection to the host.

Here are some useful links that I used to grasp all the little things involved in this project:

Committer:
thetazzbot
Date:
Wed Dec 14 00:49:38 2016 +0000
Revision:
4:05f4ace9508a
Child:
6:29a04fe27b5e
Refactored code for Gamepad vs Joystic, and corrected usb descriptor

Who changed what in which revision?

UserRevisionLine numberNew contents of line
thetazzbot 4:05f4ace9508a 1 /* USBGamepad.h */
thetazzbot 4:05f4ace9508a 2 /* USB device example: Gamepad*/
thetazzbot 4:05f4ace9508a 3 /* Arcade style buttons to USB interface for use with RetroPie */
thetazzbot 4:05f4ace9508a 4
thetazzbot 4:05f4ace9508a 5
thetazzbot 4:05f4ace9508a 6 #ifndef USBGAMEPAD_H
thetazzbot 4:05f4ace9508a 7 #define USBGAMEPAD_H
thetazzbot 4:05f4ace9508a 8
thetazzbot 4:05f4ace9508a 9 #include "USBHID.h"
thetazzbot 4:05f4ace9508a 10
thetazzbot 4:05f4ace9508a 11 #define REPORT_ID_JOYSTICK 4
thetazzbot 4:05f4ace9508a 12
thetazzbot 4:05f4ace9508a 13 // Length of our report. This equates to four bytes (32 bits=32 buttons)
thetazzbot 4:05f4ace9508a 14 // Important: This must be kept in sync
thetazzbot 4:05f4ace9508a 15 // with the actual joystick report format sent in update().
thetazzbot 4:05f4ace9508a 16 #define REPORT_LEN 0x04
thetazzbot 4:05f4ace9508a 17
thetazzbot 4:05f4ace9508a 18 /* Common usage */
thetazzbot 4:05f4ace9508a 19 enum JOY_BUTTON {
thetazzbot 4:05f4ace9508a 20 JOY_B0 = 0x0001,
thetazzbot 4:05f4ace9508a 21 JOY_B1 = 0x0002,
thetazzbot 4:05f4ace9508a 22 JOY_B2 = 0x0004,
thetazzbot 4:05f4ace9508a 23 JOY_B3 = 0x0008,
thetazzbot 4:05f4ace9508a 24 JOY_B4 = 0x0010,
thetazzbot 4:05f4ace9508a 25 JOY_B5 = 0x0020,
thetazzbot 4:05f4ace9508a 26 JOY_B6 = 0x0040,
thetazzbot 4:05f4ace9508a 27 JOY_B7 = 0x0080,
thetazzbot 4:05f4ace9508a 28 JOY_B8 = 0x0100,
thetazzbot 4:05f4ace9508a 29 JOY_B9 = 0x0200,
thetazzbot 4:05f4ace9508a 30 JOY_B10 = 0x0400,
thetazzbot 4:05f4ace9508a 31 JOY_B11 = 0x0800,
thetazzbot 4:05f4ace9508a 32 JOY_B12 = 0x1000,
thetazzbot 4:05f4ace9508a 33 JOY_B13 = 0x2000,
thetazzbot 4:05f4ace9508a 34 JOY_B14 = 0x4000,
thetazzbot 4:05f4ace9508a 35 JOY_B15 = 0x8000
thetazzbot 4:05f4ace9508a 36 };
thetazzbot 4:05f4ace9508a 37
thetazzbot 4:05f4ace9508a 38 /**
thetazzbot 4:05f4ace9508a 39 *
thetazzbot 4:05f4ace9508a 40 * USBJoystick example
thetazzbot 4:05f4ace9508a 41 * @code
thetazzbot 4:05f4ace9508a 42 * #include "mbed.h"
thetazzbot 4:05f4ace9508a 43 * #include "USBJoystick.h"
thetazzbot 4:05f4ace9508a 44 *
thetazzbot 4:05f4ace9508a 45 * USBJoystick joystick;
thetazzbot 4:05f4ace9508a 46 *
thetazzbot 4:05f4ace9508a 47 * int main(void)
thetazzbot 4:05f4ace9508a 48 * {
thetazzbot 4:05f4ace9508a 49 * while (1)
thetazzbot 4:05f4ace9508a 50 * {
thetazzbot 4:05f4ace9508a 51 * joystick.move(20, 0);
thetazzbot 4:05f4ace9508a 52 * wait(0.5);
thetazzbot 4:05f4ace9508a 53 * }
thetazzbot 4:05f4ace9508a 54 * }
thetazzbot 4:05f4ace9508a 55 *
thetazzbot 4:05f4ace9508a 56 * @endcode
thetazzbot 4:05f4ace9508a 57 *
thetazzbot 4:05f4ace9508a 58 *
thetazzbot 4:05f4ace9508a 59 * @code
thetazzbot 4:05f4ace9508a 60 * #include "mbed.h"
thetazzbot 4:05f4ace9508a 61 * #include "USBJoystick.h"
thetazzbot 4:05f4ace9508a 62 * #include <math.h>
thetazzbot 4:05f4ace9508a 63 *
thetazzbot 4:05f4ace9508a 64 * USBJoystick joystick;
thetazzbot 4:05f4ace9508a 65 *
thetazzbot 4:05f4ace9508a 66 * int main(void)
thetazzbot 4:05f4ace9508a 67 * {
thetazzbot 4:05f4ace9508a 68 * while (1) {
thetazzbot 4:05f4ace9508a 69 * // Basic Joystick
thetazzbot 4:05f4ace9508a 70 * joystick.update(tx, y, z, buttonBits);
thetazzbot 4:05f4ace9508a 71 * wait(0.001);
thetazzbot 4:05f4ace9508a 72 * }
thetazzbot 4:05f4ace9508a 73 * }
thetazzbot 4:05f4ace9508a 74 * @endcode
thetazzbot 4:05f4ace9508a 75 */
thetazzbot 4:05f4ace9508a 76
thetazzbot 4:05f4ace9508a 77
thetazzbot 4:05f4ace9508a 78 class USBGamepad: public USBHID {
thetazzbot 4:05f4ace9508a 79 public:
thetazzbot 4:05f4ace9508a 80
thetazzbot 4:05f4ace9508a 81 /**
thetazzbot 4:05f4ace9508a 82 * Constructor
thetazzbot 4:05f4ace9508a 83 *
thetazzbot 4:05f4ace9508a 84 * @param vendor_id Your vendor_id (default: 0x1234)
thetazzbot 4:05f4ace9508a 85 * @param product_id Your product_id (default: 0x0002)
thetazzbot 4:05f4ace9508a 86 * @param product_release Your product_release (default: 0x0001)
thetazzbot 4:05f4ace9508a 87 */
thetazzbot 4:05f4ace9508a 88 USBGamepad(uint16_t vendor_id = 0x1234, uint16_t product_id = 0x0100, uint16_t product_release = 0x0001, int waitForConnect = true):
thetazzbot 4:05f4ace9508a 89 USBHID(0,REPORT_LEN, vendor_id, product_id, product_release, false)
thetazzbot 4:05f4ace9508a 90 {
thetazzbot 4:05f4ace9508a 91 _init();
thetazzbot 4:05f4ace9508a 92 connect(waitForConnect);
thetazzbot 4:05f4ace9508a 93 };
thetazzbot 4:05f4ace9508a 94
thetazzbot 4:05f4ace9508a 95 /**
thetazzbot 4:05f4ace9508a 96 * Write a state of the USBGamepad
thetazzbot 4:05f4ace9508a 97 *
thetazzbot 4:05f4ace9508a 98 * @param buttons buttons state, as a bit mask (combination with '|' of JOY_Bn values)
thetazzbot 4:05f4ace9508a 99 * @returns true if there is no error, false otherwise
thetazzbot 4:05f4ace9508a 100 */
thetazzbot 4:05f4ace9508a 101 bool update(uint32_t buttons);
thetazzbot 4:05f4ace9508a 102
thetazzbot 4:05f4ace9508a 103 /**
thetazzbot 4:05f4ace9508a 104 * Write a state of the USBGamepad
thetazzbot 4:05f4ace9508a 105 *
thetazzbot 4:05f4ace9508a 106 * @returns true if there is no error, false otherwise
thetazzbot 4:05f4ace9508a 107 */
thetazzbot 4:05f4ace9508a 108 bool update();
thetazzbot 4:05f4ace9508a 109
thetazzbot 4:05f4ace9508a 110 /**
thetazzbot 4:05f4ace9508a 111 * Press one or several buttons
thetazzbot 4:05f4ace9508a 112 *
thetazzbot 4:05f4ace9508a 113 * @param buttons button state, as a bitwise combination of JOY_Bn values
thetazzbot 4:05f4ace9508a 114 * @returns true if there is no error, false otherwise
thetazzbot 4:05f4ace9508a 115 */
thetazzbot 4:05f4ace9508a 116 bool buttons(uint32_t buttons);
thetazzbot 4:05f4ace9508a 117
thetazzbot 4:05f4ace9508a 118 /*
thetazzbot 4:05f4ace9508a 119 * To define the report descriptor. Warning: this method has to store the length of the report descriptor in reportLength.
thetazzbot 4:05f4ace9508a 120 *
thetazzbot 4:05f4ace9508a 121 * @returns pointer to the report descriptor
thetazzbot 4:05f4ace9508a 122 */
thetazzbot 4:05f4ace9508a 123 virtual uint8_t * reportDesc();
thetazzbot 4:05f4ace9508a 124
thetazzbot 4:05f4ace9508a 125 /* USB descriptor string overrides */
thetazzbot 4:05f4ace9508a 126 virtual uint8_t *stringImanufacturerDesc();
thetazzbot 4:05f4ace9508a 127 virtual uint8_t *stringIserialDesc();
thetazzbot 4:05f4ace9508a 128 virtual uint8_t *stringIproductDesc();
thetazzbot 4:05f4ace9508a 129
thetazzbot 4:05f4ace9508a 130 private:
thetazzbot 4:05f4ace9508a 131 uint16_t _buttonsLo;
thetazzbot 4:05f4ace9508a 132 uint16_t _buttonsHi;
thetazzbot 4:05f4ace9508a 133
thetazzbot 4:05f4ace9508a 134 void _init();
thetazzbot 4:05f4ace9508a 135 };
thetazzbot 4:05f4ace9508a 136
thetazzbot 4:05f4ace9508a 137 #endif