Send 1 Sysex message depending on momentary switch State

Status
Not open for further replies.
Hi oddson.
I don't know if that should be enough, because i just noticed that some presets don't have SW2. But i get your point.
Wouldn't be a problem if both SWs had the same value in those cases.
Need to check how that behaves in the Seq mode, where it sends a huge amount of cc# (16 channels, so...), but il see if i'm capable of reaching that flag variable thing!

Thank you for your invaluable help.
 
My present sketch is:

Code:
// 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};


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;
int pcrec;
int pcold;
int pcnew;
int sw1recd1 = 0;
int sw1recd2 = 0;
int sw1oldd1 = 0;
int sw1oldd2 = 0;
int sw1newd1 = 0;
int sw1newd2 = 0;
int sw2recd1 = 0;
int sw2recd2 = 0;
int sw2oldd1 = 0;
int sw2oldd2 = 0;
int sw2newd1 = 0;
int sw2newd2 = 0;


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 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 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 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;
}


void setup(void)
{ 
    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 '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);
}
unsigned long t=0;


void loop(void)
{
int type, note, velocity, channel, d1, d2;


if (usbMIDI.read()) {                    // Is there a MIDI message incoming ?
    byte type = usbMIDI.getType();
    switch (type) {
      case usbMIDI.NoteOn:
        note = usbMIDI.getData1();
        velocity = usbMIDI.getData2();
        channel = usbMIDI.getChannel();
        if (velocity > 0) {
          Serial.println(String("Note On:  ch=") + channel + ", note=" + note + ", velocity=" + velocity);
        } else {
          Serial.println(String("Note Off: ch=") + channel + ", note=" + note);
        }
        break;
      case usbMIDI.NoteOff:
        note = usbMIDI.getData1();
        velocity = usbMIDI.getData2();
        channel = usbMIDI.getChannel();
        Serial.println(String("Note Off: ch=") + channel + ", note=" + note + ", velocity=" + velocity);
        break;
      default:
        d1 = usbMIDI.getData1();
        d2 = usbMIDI.getData2();
        Serial.println(String("Message, type=") + type + ", data = " + d1 + " " + d2);
        
    }
    t = millis();
  }
  if (millis() - t > 10000) {
    t += 10000;
    Serial.println("(inactivity)");
  }
   
  //usbMIDI.read();
  //if (usbMIDI.getType() == 0xB0) {//check for CC
   //const byte korgdata1 = usbMIDI.getData1();   
   //const byte korgdata2 = usbMIDI.getData2();
   //  Serial.print(korgdata1);Serial.print(" "); 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 > 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();
        }
      }
    } 
    
  }

The output in this Combi is (as you can see this one doesn't have cc# 80 or 81, but it is for SW1 usage):

Code:
Message, type=192, data = 7 0
Message, type=176, data = 65 0


I have absolutely no clue where to start with this flag variable thing, althought i declared some of them thinking that that can be a starting point, but honestly at this moment i don't know where to use them.

These are the flag variables i thought may eventualy need to be used:

Code:
int pcrec; // Program Change receive
int pcold; // Old Program Change value
int pcnew; // New Program Change value. Just in case i decide to have the Preset numbers on the screen
int sw1recd1 = 0; // SW1 Data1 received value
int sw1recd2 = 0; // SW1 Data2 received value
int sw1oldd1 = 0; // SW1 Data1 old value
int sw1oldd2 = 0; // SW1 Data2 old value
int sw1newd1 = 0; // SW1 Data1 new value ... daah
int sw1newd2 = 0; // SW1 Data2 new value ... daah
int sw2recd1 = 0; // SW2 Data1 received value
int sw2recd2 = 0; // SW2 Data2 received value
int sw2oldd1 = 0; // SW2 Data1 old value
int sw2oldd2 = 0; // SW2 Data2 old value
int sw2newd1 = 0; // SW2 Data1 new value
int sw2newd2 = 0; // SW2 Data2 new value

I'm sure this is something easy for anyone used to program stuff but for someone that never wrote one single line of code on its one unless copy this or that and make slight changes seems the athomic bomb formulae.
 
What does synth send if there is only SW1 CC value? Just one CC?

Are there not also situations where CC other than SW1 and SW2 values are sent? (Didn't you discuss one above??)

Assuming I understand and it is the last CC messages that are relevant; I would use an elapsedMillis counter reset when you've extracted the values.

On getting any message that consititutes a 'listen' signal you reset the value to zero and blank the CC values (so you can tell when they are not refilled)

Then a conditional statement can check if this the timer remains below some threshold and the next read MIDI is a CC value (with D2 == 0 ?? ).
(Any other messages would close off the scanning as if the time exhausted??)

It then assigns this value SW2 to SW1 (which does nothing the first time but moves the last read to the first button otherwise) and assigns the fress CC D1 value to SW2.

When the time value passes the threshold then if SW1 remains empty you would assign it SW2s value (and change SW2 if you have some default rule for it when not given).

Otherwise when the time threshold passes you will have the last two read CC values in SW1 and SW2 and it will not be reset until another event resets the ellaspsedMillis value.


In (near/pseudo) code it would be something like:
Code:
elapsedMillis scanStart; // add to the pre-setup code


...
void loop(){
  usbMIDI.read();
  if (scanStart < 8) {
    SW1 = SW2;
    SW2 = D1;
  }else{
    scanStart = 0;
    if (!SW1) {
      SW1 = SW2; 
      SW2 = 81; // or leave it as is or blank it or whatever you want
    }
  }
}

Using a timer removes the need to get some explicit sequence of MIDI to close it off.

I would assume the mSec wait should be single digits.
 
When there is no assignment for SW2 it only send 1 CC, which is the SW1 CC#.

When program change is done throught the Synth itself it sends:

Code:
Message, type=176, data = 0 0
Message, type=176, data = 32 0
Message, type=192, data = 9 0
Message, type=176, data = 80 0
Message, type=176, data = 81 0



If the Program Change is sent throught some controller it only sends for a Combi or Program:

Code:
Message, type=176, data = 80 0
Message, type=176, data = 81 0

If it is a Seq it sends a lot of stuff, but the 2 last CC are for Channel 1 (it goes to 16 channel and goes back to Channel 1 only to send these two last ones that relates to the SW1 and SW2 buttons, always:

Code:
Message, ch, type=176,channel=1, data = 0 0
Message, ch, type=176,channel=1, data = 32 0
Message, type=192,channel1, data = 0 0
Message, ch, type=176,channel=1, data = 7 127
Message, ch, type=176,channel=1, data = 10 64
Message, ch, type=176,channel=1, data = 93 0
Message, ch, type=176,channel=1, data = 91 0
Message, ch, type=176,channel=2, data = 0 0
Message, ch, type=176,channel=2, data = 32 0
Message, type=192,channel2, data = 0 0
Message, ch, type=176,channel=2, data = 7 127
Message, ch, type=176,channel=2, data = 10 64
Message, ch, type=176,channel=2, data = 93 0
Message, ch, type=176,channel=2, data = 91 0
Message, ch, type=176,channel=3, data = 0 0
Message, ch, type=176,channel=3, data = 32 0
Message, type=192,channel3, data = 0 0
Message, ch, type=176,channel=3, data = 7 127
Message, ch, type=176,channel=3, data = 10 64
Message, ch, type=176,channel=3, data = 93 0
Message, ch, type=176,channel=3, data = 91 0
Message, ch, type=176,channel=4, data = 0 0
Message, ch, type=176,channel=4, data = 32 0
Message, type=192,channel4, data = 0 0
MessagMessage, ch, type=176,channel=13, data = 10 64
Message, ch, type=176,channel=13, data = 93 0
Message, ch, type=176,channel=13, data = 91 0
Message, ch, type=176,channel=14, data = 0 0
Message, ch, type=176,channel=14, data = 32 0
Message, type=192,channel14, data = 0 0
Message, ch, type=176,channel=14, data = 7 127
Message, ch, type=176,channel=14, data = 10 64
Message, ch, type=176,channel=14, data = 93 0
Message, ch, type=176,channel=14, data = 91 0
Message, ch, type=176,channel=15, data = 0 0
Message, ch, type=176,channel=15, data = 32 0
Message, type=192,channel15, data = 0 0
Message, ch, type=176,channel=15, data = 7 127
Message, ch, type=176,channel=15, data = 10 64
Message, ch, type=176,channel=15, data = 93 0
Message, ch, type=176,channel=15, data = 91 0
Message, ch, type=176,channel=16, data = 0 0
Message, ch, type=176,channel=16, data = 32 0
Message, type=192,channel16, data = 0 0
Message, ch, type=176,channel=16, data = 7 127
Message, ch, type=176,channel=16, data = 10 64
Message, ch, type=176,channel=16, data = 93 0
Message, ch, type=176,channel=16, data = 91 0
Message, ch, type=176,channel=1, data = 80 0
Message, ch, type=176,channel=1, data = 81 0
 
I found some Combi's that have Data2 = 127.
Ah, and I will need the data2, to read the button state, but i think after getting data1 correctly my little old brain will be able to reach it then. (I suppose, hehe)

Yes, the last messages are the ones i need.
 
At this moment the sketch is like this:

Code:
// 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};


int SW1 = 0;
int SW2 = 0;
int d1 = 0;
int d2 = 0;
int ch;
int SW1D1 = 0;
int SW1D2 = 0;
int SW2D1 = 0;
int SW2D2 = 0;




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;
elapsedMillis scanStart;


// 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 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 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 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");
  usbMIDI.sendControlChange(SW1, d2, 1);
  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");
  usbMIDI.sendControlChange(SW2, d2, 1);
  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;
}


void setup(void)
{ 
    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 '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);
}
unsigned long t=0;


void loop(void)
{
 
int type, note, velocity, channel, d1, d2;


if (usbMIDI.read()) {                    // Is there a MIDI message incoming ?
    byte type = usbMIDI.getType();
    switch (type) {
            
        case usbMIDI.ControlChange: 
        d1 = usbMIDI.getData1();
        d2 = usbMIDI.getData2();
        channel = usbMIDI.getChannel();
        Serial.println(String("Message, type=") + type + ",channel " + channel + ", data = " + d1 + " " + d2);
         
    if (scanStart < 8) {
    SW1 = SW2;
    Serial.println (String("scanStart<8 SW1=SW2: SW1      ") + SW1 + ", SW2 " + SW2); // This print is only to control what is happening
    SW2 = d1;
    Serial.println (String("SW2=dl:              " "SW2      ") + SW2 + ",  D1 " + d1); // This print is only to control what is happening
   }else{
    scanStart = 0;
    if (!SW1) {
      SW1 = SW2; 
      Serial.println (String("scanStart = 0: SW1=SW2 " "=SW1   ") + SW1 + ", SW2 " + SW2); // This print is only to control what is happening
      
      SW2 = 81; // or leave it as is or blank it or whatever you want
  }
     
  }
     
       }
        t = millis(); 
    
 }
 if (millis() - t > 10000) {
   t += 10000;
   Serial.println("(inactivity)");
 }


   


    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 > SW1BUTTON_X) && (x < (SW1BUTTON_X + SW1BUTTON_W))) {
        if ((y > SW1BUTTON_Y) && (y <= (SW1BUTTON_Y + SW1BUTTON_H))) {
          Serial.println("SW1 btn hit"); 
          Serial.println(String("Message,") + ",channel " + channel + ",data = " + SW1 + " " + d2);
          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"); 
          Serial.println(String("Message,") + ",channel " + channel + ",data = " + SW2 + " " + d2);
          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();
        }
      }
    } 
    
  }

