Last active
August 29, 2015 14:15
-
-
Save grapefrukt/c391ca6be3e7b450f136 to your computer and use it in GitHub Desktop.
This is the code I use for my Teensy 2.0++ Joystick interface in my Arcade machine.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define STATIC_ASSERT(COND,MSG) typedef char static_assertion_##MSG[(COND)?1:-1] | |
#define CHECK_SIZE(a) (sizeof(a)/sizeof(a[0])) | |
#define NUM_DIR 4 | |
#define NUM_BUT 6 | |
#define NUM_INPUT (NUM_DIR + NUM_BUT) | |
#define LEFT 0 | |
#define RIGHT 1 | |
#define UP 2 | |
#define DOWN 3 | |
#define BUTTON_0 4 | |
#define BUTTON_1 5 | |
#define BUTTON_2 6 | |
#define BUTTON_3 7 | |
#define BUTTON_START 8 | |
#define BUTTON_CREDIT 9 | |
#define X_AXIS 0 | |
#define Y_AXIS 1 | |
// this index in the player 1 keymap will act as a shift button (typically set to start) | |
#define SHIFT_BUTTON BUTTON_START | |
#define LOOP_DELAY_MS 2 | |
#define SLEEP_THRESHOLD 3000 | |
#define KEY_PRESS_DURATION_MS 100 | |
// buttons are: left, right, up, down, 1, 2, 3, 4, start, credit | |
const int map1[] = { 2, 1, 4, 3, 5, 32, 7, 8, 9, 11 }; | |
const int map2[] = { 20, 19, 22, 21, 24, 25, 23, 26, 10, 12 }; | |
// make sure the button/direction counts are correct | |
STATIC_ASSERT(CHECK_SIZE(map1) == NUM_INPUT, player1); | |
STATIC_ASSERT(CHECK_SIZE(map2) == NUM_INPUT, player2); | |
// stores the current input state, double buffered to be able to keep track of changes | |
bool state1a[NUM_INPUT]; | |
bool state1b[NUM_INPUT]; | |
bool state2a[NUM_INPUT]; | |
bool state2b[NUM_INPUT]; | |
// these pointers swap back and forth between the buffers | |
bool* state1 = state1a; | |
bool* state2 = state2a; | |
bool* state1_back = state1b; | |
bool* state2_back = state2b; | |
int sleepHeld = 0; | |
void setup() { | |
// we decide when to send joystick updates | |
Joystick.useManualSend(true); | |
// set all mapped pins to input using a pullup | |
setMode(map1, INPUT_PULLUP); | |
setMode(map2, INPUT_PULLUP); | |
} | |
void setMode(const int map[], int mode){ | |
for (int i = 0; i < NUM_INPUT; i++) pinMode(map[i], mode); | |
} | |
void readPlayer(const int map[], bool state[]) { | |
for (int i = 0; i < NUM_INPUT; i++) state[i] = !digitalRead(map[i]); | |
} | |
void swapBuffers() { | |
state1 = (state1 == state1a) ? state1b : state1a; | |
state2 = (state2 == state2a) ? state2b : state2a; | |
state1_back = (state1 == state1a) ? state1b : state1a; | |
state2_back = (state2 == state2a) ? state2b : state2a; | |
} | |
void updateAxis(const int player, const int axis) { | |
int mapIndex = 0; | |
if (axis == Y_AXIS) mapIndex += 2; | |
bool* state = player == 1 ? state1 : state2; | |
bool* state_back = player == 1 ? state1_back : state2_back; | |
// front and back buffers are the same, bail | |
if (state[mapIndex] == state_back[mapIndex] && state[mapIndex + 1] == state_back[mapIndex + 1]) return; | |
int value = 512; | |
if (state[mapIndex]) value = 0; | |
if (state[mapIndex + 1]) value = 1024; | |
if (player == 1 && axis == X_AXIS) Joystick.X(value); | |
else if (player == 1 && axis == Y_AXIS) Joystick.Y(value); | |
else if (player == 2 && axis == X_AXIS) Joystick.Z(value); | |
else if (player == 2 && axis == Y_AXIS) Joystick.Zrotate(value); | |
} | |
void updateButtons(const int player) { | |
int mapIndex = NUM_DIR; | |
bool* state = player == 1 ? state1 : state2; | |
bool* state_back = player == 1 ? state1_back : state2_back; | |
for (int i = 0; i < NUM_BUT; i++) { | |
// front and back buffers are the same, skip this | |
if (state[mapIndex + i] == state_back[mapIndex + i]) continue; | |
Joystick.button(1 + NUM_BUT * (player - 1) + i, state[mapIndex + i]); | |
} | |
} | |
bool onDown(const int player, const int buttonIndex) { | |
bool* state = player == 1 ? state1 : state2; | |
bool* state_back = player == 1 ? state1_back : state2_back; | |
// returns true if this button is pressed and was not pressed last update | |
return state[buttonIndex] && state[buttonIndex] != state_back[buttonIndex]; | |
} | |
void mediaKey(const int key) { | |
Keyboard.set_media(key); | |
Keyboard.send_now(); | |
delay(KEY_PRESS_DURATION_MS); | |
Keyboard.set_media(0); | |
Keyboard.send_now(); | |
} | |
void regularKey(const int key) { | |
Keyboard.set_key1(key); | |
Keyboard.send_now(); | |
delay(KEY_PRESS_DURATION_MS); | |
Keyboard.set_key1(0); | |
Keyboard.send_now(); | |
} | |
void modifiedKey(const int key) { | |
Keyboard.set_modifier(MODIFIERKEY_SHIFT | MODIFIERKEY_ALT); | |
Keyboard.set_key1(key); | |
Keyboard.send_now(); | |
delay(KEY_PRESS_DURATION_MS); | |
Keyboard.set_modifier(0); | |
Keyboard.set_key1(0); | |
Keyboard.send_now(); | |
} | |
void loop() { | |
// swap the input buffers | |
swapBuffers(); | |
readPlayer(map1, state1); | |
readPlayer(map2, state2); | |
if (state1[SHIFT_BUTTON] && state1_back[SHIFT_BUTTON]) { | |
if (onDown(1, BUTTON_0)) regularKey(KEY_5); | |
if (onDown(1, BUTTON_2)) regularKey(KEY_TAB); | |
if (onDown(2, BUTTON_START)) regularKey(KEY_ESC); | |
if (onDown(1, UP)) modifiedKey(KEY_UP); | |
if (onDown(1, DOWN)) modifiedKey(KEY_DOWN); | |
if (state2[SHIFT_BUTTON]) { | |
sleepHeld += LOOP_DELAY_MS; | |
if (sleepHeld > SLEEP_THRESHOLD) { | |
sleepHeld = 0; | |
modifiedKey(KEY_F1); | |
} | |
} else { | |
sleepHeld = 0; | |
} | |
} else { | |
updateAxis(1, X_AXIS); | |
updateAxis(1, Y_AXIS); | |
updateAxis(2, X_AXIS); | |
updateAxis(2, Y_AXIS); | |
updateButtons(1); | |
updateButtons(2); | |
} | |
Joystick.send_now(); | |
delay(LOOP_DELAY_MS); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment