With 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.
With 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.
With 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.