|
|
|
|
|
|
|
#include "Arduino.h"
|
|
|
|
|
|
|
|
#ifndef GAMEPAD_COUNT
|
|
|
|
#define GAMEPAD_COUNT 2
|
|
|
|
#endif
|
|
|
|
#define BUTTON_COUNT 12 // SNES has 12, NES only has 8
|
|
|
|
|
|
|
|
#include "pins.h"
|
|
|
|
|
|
|
|
//shared pins between all controllers
|
|
|
|
static const int LATCH_PIN = OR_PIN_1; // brown
|
|
|
|
static const int CLOCK_PIN = OR_PIN_2; // white
|
|
|
|
|
|
|
|
//individual data pin for each controller
|
|
|
|
static const int DATA_PIN[GAMEPAD_COUNT] = {
|
|
|
|
OR_PIN_3,
|
|
|
|
#if GAMEPAD_COUNT > 1
|
|
|
|
OR_PIN_4,
|
|
|
|
#endif
|
|
|
|
#if GAMEPAD_COUNT > 2
|
|
|
|
OR_PIN_5,
|
|
|
|
#endif
|
|
|
|
#if GAMEPAD_COUNT > 3
|
|
|
|
OR_PIN_6,
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
// power red, ground black
|
|
|
|
|
|
|
|
//#define DEBUG
|
|
|
|
|
|
|
|
#include "gamepad/Gamepad.h"
|
|
|
|
|
|
|
|
static const uint8_t translateToNES[12] = {1, 8, 2, 3, 4, 5, 6, 7, 0, 8, 8, 8};
|
|
|
|
|
|
|
|
static const uint8_t nesButtons[4] = {0, 2, 3, 8};
|
|
|
|
static const uint8_t snesButtons[8] = {0, 1, 2, 3, 8, 9, 10, 11};
|
|
|
|
|
|
|
|
class GameControllers {
|
|
|
|
public:
|
|
|
|
enum Type
|
|
|
|
{
|
|
|
|
NES = 0,
|
|
|
|
SNES = 1,
|
|
|
|
};
|
|
|
|
|
|
|
|
enum Button
|
|
|
|
{
|
|
|
|
B = 0,
|
|
|
|
Y = 1,
|
|
|
|
SELECT = 2,
|
|
|
|
START = 3,
|
|
|
|
UP = 4,
|
|
|
|
DOWN = 5,
|
|
|
|
LEFT = 6,
|
|
|
|
RIGHT = 7,
|
|
|
|
A = 8,
|
|
|
|
X = 9,
|
|
|
|
L = 10,
|
|
|
|
R = 11,
|
|
|
|
};
|
|
|
|
Type types[GAMEPAD_COUNT];
|
|
|
|
int latchPin;
|
|
|
|
int clockPin;
|
|
|
|
int dataPins[GAMEPAD_COUNT];
|
|
|
|
long buttons[GAMEPAD_COUNT][12];
|
|
|
|
int maxButtons = 12;
|
|
|
|
|
|
|
|
int changedControllers[GAMEPAD_COUNT];
|
|
|
|
|
|
|
|
///This has to be initialized once for the shared pins latch and clock
|
|
|
|
void init(int latch, int clock) {
|
|
|
|
latchPin = latch;
|
|
|
|
clockPin = clock;
|
|
|
|
pinMode(latchPin, OUTPUT);
|
|
|
|
digitalWrite(latchPin, LOW);
|
|
|
|
pinMode(clockPin, OUTPUT);
|
|
|
|
digitalWrite(clockPin, HIGH);
|
|
|
|
}
|
|
|
|
|
|
|
|
///This sets the controller type and initializes its individual data pin
|
|
|
|
void setController(int controller, Type type, int dataPin) {
|
|
|
|
types[controller] = type;
|
|
|
|
dataPins[controller] = dataPin;
|
|
|
|
pinMode(dataPins[controller], INPUT_PULLUP);
|
|
|
|
for (int i = 0; i < 12; i++) {
|
|
|
|
buttons[controller][i] = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void poll(void (*controllerChanged)(const int controller)) {
|
|
|
|
memset(changedControllers, 0, sizeof(changedControllers));
|
|
|
|
|
|
|
|
digitalWrite(latchPin, HIGH);
|
|
|
|
delayMicroseconds(12);
|
|
|
|
digitalWrite(latchPin, LOW);
|
|
|
|
delayMicroseconds(6);
|
|
|
|
//Serial.print("snes: ");
|
|
|
|
for (int i = 0; i < maxButtons; i++) {
|
|
|
|
for (int c = 0; c < GAMEPAD_COUNT; c++) {
|
|
|
|
if (digitalRead(dataPins[c])) {
|
|
|
|
// up
|
|
|
|
//Serial.print("-");
|
|
|
|
if (-1 != buttons[c][i]) {
|
|
|
|
buttons[c][i] = -1;
|
|
|
|
changedControllers[c] = 1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// down
|
|
|
|
//Serial.print(i);
|
|
|
|
//++buttons[c][i];
|
|
|
|
//changedControllers[c] = 1;
|
|
|
|
if (0 != buttons[c][i]) {
|
|
|
|
buttons[c][i] = 0;
|
|
|
|
changedControllers[c] = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
digitalWrite(clockPin, LOW);
|
|
|
|
delayMicroseconds(6);
|
|
|
|
digitalWrite(clockPin, HIGH);
|
|
|
|
delayMicroseconds(6);
|
|
|
|
}
|
|
|
|
//Serial.println();
|
|
|
|
|
|
|
|
for (int c = 0; c < GAMEPAD_COUNT; c++) {
|
|
|
|
// have any buttons changed state?
|
|
|
|
if (1 == changedControllers[c]) {
|
|
|
|
controllerChanged(c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t translate(int controller, uint8_t b) {
|
|
|
|
if (types[controller] == SNES)
|
|
|
|
return b;
|
|
|
|
return translateToNES[b];
|
|
|
|
}
|
|
|
|
|
|
|
|
///returns if button is currently down
|
|
|
|
bool down(int controller, uint8_t b) {
|
|
|
|
return buttons[controller][translate(controller, b)] >= 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void pressAll(int controller, void (*pushButton)(const int controller, const uint8_t btn)) {
|
|
|
|
if (types[controller] == SNES) {
|
|
|
|
for (uint8_t i = 0; i < sizeof(snesButtons); i++) {
|
|
|
|
if (buttons[controller][snesButtons[i]] >= 0) {
|
|
|
|
pushButton(controller, snesButtons[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (uint8_t i = 0; i < sizeof(nesButtons); i++) {
|
|
|
|
if (buttons[controller][translateToNES[nesButtons[i]]] >= 0) {
|
|
|
|
pushButton(controller, nesButtons[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
static const int translateToHid[12] = {
|
|
|
|
BUTTON_B,
|
|
|
|
BUTTON_Y,
|
|
|
|
BUTTON_SELECT,
|
|
|
|
BUTTON_START,
|
|
|
|
DPAD_UP,
|
|
|
|
DPAD_DOWN,
|
|
|
|
DPAD_LEFT,
|
|
|
|
DPAD_RIGHT,
|
|
|
|
BUTTON_A,
|
|
|
|
BUTTON_X,
|
|
|
|
BUTTON_L,
|
|
|
|
BUTTON_R,
|
|
|
|
};
|
|
|
|
|
|
|
|
GameControllers controllers;
|
|
|
|
|
|
|
|
GAMEPAD_CLASS gamepad;
|
|
|
|
|
|
|
|
void dummyControllerChanged(const int c) {
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
void controllerChangedDebug(const int c) {
|
|
|
|
Serial.print("controllerChanged!!!!: ");
|
|
|
|
Serial.println(c);
|
|
|
|
Serial.print("c: ");
|
|
|
|
Serial.print(c);
|
|
|
|
for (uint8_t btn = 0; btn < 12; btn++) {
|
|
|
|
Serial.print("; ");
|
|
|
|
Serial.print(btn);
|
|
|
|
Serial.print(", ");
|
|
|
|
Serial.print(controllers.buttons[c][controllers.translate(c, btn)]);
|
|
|
|
Serial.print(",");
|
|
|
|
Serial.print(controllers.buttons[c][btn]);
|
|
|
|
}
|
|
|
|
Serial.println("");
|
|
|
|
}
|
|
|
|
#endif // DEBUG
|
|
|
|
|
|
|
|
void setup() {
|
|
|
|
bool allNes = true;
|
|
|
|
#ifdef DEBUG
|
|
|
|
delay(5000);
|
|
|
|
#endif // DEBUG
|
|
|
|
gamepad.begin();
|
|
|
|
|
|
|
|
//initialize shared pins
|
|
|
|
controllers.init(LATCH_PIN, CLOCK_PIN);
|
|
|
|
|
|
|
|
//activate first controller and set the type to SNES
|
|
|
|
for (int c = 0; c < GAMEPAD_COUNT; c++) {
|
|
|
|
controllers.setController(c, GameControllers::SNES, DATA_PIN[c]);
|
|
|
|
}
|
|
|
|
|
|
|
|
// poll controllers once to detect NES vs SNES
|
|
|
|
controllers.poll(dummyControllerChanged);
|
|
|
|
|
|
|
|
for (int c = 0; c < GAMEPAD_COUNT; c++) {
|
|
|
|
// for NES, A+X+L+R are down always, re-initialize
|
|
|
|
if (controllers.down(c, GameControllers::A) && controllers.down(c, GameControllers::X) && controllers.down(c, GameControllers::L) && controllers.down(c, GameControllers::R)) {
|
|
|
|
#ifdef DEBUG
|
|
|
|
Serial.println("detected NES");
|
|
|
|
#endif // DEBUG
|
|
|
|
controllers.types[c] = GameControllers::NES;
|
|
|
|
} else {
|
|
|
|
#ifdef DEBUG
|
|
|
|
Serial.println("detected SNES");
|
|
|
|
#endif // DEBUG
|
|
|
|
allNes = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (allNes) {
|
|
|
|
#ifdef DEBUG
|
|
|
|
Serial.println("detected ONLY NES");
|
|
|
|
#endif // DEBUG
|
|
|
|
controllers.maxButtons = 8;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void pushButton(const int c, const uint8_t btn) {
|
|
|
|
gamepad.press(c, translateToHid[btn]);
|
|
|
|
}
|
|
|
|
|
|
|
|
void controllerChanged(const int c) {
|
|
|
|
#ifdef DEBUG
|
|
|
|
controllerChangedDebug(c);
|
|
|
|
#endif // DEBUG
|
|
|
|
|
|
|
|
gamepad.buttons(c, 0);
|
|
|
|
// if start and select are held at the same time, send menu and only menu
|
|
|
|
if (controllers.down(c, GameControllers::START) && controllers.down(c, GameControllers::SELECT)) {
|
|
|
|
gamepad.press(c, BUTTON_MENU);
|
|
|
|
} else {
|
|
|
|
// actually send buttons held
|
|
|
|
controllers.pressAll(c, pushButton);
|
|
|
|
}
|
|
|
|
if (controllers.down(c, GameControllers::DOWN)) {
|
|
|
|
if (controllers.down(c, GameControllers::RIGHT)) {
|
|
|
|
gamepad.setHatSync(c, DPAD_DOWN_RIGHT);
|
|
|
|
} else if (controllers.down(c, GameControllers::LEFT)) {
|
|
|
|
gamepad.setHatSync(c, DPAD_DOWN_LEFT);
|
|
|
|
} else {
|
|
|
|
gamepad.setHatSync(c, DPAD_DOWN);
|
|
|
|
}
|
|
|
|
} else if (controllers.down(c, GameControllers::UP)) {
|
|
|
|
if (controllers.down(c, GameControllers::RIGHT)) {
|
|
|
|
gamepad.setHatSync(c, DPAD_UP_RIGHT);
|
|
|
|
} else if (controllers.down(c, GameControllers::LEFT)) {
|
|
|
|
gamepad.setHatSync(c, DPAD_UP_LEFT);
|
|
|
|
} else {
|
|
|
|
gamepad.setHatSync(c, DPAD_UP);
|
|
|
|
}
|
|
|
|
} else if (controllers.down(c, GameControllers::RIGHT)) {
|
|
|
|
gamepad.setHatSync(c, DPAD_RIGHT);
|
|
|
|
} else if (controllers.down(c, GameControllers::LEFT)) {
|
|
|
|
gamepad.setHatSync(c, DPAD_LEFT);
|
|
|
|
} else {
|
|
|
|
gamepad.setHatSync(c, DPAD_CENTERED);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void loop() {
|
|
|
|
if (gamepad.isConnected()) {
|
|
|
|
controllers.poll(controllerChanged); //read all controllers at once
|
|
|
|
}
|
|
|
|
}
|