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 /* Copyright (c) 2010-2011 mbed.org, MIT License
thetazzbot 4:05f4ace9508a 2 * Modified Mouse code for Joystick - WH 2012
thetazzbot 4:05f4ace9508a 3 * Modified for RetroPie MW 2016
thetazzbot 4:05f4ace9508a 4 *
thetazzbot 4:05f4ace9508a 5 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
thetazzbot 4:05f4ace9508a 6 * and associated documentation files (the "Software"), to deal in the Software without
thetazzbot 4:05f4ace9508a 7 * restriction, including without limitation the rights to use, copy, modify, merge, publish,
thetazzbot 4:05f4ace9508a 8 * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
thetazzbot 4:05f4ace9508a 9 * Software is furnished to do so, subject to the following conditions:
thetazzbot 4:05f4ace9508a 10 *
thetazzbot 4:05f4ace9508a 11 * The above copyright notice and this permission notice shall be included in all copies or
thetazzbot 4:05f4ace9508a 12 * substantial portions of the Software.
thetazzbot 4:05f4ace9508a 13 *
thetazzbot 4:05f4ace9508a 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
thetazzbot 4:05f4ace9508a 15 * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
thetazzbot 4:05f4ace9508a 16 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
thetazzbot 4:05f4ace9508a 17 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
thetazzbot 4:05f4ace9508a 18 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
thetazzbot 4:05f4ace9508a 19 */
thetazzbot 4:05f4ace9508a 20
thetazzbot 4:05f4ace9508a 21 #include "stdint.h"
thetazzbot 4:05f4ace9508a 22 #include "USBGamepad.h"
thetazzbot 4:05f4ace9508a 23
thetazzbot 4:05f4ace9508a 24
thetazzbot 4:05f4ace9508a 25
thetazzbot 4:05f4ace9508a 26 bool USBGamepad::update(uint32_t buttons)
thetazzbot 4:05f4ace9508a 27 {
thetazzbot 4:05f4ace9508a 28 _buttonsLo = (uint16_t)(buttons & 0xffff);
thetazzbot 4:05f4ace9508a 29 _buttonsHi = (uint16_t)((buttons >> 16) & 0xffff);
thetazzbot 4:05f4ace9508a 30 return update();
thetazzbot 4:05f4ace9508a 31 }
thetazzbot 4:05f4ace9508a 32
thetazzbot 4:05f4ace9508a 33 bool USBGamepad::update()
thetazzbot 4:05f4ace9508a 34 {
thetazzbot 4:05f4ace9508a 35 HID_REPORT report;
thetazzbot 4:05f4ace9508a 36
thetazzbot 4:05f4ace9508a 37 // Fill the report according to the Gamepad Descriptor
thetazzbot 4:05f4ace9508a 38 #define put(idx, val) (report.data[idx] = (val) & 0xff, report.data[(idx)+1] = ((val) >> 8) & 0xff)
thetazzbot 4:05f4ace9508a 39 put(0, _buttonsLo); // 0..1 2 bytes
thetazzbot 4:05f4ace9508a 40 put(2, _buttonsHi);// 2..3 2 bytes, 32 buttons
thetazzbot 4:05f4ace9508a 41
thetazzbot 4:05f4ace9508a 42 // important: keep reportLen in sync with the actual byte length of
thetazzbot 4:05f4ace9508a 43 // the reports we build here
thetazzbot 4:05f4ace9508a 44 report.length = REPORT_LEN;
thetazzbot 4:05f4ace9508a 45
thetazzbot 4:05f4ace9508a 46 // send the report
thetazzbot 4:05f4ace9508a 47 return sendNB(&report);
thetazzbot 4:05f4ace9508a 48 }
thetazzbot 4:05f4ace9508a 49
thetazzbot 4:05f4ace9508a 50 bool USBGamepad::buttons(uint32_t buttons)
thetazzbot 4:05f4ace9508a 51 {
thetazzbot 4:05f4ace9508a 52 _buttonsLo = (uint16_t)(buttons & 0xffff);
thetazzbot 4:05f4ace9508a 53 _buttonsHi = (uint16_t)((buttons >> 16) & 0xffff);
thetazzbot 4:05f4ace9508a 54 return update();
thetazzbot 4:05f4ace9508a 55 }
thetazzbot 4:05f4ace9508a 56
thetazzbot 4:05f4ace9508a 57
thetazzbot 4:05f4ace9508a 58 void USBGamepad::_init() {
thetazzbot 4:05f4ace9508a 59
thetazzbot 4:05f4ace9508a 60 _buttonsLo = 0x0000; // 16 buttons
thetazzbot 4:05f4ace9508a 61 _buttonsHi = 0x0000; // 16 buttons
thetazzbot 4:05f4ace9508a 62
thetazzbot 4:05f4ace9508a 63 }
thetazzbot 4:05f4ace9508a 64
thetazzbot 4:05f4ace9508a 65
thetazzbot 4:05f4ace9508a 66 uint8_t * USBGamepad::reportDesc()
thetazzbot 4:05f4ace9508a 67 {
thetazzbot 4:05f4ace9508a 68 // Descriptor generated by USBHID Descriptor tool
thetazzbot 4:05f4ace9508a 69 static uint8_t reportDescriptor[] = {
thetazzbot 4:05f4ace9508a 70 0x05, 0x01, // USAGE_PAGE (Generic Desktop)
thetazzbot 4:05f4ace9508a 71 0x15, 0x00, // LOGICAL_MINIMUM (0)
thetazzbot 4:05f4ace9508a 72 0x09, 0x05, // USAGE (Game Pad)
thetazzbot 4:05f4ace9508a 73 0xa1, 0x01, // COLLECTION (Application)
thetazzbot 4:05f4ace9508a 74 0x05, 0x09, // USAGE_PAGE (Button)
thetazzbot 4:05f4ace9508a 75 0x19, 0x01, // USAGE_MINIMUM (Button 1)
thetazzbot 4:05f4ace9508a 76 0x29, 0x20, // USAGE_MAXIMUM (Button 32)
thetazzbot 4:05f4ace9508a 77 0x15, 0x00, // LOGICAL_MINIMUM (0)
thetazzbot 4:05f4ace9508a 78 0x25, 0x01, // LOGICAL_MAXIMUM (1)
thetazzbot 4:05f4ace9508a 79 0x75, 0x01, // REPORT_SIZE (1)
thetazzbot 4:05f4ace9508a 80 0x95, 0x20, // REPORT_COUNT (32)
thetazzbot 4:05f4ace9508a 81 0x55, 0x00, // UNIT_EXPONENT (0)
thetazzbot 4:05f4ace9508a 82 0x65, 0x00, // UNIT (None)
thetazzbot 4:05f4ace9508a 83 0x81, 0x02, // INPUT (Data,Var,Abs)
thetazzbot 4:05f4ace9508a 84 0xc0 // END_COLLECTION
thetazzbot 4:05f4ace9508a 85 };
thetazzbot 4:05f4ace9508a 86
thetazzbot 4:05f4ace9508a 87 reportLength = sizeof(reportDescriptor);
thetazzbot 4:05f4ace9508a 88 return reportDescriptor;
thetazzbot 4:05f4ace9508a 89 }
thetazzbot 4:05f4ace9508a 90
thetazzbot 4:05f4ace9508a 91 uint8_t * USBGamepad::stringImanufacturerDesc() {
thetazzbot 4:05f4ace9508a 92 static uint8_t stringImanufacturerDescriptor[] = {
thetazzbot 4:05f4ace9508a 93 0x14, /*bLength*/
thetazzbot 4:05f4ace9508a 94 STRING_DESCRIPTOR, /*bDescriptorType 0x03*/
thetazzbot 4:05f4ace9508a 95 't',0,'h',0,'e',0,'t',0,'a',0,'z',0,'z',0,'b',0,'t',0 /*bString iManufacturer - mjrcorp*/
thetazzbot 4:05f4ace9508a 96 };
thetazzbot 4:05f4ace9508a 97 return stringImanufacturerDescriptor;
thetazzbot 4:05f4ace9508a 98 }
thetazzbot 4:05f4ace9508a 99
thetazzbot 4:05f4ace9508a 100 uint8_t * USBGamepad::stringIserialDesc() {
thetazzbot 4:05f4ace9508a 101 static uint8_t stringIserialDescriptor[] = {
thetazzbot 4:05f4ace9508a 102 0x16, /*bLength*/
thetazzbot 4:05f4ace9508a 103 STRING_DESCRIPTOR, /*bDescriptorType 0x03*/
thetazzbot 4:05f4ace9508a 104 '0',0,'1',0,'2',0,'3',0,'4',0,'5',0,'6',0,'7',0,'8',0,'9',0, /*bString iSerial - 0123456789*/
thetazzbot 4:05f4ace9508a 105 };
thetazzbot 4:05f4ace9508a 106 return stringIserialDescriptor;
thetazzbot 4:05f4ace9508a 107 }
thetazzbot 4:05f4ace9508a 108
thetazzbot 4:05f4ace9508a 109 uint8_t * USBGamepad::stringIproductDesc() {
thetazzbot 4:05f4ace9508a 110 static uint8_t stringIproductDescriptor[] = {
thetazzbot 4:05f4ace9508a 111 0x1E, /*bLength*/
thetazzbot 4:05f4ace9508a 112 STRING_DESCRIPTOR, /*bDescriptorType 0x03*/
thetazzbot 4:05f4ace9508a 113 'A',0,'r',0,'c',0,'a',0,'d',0,'e',0,' ',0,'G',0,
thetazzbot 4:05f4ace9508a 114 'a',0,'m',0,'e',0,'p',0,'a',0,'d',0 /*String iProduct */
thetazzbot 4:05f4ace9508a 115 };
thetazzbot 4:05f4ace9508a 116 return stringIproductDescriptor;
thetazzbot 4:05f4ace9508a 117 }