795 lines
13 KiB
C++
795 lines
13 KiB
C++
//Sketch -> Include Library > Manage Library... > Search for "Encoder" then install it.
|
|
#include <Encoder.h>
|
|
|
|
// Arduino Uno Sound Voltex Controller Script
|
|
// By 5argon
|
|
// https://github.com/5argon/SDVXArduinoUno
|
|
// Contact - @5argondesu
|
|
|
|
|
|
//Uncomment to send serial button message to Arduino debug console.
|
|
//Serial message also activates pin 1 (TX), so if your config used pin 1 this may causes problems.
|
|
//#define SERIAL_DEBUGGING
|
|
|
|
// -- Setup your button connections to the Arduino Uno pins here. --
|
|
|
|
//Button switch. The other side should be connecting to GND. (Switch is in pull-up mode when not pressed)
|
|
int const BTA = 0;
|
|
int const BTB = 12;
|
|
int const BTC = 6;
|
|
int const BTD = 11;
|
|
int const FXL = 5;
|
|
int const FXR = 7;
|
|
int const START = 555; //My controller don't have a start button
|
|
|
|
//Button's LED. From Arduino pin to LED + resistor to GND.
|
|
int const BTLA = A5;
|
|
int const BTLB = A4;
|
|
int const BTLC = A2;
|
|
int const BTLD = A0;
|
|
int const FXLL = 8;
|
|
int const FXLR = 10;
|
|
int const STARTL = 556; //My controller don't have a start button
|
|
|
|
//Encoders. If you are using a Copal, the white cable is A and the green cable is B. (Red goes to 5V and black goes to GND)
|
|
//VOLL is the left encoder, VOLR is the right encoder. Please DO NOT use pin 13 on Uno. (LED connected pin)
|
|
|
|
//You MUST connect one of each encoder's white or green cable to pin 2 and 3. These 2 pins on an Uno are special external interrupt pins. (It's fast)
|
|
//For example, VOLLA = 2 and VOLLRA = 3. (both encoder's white). The green cable can choose any pin.
|
|
//In short, this will make both encoder good. Without this you cannot pass a chart like Firestorm EXH.
|
|
|
|
int const VOLLA = 2;
|
|
int const VOLLB = 4;
|
|
int const VOLRA = 3;
|
|
int const VOLRB = 9;
|
|
|
|
// -- Key mapping --
|
|
// Go to http://www.usb.org/developers/hidpage/Hut1_12v2.pdf page 53
|
|
// For the list of key specifications from official USB devs.
|
|
// Or this for Japanese keyboards http://www2d.biglobe.ne.jp/~msyk/keyboard/layout/usbkeycode.html (look at the last column, it's in HEX)
|
|
|
|
uint8_t keyBTA = 22; //s
|
|
uint8_t keyBTB = 7; //d
|
|
uint8_t keyBTC = 14; //k
|
|
uint8_t keyBTD = 15; //l
|
|
|
|
uint8_t keyFXL = 6; //c
|
|
uint8_t keyFXR = 16; //m
|
|
//日本人の方はこちらです
|
|
//uint8_t keyFXL = 0x8A; //無変換
|
|
//uint8_t keyFXR = 0x8B; //変換
|
|
|
|
|
|
uint8_t keySTART = 43; //Enter
|
|
|
|
uint8_t keyVOLLCCW = 20; //q
|
|
uint8_t keyVOLLCW = 26; //w
|
|
uint8_t keyVOLRCCW = 18; //o
|
|
uint8_t keyVOLRCW = 19; //p
|
|
|
|
// -- More options --
|
|
|
|
//If you experience double presses of buttons when you pressed only once, try increasing this value.
|
|
unsigned const long debounceDelay = 5;
|
|
//Bigger value makes the knob less sensitive. Note that it should not be 0. When it's 0 when you spin quickly the knob will report both CW and CCW directions.
|
|
//Value too low can also make the knob go either direction randomly when you just touch the knob.
|
|
unsigned const long spinTolerance = 4;
|
|
|
|
// ----
|
|
|
|
Encoder encL(VOLLA,VOLLB);
|
|
Encoder encR(VOLRA,VOLRB);
|
|
long encLPosition, encRPosition = 0;
|
|
|
|
uint8_t buf[8] = { 0 };
|
|
|
|
typedef enum KnobState {
|
|
CW,
|
|
CCW,
|
|
Stop
|
|
} KnobState;
|
|
|
|
bool BTAState, BTBState, BTCState, BTDState, FXLState, FXRState, StartState = LOW;
|
|
bool BTAHwState, BTBHwState, BTCHwState, BTDHwState, FXLHwState, FXRHwState, StartHwState = LOW;
|
|
KnobState VOLLState, VOLRState = Stop;
|
|
unsigned long BTADebounce, BTBDebounce, BTCDebounce, BTDDebounce, FXLDebounce, FXRDebounce, StartDebounce = 0;
|
|
|
|
|
|
|
|
void setup() {
|
|
// put your setup code here, to run once:
|
|
Serial.begin(9600);
|
|
|
|
setupButton(BTA);
|
|
setupButton(BTB);
|
|
setupButton(BTC);
|
|
setupButton(BTD);
|
|
setupButton(FXL);
|
|
setupButton(FXR);
|
|
|
|
setupLED(BTLA);
|
|
setupLED(BTLB);
|
|
setupLED(BTLC);
|
|
setupLED(BTLD);
|
|
setupLED(FXLL);
|
|
setupLED(FXLR);
|
|
|
|
pinMode(12,OUTPUT);
|
|
digitalWrite(12,HIGH);
|
|
|
|
}
|
|
|
|
void setupButton(int button)
|
|
{
|
|
pinMode(button, INPUT_PULLUP);
|
|
}
|
|
|
|
void setupLED(int led)
|
|
{
|
|
pinMode(led, OUTPUT);
|
|
}
|
|
|
|
void checkButton(int button)
|
|
{
|
|
int hardwareState = digitalRead(button);
|
|
|
|
if(hardwareState != getButtonHwState(button))
|
|
{
|
|
setDebounce(button,millis());
|
|
}
|
|
|
|
if((millis() - getDebounce(button)) > debounceDelay)
|
|
{
|
|
if(hardwareState == HIGH)
|
|
{
|
|
if(getButtonState(button) == HIGH)
|
|
{
|
|
lightOff(button);
|
|
setButtonState(button,LOW);
|
|
buttonReleaseAction(button);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(getButtonState(button) == LOW)
|
|
{
|
|
lightOn(button);
|
|
releaseKey(button);
|
|
setButtonState(button,HIGH);
|
|
buttonPressAction(button);
|
|
}
|
|
}
|
|
}
|
|
setButtonHwState(button,hardwareState);
|
|
}
|
|
|
|
void lightOff(int inputPin)
|
|
{
|
|
digitalWrite(buttonToLight(inputPin), LOW);
|
|
}
|
|
|
|
void lightOn(int inputPin)
|
|
{
|
|
digitalWrite(buttonToLight(inputPin), HIGH);
|
|
}
|
|
|
|
void checkKnob(int knobA, int knobB)
|
|
{
|
|
int readPosition;
|
|
if(knobA == VOLLA || knobB == VOLLB)
|
|
{
|
|
readPosition = encL.read();
|
|
|
|
if(readPosition > (long)(encLPosition + spinTolerance))
|
|
{
|
|
knobLActionCCW();
|
|
encLPosition = readPosition;
|
|
}
|
|
else if(readPosition < (long)(encLPosition - spinTolerance) )
|
|
{
|
|
knobLActionCW();
|
|
encLPosition = readPosition;
|
|
}
|
|
else
|
|
{
|
|
knobLActionStop();
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
readPosition = encR.read();
|
|
|
|
if(readPosition > (long)(encRPosition + spinTolerance))
|
|
{
|
|
knobRActionCCW();
|
|
encRPosition = readPosition;
|
|
}
|
|
else if(readPosition < (long)(encRPosition - spinTolerance) )
|
|
{
|
|
knobRActionCW();
|
|
encRPosition = readPosition;
|
|
}
|
|
else
|
|
{
|
|
knobRActionStop();
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
void knobLActionCW()
|
|
{
|
|
debugInput("Spin",VOLLA);
|
|
setVolLState(CW);
|
|
}
|
|
|
|
void knobLActionCCW()
|
|
{
|
|
debugInput("Spin",VOLLB);
|
|
setVolLState(CCW);
|
|
}
|
|
|
|
void knobRActionCW()
|
|
{
|
|
debugInput("Spin",VOLRA);
|
|
setVolRState(CW);
|
|
}
|
|
|
|
void knobRActionCCW()
|
|
{
|
|
debugInput("Spin",VOLRB);
|
|
setVolRState(CCW);
|
|
}
|
|
|
|
void knobLActionStop()
|
|
{
|
|
setVolLState(Stop);
|
|
}
|
|
|
|
void knobRActionStop()
|
|
{
|
|
setVolRState(Stop);
|
|
}
|
|
|
|
void setVolLState(KnobState state)
|
|
{
|
|
//only set and send if state changes.
|
|
if(VOLLState != state)
|
|
{
|
|
VOLLState = state;
|
|
serialFromState();
|
|
}
|
|
}
|
|
|
|
void setVolRState(KnobState state)
|
|
{
|
|
//only set and send if state changes.
|
|
if(VOLRState != state)
|
|
{
|
|
VOLRState = state;
|
|
serialFromState();
|
|
}
|
|
}
|
|
|
|
void buttonPressAction(int button)
|
|
{
|
|
debugInput("Pressed",button);
|
|
serialFromState();
|
|
}
|
|
|
|
void buttonReleaseAction(int button)
|
|
{
|
|
debugInput("Released",button);
|
|
serialFromState();
|
|
}
|
|
|
|
void serialFromState()
|
|
{
|
|
//USB HID limits to 6 keys, which SDVX has 6 + 2 knobs.
|
|
//We prioritize buttons first as the knobs can be repeatedly pressed and released.
|
|
|
|
//Check buttons
|
|
int buttons[6] = {BTA,BTB,BTC,BTD,FXL,FXR};
|
|
int slotIndex = 2;
|
|
for( int i = 0; i < 6; i++)
|
|
{
|
|
if(getButtonState(buttons[i]) == HIGH)
|
|
{
|
|
buf[slotIndex] = getKey(buttons[i]);
|
|
slotIndex++;
|
|
}
|
|
}
|
|
|
|
//Check knobs, if there are more spaces in the message.
|
|
|
|
if(slotIndex < 8)
|
|
{
|
|
if(VOLLState == CW)
|
|
{
|
|
buf[slotIndex] = getKey(VOLLB);
|
|
slotIndex++;
|
|
}
|
|
else if (VOLLState == CCW)
|
|
{
|
|
buf[slotIndex] = getKey(VOLLA);
|
|
slotIndex++;
|
|
}
|
|
}
|
|
|
|
if(slotIndex < 8)
|
|
{
|
|
if(VOLRState == CW)
|
|
{
|
|
buf[slotIndex] = getKey(VOLRB);
|
|
slotIndex++;
|
|
}
|
|
else if (VOLRState == CCW)
|
|
{
|
|
buf[slotIndex] = getKey(VOLRA);
|
|
slotIndex++;
|
|
}
|
|
}
|
|
|
|
//Fill remainings with zeroes. This will be a key-release also.
|
|
for(int i = slotIndex; i < 8; i++)
|
|
{
|
|
buf[slotIndex] = 0;
|
|
}
|
|
|
|
//Send the message
|
|
Serial.write(buf, 8);
|
|
|
|
}
|
|
|
|
uint8_t getKey(int inputPin)
|
|
{
|
|
switch(inputPin)
|
|
{
|
|
case BTA:
|
|
{
|
|
return keyBTA;
|
|
break;
|
|
}
|
|
case BTB:
|
|
{
|
|
return keyBTB;
|
|
break;
|
|
}
|
|
case BTC:
|
|
{
|
|
return keyBTC;
|
|
break;
|
|
}
|
|
case BTD:
|
|
{
|
|
return keyBTD;
|
|
break;
|
|
}
|
|
case FXL:
|
|
{
|
|
return keyFXL;
|
|
break;
|
|
}
|
|
case FXR:
|
|
{
|
|
return keyFXR;
|
|
break;
|
|
}
|
|
case START:
|
|
{
|
|
return keySTART;
|
|
break;
|
|
}
|
|
case VOLLA:
|
|
{
|
|
return keyVOLLCCW;
|
|
break;
|
|
}
|
|
case VOLLB:
|
|
{
|
|
return keyVOLLCW;
|
|
break;
|
|
}
|
|
case VOLRA:
|
|
{
|
|
return keyVOLRCCW;
|
|
break;
|
|
}
|
|
case VOLRB:
|
|
{
|
|
return keyVOLRCW;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void debugInput(String message, int inputPort)
|
|
{
|
|
#ifdef SERIAL_DEBUGGING
|
|
Serial.print(message);
|
|
Serial.print(" - ");
|
|
Serial.println(getName(inputPort));
|
|
#endif
|
|
}
|
|
|
|
void setButtonState(int button, bool state)
|
|
{
|
|
switch(button)
|
|
{
|
|
case BTA:
|
|
{
|
|
BTAState = state;
|
|
break;
|
|
}
|
|
case BTB:
|
|
{
|
|
BTBState = state;
|
|
break;
|
|
}
|
|
case BTC:
|
|
{
|
|
BTCState = state;
|
|
break;
|
|
}
|
|
case BTD:
|
|
{
|
|
BTDState = state;
|
|
break;
|
|
}
|
|
case FXL:
|
|
{
|
|
FXLState = state;
|
|
break;
|
|
}
|
|
case FXR:
|
|
{
|
|
FXRState = state;
|
|
break;
|
|
}
|
|
case START:
|
|
{
|
|
StartState = state;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool getButtonState(int button)
|
|
{
|
|
switch(button)
|
|
{
|
|
case BTA:
|
|
{
|
|
return BTAState;
|
|
break;
|
|
}
|
|
case BTB:
|
|
{
|
|
return BTBState;
|
|
break;
|
|
}
|
|
case BTC:
|
|
{
|
|
return BTCState;
|
|
break;
|
|
}
|
|
case BTD:
|
|
{
|
|
return BTDState;
|
|
break;
|
|
}
|
|
case FXL:
|
|
{
|
|
return FXLState;
|
|
break;
|
|
}
|
|
case FXR:
|
|
{
|
|
return FXRState;
|
|
break;
|
|
}
|
|
case START:
|
|
{
|
|
return StartState;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
String getName(int inputPort)
|
|
{
|
|
switch(inputPort)
|
|
{
|
|
case BTA:
|
|
{
|
|
return "BTA";
|
|
break;
|
|
}
|
|
case BTB:
|
|
{
|
|
return "BTB";
|
|
break;
|
|
}
|
|
case BTC:
|
|
{
|
|
return "BTC";
|
|
break;
|
|
}
|
|
case BTD:
|
|
{
|
|
return "BTD";
|
|
break;
|
|
}
|
|
case FXL:
|
|
{
|
|
return "FXL";
|
|
break;
|
|
}
|
|
case FXR:
|
|
{
|
|
return "FXR";
|
|
break;
|
|
}
|
|
case START:
|
|
{
|
|
return "Start";
|
|
break;
|
|
}
|
|
case VOLLA:
|
|
{
|
|
return "VOL-L CW";
|
|
break;
|
|
}
|
|
case VOLLB:
|
|
{
|
|
return "VOL-L CCW";
|
|
break;
|
|
}
|
|
case VOLRA:
|
|
{
|
|
return "VOL-R CW";
|
|
break;
|
|
}
|
|
case VOLRB:
|
|
{
|
|
return "VOL-R CCW";
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool getButtonHwState(int button)
|
|
{
|
|
switch(button)
|
|
{
|
|
case BTA:
|
|
{
|
|
return BTAHwState;
|
|
break;
|
|
}
|
|
case BTB:
|
|
{
|
|
return BTBHwState;
|
|
break;
|
|
}
|
|
case BTC:
|
|
{
|
|
return BTCHwState;
|
|
break;
|
|
}
|
|
case BTD:
|
|
{
|
|
return BTDHwState;
|
|
break;
|
|
}
|
|
case FXL:
|
|
{
|
|
return FXLHwState;
|
|
break;
|
|
}
|
|
case FXR:
|
|
{
|
|
return FXRHwState;
|
|
break;
|
|
}
|
|
case START:
|
|
{
|
|
return StartHwState;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void setButtonHwState(int button, bool state)
|
|
{
|
|
switch(button)
|
|
{
|
|
case BTA:
|
|
{
|
|
BTAHwState = state;
|
|
break;
|
|
}
|
|
case BTB:
|
|
{
|
|
BTBHwState = state;
|
|
break;
|
|
}
|
|
case BTC:
|
|
{
|
|
BTCHwState = state;
|
|
break;
|
|
}
|
|
case BTD:
|
|
{
|
|
BTDHwState = state;
|
|
break;
|
|
}
|
|
case FXL:
|
|
{
|
|
FXLHwState = state;
|
|
break;
|
|
}
|
|
case FXR:
|
|
{
|
|
FXRHwState = state;
|
|
break;
|
|
}
|
|
case START:
|
|
{
|
|
StartState = state;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void setDebounce(int button, unsigned long value)
|
|
{
|
|
switch(button)
|
|
{
|
|
case BTA:
|
|
{
|
|
BTADebounce = value;
|
|
break;
|
|
}
|
|
case BTB:
|
|
{
|
|
BTBDebounce = value;
|
|
break;
|
|
}
|
|
case BTC:
|
|
{
|
|
BTCDebounce = value;
|
|
break;
|
|
}
|
|
case BTD:
|
|
{
|
|
BTDDebounce = value;
|
|
break;
|
|
}
|
|
case FXL:
|
|
{
|
|
FXLDebounce = value;
|
|
break;
|
|
}
|
|
case FXR:
|
|
{
|
|
FXRDebounce = value;
|
|
break;
|
|
}
|
|
case START:
|
|
{
|
|
StartDebounce = value;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
unsigned long getDebounce(int button)
|
|
{
|
|
switch(button)
|
|
{
|
|
case BTA:
|
|
{
|
|
return BTADebounce;
|
|
break;
|
|
}
|
|
case BTB:
|
|
{
|
|
return BTBDebounce;
|
|
break;
|
|
}
|
|
case BTC:
|
|
{
|
|
return BTCDebounce;
|
|
break;
|
|
}
|
|
case BTD:
|
|
{
|
|
return BTDDebounce;
|
|
break;
|
|
}
|
|
case FXL:
|
|
{
|
|
return FXLDebounce;
|
|
break;
|
|
}
|
|
case FXR:
|
|
{
|
|
return FXRDebounce;
|
|
break;
|
|
}
|
|
case START:
|
|
{
|
|
return StartDebounce;
|
|
}
|
|
}
|
|
}
|
|
|
|
int buttonToLight(int button)
|
|
{
|
|
switch(button)
|
|
{
|
|
case BTA:
|
|
{
|
|
return BTLA;
|
|
break;
|
|
}
|
|
case BTB:
|
|
{
|
|
return BTLB;
|
|
break;
|
|
}
|
|
case BTC:
|
|
{
|
|
return BTLC;
|
|
break;
|
|
}
|
|
case BTD:
|
|
{
|
|
return BTLD;
|
|
break;
|
|
}
|
|
case FXL:
|
|
{
|
|
return FXLL;
|
|
break;
|
|
}
|
|
case FXR:
|
|
{
|
|
return FXLR;
|
|
break;
|
|
}
|
|
case START:
|
|
{
|
|
return STARTL;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void loop() {
|
|
checkButton(BTA);
|
|
checkButton(BTB);
|
|
checkButton(BTC);
|
|
checkButton(BTD);
|
|
checkButton(FXL);
|
|
checkButton(FXR);
|
|
|
|
checkKnob(VOLLA, VOLLB);
|
|
checkKnob(VOLRA, VOLRB);
|
|
}
|
|
|
|
void pressKey()
|
|
{
|
|
//buf[2] = 128;
|
|
//Serial.write(buf, 8);
|
|
}
|
|
|
|
void releaseKey(int button)
|
|
{
|
|
//buf[0] = 0;
|
|
// buf[2] = 0;
|
|
// Serial.write(buf, 8); // Release key
|
|
}
|