// include the ResponsiveAnalogRead library for analog smoothing
#include <ResponsiveAnalogRead.h>
#include <Bounce.h> // Bounce library makes button change detection easy
// ******CONSTANT VALUES******** - customize code behaviour here!
// SET THESE SIX VALUES FOR JOYSTICK!
const int pitchPin = 0; // PIN numbers ** MUST CHANGE!
const int modPin = 1;
const int modPin2 = 1;
const int pitchMaxRaw = 1019; // Max reading with full bend up... as raw 10-bit value
const int modMaxRaw = 1019; // Max reading full mod down
// SET THESE VALUES FOR RIBBON
//const int ribbonPin = touch_pressed; // Max reading on right side... as raw 10-bit value
const int ribbonMaxRaw = 474; // Max reading on right side... as raw 10-bit value
//
int Button1 = 0; //This is the default "Button Off" and 1 is "Button On"
int Button2 = 0; //This is the default "Button Off" and 1 is "Button On"
int OldButton1 = 0; //Variable to store button1 old value
int OldButton2 = 0; //Variable to store button2 old value
byte data1On[] = {0xf0, 0x42, 0x30, 0x75, 0x41, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf7};
byte data1Off[] = {0xf0, 0x42, 0x30, 0x75, 0x41, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7};
byte data2On[] = {0xf0, 0x42, 0x30, 0x75, 0x41, 0x00, 0x00, 0x01, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf7};
byte data2Off[] = {0xf0, 0x42, 0x30, 0x75, 0x41, 0x00, 0x00, 0x01, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7};
byte progMode[] = {0xf0, 0x42, 0x30, 0x75, 0x4E, 0x02, 0xf7};
byte combiMode[] = {0xf0, 0x42, 0x30, 0x75, 0x4E, 0x00, 0xf7};
byte seqMode[] = {0xf0, 0x42, 0x30, 0x75, 0x4E, 0x04, 0xf7};
byte modeRequest[] = {0xF0, 0x42, 0x30, 0x75, 0x12, 0x00, 0xF7};
byte progRequest[] = {0xF0, 0x42, 0x30, 0x75, 0x74, 0x00, 0xF7};
const int channel = 1; // MIDI channel
const int MIDIdelay = 5; // will update MIDI only if this many milliseconds have passed
//******VARIABLES***********
// data variables and a lagged copy to compare before updating MIDI value
int pitch;
int mod;
int mod2;
int pitchRaw;
int modRaw;
int modRaw2;
int pitchLag;
int modLag;
int modLag2;
int ribbon;
int ribbonRaw;
int ribbonLag;
elapsedMillis pitchUpdate;
elapsedMillis modUpdate;
elapsedMillis modUpdate2;
elapsedMillis ribbonUpdate;
// ititialize the ReponsiveAnalogRead objects
ResponsiveAnalogRead readPitch = {pitchPin, true};
ResponsiveAnalogRead readMod = {modPin, true};
ResponsiveAnalogRead readMod2 = {modPin2, true};
#include <Adafruit_GFX.h> // Core graphics library
#define LCD_ROTATION 1 //should work in all rotations
#define USE_READID 0 //Adafruit and ILI9488 can't read ID
//#include <MCUFRIEND_kbv.h> // Hardware-specific library
//MCUFRIEND_kbv tft;
//#include <Adafruit_ILI9341.h> // Hardware-specific library
//Adafruit_ILI9341 tft(10, 9, 8);
//#include <HX8347D_kbv.h> // Hardware-specific library
//HX8347D_kbv tft;
#include <ILI9488.h> // Hardware-specific library
ILI9488 tft(10, 9, 8);
//#include <ILI9488_kbv.h> // Hardware-specific library
//ILI9488_kbv tft;
extern void Touch_initialise(int aspect, int wid, int ht);
extern bool Touch_getXY(void);
bool touch_pressed;
int pixel_x, pixel_y;
boolean SW1on = false;
boolean SW2on = false;
boolean Progon = false;
boolean Combion = false;
boolean Seqon = false;
boolean ribbonCtrl = false;
// Color definitions
#define TFT_BLACK 0x0000 /* 0, 0, 0 */
#define TFT_NAVY 0x000F /* 0, 0, 128 */
#define TFT_DARKGREEN 0x03E0 /* 0, 128, 0 */
#define TFT_DARKCYAN 0x03EF /* 0, 128, 128 */
#define TFT_MAROON 0x7800 /* 128, 0, 0 */
#define TFT_PURPLE 0x780F /* 128, 0, 128 */
#define TFT_OLIVE 0x7BE0 /* 128, 128, 0 */
#define TFT_LIGHTGREY 0xC618 /* 192, 192, 192 */
#define TFT_DARKGREY 0x7BEF /* 128, 128, 128 */
#define TFT_BLUE 0x001F /* 0, 0, 255 */
#define TFT_GREEN 0x07E0 /* 0, 255, 0 */
#define TFT_CYAN 0x07FF /* 0, 255, 255 */
#define TFT_RED 0xF800 /* 255, 0, 0 */
#define TFT_RED2 0xF88D /* 254, 18, 106 */
#define TFT_MAGENTA 0xF81F /* 255, 0, 255 */
#define TFT_YELLOW 0xFFE0 /* 255, 255, 0 */
#define TFT_WHITE 0xFFFF /* 255, 255, 255 */
#define TFT_ORANGE 0xFD20 /* 255, 165, 0 */
#define TFT_ORANGE2 0xFB20 /* 254, 102, 1 */
#define TFT_GREENYELLOW 0xAFE5 /* 173, 255, 47 */
#define TFT_PINK 0xF81F
/******************* UI details */
#define PROGFRAME_X 20
#define PROGFRAME_Y 10
#define PROGFRAME_W 90
#define PROGFRAME_H 50
#define PROGBUTTON_X PROGFRAME_X
#define PROGBUTTON_Y PROGFRAME_Y
#define PROGBUTTON_W PROGFRAME_W
#define PROGBUTTON_H PROGFRAME_H
#define COMBIFRAME_X 200
#define COMBIFRAME_Y 10
#define COMBIFRAME_W 90
#define COMBIFRAME_H 50
#define COMBIBUTTON_X COMBIFRAME_X
#define COMBIBUTTON_Y COMBIFRAME_Y
#define COMBIBUTTON_W COMBIFRAME_W
#define COMBIBUTTON_H COMBIFRAME_H
#define SEQFRAME_X 370
#define SEQFRAME_Y 10
#define SEQFRAME_W 90
#define SEQFRAME_H 50
#define SEQBUTTON_X SEQFRAME_X
#define SEQBUTTON_Y SEQFRAME_Y
#define SEQBUTTON_W SEQFRAME_W
#define SEQBUTTON_H SEQFRAME_H
#define SW1FRAME_X 90
#define SW1FRAME_Y 110
#define SW1FRAME_W 130
#define SW1FRAME_H 70
#define SW1BUTTON_X SW1FRAME_X
#define SW1BUTTON_Y SW1FRAME_Y
#define SW1BUTTON_W SW1FRAME_W
#define SW1BUTTON_H SW1FRAME_H
#define SW2FRAME_X 310
#define SW2FRAME_Y 110
#define SW2FRAME_W 130
#define SW2FRAME_H 70
#define SW2BUTTON_X SW2FRAME_X
#define SW2BUTTON_Y SW2FRAME_Y
#define SW2BUTTON_W SW2FRAME_W
#define SW2BUTTON_H SW2FRAME_H
#define RIBBONFRAME_X 10
#define RIBBONFRAME_Y 240
#define RIBBONFRAME_W 470
#define RIBBONFRAME_H 70
#define RIBBON_X RIBBONFRAME_X
#define RIBBON_Y RIBBONFRAME_Y
#define RIBBON_W RIBBONFRAME_W
#define RIBBON_H RIBBONFRAME_H
void progBtn()
{
tft.drawRect(PROGBUTTON_X, PROGBUTTON_Y, PROGBUTTON_W, PROGBUTTON_H, TFT_WHITE);
tft.fillRect(PROGBUTTON_X, PROGBUTTON_Y, PROGBUTTON_W, PROGBUTTON_H, TFT_WHITE);
tft.setCursor(40, 25);
tft.setTextColor(TFT_BLACK);
tft.setTextSize(2);
tft.println("Prog");
usbMIDI.sendSysEx(7, modeRequest, true); //The Sysex to change to Program Mode in Korg M3
Progon = false;
}
void combiBtn()
{
tft.drawRect(COMBIBUTTON_X, COMBIBUTTON_Y, COMBIBUTTON_W, COMBIBUTTON_H, TFT_WHITE);
tft.fillRect(COMBIBUTTON_X, COMBIBUTTON_Y, COMBIBUTTON_W, COMBIBUTTON_H, TFT_WHITE);
tft.setCursor(220, 25);
tft.setTextColor(TFT_BLACK);
tft.setTextSize(2);
tft.println("Combi");
usbMIDI.sendSysEx(7, combiMode, true); //The Sysex to change to Combi Mode in Korg M3
Combion = false;
}
void seqBtn()
{
tft.drawRect(SEQBUTTON_X, SEQBUTTON_Y, SEQBUTTON_W, SEQBUTTON_H, TFT_WHITE);
tft.fillRect(SEQBUTTON_X, SEQBUTTON_Y, SEQBUTTON_W, SEQBUTTON_H, TFT_WHITE);
tft.setCursor(400, 25);
tft.setTextColor(TFT_BLACK);
tft.setTextSize(2);
tft.println("Seq");
usbMIDI.sendSysEx(7, seqMode, true); //The Sysex to change to SEQ Mode in Korg M3
Seqon = false;
}
void sw1Btn()
{
tft.drawRect(SW1BUTTON_X, SW1BUTTON_Y, SW1BUTTON_W, SW1BUTTON_H, TFT_DARKGREY);
tft.fillRect(SW1BUTTON_X, SW1BUTTON_Y, SW1BUTTON_W, SW1BUTTON_H, TFT_RED);
tft.setCursor(160, 140);
tft.setTextColor(TFT_WHITE);
tft.setTextSize(2);
tft.println("SW1");
SW1on = false;
}
void sw2Btn()
{
tft.drawRect(SW2BUTTON_X, SW2BUTTON_Y, SW2BUTTON_W, SW2BUTTON_H, TFT_DARKGREY);
tft.fillRect(SW2BUTTON_X, SW2BUTTON_Y, SW2BUTTON_W, SW2BUTTON_H, TFT_RED);
tft.setCursor(340,140);
tft.setTextColor(TFT_WHITE);
tft.setTextSize(2);
tft.println("SW2");
SW2on = false;
}
void rbCtrl()
{
tft.drawRect(RIBBON_X, RIBBON_Y, RIBBON_W, RIBBON_H, TFT_LIGHTGREY);
tft.fillRect(RIBBON_X, RIBBON_Y, RIBBON_W, RIBBON_H, TFT_LIGHTGREY);
tft.setCursor(10,240);
//remap to output data and send if changed and MIDIdelay has lapsed since last update for that parameter
ribbon = map(pixel_x, 11, 474, 0, 127);
ribbon = max(ribbon, 0); // need this now that the bottom isn't zero
ribbon = min(ribbon, 127); // cap to avoid overflow
if (abs(ribbon - ribbonLag) > 0 && ribbonUpdate > MIDIdelay ) {
ribbonLag = ribbon;
usbMIDI.sendControlChange(16, ribbon, channel);
ribbonUpdate = 0;
}
ribbonCtrl = false;
}
int sysExIndex=0; // index for keeping track of incoming sysEx bytes
// handle incoming sysex messages
// Copied from the midi controller project from Gerrit.
//So, a Big Thank You to Gerrit from the pjrc forum https://forum.pjrc.com/members/47405-Gerrit
void handleSysEx(const byte* sysExData, uint16_t sysExSize, bool complete)
{
// check if this is the start of the sysEx message
if (sysExData[0]==240){
sysExIndex=0;
}
Serial.print("The sysex length is ");
Serial.print(sysExSize);
Serial.print(" bytes\t");
Serial.print("complete is\t");
Serial.print(complete);
Serial.println();
for (int i=0;i<sysExSize;i++){
sysExIndex++;
Serial.print(sysExData[i],HEX);
Serial.print(" ");
}
Serial.println();
// message end
if (complete && sysExData[sysExSize-1]==247){
Serial.print("The total sysex message length is ");
Serial.print(sysExIndex);
Serial.print(" bytes");
}
}
void setup(void)
{
usbMIDI.setHandleSysEx(handleSysEx);
uint16_t result;
Serial.begin(9600);
Serial.println(F("TFT LCD test"));
tft.begin();
int aspect = LCD_ROTATION; //PORTRAIT
tft.setRotation(aspect);
Touch_initialise(aspect, tft.width(), tft.height()); //.kbv external function
tft.fillScreen(TFT_BLACK);
// create 'SW1 Switch'
tft.drawRect(SW1BUTTON_X, SW1BUTTON_Y, SW1BUTTON_W, SW1BUTTON_H, TFT_DARKGREY);
tft.fillRect(SW1BUTTON_X, SW1BUTTON_Y, SW1BUTTON_W, SW1BUTTON_H, TFT_RED);
tft.setCursor(155, 140);
tft.setTextColor(TFT_BLACK);
tft.setTextSize(2);
tft.print("SW1");
// create 'SW2 Switch'
tft.drawRect(SW2BUTTON_X, SW2BUTTON_Y, SW2BUTTON_W, SW2BUTTON_H, TFT_DARKGREY);
tft.fillRect(SW2BUTTON_X, SW2BUTTON_Y, SW2BUTTON_W, SW2BUTTON_H, TFT_RED);
tft.setCursor(340, 140);
tft.setTextColor(TFT_BLACK);
tft.setTextSize(2);
tft.print("SW2");
// create 'PROG Switch'
tft.drawRect(PROGBUTTON_X, PROGBUTTON_Y, PROGBUTTON_W, PROGBUTTON_H, TFT_DARKGREY);
tft.fillRect(PROGBUTTON_X, PROGBUTTON_Y, PROGBUTTON_W, PROGBUTTON_H, TFT_BLUE);
tft.setCursor(40, 25);
tft.setTextColor(TFT_BLACK);
tft.setTextSize(2);
tft.print("Prog");
// create 'COMBI Switch'
tft.drawRect(COMBIBUTTON_X, COMBIBUTTON_Y, COMBIBUTTON_W, COMBIBUTTON_H, TFT_DARKGREY);
tft.fillRect(COMBIBUTTON_X, COMBIBUTTON_Y, COMBIBUTTON_W, COMBIBUTTON_H, TFT_RED);
tft.setCursor(220, 25);
tft.setTextColor(TFT_BLACK);
tft.setTextSize(2);
tft.print("Combi");
// create 'SEQ Switch'
tft.drawRect(SEQBUTTON_X, SEQBUTTON_Y, SEQBUTTON_W, SEQBUTTON_H, TFT_DARKGREY);
tft.fillRect(SEQBUTTON_X, SEQBUTTON_Y, SEQBUTTON_W, SEQBUTTON_H, TFT_YELLOW);
tft.setCursor(400, 25);
tft.setTextColor(TFT_BLACK);
tft.setTextSize(2);
tft.print("Seq");
// create 'Ribbon Emulator field'
tft.drawRect(RIBBON_X, RIBBON_Y, RIBBON_W, RIBBON_H, TFT_DARKGREY);
tft.fillRect(RIBBON_X, RIBBON_Y, RIBBON_W, RIBBON_H, TFT_LIGHTGREY);
}
void loop(void)
{
usbMIDI.read();
if (usbMIDI.read() == 1 && usbMIDI.getType() == 0xF0)
{//check for sysex
const byte korgdata1 = usbMIDI.getData1();
const byte korgdata2 = usbMIDI.getData2();
const byte *pSysExArray = usbMIDI.getSysExArray(); //capture data
Serial.print(*pSysExArray);
Serial.print(korgdata1); Serial.println(korgdata2);
}
int x = -1, y = -1; //regular pixel coordinates
touch_pressed = Touch_getXY(); //external function
#if 0
Serial.print("X="); Serial.print(pixel_x);
Serial.print(" Y="); Serial.print(pixel_y);
Serial.print(" Z="); Serial.print(touch_pressed);
Serial.println("");
#endif
if (touch_pressed) {
x = pixel_x; //copy global variable
y = pixel_y;
}
{
if((x > PROGBUTTON_X) && (x < (PROGBUTTON_X + PROGBUTTON_W))) {
if ((y > PROGBUTTON_Y) && (y <= (PROGBUTTON_Y + PROGBUTTON_H))) {
Serial.println("PROG btn hit");
delay(100); // UI debouncing
progBtn();
}
}
}
{
if((x > COMBIBUTTON_X) && (x < (COMBIBUTTON_X + COMBIBUTTON_W))) {
if ((y > COMBIBUTTON_Y) && (y <= (COMBIBUTTON_Y + COMBIBUTTON_H))) {
Serial.println("Combi btn hit");
delay(100); // UI debouncing
combiBtn();
}
}
}
{
if((x > SEQBUTTON_X) && (x < (SEQBUTTON_X + SEQBUTTON_W))) {
if ((y > SEQBUTTON_Y) && (y <= (SEQBUTTON_Y + SEQBUTTON_H))) {
Serial.println("Seq btn hit");
delay(100); // UI debouncing
seqBtn();
}
}
}
{
if((x > SW1BUTTON_X) && (x < (SW1BUTTON_X + SW1BUTTON_W))) {
if ((y > SW1BUTTON_Y) && (y <= (SW1BUTTON_Y + SW1BUTTON_H))) {
Serial.println("SW1 btn hit");
delay(100); // UI debouncing
sw1Btn();
}
}
}
{
if((x > SW2BUTTON_X) && (x < (SW2BUTTON_X + SW2BUTTON_W))) {
if ((y > SW2BUTTON_Y) && (y <= (SW2BUTTON_Y + SW2BUTTON_H))) {
Serial.println("SW2 btn hit");
delay(100); // UI debouncing
sw2Btn();
}
}
}
{
if((x > RIBBON_X) && (x < (RIBBON_X + RIBBON_W))) {
if ((y > RIBBON_Y) && (y <= (RIBBON_Y + RIBBON_H))) {
Serial.println("Ribbon pressed");
Serial.println(pixel_x);
Serial.println(ribbon);
delay(100); // UI debouncing
rbCtrl();
}
}
}
}