The result:

Code:
(inactivity)
Message, type=176,channel 1, data = 80 0
scanStart = 0: SW1=SW2 =SW1   0, SW2 0
Message, type=176,channel 1, data = 81 0
scanStart<8 SW1=SW2: SW1      81, SW2 81
SW2=dl:              SW2      81,  D1 81
(inactivity)

I see that we're getting there, but i'm missing something in the loop. It looks like the condition is considering the PC as the first "CC", which obviously it isn't.
I put some serial print commands during the ScanStart loop to see what it is doing. I hope it helps.
 
Hmmm... rereading my idea from yesterday and now I don't understand it.

The pseudo code fragment doesn't seem to match the idea in the text (or what's left of it in my head) so I'll need to rethink this.

The broad-strokes idea is to read the PC as a trigger to listen for a very short time for CC messages passing each one from SW2 to SW1 until the last one stays in SW2.

(The special case when there is one only complicates things so maybe we should add this once the main case is working.)

I'll have a go with it in real code tonight.
 
The fragment is missing the part that says 'if a PC is detected then reset the timer' rather than in the 'else'
Code:
elapsedMillis scanStart; // add to the pre-setup code


...
void loop(){
  usbMIDI.read();
  if (getType=usbMIDI.programChnage) { 
    scanStart = 0;
  }
  if (scanStart < 8) {
    SW1 = SW2;
    SW2 = D1;
  }else{
    if (!SW1) {
      SW1 = SW2; 
      SW2 = 0 ; // blank SW2 if there was only 1 CC
    }
  }
}

In your code that would be the missing 'case' under your switch.
 
Code:
const int scanMax = 250;
const byte channel = 1;
byte SW1;
byte SW1lag;
byte SW2;
byte SW2lag;
elapsedMillis scanStart;
elapsedMillis reportLast;

void setup() {
  Serial.begin(9600);
}

void loop(void)
{ 
  if (usbMIDI.read(channel)) { // limit reads to single channel
    if (usbMIDI.getType() == usbMIDI.ProgramChange){
      scanStart = 0;      
      SW1 = SW2;
      SW2 = 0;
    }else if(usbMIDI.getType() == usbMIDI.ControlChange) {
      if (scanStart <= scanMax) {
        SW1 = SW2;
        SW2 = usbMIDI.getData1();
      }
    }
  }
  if (scanStart >= scanMax) {
    if (SW1 == 0) { // if there was only 1 CC SW1 is blank and we will swap
      SW1 = SW2;
      SW2 = 0;
    }
    if ((SW1 != SW1lag) || (SW2 != SW2lag)) { // if there is a change...
      Serial.println(SW1);
      Serial.println(SW2);
      SW1lag = SW1;
      SW2lag = SW2;
      scanStart = 0;
    }
  }
}
This is close but if it doesn't get one CC it will set both to zero instead of leaving them from before but that should be easy to fix if you can confirm this is essentially what you are looking for.
 
Last edited:
Hi oddson, if there is no CC to get from the M3 (That is, the synth doesn't send them because there is none assigned to the switches on the synth's preset), it's OK to have the sw's set to 0 or the last value they received as they will have no effect on the synth.
 
I think the reset of scanStart needs to be moved out one brace (outside the conditional) so as to reset the scanner whether or not there is a new message.
Code:
    if ((SW1 != SW1lag) || (SW2 != SW2lag)) { // if there is a change...
      Serial.println(SW1);
      Serial.println(SW2);
      SW1lag = SW1;
      SW2lag = SW2;
    }
    scanStart = 0;

I was having a lot of trouble keeping the serial monitor working as it would not play nice with my MIDI host in talking to my T3.0. (Hence 9600 speed; trying anything.)

And I'd had my second pint so I just stopped when I got close.

The code works by using the SW# variables as flags for the current system state.

But the key concept is that we are parsing one midi message per pass thru the main loop.

It would be trivial to alter to an array variable and store any number of parameters sent in this way.

Also, you can of course read and process D2 values. E.g. you could read and display the program change on a character or seven-segment-numeral display or toggle LEDS over the switches based on D2 for the CC messages.

Using switch/case/break statements is an option if multiple types are needed to trigger different code but with just two I stuck with conventional conditional form.

Let me know if there are bugs or you have trouble integrating with your sketch.

If you want a line-by-line explanation of the code or any section I can do that too.
 
With your code it passes the first Program Change's CC# correctly for both SW1 and SW2 Switches, but only in the first Program Change. It doesn't detect more PC's.

Code:
(inactivity)
80
81
SW1 btn hit
Message,,channel 1,data = 80 0
(inactivity)
SW2 btn hit
Message,,channel 1,data = 81 0
(inactivity)
(inactivity)
(inactivity)
(inactivity)

The present code:

Code:
void loop(void)
 
{ 
  if (usbMIDI.read(channel)) { // limit reads to single channel
    if (usbMIDI.getType() == usbMIDI.ProgramChange){
      scanStart = 0;      
      SW1 = SW2;
      SW2 = 0;
    }else if(usbMIDI.getType() == usbMIDI.ControlChange) {
      if (scanStart <= scanMax) {
        SW1 = SW2;
        SW2 = usbMIDI.getData1();
      }
    }
  }
  if (scanStart >= scanMax) {
    if (SW1 == 0) { // if there was only 1 CC SW1 is blank and we will swap
      SW1 = SW2;
      SW2 = 0;
    }
    if ((SW1 != SW1lag) || (SW2 != SW2lag)) { // if there is a change...
      Serial.println(SW1);
      Serial.println(SW2);
      SW1lag = SW1;
      SW2lag = SW2;
    }
     scanStart = 0;
  }
 
}
 
yeah... I guess I'll have to debug it sober.

...as a hunch try changing SW1 = SW2; to SW1 = 0; in the PC part of the code (first instance).

I meant to post that as a correction last night but didn't... I thought it was harmless but that could be why it's not working as it might be messing up the flags as I thought they work but I can't quite think it through in my head right now.

(Something to do with blanking them by accident when SW2 gets copied to SW1 after the first message is processed.)
 
yeah... I guess I'll have to debug it sober.

...as a hunch try changing SW1 = SW2; to SW1 = 0; in the PC part of the code (first instance).

I meant to post that as a correction last night but didn't... I thought it was harmless but that could be why it's not working as it might be messing up the flags as I thought they work but I can't quite think it through in my head right now.

(Something to do with blanking them by accident when SW2 gets copied to SW1 after the first message is processed.)

Didn't change anything.

I don't know if i express myself correctly:
When the PC is received it works, but the issue is that the Program Changes are not being received everytime, I don't know if you understood it that way!
 
It won't do anything if the same parameters are present but the SW1 and SW2 values will still be correct if/when they are pressed again if these variables are used in the MIDI message.
E.g.: usbMIDI.sendProgramChange(SW1, channel);


Remove the conditional and it should report every time.
Code:
  if (scanStart >= scanMax) {
    if (SW1 == 0) { // if there was only 1 CC SW1 is blank and we will swap
      SW1 = SW2;
      SW2 = 0;
    }
[COLOR="#A9A9A9"]    //if ((SW1 != SW1lag) || (SW2 != SW2lag)) { // if there is a change...[/COLOR]
      Serial.println(SW1);
      Serial.println(SW2);
      SW1lag = SW1;
      SW2lag = SW2;
[COLOR="#A9A9A9"]    //}[/COLOR]
     scanStart = 0;
  }
 
....I guess the lagged values are not really needed once you're convinced it's working as the SW# variables will zero and then set whenever the PC message is received and so should work as anticipated with whatever CCs the M3 sent after the PC and before the time threshold is expired.

FYI - you may be able to drop that time-threshold to single digits as it's likely the M3 is sending the CC within a millisecond or two.

At at 250 it limits serial.print commands to something the monitor can handle when the code is doing something you didn't anticipate.

But if there are no serial.pint (or they are only sending when the code is changed) then the code can be run with a much reduced value in scanMax.
 
It was for this answer, sorry:

It won't do anything if the same parameters are present but the SW1 and SW2 values will still be correct if/when they are pressed again if these variables are used in the MIDI message.
E.g.: usbMIDI.sendProgramChange(SW1, channel);


Remove the conditional and it should report every time.
Code:
  if (scanStart >= scanMax) {
    if (SW1 == 0) { // if there was only 1 CC SW1 is blank and we will swap
      SW1 = SW2;
      SW2 = 0;
    }
[COLOR=#A9A9A9]    //if ((SW1 != SW1lag) || (SW2 != SW2lag)) { // if there is a change...[/COLOR]
      Serial.println(SW1);
      Serial.println(SW2);
      SW1lag = SW1;
      SW2lag = SW2;
[COLOR=#A9A9A9]    //}[/COLOR]
     scanStart = 0;
  }




That gives me a beatifull row of:

Code:
0
0
0
0
0
0
0
0
0
(inactivity)
0
0
0
0
0
0
0
0
0
 
Last edited:
Yeah I'll have to debug properly then...

I think it should not reset the timer at all except when a PC is read.

Try commenting out //scanStart = 0; from the PC code... if that does not work then definitely I'll have to get to a compiler etc. to sort this out.
 
Last edited:
No change! Sorry.

It seems like there is some timer or a buffer that gets stuck somewhere.

This is how it is now:

Code:
// 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


const int scanMax = 9;
//const byte channel = 1;
byte SW1;
byte SW1D2;
byte SW1lag;
byte SW2;
byte SW2D2;
byte SW2lag;
byte d2;


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;
elapsedMillis scanStart;
elapsedMillis reportLast;


// 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 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 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 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");
  usbMIDI.sendControlChange(SW1, SW1D2, 1);
  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");
  usbMIDI.sendControlChange(SW2, SW2D2, 1);
  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;
}


void setup(void)
{ 
    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 '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);
}
unsigned long t=0;


void loop(void)
{
 
{ 
  if (usbMIDI.read(channel)) { // limit reads to single channel
    if (usbMIDI.getType() == usbMIDI.ProgramChange){
      scanStart = 0;      
      SW1 = 0;
      SW2 = 0;
    }else if(usbMIDI.getType() == usbMIDI.ControlChange) {
      if (scanStart <= scanMax) {
        SW1 = SW2;
        SW1D2 = SW2D2;
        SW2 = usbMIDI.getData1();
        SW2D2 = usbMIDI.getData2();
      }
    }
  }
  if (scanStart >= scanMax) {
    if (SW1 == 0) { // if there was only 1 CC SW1 is blank and we will swap
      SW1 = SW2;
      SW2 = 0;
    }
   if ((SW1 != SW1lag) || (SW2 != SW2lag)) { // if there is a change...
      Serial.println(SW1);
      Serial.println(SW2);
      SW1lag = SW1;
      SW2lag = SW2;
    }
     scanStart = 0;
  }
 
}


   if (millis() - t > 10000) {
      t += 10000;
      Serial.println("(inactivity)");
 }


   


    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 > SW1BUTTON_X) && (x < (SW1BUTTON_X + SW1BUTTON_W))) {
        if ((y > SW1BUTTON_Y) && (y <= (SW1BUTTON_Y + SW1BUTTON_H))) {
          Serial.println("SW1 btn hit"); 
          Serial.println(String("Message,") + ",channel " + channel + ",data = " + SW1 + " " + d2);
          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"); 
          Serial.println(String("Message,") + ",channel " + channel + ",data = " + SW2 + " " + d2);
          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();
        }
      }
    } 
    
  }
 
Last edited:
Status
Not open for further replies.
Back
Top