Add Nintendo Gamecube input support
This commit is contained in:
parent
bdf7732a64
commit
849a3e6941
|
@ -12,7 +12,7 @@
|
|||
# $board-$input-$output
|
||||
# supported values:
|
||||
# $board: micro, esp32
|
||||
# $input: snes, genesis, psx, n64, radio, debug
|
||||
# $input: snes, genesis, psx, n64, gc, radio, debug
|
||||
# $output: radio, usb, usbradio, bt, switchusb, debug
|
||||
# please note not all boards are compatible with all inputs/outputs
|
||||
# for example esp32 can only do bt
|
||||
|
@ -194,16 +194,19 @@ build_flags = ${in-n64-micro.build_flags} ${out-debug.build_flags}
|
|||
extends = micro, in-n64-micro, out-radio
|
||||
src_filter = ${in-n64-micro.src_filter} ${out-radio.src_filter}
|
||||
build_flags = ${in-n64-micro.build_flags} ${out-radio.build_flags}
|
||||
lib_deps = ${in-n64-micro.lib_deps}, ${out-radio.lib_deps}
|
||||
|
||||
[env:micro-n64-usbradio]
|
||||
extends = micro, in-n64-micro, out-usbradio
|
||||
src_filter = ${in-n64-micro.src_filter} ${out-usbradio.src_filter}
|
||||
build_flags = ${in-n64-micro.build_flags} ${out-usbradio.build_flags}
|
||||
lib_deps = ${in-n64-micro.lib_deps}, ${out-usbradio.lib_deps}
|
||||
|
||||
[env:micro-n64-switchusb]
|
||||
extends = micro, in-n64-micro, out-switchusb
|
||||
src_filter = ${in-n64-micro.src_filter} ${out-switchusb.src_filter}
|
||||
build_flags = ${in-n64-micro.build_flags} ${out-switchusb.build_flags}
|
||||
lib_deps = ${in-n64-micro.lib_deps}, ${out-switchusb.lib_deps}
|
||||
|
||||
# genesis input
|
||||
|
||||
|
@ -253,7 +256,7 @@ build_flags = ${in-genesis.build_flags} ${out-switchusb.build_flags}
|
|||
|
||||
[in-psx]
|
||||
src_filter = -<*> +<Playstation.cpp>
|
||||
build_flags = ${common.build_flags} -DGAMEPAD_INPUT=3 -DGAMEPAD_COUNT=4
|
||||
build_flags = ${common.build_flags} -DGAMEPAD_INPUT=5 -DGAMEPAD_COUNT=4
|
||||
|
||||
[env:esp32-psx-bt]
|
||||
extends = esp32, in-psx, out-bt
|
||||
|
@ -290,6 +293,42 @@ extends = micro, in-psx, out-switchusb
|
|||
src_filter = ${in-psx.src_filter} ${out-switchusb.src_filter}
|
||||
build_flags = ${in-psx.build_flags} ${out-switchusb.build_flags}
|
||||
|
||||
# gc input
|
||||
|
||||
[in-gc-micro]
|
||||
src_filter = -<*> +<N64Micro.cpp>
|
||||
build_flags = ${common.build_flags} -DGAMEPAD_INPUT=5 -DGAMEPAD_COUNT=1 -DGAMECUBE
|
||||
lib_deps = https://github.com/OpenRetroPad/Nintendo
|
||||
|
||||
[env:micro-gc-usb]
|
||||
extends = micro, in-gc-micro, out-usb
|
||||
src_filter = ${in-gc-micro.src_filter} ${out-usb.src_filter}
|
||||
build_flags = ${in-gc-micro.build_flags} ${out-usb.build_flags}
|
||||
|
||||
[env:micro-gc-debug]
|
||||
extends = micro, in-gc-micro, out-debug
|
||||
src_filter = ${in-gc-micro.src_filter} ${out-debug.src_filter}
|
||||
build_flags = ${in-gc-micro.build_flags} ${out-debug.build_flags}
|
||||
|
||||
[env:micro-gc-radio]
|
||||
extends = micro, in-gc-micro, out-radio
|
||||
src_filter = ${in-gc-micro.src_filter} ${out-radio.src_filter}
|
||||
build_flags = ${in-gc-micro.build_flags} ${out-radio.build_flags}
|
||||
lib_deps = ${in-gc-micro.lib_deps}, ${out-radio.lib_deps}
|
||||
|
||||
[env:micro-gc-usbradio]
|
||||
extends = micro, in-gc-micro, out-usbradio
|
||||
src_filter = ${in-gc-micro.src_filter} ${out-usbradio.src_filter}
|
||||
build_flags = ${in-gc-micro.build_flags} ${out-usbradio.build_flags}
|
||||
lib_deps = ${in-gc-micro.lib_deps}, ${out-usbradio.lib_deps}
|
||||
|
||||
[env:micro-gc-switchusb]
|
||||
extends = micro, in-gc-micro, out-switchusb
|
||||
src_filter = ${in-gc-micro.src_filter} ${out-switchusb.src_filter}
|
||||
build_flags = ${in-gc-micro.build_flags} ${out-switchusb.build_flags}
|
||||
lib_deps = ${in-gc-micro.lib_deps}, ${out-switchusb.lib_deps}
|
||||
|
||||
|
||||
# debug input
|
||||
|
||||
[in-debug]
|
||||
|
|
|
@ -3,12 +3,16 @@ OpenRetroPad
|
|||
|
||||
Adapt various input devices to various output devices.
|
||||
|
||||
Currently supported inputs: SNES/NES, Sega Genesis/Megadrive/Atari, Playstation (and PS2) Digital and Dual shock, Nintendo 64, Nintendo Gamecube
|
||||
|
||||
Currently supported outputs: bluetooth-hid gamepad, usb-hid gamepad, nintendo switch usb gamepad, wireless usb-hid gamepad over radio
|
||||
|
||||
Build using [PlatformIO](https://platformio.org/) using `pio run` or `pio run -e $board-$input-$output` for a specific target/env.
|
||||
|
||||
env's are laid out like `$board-$input-$output`
|
||||
supported values:
|
||||
* $board: micro, esp32
|
||||
* $input: snes, genesis, psx, n64, radio, debug
|
||||
* $input: snes, genesis, psx, n64, gc, radio, debug
|
||||
* $output: radio, usb, usbradio, switchusb, bt, debug
|
||||
* please note not all boards are compatible with all inputs/outputs, for example esp32 can only do bt, micro can only do radio or usb
|
||||
|
||||
|
|
159
src/N64Micro.cpp
159
src/N64Micro.cpp
|
@ -12,7 +12,7 @@ PIN # USAGE
|
|||
VCC +3.3V ONLY
|
||||
*/
|
||||
|
||||
#define DATA_PIN 2
|
||||
#define DATA_PIN 7
|
||||
|
||||
// how often to poll, 100? 14? polling must not occur faster than every 20 ms
|
||||
#define POLL_DELAY 14
|
||||
|
@ -23,23 +23,41 @@ PIN # USAGE
|
|||
|
||||
//#define DEBUG
|
||||
|
||||
#ifdef GAMECUBE
|
||||
#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
|
||||
|
||||
#else // N64
|
||||
#define AXIS_CENTER_IN 0
|
||||
#define AXIS_MAX_IN 100
|
||||
#define AXIS_MIN_IN -100
|
||||
#define AXIS_MAX_IN 80
|
||||
#define AXIS_MIN_IN -80
|
||||
#endif
|
||||
|
||||
#include "gamepad/Gamepad.h"
|
||||
#include "util.cpp"
|
||||
|
||||
// Define a N64 Controller
|
||||
CN64Controller N64Controller(DATA_PIN);
|
||||
#ifdef GAMECUBE
|
||||
typedef CGamecubeController NintendoController;
|
||||
typedef Gamecube_Report_t ControllerReport;
|
||||
#else // N64
|
||||
typedef CN64Controller NintendoController;
|
||||
typedef N64_Report_t ControllerReport;
|
||||
#endif
|
||||
|
||||
#define REPORT_SIZE 4
|
||||
uint8_t oldReport[REPORT_SIZE];
|
||||
// Define a Controller
|
||||
NintendoController controller(DATA_PIN);
|
||||
|
||||
#define NINTENDO_REPORT_SIZE 4
|
||||
uint8_t oldReport[NINTENDO_REPORT_SIZE];
|
||||
|
||||
GAMEPAD_CLASS gamepad;
|
||||
|
||||
#ifdef DEBUG
|
||||
void print_n64_report(N64_Report_t &controller, N64_Status_t &n64_status) {
|
||||
void print_report(ControllerReport &controller) {
|
||||
Serial.print("buttons: ");
|
||||
Serial.print(controller.a ? "A" : "-");
|
||||
Serial.print(controller.b ? "B" : "-");
|
||||
|
@ -47,56 +65,61 @@ void print_n64_report(N64_Report_t &controller, N64_Status_t &n64_status) {
|
|||
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" : "-");
|
||||
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));
|
||||
#endif
|
||||
Serial.println();
|
||||
/*
|
||||
// Print device information
|
||||
Serial.print(F("Device: "));
|
||||
switch (n64_status.device) {
|
||||
case NINTENDO_DEVICE_N64_NONE:
|
||||
Serial.println(F("No N64 Controller found!"));
|
||||
return;
|
||||
break;
|
||||
case NINTENDO_DEVICE_N64_WIRED:
|
||||
Serial.println(F("Original Nintendo N64 Controller"));
|
||||
break;
|
||||
|
||||
default:
|
||||
Serial.print(F("Unknown "));
|
||||
Serial.println(n64_status.device, HEX);
|
||||
break;
|
||||
}
|
||||
*/
|
||||
}
|
||||
#endif
|
||||
|
||||
void setup() {
|
||||
#ifdef DEBUG
|
||||
Serial.begin(115200);
|
||||
if (N64Controller.begin()) {
|
||||
Serial.println(F("N64 begin() success."));
|
||||
if (controller.begin()) {
|
||||
Serial.println(F("controller.begin() success."));
|
||||
} else {
|
||||
Serial.println(F("N64 begin() fail."));
|
||||
Serial.println(F("controller.begin() fail."));
|
||||
}
|
||||
#else
|
||||
N64Controller.begin();
|
||||
controller.begin();
|
||||
#endif
|
||||
|
||||
gamepad.begin();
|
||||
|
@ -105,25 +128,24 @@ void setup() {
|
|||
void loop() {
|
||||
delay(POLL_DELAY);
|
||||
// Try to read the controller data
|
||||
if (N64Controller.read()) {
|
||||
if (controller.read()) {
|
||||
// Print Controller information
|
||||
auto controller = N64Controller.getReport();
|
||||
auto report = controller.getReport();
|
||||
|
||||
if (memcmp(oldReport, controller.raw8, REPORT_SIZE)) {
|
||||
memcpy(oldReport, controller.raw8, REPORT_SIZE);
|
||||
if (memcmp(oldReport, report.raw8, NINTENDO_REPORT_SIZE)) {
|
||||
memcpy(oldReport, report.raw8, NINTENDO_REPORT_SIZE);
|
||||
} else {
|
||||
// nothing changed
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
auto status = N64Controller.getStatus();
|
||||
print_n64_report(controller, status);
|
||||
print_report(report);
|
||||
#endif
|
||||
uint8_t c = 0; // for now just do 1 pad
|
||||
gamepad.buttons(c, 0);
|
||||
if (controller.start) {
|
||||
if (controller.ddown) {
|
||||
if (report.start) {
|
||||
if (report.ddown) {
|
||||
// then only send menu, nothing else
|
||||
gamepad.press(c, BUTTON_MENU);
|
||||
gamepad.setHatSync(c, DPAD_CENTER);
|
||||
|
@ -131,36 +153,53 @@ void loop() {
|
|||
}
|
||||
gamepad.press(c, BUTTON_START);
|
||||
}
|
||||
if (controller.a) {
|
||||
if (report.a) {
|
||||
gamepad.press(c, BUTTON_A);
|
||||
}
|
||||
if (controller.b) {
|
||||
if (report.b) {
|
||||
gamepad.press(c, BUTTON_B);
|
||||
}
|
||||
if (controller.z) {
|
||||
if (report.z) {
|
||||
gamepad.press(c, BUTTON_TR);
|
||||
}
|
||||
if (controller.l) {
|
||||
if (report.l) {
|
||||
gamepad.press(c, BUTTON_L);
|
||||
}
|
||||
if (controller.r) {
|
||||
if (report.r) {
|
||||
gamepad.press(c, BUTTON_R);
|
||||
}
|
||||
auto hat = calculateDpadDirection(controller.dup, controller.ddown, controller.dleft, controller.dright);
|
||||
auto cHat = dpadToAxis(calculateDpadDirection(controller.cup, controller.cdown, controller.cleft, controller.cright));
|
||||
gamepad.setAxis(c, translateAxis(controller.xAxis), translateAxis(-controller.yAxis), cHat.x, cHat.y, 0, 0, hat);
|
||||
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(5000);
|
||||
delay(100);
|
||||
#ifdef DEBUG
|
||||
Serial.println(F("Error reading N64 controller."));
|
||||
if (N64Controller.begin()) {
|
||||
Serial.println(F("N64 begin() success."));
|
||||
Serial.println(F("Error reading controller."));
|
||||
if (controller.begin()) {
|
||||
Serial.println(F("controller.begin() success."));
|
||||
} else {
|
||||
Serial.println(F("N64 begin() fail."));
|
||||
Serial.println(F("controller.begin() fail."));
|
||||
}
|
||||
#else
|
||||
N64Controller.begin();
|
||||
controller.begin();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,6 +37,10 @@
|
|||
#define AXIS_MAX 255
|
||||
#define AXIS_MIN 0
|
||||
|
||||
// not supported by horipad yet...
|
||||
#define TRIGGER_MAX 255
|
||||
#define TRIGGER_MIN 0
|
||||
|
||||
#else
|
||||
|
||||
#define BUTTON_A 1
|
||||
|
@ -64,9 +68,12 @@
|
|||
#define DPAD_UP_LEFT 8
|
||||
|
||||
#define AXIS_CENTER 0
|
||||
#define AXIS_MAX 32768
|
||||
#define AXIS_MAX 32767
|
||||
#define AXIS_MIN -32767
|
||||
|
||||
#define TRIGGER_MAX 255
|
||||
#define TRIGGER_MIN 0
|
||||
|
||||
#endif // nintendo switch
|
||||
|
||||
// aliases
|
||||
|
@ -87,11 +94,17 @@
|
|||
#define AXIS_CENTER_IN 0
|
||||
#endif
|
||||
#ifndef AXIS_MAX_IN
|
||||
#define AXIS_MAX_IN 32768
|
||||
#define AXIS_MAX_IN 32767
|
||||
#endif
|
||||
#ifndef AXIS_MIN_IN
|
||||
#define AXIS_MIN_IN -32767
|
||||
#endif
|
||||
#ifndef TRIGGER_MAX_IN
|
||||
#define TRIGGER_MAX_IN 127
|
||||
#endif
|
||||
#ifndef TRIGGER_MIN_IN
|
||||
#define TRIGGER_MIN_IN -127
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
#define BUTTON_1 1
|
||||
|
|
32
src/util.cpp
32
src/util.cpp
|
@ -62,18 +62,36 @@ struct Axis dpadToAxis(uint8_t dpad) {
|
|||
return axis(AXIS_CENTER, AXIS_CENTER);
|
||||
}
|
||||
|
||||
/*
|
||||
long map(long x, long in_min, long in_max, long out_min, long out_max) {
|
||||
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
|
||||
inline long translate(long x, long in_min, long in_max, long out_min, long out_max) {
|
||||
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
|
||||
}
|
||||
*/
|
||||
|
||||
inline int16_t translateAxis(int16_t v) {
|
||||
inline int16_t translateAxis(long v) {
|
||||
// pin to max/min
|
||||
if (v <= AXIS_MIN_IN) {
|
||||
return AXIS_MIN;
|
||||
} else if (v >= AXIS_MAX_IN) {
|
||||
return AXIS_MAX;
|
||||
}
|
||||
// don't map at all if translation isn't required...
|
||||
#if AXIS_CENTER_IN == AXIS_CENTER && AXIS_MIN_IN == AXIS_MIN && AXIS_MAX_IN == AXIS_MAX
|
||||
return v; // noop
|
||||
#else
|
||||
//return v == AXIS_CENTER_IN ? AXIS_CENTER : map(v, AXIS_MIN_IN, AXIS_MAX_IN, AXIS_MIN, AXIS_MAX);
|
||||
return v == AXIS_CENTER_IN ? AXIS_CENTER : (v - AXIS_MIN_IN) * (AXIS_MAX - AXIS_MIN) / (AXIS_MAX_IN - AXIS_MIN_IN) + AXIS_MIN;
|
||||
return v == AXIS_CENTER_IN ? AXIS_CENTER : translate(v, AXIS_MIN_IN, AXIS_MAX_IN, AXIS_MIN, AXIS_MAX);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline uint8_t translateTrigger(long v) {
|
||||
// pin to max/min
|
||||
if (v <= TRIGGER_MIN_IN) {
|
||||
return TRIGGER_MIN;
|
||||
} else if (v >= TRIGGER_MAX_IN) {
|
||||
return TRIGGER_MAX;
|
||||
}
|
||||
// don't map at all if translation isn't required...
|
||||
#if TRIGGER_MIN_IN == TRIGGER_MIN && TRIGGER_MAX_IN == TRIGGER_MAX
|
||||
return v; // noop
|
||||
#else
|
||||
return translate(v, TRIGGER_MIN_IN, TRIGGER_MAX_IN, TRIGGER_MIN, TRIGGER_MAX);
|
||||
#endif
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue