|
|
|
/*
|
|
|
|
N64:
|
|
|
|
LOOKING AT THE PLUG FROM CONTROLLER
|
|
|
|
. ~~~ .
|
|
|
|
.-~ ~-.
|
|
|
|
/ o o o \
|
|
|
|
/ 1 2 3 \
|
|
|
|
|_________________|
|
|
|
|
|
|
|
|
|
|
|
|
PIN # USAGE (colors from my extension cable, check your own)
|
|
|
|
|
|
|
|
1: GND - red
|
|
|
|
2: DATA - white
|
|
|
|
3: VCC +3.3V ONLY - black
|
|
|
|
|
|
|
|
GAMECUBE:
|
|
|
|
LOOKING AT THE PLUG ON FRONT OF CONSOLE (not coming from controller)
|
|
|
|
|
|
|
|
7
|
|
|
|
.-------------.
|
|
|
|
/ o \
|
|
|
|
/ \
|
|
|
|
| 1 2 3 |
|
|
|
|
| o o o |
|
|
|
|
| |
|
|
|
|
\ o o o /
|
|
|
|
\ 4 5 6 /
|
|
|
|
`-. .-'
|
|
|
|
~-._.-~
|
|
|
|
|
|
|
|
PIN # USAGE (colors from my extension cable, check your own)
|
|
|
|
|
|
|
|
1: VCC 5V rumble - grey
|
|
|
|
2: DATA - yellow
|
|
|
|
3: GND - black
|
|
|
|
4: GND - black
|
|
|
|
5: UNKNOWN
|
|
|
|
6: VCC 3.3V ONLY - red
|
|
|
|
7: GND - black
|
|
|
|
|
|
|
|
I tie all 3 grounds together
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "pins.h"
|
|
|
|
|
|
|
|
#define DATA_PIN OR_PIN_2
|
|
|
|
|
|
|
|
// how often to poll, 100? 14? polling must not occur faster than every 20 ms
|
|
|
|
#define POLL_DELAY 14
|
|
|
|
|
|
|
|
#include "Arduino.h"
|
|
|
|
|
|
|
|
#include "Nintendo.h"
|
|
|
|
|
|
|
|
//#define DEBUG
|
|
|
|
|
|
|
|
#ifdef GAMECUBE
|
|
|
|
|
|
|
|
/*
|
|
|
|
// these are my average values from my apparantly very bad wavebird controller
|
|
|
|
#define AXIS_CENTER_IN 135
|
|
|
|
#define AXIS_MAX_IN 230
|
|
|
|
#define AXIS_MIN_IN 30
|
|
|
|
|
|
|
|
#define TRIGGER_MAX_IN 236
|
|
|
|
#define TRIGGER_MIN_IN 36
|
|
|
|
*/
|
|
|
|
// these look like much more proper values from a pelican gc controller
|
|
|
|
#define AXIS_CENTER_IN 128
|
|
|
|
#define AXIS_MAX_IN 255
|
|
|
|
#define AXIS_MIN_IN 0
|
|
|
|
|
|
|
|
#define TRIGGER_MAX_IN 255
|
|
|
|
#define TRIGGER_MIN_IN 0
|
|
|
|
|
|
|
|
#else // N64
|
|
|
|
#define AXIS_CENTER_IN 0
|
|
|
|
#define AXIS_MAX_IN 80
|
|
|
|
#define AXIS_MIN_IN -80
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "gamepad/Gamepad.h"
|
|
|
|
#include "util.cpp"
|
|
|
|
|
|
|
|
#ifdef GAMECUBE
|
|
|
|
typedef CGamecubeController NintendoController;
|
|
|
|
typedef Gamecube_Report_t ControllerReport;
|
|
|
|
#define NINTENDO_REPORT_SIZE 8
|
|
|
|
#else // N64
|
|
|
|
typedef CN64Controller NintendoController;
|
|
|
|
typedef N64_Report_t ControllerReport;
|
|
|
|
#define NINTENDO_REPORT_SIZE 4
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Define a Controller
|
|
|
|
NintendoController controller(DATA_PIN);
|
|
|
|
|
|
|
|
uint8_t oldReport[NINTENDO_REPORT_SIZE];
|
|
|
|
|
|
|
|
GAMEPAD_CLASS gamepad;
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
void print_report(ControllerReport &controller) {
|
|
|
|
Serial.print("buttons: ");
|
|
|
|
Serial.print(controller.a ? "A" : "-");
|
|
|
|
Serial.print(controller.b ? "B" : "-");
|
|
|
|
Serial.print(controller.z ? "Z" : "-");
|
|
|
|
Serial.print(controller.l ? "L" : "-");
|
|
|
|
Serial.print(controller.r ? "R" : "-");
|
|
|
|
Serial.print(controller.start ? "S" : "-");
|
|
|
|
#ifdef GAMECUBE
|
|
|
|
Serial.print(controller.y ? "Y" : "-");
|
|
|
|
Serial.print(controller.x ? "X" : "-");
|
|
|
|
#endif
|
|
|
|
Serial.print(" DPAD: ");
|
|
|
|
Serial.print(controller.dup ? "U" : "-");
|
|
|
|
Serial.print(controller.ddown ? "D" : "-");
|
|
|
|
Serial.print(controller.dleft ? "L" : "-");
|
|
|
|
Serial.print(controller.dright ? "R" : "-");
|
|
|
|
Serial.print(" Y: ");
|
|
|
|
Serial.print(controller.yAxis);
|
|
|
|
Serial.print(" YT: ");
|
|
|
|
Serial.print(-translateAxis(controller.yAxis));
|
|
|
|
Serial.print(" X: ");
|
|
|
|
Serial.print(controller.xAxis);
|
|
|
|
Serial.print(" XT: ");
|
|
|
|
Serial.print(translateAxis(controller.xAxis));
|
|
|
|
#ifdef GAMECUBE
|
|
|
|
Serial.print(" CY: ");
|
|
|
|
Serial.print(controller.cyAxis);
|
|
|
|
Serial.print(" CYT: ");
|
|
|
|
Serial.print(-translateAxis(controller.cyAxis));
|
|
|
|
Serial.print(" CX: ");
|
|
|
|
Serial.print(controller.cxAxis);
|
|
|
|
Serial.print(" CXT: ");
|
|
|
|
Serial.print(translateAxis(controller.cxAxis));
|
|
|
|
Serial.print(" LEFT: ");
|
|
|
|
Serial.print(controller.left);
|
|
|
|
Serial.print(" TLEFT: ");
|
|
|
|
Serial.print(translateTrigger(controller.left));
|
|
|
|
Serial.print(" RIGHT: ");
|
|
|
|
Serial.print(controller.right);
|
|
|
|
Serial.print(" TRIGHT: ");
|
|
|
|
Serial.print(translateTrigger(controller.right));
|
|
|
|
#else // N64
|
|
|
|
Serial.print(" C: ");
|
|
|
|
Serial.print(controller.cup ? "U" : "-");
|
|
|
|
Serial.print(controller.cdown ? "D" : "-");
|
|
|
|
Serial.print(controller.cleft ? "L" : "-");
|
|
|
|
Serial.print(controller.cright ? "R" : "-");
|
|
|
|
#endif
|
|
|
|
Serial.println();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
void setup() {
|
|
|
|
// n64 low because it *should* be 3.3V
|
|
|
|
digitalWrite(DATA_PIN, LOW);
|
|
|
|
#ifdef DEBUG
|
|
|
|
Serial.begin(115200);
|
|
|
|
if (controller.begin()) {
|
|
|
|
Serial.println(F("controller.begin() success."));
|
|
|
|
} else {
|
|
|
|
Serial.println(F("controller.begin() fail."));
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
controller.begin();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
gamepad.begin();
|
|
|
|
}
|
|
|
|
|
|
|
|
void loop() {
|
|
|
|
delay(POLL_DELAY);
|
|
|
|
// Try to read the controller data
|
|
|
|
if (controller.read()) {
|
|
|
|
// Print Controller information
|
|
|
|
auto report = controller.getReport();
|
|
|
|
|
|
|
|
if (memcmp(oldReport, report.raw8, NINTENDO_REPORT_SIZE)) {
|
|
|
|
memcpy(oldReport, report.raw8, NINTENDO_REPORT_SIZE);
|
|
|
|
} else {
|
|
|
|
// nothing changed
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
print_report(report);
|
|
|
|
#endif
|
|
|
|
uint8_t c = 0; // for now just do 1 pad
|
|
|
|
gamepad.buttons(c, 0);
|
|
|
|
if (report.start) {
|
|
|
|
if (report.ddown) {
|
|
|
|
// then only send menu, nothing else
|
|
|
|
gamepad.press(c, BUTTON_MENU);
|
|
|
|
gamepad.setHatSync(c, DPAD_CENTER);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
gamepad.press(c, BUTTON_START);
|
|
|
|
}
|
|
|
|
if (report.a) {
|
|
|
|
gamepad.press(c, BUTTON_A);
|
|
|
|
}
|
|
|
|
if (report.b) {
|
|
|
|
gamepad.press(c, BUTTON_B);
|
|
|
|
}
|
|
|
|
if (report.z) {
|
|
|
|
gamepad.press(c, BUTTON_TL2);
|
|
|
|
}
|
|
|
|
if (report.l) {
|
|
|
|
gamepad.press(c, BUTTON_L);
|
|
|
|
}
|
|
|
|
if (report.r) {
|
|
|
|
gamepad.press(c, BUTTON_R);
|
|
|
|
}
|
|
|
|
auto hat = calculateDpadDirection(report.dup, report.ddown, report.dleft, report.dright);
|
|
|
|
#ifdef GAMECUBE
|
|
|
|
if (report.y) {
|
|
|
|
gamepad.press(c, BUTTON_Y);
|
|
|
|
}
|
|
|
|
if (report.x) {
|
|
|
|
gamepad.press(c, BUTTON_X);
|
|
|
|
}
|
|
|
|
gamepad.setAxis(c,
|
|
|
|
translateAxis(report.xAxis),
|
|
|
|
-translateAxis(report.yAxis),
|
|
|
|
translateAxis(report.cxAxis),
|
|
|
|
-translateAxis(report.cyAxis),
|
|
|
|
translateTrigger(report.left),
|
|
|
|
translateTrigger(report.right),
|
|
|
|
hat);
|
|
|
|
#else // N64
|
|
|
|
auto cHat = dpadToAxis(calculateDpadDirection(report.cup, report.cdown, report.cleft, report.cright));
|
|
|
|
gamepad.setAxis(c, translateAxis(report.xAxis), -translateAxis(report.yAxis), cHat.x, cHat.y, 0, 0, hat);
|
|
|
|
#endif
|
|
|
|
} else {
|
|
|
|
// Add debounce if reading failed
|
|
|
|
delay(100);
|
|
|
|
#ifdef DEBUG
|
|
|
|
Serial.println(F("Error reading controller."));
|
|
|
|
if (controller.begin()) {
|
|
|
|
Serial.println(F("controller.begin() success."));
|
|
|
|
} else {
|
|
|
|
Serial.println(F("controller.begin() fail."));
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
controller.begin();
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|