Skip to content

Instantly share code, notes, and snippets.

@grapefrukt
Last active August 29, 2015 14:15
Show Gist options
  • Save grapefrukt/c391ca6be3e7b450f136 to your computer and use it in GitHub Desktop.
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.
#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