Skip to content

Instantly share code, notes, and snippets.

@mbilker
Created March 29, 2018 00:04
Show Gist options
  • Save mbilker/6b124c40b43247cddb1fe1725b5d38f7 to your computer and use it in GitHub Desktop.
Save mbilker/6b124c40b43247cddb1fe1725b5d38f7 to your computer and use it in GitHub Desktop.
/*
* IIDX Controller
* This code is developed for a Teense 3.1 by kiyoshigawa
* It is designed to be a fully functional input device for a beatmania IIDX custom controller.
* It is designed to run as a joystick, and as such this options must be selected as part of the board type.
*/
//Uncomment ENCODER_DEBUG to get serial feedback on encoder position.
//#define ENCODER_DEBUG
//Uncomment KEYPRESS_DEBUG to get feedback on key presses and releases sent
//#define KEYPRESS_DEBUG
//#define JOYSTICK_MODE
#define KEYBOARD_MODE
#include <Encoder.h>
#include <Bounce.h>
#define POSITIVE 1
#define STOPPED 0
#define NEGATIVE -1
//number of ms to ignore changes in button state after a change
#define BOUNCE_TIME 5
//number of bounce objects to iterate through
#define NUM_BUTTONS 9
//this is the minimum number of steps before the motion of the encoders registers. Decrease to make the wheel more sensitive, increase to make the wheel less sensitive. If this number is too low, the wheel may register hits when it is not being moved.
#define STEP_THRESHOLD 10
//this is the number of miliseconds from the last movement that will register as still pressing a key for scrolling purposes. If the wheel does not move for MOVE_TIMEOUT ms, it will stop pressing up or down. If the wheel changes direction before MOVE_TIMEOUT, it will still send the alternate direction as soon as the change is detected.
#define MOVE_TIMEOUT 100
#define ENCODER_SENSITIVITY (double) 16.0
//array of button pins
// Z S X D C F V G B
int p1_buttons[] = { 0, 7, 4, 16, 3, 2, 1, 5, 6};
int p1_leds[] = {23, 22, 21, 21, 20, 19, 18, 17, 24};
#ifdef KEYBOARD_MODE
int keyboard_map[] = {KEY_Z, KEY_S, KEY_X, KEY_D, KEY_C, KEY_F, KEY_V, KEY_G, KEY_B};
#endif
//if encoders are running backwards, swap pin a and b.
int left_knob_pin_a = 11;
int left_knob_pin_b = 12;
int right_knob_pin_a = 9;
int right_knob_pin_b = 10;
//init bounce objects for all buttons;
Bounce p1_b1 = Bounce(p1_buttons[0], BOUNCE_TIME);
Bounce p1_b2 = Bounce(p1_buttons[1], BOUNCE_TIME);
Bounce p1_b3 = Bounce(p1_buttons[2], BOUNCE_TIME);
Bounce p1_b4 = Bounce(p1_buttons[3], BOUNCE_TIME);
Bounce p1_b5 = Bounce(p1_buttons[4], BOUNCE_TIME);
Bounce p1_b6 = Bounce(p1_buttons[5], BOUNCE_TIME);
Bounce p1_b7 = Bounce(p1_buttons[6], BOUNCE_TIME);
Bounce p1_b8 = Bounce(p1_buttons[7], BOUNCE_TIME);
Bounce p1_b9 = Bounce(p1_buttons[8], BOUNCE_TIME);
//array of bounce objects to iterate through
Bounce button_array[] = { p1_b1, p1_b2, p1_b3, p1_b4, p1_b5, p1_b6, p1_b7, p1_b8, p1_b9 };
//init encoder objects
Encoder knobLeft(left_knob_pin_a, left_knob_pin_b);
Encoder knobRight(right_knob_pin_a, right_knob_pin_b);
//global variables used below
long position_left = 0;
long position_right = 0;
int x_axis = 0;
int y_axis = 0;
int direction_left = STOPPED;
int direction_right = STOPPED;
unsigned long last_left_move_time = 0;
unsigned long last_right_move_time = 0;
void setup() {
Serial.begin(9600);
#ifdef ENCODER_DEBUG
Serial.println("Two Knobs Encoder Test:");
#endif
#ifdef KEYPRESS_DEBUG
Serial.println("Keypress Testing:");
#endif
//num_buttons over 2 because I have 2 button arrays and each player has half the buttons
//for(int i=0; i<(NUM_BUTTONS/2); i++){
for(int i = 0; i < NUM_BUTTONS; i++){
pinMode(p1_buttons[i], INPUT_PULLUP);
pinMode(p1_leds[i], OUTPUT);
}
#ifdef JOYSTICK_MODE
//disable other joystick functions
Joystick.Z(512);
Joystick.Zrotate(512);
Joystick.sliderLeft(512);
Joystick.sliderRight(512);
Joystick.X(-1);
#endif
}
void loop() {
//normal controller operation - keypresses send joystick commands via USB, lighting control runs based on mode last selected.
update_encoders();
update_buttons();
encoder_key_press();
}
// check state of buttons for change (bounced) and update joystick status based on this.
void update_buttons(){
for (int i = 0; i < NUM_BUTTONS; i++) {
//check for updates on each button
if (button_array[i].update()) {
//if the button was released set it to 0
if (button_array[i].risingEdge()) {
#ifdef JOYSTICK_MODE
Joystick.button(i+1, 0); // +1 because joystick buttons are from 1-32, not 0-31.
#endif
#ifdef KEYBOARD_MODE
Keyboard.release(keyboard_map[i]);
#endif
#ifdef KEYPRESS_DEBUG
Serial.print("Released ");
Serial.println(i+1);
#endif
// Turn off the corresponding LED
digitalWrite(p1_leds[i], LOW);
}
//otherwise set it to pressed
else if (button_array[i].fallingEdge()) {
#ifdef JOYSTICK_MODE
Joystick.button(i+1, 1); // +1 because joystick buttons are from 1-32, not 0-31.
#endif
#ifdef KEYBOARD_MODE
Keyboard.press(keyboard_map[i]);
#endif
#ifdef KEYPRESS_DEBUG
Serial.print("Pressed ");
Serial.println(i+1);
#endif
// Turn on the corresponding LED
digitalWrite(p1_leds[i], HIGH);
}
}
}
}
//this will use the timing from the update_encoders() function to determine if the joystick should be pressed up, down, or not at all. Note that the joystick mapped buttons for up and down will be NUM_BUTTONS + 1, 2, 3, or 4, as the first NUM_BUTTONS buttons will be used by the keys, start, and select buttons.
void encoder_key_press(){
if(direction_left == POSITIVE){
//press left up button and release left down button
Joystick.button(NUM_BUTTONS+1, 1);
Joystick.button(NUM_BUTTONS+2, 0);
}
else if(direction_left == NEGATIVE){
//press left down button and release left up button
Joystick.button(NUM_BUTTONS+1, 0);
Joystick.button(NUM_BUTTONS+2, 1);
}
else if(direction_left == STOPPED){
//release both left up and left down buttons
Joystick.button(NUM_BUTTONS+1, 0);
Joystick.button(NUM_BUTTONS+2, 0);
}
if(direction_right == POSITIVE){
//press left up button and release left down button
Joystick.button(NUM_BUTTONS+3, 1);
Joystick.button(NUM_BUTTONS+4, 0);
}
else if(direction_right == NEGATIVE){
//press left down button and release left up button
Joystick.button(NUM_BUTTONS+3, 0);
Joystick.button(NUM_BUTTONS+4, 1);
}
else if(direction_right == STOPPED){
//release both left up and left down buttons
Joystick.button(NUM_BUTTONS+3, 0);
Joystick.button(NUM_BUTTONS+4, 0);
}
}
#ifdef JOYSTICK_MODE
void update_joystick_x(long position) {
x_axis = position % 1024;
if (x_axis < 0) {
x_axis = 1024 + x_axis;
}
Joystick.X(x_axis);
}
void update_joystick_y(long position) {
y_axis = position % 1024;
if (y_axis < 0) {
y_axis = 1024 + y_axis;
}
Joystick.Y(y_axis);
}
#endif
//this will update the time variables showing when the encoders last changed position.
void update_encoders(){
long new_left, new_right, diff_left, diff_right;
//unsigned long current_time = millis();
new_left = knobLeft.read() / ENCODER_SENSITIVITY;
new_right = knobRight.read() / ENCODER_SENSITIVITY;
#ifdef JOYSTICK_MODE
update_joystick_x(new_left);
update_joystick_y(new_right);
#endif
#ifdef KEYBOARD_MODE
diff_left = new_left - position_left;
diff_right = new_right - position_right;
Mouse.move(diff_left, diff_right);
#endif
/*
//if going positive direction
if (new_left > (position_left+STEP_THRESHOLD) ) {
direction_left = POSITIVE;
last_left_move_time = current_time;
print_encoder_position();
}
//if going negative direction
else if (new_left < (position_left-STEP_THRESHOLD) ) {
direction_left = NEGATIVE;
last_left_move_time = current_time;
print_encoder_position();
}
//if it has not changed position since MOVE_TIMEOUT, stop sending either up or down
else if ((current_time - last_left_move_time) > MOVE_TIMEOUT){
direction_left = STOPPED;
}
//if going positive direction
if (new_right > (position_right+STEP_THRESHOLD) ) {
direction_right = POSITIVE;
last_right_move_time = current_time;
print_encoder_position();
}
//if going negative direction
else if (new_right < (position_right-STEP_THRESHOLD) ) {
direction_right = NEGATIVE;
last_right_move_time = current_time;
print_encoder_position();
}
//if it has not changed position since MOVE_TIMEOUT, stop sending either up or down
else if ((current_time - last_right_move_time) > MOVE_TIMEOUT){
direction_right = STOPPED;
}
*/
position_left = new_left;
position_right = new_right;
// if the value approaches the max, gracefully reset values to 0.
if (abs(position_left) > 2000000000 || abs(position_right) > 2000000000) {
knobLeft.write(0);
position_left = 0;
knobRight.write(0);
position_right = 0;
}
}
void print_encoder_position(){
#ifdef ENCODER_DEBUG
Serial.print("Left = ");
Serial.print(position_left);
Serial.print(", Right = ");
Serial.print(position_right);
Serial.print(", X = ");
Serial.print(x_axis);
Serial.print(", Y = ");
Serial.print(y_axis);
Serial.println();
#endif
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment