/* LOOKING AT THE PLUG ------------------------------- PIN 1->| o o o | o o o | o o o | \_____________________________/ PIN # USAGE (colors from my extension cable, check your own) DATA - brown CMD/COMMAND - orange N/C (9 Volts unused) - white GND - black 5V VCC - red ATT - yellow CLK/CLOCK - blue N/C - - ACK - green */ #include "Arduino.h" #include "pins.h" #define DATA1 OR_PIN_2 #define CMD1 OR_PIN_3 #define ATT1 OR_PIN_4 #define CLK1 OR_PIN_5 #if defined(ARDUINO_ARCH_ESP32) #define CTRL_BYTE_DELAY 18 #else #define CTRL_BYTE_DELAY 6 #endif // esp32 vs micro delay #ifndef GAMEPAD_COUNT #define GAMEPAD_COUNT 4 #endif // not counting dpad #define BUTTON_COUNT 12 #define JOYSTICK_STATE_SIZE 6 #define AXIS_CENTER_IN 128 #define AXIS_MAX_IN 255 #define AXIS_MIN_IN 0 //#define DEBUG #include "gamepad/Gamepad.h" #include "util.cpp" GAMEPAD_CLASS gamepad; enum { // in data[1] // in digital mode, right joysticks are these 4 buttons too PS_BTN_O = 8192, PS_BTN_X = 16384, PS_BTN_SQUARE = 32768, PS_BTN_TRIANGLE = 4096, // shoulder PS_BTN_R1 = 2048, PS_BTN_R2 = 512, PS_BTN_L1 = 1024, PS_BTN_L2 = 256, // in data[2] PS_BTN_START = 8, PS_BTN_SELECT = 1, // in digital mode, left joysticks are the d-pad PS_BTN_UP = 16, PS_BTN_DOWN = 64, PS_BTN_LEFT = 128, PS_BTN_RIGHT = 32, PS_BTN_R3 = 4, PS_BTN_L3 = 2, }; // pressing one of these buttons on the controller... (read below) static const uint16_t translateFromButton[BUTTON_COUNT] = { PS_BTN_O, PS_BTN_X, PS_BTN_SQUARE, PS_BTN_TRIANGLE, PS_BTN_R1, PS_BTN_R2, PS_BTN_L1, PS_BTN_L2, PS_BTN_START, PS_BTN_SELECT, PS_BTN_R3, PS_BTN_L3, }; // ... translates to one of these buttons over HID static const uint32_t translateToHid[BUTTON_COUNT] = { BUTTON_A, BUTTON_B, BUTTON_Y, BUTTON_X, BUTTON_R1, BUTTON_R2, BUTTON_L1, BUTTON_L2, BUTTON_START, BUTTON_SELECT, BUTTON_R3, BUTTON_L3, }; class Joystick_ { private: uint8_t olddata[JOYSTICK_STATE_SIZE]; public: uint8_t type; uint8_t data[JOYSTICK_STATE_SIZE]; Joystick_() { data[0] = 0; data[1] = 0; data[2] = 127; data[3] = 127; data[4] = 127; data[5] = 127; memcpy(olddata, data, JOYSTICK_STATE_SIZE); } bool down(int button) const { return *(uint16_t*)(data)&button; } void updateState(uint8_t c) { if (type != 0x73 && type != 0x53) { data[2] = 127; data[3] = 127; data[4] = 127; data[5] = 127; } if (type == 0x41 || type == 0x73 || type == 0x53) { if (memcmp(olddata, data, JOYSTICK_STATE_SIZE)) { memcpy(olddata, data, JOYSTICK_STATE_SIZE); sendState(c); } } } signed char getHat() { if (down(PS_BTN_DOWN)) { if (down(PS_BTN_RIGHT)) { return DPAD_DOWN_RIGHT; } else if (down(PS_BTN_LEFT)) { return DPAD_DOWN_LEFT; } else { return DPAD_DOWN; } } else if (down(PS_BTN_UP)) { if (down(PS_BTN_RIGHT)) { return DPAD_UP_RIGHT; } else if (down(PS_BTN_LEFT)) { return DPAD_UP_LEFT; } else { return DPAD_UP; } } else if (down(PS_BTN_RIGHT)) { return DPAD_RIGHT; } else if (down(PS_BTN_LEFT)) { return DPAD_LEFT; } else { return DPAD_CENTERED; } } #ifdef DEBUG void printBin(uint8_t c) { //Serial.print(c); uint8_t mask = 1; for (uint8_t _i = 0; _i <= 7; _i++) { Serial.print((c & mask) ? "1" : "0"); mask *= 2; } } #endif void sendState(uint8_t c) { #ifdef DEBUG Serial.print(c); Serial.print(": type: 0x"); Serial.print(type, HEX); Serial.print(" hex: 0x"); Serial.print(data[0], HEX); Serial.print(" 0x"); Serial.print(data[1], HEX); Serial.print(" 0x"); Serial.print(data[2], HEX); Serial.print(" 0x"); Serial.print(data[3], HEX); Serial.print(" 0x"); Serial.print(data[4], HEX); Serial.print(" 0x"); Serial.print(data[5], HEX); Serial.print(" dec: "); printBin(data[0]); Serial.print(" "); printBin(data[1]); Serial.print(" "); printBin(data[2]); Serial.print(" "); printBin(data[3]); Serial.print(" "); printBin(data[4]); Serial.print(" "); printBin(data[5]); Serial.println(); uint8_t mask = 1; for (uint8_t _i = 0; _i <= 8; _i++) { if (data[0] & mask) { Serial.print("0 db: "); Serial.println(mask); } if (data[1] & mask) { Serial.print("1 db: "); Serial.println(mask); } mask *= 2; } uint16_t mask2 = 1; for (uint8_t _i = 0; _i <= 32; _i++) { if (down(mask2)) { Serial.print("db: "); Serial.println(mask2); } mask2 *= 2; } Serial.flush(); #endif //gamepad.buttons(i, *(uint16_t*)(&data[0])); //gamepad.setHatSync(i, DPAD_CENTERED); gamepad.buttons(c, 0); // if start and select are held at the same time, send menu and only menu if (down(PS_BTN_START) && down(PS_BTN_SELECT)) { gamepad.press(c, BUTTON_MENU); } else { // actually send buttons held for (uint8_t btn = 0; btn < BUTTON_COUNT; btn++) { if (down(translateFromButton[btn])) { gamepad.press(c, translateToHid[btn]); } } } gamepad.setAxis(c, translateAxis(data[4]), translateAxis(data[5]), translateAxis(data[2]), translateAxis(data[3]), 0, 0, getHat()); } }; Joystick_ Joystick[GAMEPAD_COUNT]; uint8_t shift(uint8_t _dataOut) // Does the actual shifting, both in and out simultaneously { uint8_t _temp = 0; uint8_t _dataIn = 0; delayMicroseconds(100); //max acknowledge waiting time 100us for (uint8_t _i = 0; _i <= 7; _i++) { if (_dataOut & (1 << _i)) // write bit digitalWrite(CMD1, HIGH); else digitalWrite(CMD1, LOW); digitalWrite(CLK1, LOW); // read bit delayMicroseconds(CTRL_BYTE_DELAY); _temp = digitalRead(DATA1); if (_temp) { _dataIn = _dataIn | (B00000001 << _i); } digitalWrite(CLK1, HIGH); delayMicroseconds(CTRL_BYTE_DELAY); } return _dataIn; } void setup() { #ifdef DEBUG Serial.begin(115200); #endif gamepad.begin(); pinMode(DATA1, INPUT_PULLUP); pinMode(CMD1, OUTPUT); pinMode(ATT1, OUTPUT); pinMode(CLK1, OUTPUT); } void loop() { // http://problemkaputt.de/psx-spx.htm#controllerandmemorycardsignals uint8_t head, padding, multitap; // first: read gamepad normally digitalWrite(ATT1, LOW); //digitalWrite(ATT2, LOW); head = shift(0x01); Joystick[0].type = shift(0x42); padding = shift(0x01); //read multitap in next command Joystick[0].data[0] = ~shift(0x00); //buttons Joystick[0].data[1] = ~shift(0x00); //buttons Joystick[0].data[2] = shift(0x00); //right analog Joystick[0].data[3] = shift(0x00); //right analog Joystick[0].data[4] = shift(0x00); //left analog Joystick[0].data[5] = shift(0x00); //left analog digitalWrite(ATT1, HIGH); //digitalWrite(ATT2, HIGH); //delay(100); // second: check and read multitap digitalWrite(ATT1, LOW); head = shift(0x01); multitap = shift(0x42); padding = shift(0x00); //next time normal read if (multitap == 0x80) { for (uint8_t i = 0; i < GAMEPAD_COUNT; i++) { Joystick[i].type = shift(0x00); padding = shift(0x00); Joystick[i].data[0] = ~shift(0x00); //buttons Joystick[i].data[1] = ~shift(0x00); //buttons Joystick[i].data[2] = shift(0x00); //right analog Joystick[i].data[3] = shift(0x00); //right analog Joystick[i].data[4] = shift(0x00); //left analog Joystick[i].data[5] = shift(0x00); //left analog } } digitalWrite(ATT1, HIGH); #ifdef DEBUGE Serial.print(" multitap: "); Serial.println(multitap, HEX); #endif for (uint8_t i = 0; i < GAMEPAD_COUNT; i++) { Joystick[i].updateState(i); } delayMicroseconds(500); // todo: proper value for this... does it depend on number of gamepads/multitap ? }