|
|
|
/* -------------------------------------------------------------------------
|
|
|
|
Saturn controller socket (looking face-on at the front of the socket):
|
|
|
|
___________________
|
|
|
|
/ 1 2 3 4 5 6 7 8 9 \
|
|
|
|
|___________________|
|
|
|
|
|
|
|
|
Saturn controller plug (looking face-on at the front of the controller plug):
|
|
|
|
___________________
|
|
|
|
/ 9 8 7 6 5 4 3 2 1 \
|
|
|
|
|___________________|
|
|
|
|
|
|
|
|
Saturn
|
|
|
|
-----------------
|
|
|
|
1 VCC - black
|
|
|
|
2 DATA1 - white
|
|
|
|
3 DATA0 - gray
|
|
|
|
4 SEL1 - blue
|
|
|
|
5 SEL0 - green
|
|
|
|
6 TL (5V) - yellow
|
|
|
|
7 DATA3 - orange
|
|
|
|
8 DATA2 - red
|
|
|
|
9 GND - brown
|
|
|
|
|
|
|
|
NOTE: The receiver of the Retro Bit 2.4GHz controller needs to be plugged
|
|
|
|
in after the adapter has been connected to USB and the RETROBIT_WL
|
|
|
|
define needs to be uncommented.
|
|
|
|
------------------------------------------------------------------------- */
|
|
|
|
#include "Arduino.h"
|
|
|
|
|
|
|
|
#include <digitalWriteFast.h>
|
|
|
|
//#define digitalWriteFast digitalWrite
|
|
|
|
//#define digitalReadFast digitalRead
|
|
|
|
|
|
|
|
#ifndef GAMEPAD_COUNT
|
|
|
|
#define GAMEPAD_COUNT 2
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "gamepad/Gamepad.h"
|
|
|
|
|
|
|
|
// How many microseconds to wait after setting select lines? (2µs is enough according to the Saturn developer's manual)
|
|
|
|
// 20µs is a "safe" value that seems to work for original Saturn controllers and Retrobit wired controllers
|
|
|
|
#define SELECT_PAUSE 10
|
|
|
|
|
|
|
|
// Uncomment to support the Retro Bit 2.4GHz wireless controller (this will increase lag a lot)
|
|
|
|
//#define RETROBIT_WL
|
|
|
|
|
|
|
|
#include "pins.h"
|
|
|
|
|
|
|
|
// pins
|
|
|
|
#define P1_2 OR_PIN_2
|
|
|
|
#define P1_3 OR_PIN_3
|
|
|
|
#define P1_6 OR_PIN_4
|
|
|
|
#define P1_7 OR_PIN_1
|
|
|
|
#define P1_8 OR_PIN_11
|
|
|
|
|
|
|
|
#define PX_4 OR_PIN_6
|
|
|
|
#define PX_5 OR_PIN_5
|
|
|
|
|
|
|
|
#if GAMEPAD_COUNT == 2
|
|
|
|
|
|
|
|
#define P2_2 OR_PIN_20
|
|
|
|
#define P2_3 OR_PIN_21
|
|
|
|
#define P2_6 OR_PIN_10
|
|
|
|
#define P2_7 OR_PIN_18
|
|
|
|
#define P2_8 OR_PIN_19
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Set up USB HID gamepads
|
|
|
|
GAMEPAD_CLASS gamepad;
|
|
|
|
|
|
|
|
ScratchGamepad currentGamepad;
|
|
|
|
|
|
|
|
// Read R, X, Y, Z
|
|
|
|
void read1() {
|
|
|
|
// Set select outputs to 00
|
|
|
|
digitalWriteFast(PX_4, LOW);
|
|
|
|
digitalWriteFast(PX_5, LOW);
|
|
|
|
delayMicroseconds(SELECT_PAUSE);
|
|
|
|
if (!digitalReadFast(P1_3)) {
|
|
|
|
// Z
|
|
|
|
currentGamepad.press(0, BUTTON_R);
|
|
|
|
}
|
|
|
|
if (!digitalReadFast(P1_2)) {
|
|
|
|
// Y
|
|
|
|
currentGamepad.press(0, BUTTON_X);
|
|
|
|
}
|
|
|
|
if (!digitalReadFast(P1_8)) {
|
|
|
|
// X
|
|
|
|
currentGamepad.press(0, BUTTON_L);
|
|
|
|
}
|
|
|
|
if (!digitalReadFast(P1_7)) {
|
|
|
|
// R
|
|
|
|
currentGamepad.press(0, BUTTON_R2);
|
|
|
|
}
|
|
|
|
#if GAMEPAD_COUNT == 2
|
|
|
|
if (!digitalReadFast(P2_3)) {
|
|
|
|
// Z
|
|
|
|
currentGamepad.press(1, BUTTON_R);
|
|
|
|
}
|
|
|
|
if (!digitalReadFast(P2_2)) {
|
|
|
|
// Y
|
|
|
|
currentGamepad.press(1, BUTTON_X);
|
|
|
|
}
|
|
|
|
if (!digitalReadFast(P2_8)) {
|
|
|
|
// X
|
|
|
|
currentGamepad.press(1, BUTTON_L);
|
|
|
|
}
|
|
|
|
if (!digitalReadFast(P2_7)) {
|
|
|
|
// R
|
|
|
|
currentGamepad.press(1, BUTTON_R2);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read ST, A, C, B
|
|
|
|
void read2() {
|
|
|
|
// Toggle select outputs (01->10 or 10->01)
|
|
|
|
digitalWriteFast(PX_4, HIGH);
|
|
|
|
digitalWriteFast(PX_5, LOW);
|
|
|
|
delayMicroseconds(SELECT_PAUSE);
|
|
|
|
if (!digitalReadFast(P1_3)) {
|
|
|
|
// B
|
|
|
|
currentGamepad.press(0, BUTTON_B);
|
|
|
|
}
|
|
|
|
if (!digitalReadFast(P1_2)) {
|
|
|
|
// C
|
|
|
|
currentGamepad.press(0, BUTTON_A);
|
|
|
|
}
|
|
|
|
if (!digitalReadFast(P1_8)) {
|
|
|
|
// A
|
|
|
|
currentGamepad.press(0, BUTTON_Y);
|
|
|
|
}
|
|
|
|
if (!digitalReadFast(P1_7)) {
|
|
|
|
// ST
|
|
|
|
currentGamepad.press(0, BUTTON_START);
|
|
|
|
}
|
|
|
|
#if GAMEPAD_COUNT == 2
|
|
|
|
if (!digitalReadFast(P2_3)) {
|
|
|
|
// B
|
|
|
|
currentGamepad.press(1, BUTTON_B);
|
|
|
|
}
|
|
|
|
if (!digitalReadFast(P2_2)) {
|
|
|
|
// C
|
|
|
|
currentGamepad.press(1, BUTTON_A);
|
|
|
|
}
|
|
|
|
if (!digitalReadFast(P2_8)) {
|
|
|
|
// A
|
|
|
|
currentGamepad.press(1, BUTTON_Y);
|
|
|
|
}
|
|
|
|
if (!digitalReadFast(P2_7)) {
|
|
|
|
// ST
|
|
|
|
currentGamepad.press(1, BUTTON_START);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read DR, DL, DD, DU
|
|
|
|
void read3() {
|
|
|
|
// Set select outputs to 10 from 11 (toggle)
|
|
|
|
digitalWriteFast(PX_4, LOW);
|
|
|
|
digitalWriteFast(PX_5, HIGH);
|
|
|
|
delayMicroseconds(SELECT_PAUSE);
|
|
|
|
if (!digitalReadFast(P1_3)) {
|
|
|
|
// UP
|
|
|
|
currentGamepad.pressDpad(0, DPAD_BIT_UP);
|
|
|
|
}
|
|
|
|
if (!digitalReadFast(P1_2)) {
|
|
|
|
// DOWN
|
|
|
|
currentGamepad.pressDpad(0, DPAD_BIT_DOWN);
|
|
|
|
}
|
|
|
|
if (!digitalReadFast(P1_8)) {
|
|
|
|
// LEFT
|
|
|
|
currentGamepad.pressDpad(0, DPAD_BIT_LEFT);
|
|
|
|
}
|
|
|
|
if (!digitalReadFast(P1_7)) {
|
|
|
|
// RIGHT
|
|
|
|
currentGamepad.pressDpad(0, DPAD_BIT_RIGHT);
|
|
|
|
}
|
|
|
|
#if GAMEPAD_COUNT == 2
|
|
|
|
if (!digitalReadFast(P2_3)) {
|
|
|
|
// UP
|
|
|
|
currentGamepad.pressDpad(1, DPAD_BIT_UP);
|
|
|
|
}
|
|
|
|
if (!digitalReadFast(P2_2)) {
|
|
|
|
// DOWN
|
|
|
|
currentGamepad.pressDpad(1, DPAD_BIT_DOWN);
|
|
|
|
}
|
|
|
|
if (!digitalReadFast(P2_8)) {
|
|
|
|
// LEFT
|
|
|
|
currentGamepad.pressDpad(1, DPAD_BIT_LEFT);
|
|
|
|
}
|
|
|
|
if (!digitalReadFast(P2_7)) {
|
|
|
|
// RIGHT
|
|
|
|
currentGamepad.pressDpad(1, DPAD_BIT_RIGHT);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read L, *, *, *
|
|
|
|
void read4() {
|
|
|
|
// Set select outputs to 11
|
|
|
|
digitalWriteFast(PX_4, HIGH);
|
|
|
|
digitalWriteFast(PX_5, HIGH);
|
|
|
|
delayMicroseconds(SELECT_PAUSE);
|
|
|
|
if (!digitalReadFast(P1_7)) {
|
|
|
|
// L
|
|
|
|
currentGamepad.press(0, BUTTON_L2);
|
|
|
|
}
|
|
|
|
#if GAMEPAD_COUNT == 2
|
|
|
|
if (!digitalReadFast(P2_7)) {
|
|
|
|
// L
|
|
|
|
currentGamepad.press(1, BUTTON_L2);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void setup() {
|
|
|
|
gamepad.begin();
|
|
|
|
|
|
|
|
// Set P1 data pins as inputs and enable pull-up resistors
|
|
|
|
pinMode(P1_3, INPUT_PULLUP);
|
|
|
|
pinMode(P1_2, INPUT_PULLUP);
|
|
|
|
pinMode(P1_7, INPUT_PULLUP);
|
|
|
|
pinMode(P1_8, INPUT_PULLUP);
|
|
|
|
digitalWrite(P1_3, HIGH);
|
|
|
|
digitalWrite(P1_2, HIGH);
|
|
|
|
digitalWrite(P1_7, HIGH);
|
|
|
|
digitalWrite(P1_8, HIGH);
|
|
|
|
|
|
|
|
// Set P1 TL as input and enable pull-up resistor
|
|
|
|
pinMode(P1_6, INPUT_PULLUP);
|
|
|
|
digitalWrite(P1_6, HIGH);
|
|
|
|
|
|
|
|
#if GAMEPAD_COUNT == 2
|
|
|
|
// Set P2 data pins as inputs and enable pull-up resistors
|
|
|
|
pinMode(P2_3, INPUT_PULLUP);
|
|
|
|
pinMode(P2_2, INPUT_PULLUP);
|
|
|
|
pinMode(P2_7, INPUT_PULLUP);
|
|
|
|
pinMode(P2_8, INPUT_PULLUP);
|
|
|
|
digitalWrite(P2_3, HIGH);
|
|
|
|
digitalWrite(P2_2, HIGH);
|
|
|
|
digitalWrite(P2_7, HIGH);
|
|
|
|
digitalWrite(P2_8, HIGH);
|
|
|
|
|
|
|
|
// Set P2 TL as input and enable pull-up resistor
|
|
|
|
pinMode(P2_6, INPUT_PULLUP);
|
|
|
|
digitalWrite(P2_6, HIGH);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Set P1+P2 select pins as outputs and set them HIGH
|
|
|
|
pinMode(PX_4, OUTPUT);
|
|
|
|
pinMode(PX_5, OUTPUT);
|
|
|
|
digitalWrite(PX_4, HIGH);
|
|
|
|
digitalWrite(PX_5, HIGH);
|
|
|
|
|
|
|
|
// Wait for the controller(s) to settle
|
|
|
|
delay(100);
|
|
|
|
}
|
|
|
|
|
|
|
|
void loop() {
|
|
|
|
// Read all button and axes states
|
|
|
|
read3();
|
|
|
|
read2();
|
|
|
|
read1();
|
|
|
|
read4();
|
|
|
|
|
|
|
|
// Send data to USB if values have changed
|
|
|
|
for (uint8_t gp = 0; gp < GAMEPAD_COUNT; gp++) {
|
|
|
|
if (currentGamepad.changed(gp, gamepad)) {
|
|
|
|
const auto hat = gamepad.getHat(gp);
|
|
|
|
if (hat == DPAD_DOWN && gamepad.isPressed(gp, BUTTON_START)) {
|
|
|
|
gamepad.buttons(gp, 0);
|
|
|
|
gamepad.press(gp, BUTTON_MENU);
|
|
|
|
gamepad.setHatSync(gp, DPAD_CENTERED);
|
|
|
|
currentGamepad.changed(gp, gamepad);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
gamepad.setHatSync(gp, hat);
|
|
|
|
}
|
|
|
|
// Clear button data
|
|
|
|
currentGamepad.reset(gp);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef RETROBIT_WL
|
|
|
|
// This delay is needed for the retro bit 2.4GHz wireless controller, making it more or less useless with this adapter
|
|
|
|
delay(17);
|
|
|
|
#endif
|
|
|
|
}
|