Monthly Archives: April 2015

Arcade Controller Conversion…Part 2

arcade2With the case now modified to fit the new joystick and buttons I moved onto the interface board. Rather than buying one I decided from the onset that I was going to design my own. Those you buy of the shelf in my opinion are just way overpriced for what you get. The majority of which are keyboard encoders. All I needed after all was a simple joystick to USB converter with support for up to eight buttons. The design I came up with encompasses a Minimus AVR development board I already had lying around. These boards are ideal for USB projects. They feature an Atmel  AT90USB162 micro controller with full speed USB controller which makes implementing USB applications a breeze. For the firmware I opted to use the LUFA (Lightweight USB Framework for AVRs, formerly known as MyUSB). I have used LUFA a lot in recent years because I find it extremely easy to implement as well as being extremely well documented.

arcade_controllerWith the design laid out on strip board I started on the firmware. Rather than starting from scratch I took the joystick class driver example provided with the LUFA and begun modifying it to suit my needs. The example project implements a Human Interface Device (HID) class joystick driver. Since most operating systems support the USB HID classes out of the box there is no need for any third party drivers. Within the project is a call back function used to create the HID report to be transferred to the host. All that was needed was, during the call back function, to read the joystick and button states and add them to the generated report. The main USB call would then take care of transferring this report to the host.

 bool CALLBACK_HID_Device_CreateHIDReport( USB_ClassInfo_HID_Device_t* const HIDInterfaceInfo,  
                                           uint8_t* const ReportID,  
                                           const uint8_t ReportType,  
                                           void* ReportData,  
                                           uint16_t* const ReportSize)  
 {  
      /* New joystick report */  
      USB_JoystickReport_Data_t* JoystickReport = (USB_JoystickReport_Data_t*)ReportData;  
      /* Get joystick states */  
      uint8_t JoystickStatus_LCL = ( JOYSTICK_PORT_PIN & JOYSTICK_ALL_MASK ) ^ JOYSTICK_ALL_MASK;  
      /* Get button states */  
      uint8_t ButtonStatus_LCL = ( BUTTONS_PORT_PIN & BUTTONS_ALL_MASK ) ^ BUTTONS_ALL_MASK;  
      /* Check joystick Y axis */  
      if( JoystickStatus_LCL & JOYSTICK_UP_MASK ) JoystickReport->Y = -100;  
      else if( JoystickStatus_LCL & JOYSTICK_DOWN_MASK ) JoystickReport->Y = 100;  
      /* Check joystick X axis */  
      if( JoystickStatus_LCL & JOYSTICK_LEFT_MASK ) JoystickReport->X = -100;  
      else if( JoystickStatus_LCL & JOYSTICK_RIGHT_MASK ) JoystickReport->X = 100;  
      /* Check joystick buttons */  
      if( ButtonStatus_LCL & BUTTONS_BUTTON1_MASK ) JoystickReport->Button |= (1 << 0);  
      if( ButtonStatus_LCL & BUTTONS_BUTTON2_MASK ) JoystickReport->Button |= (1 << 1);  
      if( ButtonStatus_LCL & BUTTONS_BUTTON3_MASK ) JoystickReport->Button |= (1 << 2);  
      if( ButtonStatus_LCL & BUTTONS_BUTTON4_MASK ) JoystickReport->Button |= (1 << 3);  
      if( ButtonStatus_LCL & BUTTONS_BUTTON5_MASK ) JoystickReport->Button |= (1 << 4);  
      if( ButtonStatus_LCL & BUTTONS_BUTTON6_MASK ) JoystickReport->Button |= (1 << 5);  
      if( ButtonStatus_LCL & BUTTONS_BUTTON7_MASK ) JoystickReport->Button |= (1 << 6);  
      if( ButtonStatus_LCL & BUTTONS_BUTTON8_MASK ) JoystickReport->Button |= (1 << 7);  
      /* Set report size */  
      *ReportSize = sizeof(USB_JoystickReport_Data_t);  
      return false;  
 }  

Every USB device must contain an embedded device descriptor which describe to the host information such as what the device is, who makes it, what version of USB it supports etc. This device descriptor contains a vendor ID (a 16-bit number) which is assigned by the USB Implementers Forum to a specific company. Who in turn assign a product ID (again a 16-bit number) to individual products. In the case of the LUFA joystick example the vendor ID and product ID used are 0x03EB and 0x2043 respectively. Refering to the USB ID Repository on the Linux USB home page shows the registered vendor ID as “Atmel Corp.” (no surprise there) and the product ID as “LUFA Joystick Demo Application”. The VID and PID are communicated to the computer when the device is plugged in, along with text strings describing the vendor and product as well as additional descriptors. I choose to modify these vendor and product strings to something more meaningful to me.

output_BGcEfUWith the code compiled and programmed into the AT90USB162 the first test was to check it worked under windows. Once plugged in windows identified the device and installed a suitable driver. Then using the game controllers configuration wizard I was then able to confirm the joystick and buttons were functioning correctly.

Moving on time to start testing on the Raspberry Pi. RetroPie detected the controller and allowed it to be configured for use. This is fine for navigating the RetroPie front end but in order to use it with any of the emulators (apart from MAME who has its own set-up) the controller needs registering for use with RetroArch. So exiting RetroPie and checking available USB devices shows the device has been detected on bus 001 device 005. The vendor ID and product ID clearly shown.

 Bus 001 Device 002: ID 0424:9512 Standard Microsystems Corp.  
 Bus 001 Device 002: ID 0424:9514 Standard Microsystems Corp. 
 Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
 Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp. 
 Bus 001 Device 004: ID 7392:7811 Edimax Technology Co., Ltd EW-7811Un 802.11n Wireless Adapter [Realtek RTL8188CUS]
 Bus 001 Device 005: ID 03eb:2043 Atmel Corp. LUFA Joystick Demo Application 

Then registering the controller for use with RetroArch using the “Register RetroArch controller” script. You can see the modified manufacturer and product strings read by the script.

  Using joypad: Mikes Modz Arcade Controller  
 Joypads tend to have stale state after opened.  
 Press some buttons and move some axes around to make sure joypad state is completely neutral before proceeding.  
 When done, press Enter ...   
 Configuring binds for player #1 on joypad #0.  
 B button (down)  
      Joybutton pressed: 0  
 Y button (left)  
      Joybutton pressed: 1  
 Select button  
      Joybutton pressed: 2  
 Start button  
      Joybutton pressed: 3  
 Up D-pad  
      Joyaxis moved: Axis 1, Value -32767  
 Down D-pad  
      Joyaxis moved: Axis 1, Value 32767  
 Left D-pad  
      Joyaxis moved: Axis 0, Value -32767  
 Right D-pad  
      Joyaxis moved: Axis 0, Value 32767  
 A button (right)  
      Joybutton pressed: 4  
 X button (top)  
      Joybutton pressed: 5  
 L button (shoulder)  
      Joybutton pressed: 6  
 R button (shoulder)  
      Joybutton pressed: 7  
 L2 button (trigger)  
      Timed out ...  
 R2 button (trigger)  
      Timed out ...  
 L3 button (thumb)  
      Timed out ...  
 R3 button (thumb)  
      Timed out ...  
 Left analog X+ (right)  
      Timed out ...  
 Left analog X- (left)  
      Timed out ...  
 Left analog Y+ (down)  
      Timed out ...  
 Left analog Y- (up)  
      Timed out ...  
 Right analog X+ (right)  
      Timed out ...  
 Right analog X- (left)  
      Timed out ...  
 Right analog Y+ (down)  
      Timed out ...  
 Right analog Y- (up)  
      Timed out ...  
 Writing autoconfig profile to: /opt/retropie/emulators/retroarch/configs/tempconfig.cfg.  
 input_player1_joypad_index = "0"  
 input_player1_b_btn = "0"  
 input_player1_y_btn = "1"  
 input_player1_select_btn = "2"  
 input_player1_start_btn = "3"  
 input_player1_up_axis = "-1"  
 input_player1_down_axis = "+1"  
 input_player1_left_axis = "-0"  
 input_player1_right_axis = "+0"  
 input_player1_a_btn = "4"  
 input_player1_x_btn = "5"  
 input_player1_l_btn = "6"  
 input_player1_r_btn = "7"  
 = = = = = = = = = = = = = = = = = = = = =  
 Configuring RetroArch-AutoConfigs  
 = = = = = = = = = = = = = = = = = = = = =  
 Remapping controller hotkeys  
 Processing /opt/retropie/emulators/retroarch/configs/2Axes11KeysGamePad.cfg  
 Processing /opt/retropie/emulators/retroarch/configs/ControlBlockArcadeGamepad.cfg  
 Processing /opt/retropie/emulators/retroarch/configs/ControlBlockSNESGamepad.cfg  
 Processing /opt/retropie/emulators/retroarch/configs/DragonRise_Inc.___Generic___USB__Joystick__.cfg  
 Processing /opt/retropie/emulators/retroarch/configs/Generic_X-Box_pad.cfg  
 Processing /opt/retropie/emulators/retroarch/configs/GreenAsia_Inc.____USB_Joystick_____.cfg  
 Processing /opt/retropie/emulators/retroarch/configs/HuiJiaSNEStoUSBConverter.cfg  
 Processing /opt/retropie/emulators/retroarch/configs/JessTechColourRumblePad.cfg  
 Processing /opt/retropie/emulators/retroarch/configs/LogitechGamepadF710.cfg  
 Processing /opt/retropie/emulators/retroarch/configs/LogitechLogitechCordlessRumblePad2.cfg  
 Processing /opt/retropie/emulators/retroarch/configs/Logitech_Logitech_Dual_Action.cfg  
 Processing /opt/retropie/emulators/retroarch/configs/Logitech_Logitech_RumblePad_2_USB.cfg  
 Processing /opt/retropie/emulators/retroarch/configs/Microsoft_Sidewinder_Dual_Strike_USB_version_1.cfg  
 Processing /opt/retropie/emulators/retroarch/configs/Microsoft_X-Box_360_pad.cfg  
 Processing /opt/retropie/emulators/retroarch/configs/MikesModzArcadeController.cfg  
 Processing /opt/retropie/emulators/retroarch/configs/MY-POWER_CO__LTD__2In1_USB_Joystick.cfg  
 Processing /opt/retropie/emulators/retroarch/configs/PS3ControllerBT.cfg  
 Processing /opt/retropie/emulators/retroarch/configs/PS3Controller.cfg  
 Processing /opt/retropie/emulators/retroarch/configs/PS3ControllerUSB.cfg  
 Processing /opt/retropie/emulators/retroarch/configs/RockCandyGamepadforPS3.cfg  
 Processing /opt/retropie/emulators/retroarch/configs/Saitek_P880.cfg  
 Processing /opt/retropie/emulators/retroarch/configs/SNES-to-GamepadDevice.cfg  
 Processing /opt/retropie/emulators/retroarch/configs/Sony_PLAYSTATION(R)3_Controller.cfg  
 Processing /opt/retropie/emulators/retroarch/configs/Thrustmaster_Dual_Trigger_3-in-1.cfg  
 Processing /opt/retropie/emulators/retroarch/configs/THRUSTMASTER_FireStorm_Dual_Analog_2.cfg  
 Processing /opt/retropie/emulators/retroarch/configs/Thrustmaster_T_Mini_Wireless.cfg  
 Processing /opt/retropie/emulators/retroarch/configs/USB_2-axis_8-button_gamepad.cfg  
 Processing /opt/retropie/emulators/retroarch/configs/USB_Gamepad.cfg  
 Processing /opt/retropie/emulators/retroarch/configs/USBGamepad.cfg  
 Processing /opt/retropie/emulators/retroarch/configs/Xbox_360_Wireless_Receiver.cfg  
 Processing /opt/retropie/emulators/retroarch/configs/XboxGamepad(userspacedriver).cfg  

With the controller now registered it was time to start playing. First up Teenage Mutant Ninja Turtles on the SNES which worked great. The Seimitsu joystick has a great feel with lovely movement not too stiff but not too loose either. The buttons respond perfectly with minimal effort, if anything they are little bit to sensitive but that’s fine.

Arcade Controller Conversion…Part 1

I love playing retro arcade games and most recently I have taken to playing these classic games on my Raspberry Pi 2 using the RetroPie. For those who aren’t aware the RetroPie project is a collection of works that all have the overall goal to turn the Raspberry Pi into a dedicated retro-gaming console. Up until now I have been using a couple of cheap USB NES controllers. These controllers are great but nothing compares to the feel of an authentic arcade controller. So I set about building my own controller that could be plugged directly into my Raspberry Pi.

arcade4The first issue would be the enclosure. Do I build something? Or do I buy a flat packed kit from eBay? Neither option was going to be cheap. Then I stumbled on someone selling a second hand PlayStation arcade controller on eBay which at the time was listed at 99p plus postage. I figured that has got to be worth a punt. Even if I can only salvage the case and possibly a few other parts then so be it. So I placed a bid and ended up winning it for just over a pound. Result! I wasn’t expecting a lot but was pleasantly surprised when it arrived. The build quality was a lot better than I had expected. It had obviously seen a fair bit of use and the joystick micro switches were worn out. But still I wasn’t disappointed as the case was in great condition.

arcade5I begun disassembling it as soon as it arrived. I removed the bottom plate, the cable, the joystick and the eight push buttons. I had no plans to use the interface board so I de-soldered all of the connections and left the PCB in situ. Next I measured the holes for the push buttons which turned out to be 30mm which was good news since this a standard size for arcade buttons. Selecting a suitable joystick proved to be more troublesome. There isn’t a lot of clearance for the joystick once mounted so I needed to find one with the lowest possible profile.

In the end I went for a Seimitsu LS-58-01and Seimitsu PS-15 buttons. The LS-58 a great quality small form factor joystick. Not to stiff and with an adequate throw distance for my liking. The micro switches are soldered directly into a PCB. This worked out perfect as I later went on to use the PCB to mount it to the original mounting posts. Being 30mm the PS-15’s fitted perfectly with no modifications required. The joystick however required a few modifications. In order to drill the mounting holes in the PCB I first had to remove the common connections for each micro switch. Rather than completely removing the micro switches I simply cut the tabs and de-soldered. Then after drilling the holes I used Kynar wire to re-connect them.

arcade1The next problem was getting the joystick to fit flush with the case. Because of the way the case had been moulded I needed to remove some of the plastic surrounding the stick. Thankfully this didn’t comprise the rigidity of the joystick. The end result wasn’t pretty but at least it now fitted flush with the case. I finished off by connecting all of the switches to my new interface board which I will move onto in the next post.