NeoGeo joystick to PC / PS3

    This site uses cookies. By continuing to browse this site, you are agreeing to our Cookie Policy.

    • NeoGeo joystick to PC / PS3

      NOTICE!!!! CODE BELOW IS MEANT AS EXAMPLE AND FILES ARE FOR IDE 1.6.5 (I might add later implementation for never versions)

      I have been installing RetroPie setup and wanted to use Neo Controllers (AES, CD and my homemade controllers) with it. I have plenty of Arduino boards bought from ebay / aliexpress cheap and I decided to make simple converter. I chose Arduino pro micro as board because its small and uses atmega32u4 which USB controller is quite well supported within Arduino IDE. I first made just USB HID that was simple joystick with X/Y plus buttons and used bit too big enclosure, but then decided that it would be nice if PS3 was supported and with working home button.

      To get that work, I had to mimic PS3 controller almost identically, but I also found few things that I can change. Anyway, this is now the current version. It supports standard neo buttons + 3 kick buttons to be mapped. Pressing select + start acts like ps home. In the future I'm going to add different button mappings that can be change on the fly using start+select + button / direction.

      Pro Micro is tiny board, and you can get them like for $4-5




      Standard db15 works well with AES controller



      Controller, in this case the my Metal Slug Station's controller, shows up in windows as controller with 13 buttons, x and y (left analog stick), z and rz (right analog stick) and hat switch (dpad).

      The post was edited 1 time, last by miisalo ().

    • If someone would like to build something like this, here is how the code works.

      -------------------------------------

      HID Descriptor

      First we need to make HID descriptor for the joystick. Atmega32u4 based arduinos already have descriptors for mouse and keyboard. I originally commented out the those reports and just sent the descriptor for the PS3 controller, but after fiddling around I noticed that it works as composite device too. So currently the code send it as report 3. HID descriptors are in file HID.cpp found under Arduino apps Java/hardware/arduino/avr/cores/arduino folder. I added the descriptor as else definition after RAWHID

      Source Code

      1. #ifdef RAWHID_ENABLED
      2. // RAW HID
      3. 0x06, LSB(RAWHID_USAGE_PAGE), MSB(RAWHID_USAGE_PAGE), // 30
      4. 0x0A, LSB(RAWHID_USAGE), MSB(RAWHID_USAGE),
      5. 0xA1, 0x01, // Collection 0x01
      6. 0x85, 0x03, // REPORT_ID (3)
      7. 0x75, 0x08, // report size = 8 bits
      8. 0x15, 0x00, // logical minimum = 0
      9. 0x26, 0xFF, 0x00, // logical maximum = 255
      10. 0x95, 64, // report count TX
      11. 0x09, 0x01, // usage
      12. 0x81, 0x02, // Input (array)
      13. 0x95, 64, // report count RX
      14. 0x09, 0x02, // usage
      15. 0x91, 0x02, // Output (array)
      16. 0xC0 // end collection
      17. #else
      18. 0x05, 0x01, // USAGE_PAGE (Generic Desktop)
      19. 0x09, 0x05, // USAGE (Gamepad)
      20. 0xa1, 0x01, // COLLECTION (Application)
      21. 0x85, 0x03, // REPORT_ID (3)
      22. 0x15, 0x00, // LOGICAL_MINIMUM (0)
      23. 0x25, 0x01, // LOGICAL_MAXIMUM (1)
      24. 0x35, 0x00, // PHYSICAL_MINIMUM (0)
      25. 0x45, 0x01, // PHYSICAL_MAXIMUM (1)
      26. 0x75, 0x01, // REPORT_SIZE (1)
      27. 0x95, 0x0d, // REPORT_COUNT (13)
      28. 0x05, 0x09, // USAGE_PAGE (Button)
      29. 0x19, 0x01, // USAGE_MINIMUM (Button 1)
      30. 0x29, 0x0d, // USAGE_MAXIMUM (Button 13)
      31. 0x81, 0x02, // INPUT (Data,Var,Abs)
      32. 0x95, 0x03, // REPORT_COUNT (3)
      33. 0x81, 0x01, // INPUT (Cnst,Ary,Abs)
      34. 0x05, 0x01, // USAGE_PAGE (Generic Desktop)
      35. 0x25, 0x07, // LOGICAL_MAXIMUM (7)
      36. 0x46, 0x3b, 0x01, // PHYSICAL_MAXIMUM (315)
      37. 0x75, 0x04, // REPORT_SIZE (4)
      38. 0x95, 0x01, // REPORT_COUNT (1)
      39. 0x65, 0x14, // UNIT (Eng Rot:Angular Pos)
      40. 0x09, 0x39, // USAGE (Hat switch)
      41. 0x81, 0x42, // INPUT (Data,Var,Abs,Null)
      42. 0x65, 0x00, // UNIT (None)
      43. 0x95, 0x01, // REPORT_COUNT (1)
      44. 0x81, 0x01, // INPUT (Cnst,Ary,Abs)
      45. 0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255)
      46. 0x46, 0xff, 0x00, // PHYSICAL_MAXIMUM (255)
      47. 0x75, 0x08, // REPORT_SIZE (8)
      48. 0x09, 0x30, // USAGE (X)
      49. 0x09, 0x31, // USAGE (Y)
      50. 0x09, 0x32, // USAGE (Z)
      51. 0x09, 0x35, // USAGE (Rz)
      52. 0x95, 0x04, // REPORT_COUNT (4)
      53. 0x81, 0x02, // INPUT (Data,Var,Abs)
      54. 0x06, 0x00, 0xff, // USAGE_PAGE (Vendor Specific)
      55. 0x09, 0x20, // ?
      56. 0x09, 0x21, // ?
      57. 0x09, 0x22, // ?
      58. 0x09, 0x23, // ?
      59. 0x09, 0x24, // ?
      60. 0x09, 0x25, // ?
      61. 0x09, 0x26, // ?
      62. 0x09, 0x27, // ?
      63. 0x09, 0x28, // ?
      64. 0x09, 0x29, // ?
      65. 0x09, 0x2a, // ?
      66. 0x09, 0x2b, // ?
      67. 0x95, 0x0C, // REPORT_COUNT (12) */
      68. 0x81, 0x02, // INPUT (Data,Var,Abs)
      69. 0x0a, 0x21, 0x26, // asking Magic bytes???
      70. 0x95, 0x08, // REPORT_COUNT (8)
      71. 0xb1, 0x02, // FEATURE (Data,Var,Abs)
      72. 0xc0, // END_COLLECTION
      Display All







      This is what PS3 controllers sends. Any HID joystick with X/Y also works without problems but then home button is not supported. In the attachment, there is commented example of working simple joystick/gamepad hid. I tried to minimize the descriptor and found that feature report (starts from 187.) and at least one of those unkown vendor specific values (starting from 173.) is needed so that PS3 will send class get_report and you can return so called magic bytes.


      MAGIC BYTES

      Home button is "vendor specific" feature and if controller says that "hey, I got features for you" right way, PS3 will ask about them with sending class interface get report request. The report that must be send back is something that people refer as "magic bytes".

      Magic bytes are 0x21, 0x26, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00

      Now in my case, I have defined composite device that can send different kind of reports. In line 132, I defined that PS3 controller sends report #3, so that must be included.


      Source Code

      1. const u8 magic_init_bytes[] PROGMEM = {0x03, 0x21, 0x26, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00}; // 0x03 is needed before magic bytes



      The arduino core routines parse the class interface requests in the HID_Setup function. There is commented call for HID_GetReport(). I added the test over that for the correct report and then sent the magic bytes using USB_SendControl (notice that TRANSFER_PGM should be used because magic bytes are in PROGMEM)

      Source Code

      1. // FOR PS3 HOME BUTTON ...
      2. if (REQUEST_DEVICETOHOST_CLASS_INTERFACE == requestType)
      3. {
      4. if (HID_GET_REPORT == r)
      5. {
      6. if (setup.wValueH << 8 | setup.wValueL == 0x0303 ) // = 0x<reporttype><reportnumber>
      7. {
      8. USB_SendControl(TRANSFER_PGM, magic_init_bytes,9);
      9. }
      10. //HID_GetReport();
      11. return true;
      12. }
      Display All
    • IMPLEMENTING CLASS FOR THE CONTROLLER

      I quickly implemented class and data structure that can be used to send the reports, I will later rewrite it. First I defined the data structure and class for the report in the USBAPI.h (same folder as HID.cpp)

      Source Code

      1. //================================================================================
      2. //================================================================================
      3. // PS3Controller
      4. //================================================================================
      5. //================================================================================
      6. // Implemented in HID.cpp
      7. typedef struct PS3ControllerState // store all the controller parameters
      8. {
      9. union { // union used so that buttons can be accessed either by
      10. uint16_t buttons; // 16 bit button variable
      11. struct { // or by 16 one bit variables
      12. uint8_t square :1;
      13. uint8_t cross :1;
      14. uint8_t circle :1;
      15. uint8_t triangle :1;
      16. uint8_t l1 :1;
      17. uint8_t r1 :1;
      18. uint8_t l2 :1;
      19. uint8_t r2 :1;
      20. uint8_t select :1;
      21. uint8_t start :1;
      22. uint8_t :1; // dummy, does not exists in dualshock
      23. uint8_t :1; // dummy, does not exists in dualshock
      24. uint8_t home :1; // home button, needs feature bytes (magic bytes) to be send for class get_report call
      25. uint8_t :1; // dummy, does not exists in dualshock
      26. uint8_t :1; // dummy, does not exists in dualshock
      27. };
      28. };
      29. uint8_t dpad; // d-pad plus dummies bits
      30. uint8_t lx; // left analogstick x 0x80 = middle
      31. uint8_t ly; // left analogstick y
      32. uint8_t rx; // right analogstick x
      33. uint8_t ry; // right analogstick y
      34. uint8_t prUp; // wild guess that four values are d pad pressures or something to do with them
      35. uint8_t prRight; //
      36. uint8_t prDown; //
      37. uint8_t prLeft; //
      38. uint8_t prSquare; //rest of the pressure values 0 - 255
      39. uint8_t prCross;
      40. uint8_t prTriangle;
      41. uint8_t prCircle;
      42. uint8_t prL1;
      43. uint8_t prR1;
      44. uint8_t prL2;
      45. uint8_t prR2;
      46. } PS3ControllerState_t;
      47. class PS3Controller_
      48. {
      49. private:
      50. PS3ControllerState_t currentState;
      51. PS3ControllerState_t sentState;
      52. public:
      53. PS3Controller_();
      54. void setState(PS3ControllerState_t *ControllerState);
      55. PS3ControllerState_t getState();
      56. void initController();
      57. void sendState();
      58. };
      59. extern PS3Controller_ PS3Controller;
      Display All
      And actual implementation in HID.cpp

      Source Code

      1. //================================================================================
      2. //================================================================================
      3. // PS3Controller
      4. //
      5. PS3Controller_::PS3Controller_()
      6. {
      7. }
      8. void PS3Controller_::initController()
      9. {
      10. // would be less memory consuming to define this as static data and memcopy?
      11. this->currentState.buttons = 0;
      12. this->currentState.dpad = 8;
      13. this->currentState.lx = 0x80;
      14. this->currentState.ly = 0x80;
      15. this->currentState.rx = 0x80;
      16. this->currentState.ry = 0x80;
      17. this->currentState.prUp = 0;
      18. this->currentState.prRight = 0;
      19. this->currentState.prDown = 0;
      20. this->currentState.prLeft = 0;
      21. this->currentState.prSquare = 0;
      22. this->currentState.prCross = 0;
      23. this->currentState.prTriangle = 0;
      24. this->currentState.prCircle = 0;
      25. this->currentState.prL1 = 0;
      26. this->currentState.prL2 = 0;
      27. this->currentState.prR1 = 0;
      28. this->currentState.prR2 = 0;
      29. // always send
      30. this->sendState();
      31. this->sentState = this->currentState;
      32. }
      33. void PS3Controller_::sendState()
      34. {
      35. // only send if state has changed
      36. // note that sending the structure is only safe if there is no aligment padding
      37. // in the structures. Also compare like these is safe with padding only if structure is init with memset
      38. // to also set the padding. This is not needed for atmega32u4 and 328, but may not be safe with other processors
      39. // Safer method would be map structure variables to the byte array
      40. if (memcmp(&(this->currentState), &(this->sentState), sizeof(PS3ControllerState_t)) != 0)
      41. {
      42. HID_SendReport(3, &(this->currentState), sizeof(PS3ControllerState_t));
      43. this->sentState = this->currentState;
      44. }
      45. }
      46. PS3ControllerState_t PS3Controller_::getState()
      47. {
      48. return this->currentState;
      49. }
      50. void PS3Controller_::setState(PS3ControllerState_t *controllerState)
      51. {
      52. this->currentState = *controllerState;
      53. }
      Display All
      And define singleton objet in HID.cpp for PS3Controller.

      Source Code

      1. // Singletons for mouse and keyboard
      2. Mouse_ Mouse;
      3. Keyboard_ Keyboard;
      4. PS3Controller_ PS3Controller;

      THE ACTUAL ARDUINO SKETCH

      Again, this is quick implementation and could use more modularity. I used the digitalRead instead of directly reading ports, because speed is not issue here and code can be ported more easily to different 'duinos.



      Source Code

      1. // PIN mappings, usings constant ints would be more type safe but lets save some memory
      2. #define PIN_UP 16
      3. #define PIN_LEFT 14
      4. #define PIN_A 15
      5. #define PIN_C A0
      6. #define PIN_START A1
      7. #define PIN_KICK2 A2
      8. #define PIN_KICK3 A3
      9. #define PIN_KICK1 2
      10. #define PIN_SELECT 3
      11. #define PIN_D 4
      12. #define PIN_B 5
      13. #define PIN_RIGHT 6
      14. #define PIN_DOWN 7
      15. // controller state
      16. PS3ControllerState_t controllerState;
      17. /***************************************************************
      18. * SETUP routine
      19. *
      20. * Initialize pins to input with pullup
      21. * Initialize controller state
      22. ***************************************************************/
      23. void setup() {
      24. pinMode(PIN_UP, INPUT_PULLUP);
      25. pinMode(PIN_DOWN, INPUT_PULLUP);
      26. pinMode(PIN_LEFT, INPUT_PULLUP);
      27. pinMode(PIN_RIGHT, INPUT_PULLUP);
      28. pinMode(PIN_A, INPUT_PULLUP);
      29. pinMode(PIN_B, INPUT_PULLUP);
      30. pinMode(PIN_C, INPUT_PULLUP);
      31. pinMode(PIN_D, INPUT_PULLUP);
      32. pinMode(PIN_KICK1, INPUT_PULLUP);
      33. pinMode(PIN_KICK2, INPUT_PULLUP);
      34. pinMode(PIN_KICK3, INPUT_PULLUP);
      35. pinMode(PIN_SELECT, INPUT_PULLUP);
      36. pinMode(PIN_START, INPUT_PULLUP);
      37. PS3Controller.initController(); // Initialize controller and get current state
      38. controllerState = PS3Controller.getState();
      39. }
      40. /***************************************************************
      41. * MAP 4 button layout to the controller
      42. ***************************************************************/
      43. void map4ButtonLayout_1()
      44. {
      45. controllerState.square = !digitalRead(PIN_A);
      46. controllerState.cross = !digitalRead(PIN_B);
      47. controllerState.circle = !digitalRead(PIN_C);
      48. controllerState.triangle = !digitalRead(PIN_D);
      49. }
      50. /***************************************************************
      51. * MAP start and select to the controller
      52. ***************************************************************/
      53. void mapStartSelect()
      54. {
      55. controllerState.start = !digitalRead(PIN_START);
      56. controllerState.select = !digitalRead(PIN_SELECT);
      57. }
      58. /***************************************************************
      59. * MAP start and select as home button to the controller
      60. * this must be called after mapStartSelect
      61. ***************************************************************/
      62. void mapHome()
      63. {
      64. if (controllerState.start && controllerState.select)
      65. {
      66. controllerState.start = 0;
      67. controllerState.select = 0;
      68. controllerState.home = 1;
      69. } else {
      70. controllerState.home = 0;
      71. }
      72. }
      73. /***************************************************************
      74. * MAP joystict to dpad
      75. ***************************************************************/
      76. void mapJoystick()
      77. {
      78. controllerState.dpad = 8;
      79. if (!digitalRead(PIN_UP)) {
      80. controllerState.dpad = 0;
      81. if (!digitalRead(PIN_RIGHT)) controllerState.dpad = 1;
      82. } else if (!digitalRead(PIN_RIGHT)) {
      83. controllerState.dpad = 2;
      84. if (!digitalRead(PIN_DOWN)) controllerState.dpad = 3;
      85. } else if (!digitalRead(PIN_DOWN)) {
      86. controllerState.dpad = 4;
      87. if (!digitalRead(PIN_LEFT)) controllerState.dpad = 5;
      88. } else if (!digitalRead(PIN_LEFT)) {
      89. controllerState.dpad = 6;
      90. if (!digitalRead(PIN_UP)) controllerState.dpad = 7;
      91. };
      92. }
      93. /***************************************************************
      94. * MAIN LOOP - Map and send state
      95. ***************************************************************/
      96. void loop() {
      97. map4ButtonLayout_1();
      98. mapStartSelect();
      99. mapHome();
      100. mapJoystick();
      101. PS3Controller.setState(&controllerState);
      102. PS3Controller.sendState();
      103. }
      Display All
      Files

      The post was edited 1 time, last by miisalo ().

    • Hi miisall
      thanks for this I've been looking for info re driving a PS3 from an Arduino.
      Im designing equipment to let severely disabled play console games so this is great.

      Unfortunately when I try you sketch with the HIDD.cpp & USBAPI.h files copied I get a problem. Im using a Leonardo and IDE1.0.5


      C:\Users\Gandboss\Google Drive\SWare progs & ref\Arduino IDE + MoJo Libs\Arduino 1.0.5 with experimental PS3 addins\hardware\arduino\cores\arduino\CDC.cpp:133: error: prototype for 'void Serial_::begin(uint16_t)' does not match any in class 'Serial_'

      C:\Users\Gandboss\Google Drive\SWare progs & ref\Arduino IDE + MoJo Libs\Arduino 1.0.5 with experimental PS3 addins\hardware\arduino\cores\arduino\/USBAPI.h:80: error: candidates are: void Serial_::begin(long unsigned int, uint8_t)

      C:\Users\Gandboss\Google Drive\SWare progs & ref\Arduino IDE + MoJo Libs\Arduino 1.0.5 with experimental PS3 addins\hardware\arduino\cores\arduino\/USBAPI.h:79: error: void Serial_::begin(long unsigned int)

      C:\Users\Gandboss\Google Drive\SWare progs & ref\Arduino IDE + MoJo Libs\Arduino 1.0.5 with experimental PS3 addins\hardware\arduino\cores\arduino\CDC.cpp:141: error: no 'void Serial_::accept()' member function declared in class 'Serial_'

      Any ideas?

      cheers
      Graham
    • Graham wrote:

      Unfortunately when I try you sketch with the HIDD.cpp & USBAPI.h files copied I get a problem. Im using a Leonardo and IDE1.0.5
      Hi Graham

      This works with 1.6.5 IDE, I bet you cannot uses those files directly with 1.0.5. Let me try to install 1.0.5 to some (virtual) machine and I'll check how the HID and usbapi looks there.

      Edit: 1.0.5 has different interface for example Serial, so you cannot use newer usbapi.h, it has different prototypes for the functions implemented in the other files, you need to adapt the code for your version of files.

      The post was edited 1 time, last by miisalo ().

    • Thanks very much indeed Miikka Ill try that later.

      My expertise is in electronics and real time code and USB is a weakness of mine. I have spent a couple of years now designing a potential product (as a hobby) Ive called a MoJo that can detect fractional (almost invisible) movements from extremely disabled users that want to play games. My prototype works well and is helping a friend of mine see video below but I'm using a library that only provides basic PC joystick from a Arduino and breaks after 1.0.5.

      Direct connection to a PS3 would be great.

      Presently I need to use a device like a Titan One to achieve this at the moment thou admittedly it as allows PS4 and Xbox as well.

      Check out this CLICK - notice how little movement Giles has

      This was an early video he has gone onto complete several major games since.

      Its great as this sort of kit gets him into another world.

      Ive set up a little (just me) company called Celtic Magic that one day may be able to supply such kit customised for each user but it is likely to remain a hobby and a net loss - but worthwhile. You will get due credit if I can use your code - thanks again.

      Here is a sneak preview of the fraft web site for the MoJo product Celtic Magic - pre release site

      cheers
      Graham
    • Graham wrote:

      Thanks very much indeed Miikka Ill try that later.

      My expertise is in electronics and real time code and USB is a weakness of mine. I have spent a couple of years now designing a potential product (as a hobby) Ive called a MoJo that can detect fractional (almost invisible) movements from extremely disabled users that want to play games.
      That is awesome. If I can help you, I will.
    • Hello there

      I'm very interested by this small hid tweak, I've made an adapter to use my wii classic joystick on the ps3. I'm trying to replicate the magic bytes trick but I'm using Arduino IDE 1.8.1, and it seems that both c Files are very different now. If any of you guys is still working on this and has a working version compatible with last IDE, i would really appreciate

      regards
      Nicolas
    • I have been pretty busy and forgot that I have even made this post here. I did some research on never arduino core (IDE) on christmas and they do need new implementation for this. I need to check when I have time how this can be done. Anyway, I'm not going to sell these myself but if I update this to work with newer core (or just use old version of the IDE) and someone wants to build these, I can provide help. Maybe my friend @stt1 would be interested to build these adapters. Parts are cheap but of course there is the soldering work.