MIDI instrument - controlling Neopixels (ws2812) with softpots - only 2 out of 3 softpots are controlling pixels as expected

Hello everyone, I've been trying to solve an issue with this project for over a week now, so I have finally decided to ask the community for help. I've attached the sketch and will subsequently post the wiring diagrams in separate posts (new users can only embed one media item per post).

Some of the pin numbers for the buttons/joystick in the sketch might differ slightly from what is in the diagrams, as I am using an Arduino MEGA 2560 instead of Pro Micro. Please let me know if any additional info is needed - Thanks!

I've combined two sketches: This one is the primary sketch. It is a MIDI instrument with 3 softpots that are triggered with 3 piezos or by just holding down a button for legato. The value of each softpot is sent to a different MIDI channel, channels 1,2, and 3. There's a joystick and two pots for additional MIDI CC messages. The 4 buttons are used for Legato, Calibration, Octave +, and Octave -

I copied small pieces of this other sketch to add neopixels that light up where the softpots are touched. This sketch was originally for just 2 softpots, and I suspect that's why only 2 out of the 3 softpots are triggering the neopixels.

In the below sketch - the 3rd softpot works exactly as expected with triggering MIDI notes, but it does not trigger the neopixels when playing the instrument. It does trigger the neopixels during the calibration process (blinks red when touching for calibration like the other 2 softpots).

/*
  Arduino Ribbon Synth MIDI controller
   ------------------------------------
   ©2014 Dean Miller
*/

#include <EEPROM.h>
#include <Wire.h>
#include <Adafruit_NeoPixel.h>
#include <QuickStats.h>
#ifdef __AVR__
#include <avr/power.h>
#endif

//------- Constants -------//
#define PIEZO_THRESHOLD_ON 12

#define PIN_LED 13
#define NEO_PIN   21
#define N_PIXELS  16      //# of LEDS

#define PIN_SOFTPOT_1 A0
#define PIN_SOFTPOT_2 A1
#define PIN_SOFTPOT_3 A2
#define PIN_PIEZO_1 A3
#define PIN_PIEZO_2 A4
#define PIN_PIEZO_3 A5
#define PIN_POT_1 8
#define PIN_POT_2 6

#define PIN_BUTTON_STICK 3

#define PIN_BUTTON_RIGHT 11
#define PIN_BUTTON_UP 5
#define PIN_BUTTON_DOWN 2
#define PIN_BUTTON_LEFT 7

//Joystick
#define PIN_JOYSTICK_X 9
#define PIN_JOYSTICK_Y 10

//Joystick
#define UP 0
#define RIGHT 1
#define DOWN 2
#define LEFT 3
#define STICK 4

#define PIEZO_SAMPLES 400
#define PADDING 2

#define THRESH    200
#define N_STR     3
#define N_FRET    21


//#define MOD_THRESHOLD 30  //Modulation is not send under this value

long noteDebounceTime = 0;
int noteDebounceDelay = 25;

long lastDebounceTime = 0;
int debounceDelay = 200;


long ledDebounceTime = 0;
int ledDebounceDelay = 20;

int mod_final;

//bool isPitchBend = false;
unsigned int pitchBendLight = 0;
//bool dim = false;


//------Note Class---------//
/*
  A note class that stores some info about each note played is necessary
   to ensure that open strings are held for the specified amount of time.
   That is a problem with using the piezos as triggers instead of FSRs, they
   only register momentary impact or vibration, creating a problem for open strings.
*/

class Note {
    int _number;
    int _velocity;
    int _startTime;
    int _fretted;

  public:
    void init(int number, int velocity, int startTime, int fretted) {
      _number = number;
      _velocity = velocity;
      _startTime = startTime;
      _fretted = fretted;
    }

    int number() {
      return _number;
    }

    int velocity() {
      return _velocity;
    }

    int fretted() {
      return _fretted;
    }

    int timeActive() {
      return millis() - _startTime;
    }
};

//------ Global Variables ---------//

/*
  fret defs stored in EEPROM for calibration purposes.
  Lower output voltages from USB ports result in different values read from
  SoftPots and wonky fret definitions.*/

int F0 = 248;  // all bigger softpot values will be treated as open string
int F21 = 0;

int fretDefs[N_STR][N_FRET];
//short fretDefs[N_STR][N_FRET];

int piezoVals[] = {
  0, 0, 0
};
int piezoPins[] = {
  PIN_PIEZO_1, PIN_PIEZO_2, PIN_PIEZO_3
};

int softPotVals[] = {
  0, 0, 0
};
int softPotPins[] = {
  PIN_SOFTPOT_1, PIN_SOFTPOT_2, PIN_SOFTPOT_3
};

int potVal1Old = -1;
int potVal2Old = -1;
int softPotValsOld[] = {
  0, 0, 0
};

int fretTouched[N_STR];
int noteFretted[N_STR];

int stringActive[] = {
  false, false, false
};
int stringPlucked[] = {
  false, false, false
};

Note *activeNotes[N_STR];

int calibrationMax[] = {
  0, 0, 0
};
int calibrationMin[N_STR];

//true for low strings, false for high strings
int stringSetLow = true;
int octave = 3;


//E A D
int offsets_low[] = {16, 21, 26};

//G B E
int offsets_high[] = {19, 23, 28};

//default offsets
int offsets[] = {52, 57, 62};

//states of control buttons
int buttonStates[] = {
  false, false, false, false, false
};
int stickActive = false;
int stickZeroX = 0;
int stickZeroY = 0;
int stickState = false;

int altControlSet = false;
int stickXY = false;
int fullLegatoMode = false;

int minDurationOpen = 75;
int minVelocity = 75;

Adafruit_NeoPixel pixels = Adafruit_NeoPixel(N_PIXELS, NEO_PIN, NEO_GRB + NEO_KHZ800);
int led_number[2] = {0, 0};
int led_color;
int led;
int prev_led;

unsigned long last_read;

QuickStats stats;

//--------- Setup ----------------//
void setup() {

  //read fret definitions from EEPROM
  for (int i = 0; i < N_STR; i++) {
    fretDefs[i][0] = F0;
    for (int j = 1; j < 21; j++) {
      fretDefs[i][j] = EEPROM.read(j + (21 * i));
    }
    fretDefs[i][N_FRET] = 0;
    calibrationMin[i] = EEPROM.read(21 + (21 * i));
  }

  //begin at MIDI spec baud rate
  //Serial.begin(9600);
  //Serial.println("START");
  Serial.begin(31250);
  pinMode(PIN_SOFTPOT_1, INPUT);
  pinMode(PIN_SOFTPOT_2, INPUT);
  pinMode(PIN_SOFTPOT_3, INPUT);
  pinMode(PIN_PIEZO_1, INPUT);
  pinMode(PIN_PIEZO_2, INPUT);
  pinMode(PIN_PIEZO_3, INPUT);
  digitalWrite(PIN_SOFTPOT_1, HIGH);
  digitalWrite(PIN_SOFTPOT_2, HIGH);
  digitalWrite(PIN_SOFTPOT_3, HIGH);

  pinMode(PIN_BUTTON_RIGHT, INPUT);
  digitalWrite(PIN_BUTTON_RIGHT, HIGH);

  pinMode(PIN_BUTTON_LEFT, INPUT);
  digitalWrite(PIN_BUTTON_LEFT, HIGH);

  pinMode(PIN_BUTTON_UP, INPUT);
  digitalWrite(PIN_BUTTON_UP, HIGH);

  pinMode(PIN_BUTTON_DOWN, INPUT);
  digitalWrite(PIN_BUTTON_DOWN, HIGH);

  pinMode(PIN_BUTTON_STICK, INPUT);
  digitalWrite(PIN_BUTTON_STICK, HIGH);


  pinMode(PIN_LED, OUTPUT);

  pixels.begin();
  pixels.setBrightness(50);
  pixels.show();

  while (millis() < 500) {
    for (int i = 0; i < N_STR; i++) {
      int val = analogRead(softPotPins[i]);
      if (val > calibrationMax[i]) calibrationMax[i] = val;
    }

    //calibrate joystick
    stickZeroX = analogRead(PIN_JOYSTICK_X);
    stickZeroY = analogRead(PIN_JOYSTICK_Y);
  }
}

//----------Main Loop---------------//
void loop() {
  //reset
  for (int i = 0; i < N_STR; i++) {
    stringPlucked[i] = false;
    piezoVals[i] = false;
  }

  //read values of all sensors
  readSensors();

  determineFrets();

  //if we are in full legato mode, run the function
  if (fullLegatoMode) {
    fullLegato();
  }

  //otherwise just do the regular thing
  else {
    //test for legato action
    legatoTest();
   // onLED(N_PIXELS, 0, 0, 0);
    //use this info to determine which notes to pluck
    pickNotes();
    onLED(N_PIXELS, 0, 0, 0);
  }

  //send not off messages and reset necessary things
  cleanUp();

  //check for control changes
  readControls();
}

void readSensors() {
  for (int i = 0; i < N_STR; i++) {

    //read piezo vals
    int piezoVal = analogRead(piezoPins[i]);
    

    //if the value breaks the threshold read for max amplitude
    /* TODO: this is less than ideal. Have it determine which piezos were triggered and
       then sample them all at once for better support for polyphonic stuff.
    */

    if (piezoVal > PIEZO_THRESHOLD_ON) {
      int v_new = piezoVal;
      for (int sample = 0; sample < PIEZO_SAMPLES; sample++) {
        piezoVal = analogRead(piezoPins[i]);
        if (piezoVal > v_new) {
          v_new = piezoVal;
        }
      }
      piezoVals[i] = v_new;
      piezoVals[i] = map(piezoVals[i], 0, 500, 0, 127);
      piezoVals[i] = constrain(piezoVals[i], minVelocity, 127);
    }

    //read the value of all the softPots
    softPotVals[i] = analogRead(softPotPins[i]);
    softPotVals[i] = map(softPotVals[i], calibrationMin[i], calibrationMax[i], 0, 255);
    softPotVals[i] = constrain(softPotVals[i], 0, 255);
  }
}

void determineFrets () {
  static int count = 0;

  //---------Get Fret Numbers------
  for (int i = 0; i < N_STR; i++) {
  
    int softPotVal = softPotVals[i];

    //check for open strings
    if (softPotVal >= F0) {
      softPotValsOld[i] = softPotVal;
      fretTouched[i] = 0;
      led_number[i] = fretTouched[i];
    }

    //loop through the array of fret definitions
    for (int j = 1; j < N_FRET; j++) {

      int k = j - 1;
      if (softPotVal <= fretDefs[i][k] &&
          softPotVal > fretDefs[i][j] &&
          abs(softPotVal - softPotValsOld[i]) > PADDING) {

        softPotValsOld[i] = softPotVal;
        fretTouched[i] = j-2;
        led_number[i] = fretTouched[i];
        // Serial.print("string= ");  Serial.print(i);  Serial.print("  FretTouched= ");  Serial.print(j); Serial.print("  softpotval = ");  Serial.println(softPotVal);  //count=0;}
      }
    }
    if (softPotVal <= fretDefs[i][20]) {
      softPotValsOld[i] = softPotVal;
      fretTouched[i] = N_FRET;
      led_number[i] = fretTouched[i];
    }
  }
}

void pickNotes() {
  for (int i = 0; i < N_STR; i++) {

    //if the piezo was hit, play the fretted note
    if (piezoVals[i]) {
      switch (i) {
        case 0:
          noteFretted[i] = fretTouched[i] + offsets[i];
          break;
        case 1:
          noteFretted[i] = fretTouched[i] + offsets[i];
          break;
        case 2:
          noteFretted[i] = fretTouched[i] + offsets[i];
          break;
      }

      if (stringActive[i]) {

        //turn off the currently active note on that string
        noteOff(0x80 + i, activeNotes[i]->number(), 0);
        free(activeNotes[i]);
      }

      if (!stringActive[i]) {

        //mark string as active
        stringActive[i] = true;
      }
      //register with active notes
      activeNotes[i] = (Note *) malloc(sizeof(Note));

      if (fretTouched[i] > 0) activeNotes[i]->init(noteFretted[i], piezoVals[i], millis(), true);

      else activeNotes[i]->init(noteFretted[i], piezoVals[i], millis(), false);

      //turn on fretted note
      noteOn(0x90 + i, activeNotes[i]->number(), activeNotes[i]->velocity());

      //mark that the string was plucked
      stringPlucked[i] = true;
    }
  }
}

void legatoTest() {
  for (int i = 0; i < N_STR; i++) {
    if (stringActive[i]) {
      switch (i) {
        case 0:
          noteFretted[i] = fretTouched[i] + offsets[i];
          break;
        case 1:
          noteFretted[i] = fretTouched[i] + offsets[i];
          break;
        case 2:
          noteFretted[i] = fretTouched[i] + offsets[i];
          break;
      }

      if (noteFretted[i] != activeNotes[i]->number() && fretTouched[i]) {
        //turn on new note
        int vel = activeNotes[i]->velocity();
        noteOn(0x90 + i, noteFretted[i], vel);

        //turn off old note
        noteOff(0x80 + i, activeNotes[i]->number(), 0);
        free(activeNotes[i]);

        //register new note as the active one
        activeNotes[i] = (Note *) malloc(sizeof(Note));
        activeNotes[i]->init(noteFretted[i], vel, millis(), true);
      }
    }
  }
}
/*


*/
void fullLegato() {
  for (int i = 0; i < N_STR; i++) {
    if (fretTouched[i]) {

      switch (i) {
        case 0:
          noteFretted[i] = fretTouched[i] + offsets[i];
          break;
        case 1:
          noteFretted[i] = fretTouched[i] + offsets[i];
          break;
        case 2:
          noteFretted[i] = fretTouched[i] + offsets[i];
          break;
      }

      int vel = 127;

      if (!stringActive[i]) {
        noteOn(0x90 + i, noteFretted[i], vel);

        //register new note as the active one
        activeNotes[i] = (Note *) malloc(sizeof(Note));
        activeNotes[i]->init(noteFretted[i], vel, millis(), true);
        stringActive[i] = true;
      }
      else {

        if (noteFretted[i] != activeNotes[i]->number()) {
          int vel = 80;
          noteOn(0x90 + i, noteFretted[i], vel);

          //turn off old note
          noteOff(0x80 + i, activeNotes[i]->number(), 0);
          free(activeNotes[i]);

          //register new note as the active one
          activeNotes[i] = (Note *) malloc(sizeof(Note));
          activeNotes[i]->init(noteFretted[i], vel, millis(), true);
        }
      }
    }
  }
}

 
void cleanUp() {
  for (int i = 0; i < N_STR; i++) {

    //no fret is touched and the string is marked active
    if (!fretTouched[i] && stringActive[i]) {

      //if open string
      if (!activeNotes[i]->fretted()) {
        if (activeNotes[i]->timeActive() > minDurationOpen) {
          //turn off the active note
          noteOff(0x80 + i, activeNotes[i]->number(), 0);

          //mark as inactive
          stringActive[i] = false;
          free(activeNotes[i]);
        }
      }
      else {
        //turn off the active note
        noteOff(0x80 + i, activeNotes[i]->number(), 0);

        //mark as inactive
        stringActive[i] = false;
        free(activeNotes[i]);
      }
    }
  }
}

void readControls() {

  if (altControlSet) {
    minDurationOpen = analogRead(PIN_POT_1);
    minDurationOpen = map(minDurationOpen, 0, 1023, 0, 255 );

    minVelocity = analogRead(PIN_POT_2);
    minVelocity = map(minVelocity, 0, 1023, 0, 127);
  }

  //Potentiometers set the values for controllers 69 and 7
  else {
    int potVal1New = analogRead(PIN_POT_1);
    int potVal2New = analogRead(PIN_POT_2);

    if (abs(potVal1New - potVal1Old) > 30 || potVal1New == 0 || potVal1New == 1023 ) {
      if ((potVal1New - potVal1Old) != 0) {
        //Send MIDI control change message
        int val = map(potVal1New, 0, 1023, 0, 127);
        val = constrain(val, 0, 127);
        controllerChange(69, val);
        potVal1Old = potVal1New;
      }
    }

    if (abs(potVal2New - potVal2Old) > 30 || potVal2New == 0 || potVal2New == 1023) {
      if ((potVal2New - potVal2Old) != 0) {
        //Send MIDI control change message
        int val = map(potVal2New, 0, 1023, 0, 127);
        val = constrain(val, 0, 127);
        controllerChange(7, val);
        potVal2Old = potVal2New;
      }
    }
  }

  //-----ENGAGE FULL LEGATO MODE-----
  //removes the need for triggering with piezo for fretted notes
  if (digitalRead(PIN_BUTTON_LEFT) == LOW && !buttonStates[LEFT]) {
    fullLegatoMode = true;
    buttonStates[LEFT] = true;
  }
  if (digitalRead(PIN_BUTTON_LEFT) == HIGH && buttonStates[LEFT]) {
    fullLegatoMode = false;
    buttonStates[LEFT] = false;
  }

  led = max(led_number[0],led_number[1]);
  Serial.println(led_number[0]);
   //Serial.println(led_number[1]);
  if (led == -1){
    led = 0;
    onLED(N_PIXELS,0,0,0);
  }
    
  else{
    led = led + 1;
    led = map(led,0,N_FRET - 1,0,N_PIXELS);
    led_color = map(led,0,30,0,255);
    //Serial.println(led_number);
    if((millis() - ledDebounceTime ) > ledDebounceDelay){
      for(int i=0;i<led;i++){
        //pixels.Color takes RGB values, from 0,0,0 up to 255,255,255
        pixels.setPixelColor(i, Wheel(led_color));
      }
      if(prev_led > led)
        for(int i=led;i<N_PIXELS;i++){
          //pixels.Color takes RGB values, from 0,0,0 up to 255,255,255
          pixels.setPixelColor(i, (pixels.Color(0, 0, 0)));
        }
        pixels.show();
        ledDebounceTime = millis(); 
        prev_led = led ;
    }
  }

/*

  //switch to alt control set
  if (digitalRead(PIN_BUTTON_LEFT) == LOW && !buttonStates[LEFT]) {
    altControlSet = !altControlSet;
    buttonStates[LEFT] = true;
  }
  if (digitalRead(PIN_BUTTON_LEFT) == HIGH && buttonStates[LEFT]) buttonStates[LEFT] = false;


  //Right button triggers calibration
  if (digitalRead(PIN_BUTTON_RIGHT) == LOW && !buttonStates[RIGHT]) {
    buttonStates[RIGHT] = true;
    calibrate();
  }

*/

  //---- CHANGING THE OCTAVE -------//
  //UP and down buttons used to change offset/octave. Cycles through EAD and GBE like an infinite guitar neck.

  //---- UP BUTTON ----
  if (digitalRead(PIN_BUTTON_UP) == LOW) {
    //change the set of strings
    if (!buttonStates[UP]) {

      octave = octave - 1;

      stringSetLow = !stringSetLow;

      for (int i = 0; i < N_STR; i++) {
        //if low
        if (stringSetLow) {
          offsets[i] = offsets_low[i] + (12 * octave);
        }
        //if high
        if (!stringSetLow) {
          offsets[i] = offsets_high[i] + (12 * octave);
        }
      }
    }

    buttonStates[UP] = true;
  }
  //reset state once button is no longer being pressed
  if (digitalRead(PIN_BUTTON_UP) == HIGH && buttonStates[UP]) buttonStates[UP] = false;

  //----DOWN BUTTON----
  if (digitalRead(PIN_BUTTON_DOWN) == LOW) {
    //change the set of strings
    if (!buttonStates[DOWN]) {

      octave = octave + 1;

      stringSetLow = !stringSetLow;

      for (int i = 0; i < N_STR; i++) {
        //if low
        if (stringSetLow) {
          offsets[i] = offsets_low[i] + (12 * octave);
        }
        //if high
        if (!stringSetLow) {
          offsets[i] = offsets_high[i] + (12 * octave);
        }
      }
    }

    buttonStates[DOWN] = true;
  }
  //reset state once button is no longer being pressed
  if (digitalRead(PIN_BUTTON_DOWN) == HIGH && buttonStates[DOWN]) buttonStates[DOWN] = false;

  //switch stick to xy mode
  if (digitalRead(PIN_BUTTON_RIGHT) == LOW && !buttonStates[RIGHT]) {
    stickXY = !stickXY;
    buttonStates[RIGHT] = true;
  }
  if (digitalRead(PIN_BUTTON_RIGHT) == HIGH && buttonStates[RIGHT]) buttonStates[RIGHT] = false;

  //--------JOYSTICK-------//
  /* Click down the joystick to activate it. In regular mode it will read absolute position from the
     center in any direction (sends to modwheel, MIDI controller 1), and in XY mode the x axis
     sends to controller 2 (breath) and the y axis sends to controller 4 (foot).  */

  if (digitalRead(PIN_BUTTON_STICK) == LOW) {
    //activate joystick
    if (!buttonStates[STICK]) {
      //make sure modwheel value is set to 0 when stick is off
      if (stickActive) controllerChange(1, 0);
      stickActive = !stickActive;
    }
    buttonStates[STICK] = true;
  }
  //reset once stick is no longer being pressed
  if (digitalRead(PIN_BUTTON_STICK) == HIGH && buttonStates[STICK]) buttonStates[STICK] = false;

  if (stickActive) {
    //read positions from center
    float xPos = map(analogRead(PIN_JOYSTICK_X), stickZeroX, 1023, 0, 127);
    float yPos = map(analogRead(PIN_JOYSTICK_Y), stickZeroY, 1023, 0, 127);

    //get absolute position from center
    float z = sqrt(sq(xPos) + sq(yPos));
    int stickVal = (int)constrain(z, 0, 127);

    if (stickVal > 0) {
      stickState = true;
      if (stickXY) {
        controllerChange(2, abs(xPos));
        controllerChange(4, abs(yPos));
      }
      else controllerChange(1, stickVal);
    }
    else if (stickState && stickVal == 0) {
      stickState = false;
      if (stickXY) {
        controllerChange(2, 0);
        controllerChange(4, 0);
      }
      else controllerChange(1, 0);
    }
  }
}

//----CALIBRATION----//
/* Likely will only have to calibrate once. This is done by activating calibration mode and
   "plucking" each note on each string starting with the upper bound (after the 21st fret) and descending down
   to just after the 1st fret. Starts with the low E string, then the A string and then the D string.
   fret definitions are stored in EEPROM. Once it is calibrated for the voltage put out by the MIDI --> USB board
   you can just have the calibration button do something else because you probably won't need it again.
*/
void unset(int i){
  //this function doesn't even do anything!!
}

void calibrate() {

  for (int i = 0; i < N_STR; i++) {
    //Flash the LED too indicate calibration
    /*
      digitalWrite(PIN_LED, HIGH);
      delay(100);
      digitalWrite(PIN_LED, LOW);
      delay(100);
      digitalWrite(PIN_LED, HIGH);
      delay(100);
      digitalWrite(PIN_LED, LOW);
      delay(100);
      digitalWrite(PIN_LED, HIGH);
    */
    //Flash the LED too indicate calibration
    onLED(10, 250, 0, 0);
    delay(100);
    clrLED();
    onLED(10, 250, 0, 0);
    delay(100);
    clrLED();
    onLED(10, 250, 0, 0);
    delay(100);
    clrLED();
  
    int sensorMax = 0;
    int sensorMin = 1023;
    int val;

    //loop through the array of fret definitions
    for (int j = N_FRET; j > 0; j--) {

      int response = false;

      //wait for response
      while (!response) {

        //read piezo val
        int piezoVal = analogRead(piezoPins[i]);

        //get the sensor min value (highest fret) on the first round
        if (j == N_FRET) {
          int fretVal = analogRead(softPotPins[i]);
          if (fretVal > sensorMax) (sensorMax = fretVal);
           clrLED();
          //if the piezo is hit, register this as the definition for this fret
          if (piezoVal > PIEZO_THRESHOLD_ON) {
            int fretVal = analogRead(softPotPins[i]);
            sensorMin = fretVal;
            val = fretVal;
            response = true;
            clrLED();
          }
        }

        else {
          //get the rest of the fret definitions
          //if the piezo is hit, register this as the definition for this fret
          if (piezoVal > PIEZO_THRESHOLD_ON) {
            int fretVal = analogRead(softPotPins[i]);
            fretVal = map(fretVal, sensorMin, sensorMax, 0, 255);
            fretVal = constrain(fretVal, 0, 255);
            val = fretVal;
            response = true;
            clrLED();
          }
        }
      }

      //write to memory
      digitalWrite(PIN_LED, LOW);
      EEPROM.write(j + (N_FRET * i), val);

      delay(100);
      onLED(10, 250, 0, 0);
//      delay(100);
      digitalWrite(PIN_LED, HIGH);
    }


    //update global definitions
    calibrationMin[i] = EEPROM.read(N_FRET + (N_FRET * i));

    for (int j = 1; j < N_FRET; j++) {
      fretDefs[i][j] = EEPROM.read(j + (i * N_FRET));
    }
    clrLED();
  }
  buttonStates[RIGHT] = false;
  digitalWrite(PIN_LED, LOW);
}
void onLED(int led, int red, int green, int blue) {
  for (int i = 0; i < N_STR; i++) {
  
    //pixels.Color takes RGB values, from 0,0,0 up to 255,255,255
    pixels.setPixelColor(i, pixels.Color(red, green, blue));
  }
  pixels.show();
}

void clrLED() {
  for (int i = 0; i < N_PIXELS; i++){
    pixels.setPixelColor(i, pixels.Color(0, 0, 0)); // turn off
  }
  pixels.show();
}
//-------------MIDI functions-----------------

//note-on message
void noteOn(int cmd, int pitch, int velocity) {

  Serial.write(byte(cmd));
  Serial.write(byte(pitch));
  Serial.write(byte(velocity));
  digitalWrite(PIN_LED, HIGH);
}
//note-off message
void noteOff(int cmd, int pitch, int velocity) {

  Serial.write(byte(cmd));
  Serial.write(byte(pitch));
  Serial.write(byte(0));
  digitalWrite(PIN_LED, LOW);
}

//Sends controller change to the specified controller
void controllerChange(int controller, int value) {
  Serial.write(byte(0xb0));
  Serial.write(byte(controller));
  Serial.write(byte(value));

  Serial.write(byte(0xb1));
  Serial.write(byte(controller));
  Serial.write(byte(value));

  Serial.write(byte(0xb2));
  Serial.write(byte(controller));
  Serial.write(byte(value));
}

// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
  WheelPos = 255 - WheelPos;
  if(WheelPos < 85) {
    return pixels.Color(255 - WheelPos * 3 + pitchBendLight, 0, WheelPos * 3 + pitchBendLight);
  }
  if(WheelPos < 170) {
    WheelPos -= 85;
    return pixels.Color(0, WheelPos * 3 + pitchBendLight , 255 - WheelPos * 3 + pitchBendLight);
  }
  else{
    WheelPos -= 170;
    return pixels.Color(WheelPos * 3 + pitchBendLight , 255 - WheelPos * 3 + pitchBendLight , 0 );
  }
  if(mod_final> 50){
  }
}

3 - Potentiometers

4 - Momentary Buttons

Here is a problem:

int led_number[2] = {0, 0};

  //---------Get Fret Numbers------
  for (int i = 0; i < N_STR; i++)
  {
      led_number[i] = fretTouched[i];

You are going off the end of the led_number array, probably overwriting some other variable.

1 Like

Thanks for your reply Johnwasser!

I meant to note in my post that I tried changing the led_number array to
int led_number[3] = {0, 0, 0};

but did not get any success.

Is that what you mean by "going off the end of the led_number array" . . . that the array is only setup for 2 instead of 3? I suspect you are referring to something more technically advanced than that :slight_smile:

Yes. I had no way of knowing you had fixed that mistake already.

1 Like

Oh okay, yes I had changed it back to the original 2 since the pixels still did not work when the 3rd softpot was touched. Good to know that I was at least on the right track. I will change it back and post the updated sketch/results. Thanks!

I updated the led_number array to:
int led_number[3] = {0, 0, 0};

Then I rebooted the Arduino & retested. The 3rd softpot still does not trigger the pixels for some reason (except during the calibration process it blinks them red when the softpot is touched, while its corresponding piezo sensor is triggered). Updated sketch below

/*
  Arduino Ribbon Synth MIDI controller
   ------------------------------------
   ©2014 Dean Miller
*/

#include <EEPROM.h>
#include <Wire.h>
#include <Adafruit_NeoPixel.h>
#include <QuickStats.h>
#ifdef __AVR__
#include <avr/power.h>
#endif

//------- Constants -------//
#define PIEZO_THRESHOLD_ON 12

#define PIN_LED 13
#define NEO_PIN   21
#define N_PIXELS  16      //# of LEDS

#define PIN_SOFTPOT_1 A0
#define PIN_SOFTPOT_2 A1
#define PIN_SOFTPOT_3 A2
#define PIN_PIEZO_1 A3
#define PIN_PIEZO_2 A4
#define PIN_PIEZO_3 A5
#define PIN_POT_1 8
#define PIN_POT_2 6

#define PIN_BUTTON_STICK 3

#define PIN_BUTTON_RIGHT 11
#define PIN_BUTTON_UP 5
#define PIN_BUTTON_DOWN 2
#define PIN_BUTTON_LEFT 7

//Joystick
#define PIN_JOYSTICK_X 9
#define PIN_JOYSTICK_Y 10

//Joystick
#define UP 0
#define RIGHT 1
#define DOWN 2
#define LEFT 3
#define STICK 4

#define PIEZO_SAMPLES 400
#define PADDING 2

#define THRESH    200
#define N_STR     3
#define N_FRET    21


//#define MOD_THRESHOLD 30  //Modulation is not send under this value

long noteDebounceTime = 0;
int noteDebounceDelay = 25;

long lastDebounceTime = 0;
int debounceDelay = 200;


long ledDebounceTime = 0;
int ledDebounceDelay = 20;

int mod_final;

//bool isPitchBend = false;
unsigned int pitchBendLight = 0;
//bool dim = false;


//------Note Class---------//
/*
  A note class that stores some info about each note played is necessary
   to ensure that open strings are held for the specified amount of time.
   That is a problem with using the piezos as triggers instead of FSRs, they
   only register momentary impact or vibration, creating a problem for open strings.
*/

class Note {
    int _number;
    int _velocity;
    int _startTime;
    int _fretted;

  public:
    void init(int number, int velocity, int startTime, int fretted) {
      _number = number;
      _velocity = velocity;
      _startTime = startTime;
      _fretted = fretted;
    }

    int number() {
      return _number;
    }

    int velocity() {
      return _velocity;
    }

    int fretted() {
      return _fretted;
    }

    int timeActive() {
      return millis() - _startTime;
    }
};

//------ Global Variables ---------//

/*
  fret defs stored in EEPROM for calibration purposes.
  Lower output voltages from USB ports result in different values read from
  SoftPots and wonky fret definitions.*/

int F0 = 248;  // all bigger softpot values will be treated as open string
int F21 = 0;

int fretDefs[N_STR][N_FRET];
//short fretDefs[N_STR][N_FRET];

int piezoVals[] = {
  0, 0, 0
};
int piezoPins[] = {
  PIN_PIEZO_1, PIN_PIEZO_2, PIN_PIEZO_3
};

int softPotVals[] = {
  0, 0, 0
};
int softPotPins[] = {
  PIN_SOFTPOT_1, PIN_SOFTPOT_2, PIN_SOFTPOT_3
};

int potVal1Old = -1;
int potVal2Old = -1;
int softPotValsOld[] = {
  0, 0, 0
};

int fretTouched[N_STR];
int noteFretted[N_STR];

int stringActive[] = {
  false, false, false
};
int stringPlucked[] = {
  false, false, false
};

Note *activeNotes[N_STR];

int calibrationMax[] = {
  0, 0, 0
};
int calibrationMin[N_STR];

//true for low strings, false for high strings
int stringSetLow = true;
int octave = 3;


//E A D
int offsets_low[] = {16, 21, 26};

//G B E
int offsets_high[] = {19, 23, 28};

//default offsets
int offsets[] = {52, 57, 62};

//states of control buttons
int buttonStates[] = {
  false, false, false, false, false
};
int stickActive = false;
int stickZeroX = 0;
int stickZeroY = 0;
int stickState = false;

int altControlSet = false;
int stickXY = false;
int fullLegatoMode = false;

int minDurationOpen = 75;
int minVelocity = 75;

Adafruit_NeoPixel pixels = Adafruit_NeoPixel(N_PIXELS, NEO_PIN, NEO_GRB + NEO_KHZ800);
int led_number[3] = {0, 0, 0};
int led_color;
int led;
int prev_led;

unsigned long last_read;

QuickStats stats;

//--------- Setup ----------------//
void setup() {

  //read fret definitions from EEPROM
  for (int i = 0; i < N_STR; i++) {
    fretDefs[i][0] = F0;
    for (int j = 1; j < 21; j++) {
      fretDefs[i][j] = EEPROM.read(j + (21 * i));
    }
    fretDefs[i][N_FRET] = 0;
    calibrationMin[i] = EEPROM.read(21 + (21 * i));
  }

  //begin at MIDI spec baud rate
  //Serial.begin(9600);
  //Serial.println("START");
  Serial.begin(31250);
  pinMode(PIN_SOFTPOT_1, INPUT);
  pinMode(PIN_SOFTPOT_2, INPUT);
  pinMode(PIN_SOFTPOT_3, INPUT);
  pinMode(PIN_PIEZO_1, INPUT);
  pinMode(PIN_PIEZO_2, INPUT);
  pinMode(PIN_PIEZO_3, INPUT);
  digitalWrite(PIN_SOFTPOT_1, HIGH);
  digitalWrite(PIN_SOFTPOT_2, HIGH);
  digitalWrite(PIN_SOFTPOT_3, HIGH);

  pinMode(PIN_BUTTON_RIGHT, INPUT);
  digitalWrite(PIN_BUTTON_RIGHT, HIGH);

  pinMode(PIN_BUTTON_LEFT, INPUT);
  digitalWrite(PIN_BUTTON_LEFT, HIGH);

  pinMode(PIN_BUTTON_UP, INPUT);
  digitalWrite(PIN_BUTTON_UP, HIGH);

  pinMode(PIN_BUTTON_DOWN, INPUT);
  digitalWrite(PIN_BUTTON_DOWN, HIGH);

  pinMode(PIN_BUTTON_STICK, INPUT);
  digitalWrite(PIN_BUTTON_STICK, HIGH);


  pinMode(PIN_LED, OUTPUT);

  pixels.begin();
  pixels.setBrightness(50);
  pixels.show();

  while (millis() < 500) {
    for (int i = 0; i < N_STR; i++) {
      int val = analogRead(softPotPins[i]);
      if (val > calibrationMax[i]) calibrationMax[i] = val;
    }

    //calibrate joystick
    stickZeroX = analogRead(PIN_JOYSTICK_X);
    stickZeroY = analogRead(PIN_JOYSTICK_Y);
  }
}

//----------Main Loop---------------//
void loop() {
  //reset
  for (int i = 0; i < N_STR; i++) {
    stringPlucked[i] = false;
    piezoVals[i] = false;
  }

  //read values of all sensors
  readSensors();

  determineFrets();

  //if we are in full legato mode, run the function
  if (fullLegatoMode) {
    fullLegato();
  }

  //otherwise just do the regular thing
  else {
    //test for legato action
    legatoTest();
   // onLED(N_PIXELS, 0, 0, 0);
    //use this info to determine which notes to pluck
    pickNotes();
    onLED(N_PIXELS, 0, 0, 0);
  }

  //send not off messages and reset necessary things
  cleanUp();

  //check for control changes
  readControls();
}

void readSensors() {
  for (int i = 0; i < N_STR; i++) {

    //read piezo vals
    int piezoVal = analogRead(piezoPins[i]);
    

    //if the value breaks the threshold read for max amplitude
    /* TODO: this is less than ideal. Have it determine which piezos were triggered and
       then sample them all at once for better support for polyphonic stuff.
    */

    if (piezoVal > PIEZO_THRESHOLD_ON) {
      int v_new = piezoVal;
      for (int sample = 0; sample < PIEZO_SAMPLES; sample++) {
        piezoVal = analogRead(piezoPins[i]);
        if (piezoVal > v_new) {
          v_new = piezoVal;
        }
      }
      piezoVals[i] = v_new;
      piezoVals[i] = map(piezoVals[i], 0, 500, 0, 127);
      piezoVals[i] = constrain(piezoVals[i], minVelocity, 127);
    }

    //read the value of all the softPots
    softPotVals[i] = analogRead(softPotPins[i]);
    softPotVals[i] = map(softPotVals[i], calibrationMin[i], calibrationMax[i], 0, 255);
    softPotVals[i] = constrain(softPotVals[i], 0, 255);
  }
}

void determineFrets () {
  static int count = 0;

  //---------Get Fret Numbers------
  for (int i = 0; i < N_STR; i++) {
  
    int softPotVal = softPotVals[i];

    //check for open strings
    if (softPotVal >= F0) {
      softPotValsOld[i] = softPotVal;
      fretTouched[i] = 0;
      led_number[i] = fretTouched[i];
    }

    //loop through the array of fret definitions
    for (int j = 1; j < N_FRET; j++) {

      int k = j - 1;
      if (softPotVal <= fretDefs[i][k] &&
          softPotVal > fretDefs[i][j] &&
          abs(softPotVal - softPotValsOld[i]) > PADDING) {

        softPotValsOld[i] = softPotVal;
        fretTouched[i] = j-2;
        led_number[i] = fretTouched[i];
        // Serial.print("string= ");  Serial.print(i);  Serial.print("  FretTouched= ");  Serial.print(j); Serial.print("  softpotval = ");  Serial.println(softPotVal);  //count=0;}
      }
    }
    if (softPotVal <= fretDefs[i][20]) {
      softPotValsOld[i] = softPotVal;
      fretTouched[i] = N_FRET;
      led_number[i] = fretTouched[i];
    }
  }
}

void pickNotes() {
  for (int i = 0; i < N_STR; i++) {

    //if the piezo was hit, play the fretted note
    if (piezoVals[i]) {
      switch (i) {
        case 0:
          noteFretted[i] = fretTouched[i] + offsets[i];
          break;
        case 1:
          noteFretted[i] = fretTouched[i] + offsets[i];
          break;
        case 2:
          noteFretted[i] = fretTouched[i] + offsets[i];
          break;
      }

      if (stringActive[i]) {

        //turn off the currently active note on that string
        noteOff(0x80 + i, activeNotes[i]->number(), 0);
        free(activeNotes[i]);
      }

      if (!stringActive[i]) {

        //mark string as active
        stringActive[i] = true;
      }
      //register with active notes
      activeNotes[i] = (Note *) malloc(sizeof(Note));

      if (fretTouched[i] > 0) activeNotes[i]->init(noteFretted[i], piezoVals[i], millis(), true);

      else activeNotes[i]->init(noteFretted[i], piezoVals[i], millis(), false);

      //turn on fretted note
      noteOn(0x90 + i, activeNotes[i]->number(), activeNotes[i]->velocity());

      //mark that the string was plucked
      stringPlucked[i] = true;
    }
  }
}

void legatoTest() {
  for (int i = 0; i < N_STR; i++) {
    if (stringActive[i]) {
      switch (i) {
        case 0:
          noteFretted[i] = fretTouched[i] + offsets[i];
          break;
        case 1:
          noteFretted[i] = fretTouched[i] + offsets[i];
          break;
        case 2:
          noteFretted[i] = fretTouched[i] + offsets[i];
          break;
      }

      if (noteFretted[i] != activeNotes[i]->number() && fretTouched[i]) {
        //turn on new note
        int vel = activeNotes[i]->velocity();
        noteOn(0x90 + i, noteFretted[i], vel);

        //turn off old note
        noteOff(0x80 + i, activeNotes[i]->number(), 0);
        free(activeNotes[i]);

        //register new note as the active one
        activeNotes[i] = (Note *) malloc(sizeof(Note));
        activeNotes[i]->init(noteFretted[i], vel, millis(), true);
      }
    }
  }
}
/*


*/
void fullLegato() {
  for (int i = 0; i < N_STR; i++) {
    if (fretTouched[i]) {

      switch (i) {
        case 0:
          noteFretted[i] = fretTouched[i] + offsets[i];
          break;
        case 1:
          noteFretted[i] = fretTouched[i] + offsets[i];
          break;
        case 2:
          noteFretted[i] = fretTouched[i] + offsets[i];
          break;
      }

      int vel = 127;

      if (!stringActive[i]) {
        noteOn(0x90 + i, noteFretted[i], vel);

        //register new note as the active one
        activeNotes[i] = (Note *) malloc(sizeof(Note));
        activeNotes[i]->init(noteFretted[i], vel, millis(), true);
        stringActive[i] = true;
      }
      else {

        if (noteFretted[i] != activeNotes[i]->number()) {
          int vel = 80;
          noteOn(0x90 + i, noteFretted[i], vel);

          //turn off old note
          noteOff(0x80 + i, activeNotes[i]->number(), 0);
          free(activeNotes[i]);

          //register new note as the active one
          activeNotes[i] = (Note *) malloc(sizeof(Note));
          activeNotes[i]->init(noteFretted[i], vel, millis(), true);
        }
      }
    }
  }
}

 
void cleanUp() {
  for (int i = 0; i < N_STR; i++) {

    //no fret is touched and the string is marked active
    if (!fretTouched[i] && stringActive[i]) {

      //if open string
      if (!activeNotes[i]->fretted()) {
        if (activeNotes[i]->timeActive() > minDurationOpen) {
          //turn off the active note
          noteOff(0x80 + i, activeNotes[i]->number(), 0);

          //mark as inactive
          stringActive[i] = false;
          free(activeNotes[i]);
        }
      }
      else {
        //turn off the active note
        noteOff(0x80 + i, activeNotes[i]->number(), 0);

        //mark as inactive
        stringActive[i] = false;
        free(activeNotes[i]);
      }
    }
  }
}

void readControls() {

  if (altControlSet) {
    minDurationOpen = analogRead(PIN_POT_1);
    minDurationOpen = map(minDurationOpen, 0, 1023, 0, 255 );

    minVelocity = analogRead(PIN_POT_2);
    minVelocity = map(minVelocity, 0, 1023, 0, 127);
  }

  //Potentiometers set the values for controllers 69 and 7
  else {
    int potVal1New = analogRead(PIN_POT_1);
    int potVal2New = analogRead(PIN_POT_2);

    if (abs(potVal1New - potVal1Old) > 30 || potVal1New == 0 || potVal1New == 1023 ) {
      if ((potVal1New - potVal1Old) != 0) {
        //Send MIDI control change message
        int val = map(potVal1New, 0, 1023, 0, 127);
        val = constrain(val, 0, 127);
        controllerChange(69, val);
        potVal1Old = potVal1New;
      }
    }

    if (abs(potVal2New - potVal2Old) > 30 || potVal2New == 0 || potVal2New == 1023) {
      if ((potVal2New - potVal2Old) != 0) {
        //Send MIDI control change message
        int val = map(potVal2New, 0, 1023, 0, 127);
        val = constrain(val, 0, 127);
        controllerChange(7, val);
        potVal2Old = potVal2New;
      }
    }
  }

  //-----ENGAGE FULL LEGATO MODE-----
  //removes the need for triggering with piezo for fretted notes
  if (digitalRead(PIN_BUTTON_LEFT) == LOW && !buttonStates[LEFT]) {
    fullLegatoMode = true;
    buttonStates[LEFT] = true;
  }
  if (digitalRead(PIN_BUTTON_LEFT) == HIGH && buttonStates[LEFT]) {
    fullLegatoMode = false;
    buttonStates[LEFT] = false;
  }

  led = max(led_number[0],led_number[1]);
  Serial.println(led_number[0]);
   //Serial.println(led_number[1]);
  if (led == -1){
    led = 0;
    onLED(N_PIXELS,0,0,0);
  }
    
  else{
    led = led + 1;
    led = map(led,0,N_FRET - 1,0,N_PIXELS);
    led_color = map(led,0,30,0,255);
    //Serial.println(led_number);
    if((millis() - ledDebounceTime ) > ledDebounceDelay){
      for(int i=0;i<led;i++){
        //pixels.Color takes RGB values, from 0,0,0 up to 255,255,255
        pixels.setPixelColor(i, Wheel(led_color));
      }
      if(prev_led > led)
        for(int i=led;i<N_PIXELS;i++){
          //pixels.Color takes RGB values, from 0,0,0 up to 255,255,255
          pixels.setPixelColor(i, (pixels.Color(0, 0, 0)));
        }
        pixels.show();
        ledDebounceTime = millis(); 
        prev_led = led ;
    }
  }

/*

  //switch to alt control set
  if (digitalRead(PIN_BUTTON_LEFT) == LOW && !buttonStates[LEFT]) {
    altControlSet = !altControlSet;
    buttonStates[LEFT] = true;
  }
  if (digitalRead(PIN_BUTTON_LEFT) == HIGH && buttonStates[LEFT]) buttonStates[LEFT] = false;


  //Right button triggers calibration
  if (digitalRead(PIN_BUTTON_RIGHT) == LOW && !buttonStates[RIGHT]) {
    buttonStates[RIGHT] = true;
    calibrate();
  }

*/

  //---- CHANGING THE OCTAVE -------//
  //UP and down buttons used to change offset/octave. Cycles through EAD and GBE like an infinite guitar neck.

  //---- UP BUTTON ----
  if (digitalRead(PIN_BUTTON_UP) == LOW) {
    //change the set of strings
    if (!buttonStates[UP]) {

      octave = octave - 1;

      stringSetLow = !stringSetLow;

      for (int i = 0; i < N_STR; i++) {
        //if low
        if (stringSetLow) {
          offsets[i] = offsets_low[i] + (12 * octave);
        }
        //if high
        if (!stringSetLow) {
          offsets[i] = offsets_high[i] + (12 * octave);
        }
      }
    }

    buttonStates[UP] = true;
  }
  //reset state once button is no longer being pressed
  if (digitalRead(PIN_BUTTON_UP) == HIGH && buttonStates[UP]) buttonStates[UP] = false;

  //----DOWN BUTTON----
  if (digitalRead(PIN_BUTTON_DOWN) == LOW) {
    //change the set of strings
    if (!buttonStates[DOWN]) {

      octave = octave + 1;

      stringSetLow = !stringSetLow;

      for (int i = 0; i < N_STR; i++) {
        //if low
        if (stringSetLow) {
          offsets[i] = offsets_low[i] + (12 * octave);
        }
        //if high
        if (!stringSetLow) {
          offsets[i] = offsets_high[i] + (12 * octave);
        }
      }
    }

    buttonStates[DOWN] = true;
  }
  //reset state once button is no longer being pressed
  if (digitalRead(PIN_BUTTON_DOWN) == HIGH && buttonStates[DOWN]) buttonStates[DOWN] = false;

  //switch stick to xy mode
  if (digitalRead(PIN_BUTTON_RIGHT) == LOW && !buttonStates[RIGHT]) {
    stickXY = !stickXY;
    buttonStates[RIGHT] = true;
  }
  if (digitalRead(PIN_BUTTON_RIGHT) == HIGH && buttonStates[RIGHT]) buttonStates[RIGHT] = false;

  //--------JOYSTICK-------//
  /* Click down the joystick to activate it. In regular mode it will read absolute position from the
     center in any direction (sends to modwheel, MIDI controller 1), and in XY mode the x axis
     sends to controller 2 (breath) and the y axis sends to controller 4 (foot).  */

  if (digitalRead(PIN_BUTTON_STICK) == LOW) {
    //activate joystick
    if (!buttonStates[STICK]) {
      //make sure modwheel value is set to 0 when stick is off
      if (stickActive) controllerChange(1, 0);
      stickActive = !stickActive;
    }
    buttonStates[STICK] = true;
  }
  //reset once stick is no longer being pressed
  if (digitalRead(PIN_BUTTON_STICK) == HIGH && buttonStates[STICK]) buttonStates[STICK] = false;

  if (stickActive) {
    //read positions from center
    float xPos = map(analogRead(PIN_JOYSTICK_X), stickZeroX, 1023, 0, 127);
    float yPos = map(analogRead(PIN_JOYSTICK_Y), stickZeroY, 1023, 0, 127);

    //get absolute position from center
    float z = sqrt(sq(xPos) + sq(yPos));
    int stickVal = (int)constrain(z, 0, 127);

    if (stickVal > 0) {
      stickState = true;
      if (stickXY) {
        controllerChange(2, abs(xPos));
        controllerChange(4, abs(yPos));
      }
      else controllerChange(1, stickVal);
    }
    else if (stickState && stickVal == 0) {
      stickState = false;
      if (stickXY) {
        controllerChange(2, 0);
        controllerChange(4, 0);
      }
      else controllerChange(1, 0);
    }
  }
}

//----CALIBRATION----//
/* Likely will only have to calibrate once. This is done by activating calibration mode and
   "plucking" each note on each string starting with the upper bound (after the 21st fret) and descending down
   to just after the 1st fret. Starts with the low E string, then the A string and then the D string.
   fret definitions are stored in EEPROM. Once it is calibrated for the voltage put out by the MIDI --> USB board
   you can just have the calibration button do something else because you probably won't need it again.
*/
void unset(int i){
  //this function doesn't even do anything!!
}

void calibrate() {

  for (int i = 0; i < N_STR; i++) {
    //Flash the LED too indicate calibration
    /*
      digitalWrite(PIN_LED, HIGH);
      delay(100);
      digitalWrite(PIN_LED, LOW);
      delay(100);
      digitalWrite(PIN_LED, HIGH);
      delay(100);
      digitalWrite(PIN_LED, LOW);
      delay(100);
      digitalWrite(PIN_LED, HIGH);
    */
    //Flash the LED too indicate calibration
    onLED(10, 250, 0, 0);
    delay(100);
    clrLED();
    onLED(10, 250, 0, 0);
    delay(100);
    clrLED();
    onLED(10, 250, 0, 0);
    delay(100);
    clrLED();
  
    int sensorMax = 0;
    int sensorMin = 1023;
    int val;

    //loop through the array of fret definitions
    for (int j = N_FRET; j > 0; j--) {

      int response = false;

      //wait for response
      while (!response) {

        //read piezo val
        int piezoVal = analogRead(piezoPins[i]);

        //get the sensor min value (highest fret) on the first round
        if (j == N_FRET) {
          int fretVal = analogRead(softPotPins[i]);
          if (fretVal > sensorMax) (sensorMax = fretVal);
           clrLED();
          //if the piezo is hit, register this as the definition for this fret
          if (piezoVal > PIEZO_THRESHOLD_ON) {
            int fretVal = analogRead(softPotPins[i]);
            sensorMin = fretVal;
            val = fretVal;
            response = true;
            clrLED();
          }
        }

        else {
          //get the rest of the fret definitions
          //if the piezo is hit, register this as the definition for this fret
          if (piezoVal > PIEZO_THRESHOLD_ON) {
            int fretVal = analogRead(softPotPins[i]);
            fretVal = map(fretVal, sensorMin, sensorMax, 0, 255);
            fretVal = constrain(fretVal, 0, 255);
            val = fretVal;
            response = true;
            clrLED();
          }
        }
      }

      //write to memory
      digitalWrite(PIN_LED, LOW);
      EEPROM.write(j + (N_FRET * i), val);

      delay(100);
      onLED(10, 250, 0, 0);
//      delay(100);
      digitalWrite(PIN_LED, HIGH);
    }


    //update global definitions
    calibrationMin[i] = EEPROM.read(N_FRET + (N_FRET * i));

    for (int j = 1; j < N_FRET; j++) {
      fretDefs[i][j] = EEPROM.read(j + (i * N_FRET));
    }
    clrLED();
  }
  buttonStates[RIGHT] = false;
  digitalWrite(PIN_LED, LOW);
}
void onLED(int led, int red, int green, int blue) {
  for (int i = 0; i < N_STR; i++) {
  
    //pixels.Color takes RGB values, from 0,0,0 up to 255,255,255
    pixels.setPixelColor(i, pixels.Color(red, green, blue));
  }
  pixels.show();
}

void clrLED() {
  for (int i = 0; i < N_PIXELS; i++){
    pixels.setPixelColor(i, pixels.Color(0, 0, 0)); // turn off
  }
  pixels.show();
}
//-------------MIDI functions-----------------

//note-on message
void noteOn(int cmd, int pitch, int velocity) {

  Serial.write(byte(cmd));
  Serial.write(byte(pitch));
  Serial.write(byte(velocity));
  digitalWrite(PIN_LED, HIGH);
}
//note-off message
void noteOff(int cmd, int pitch, int velocity) {

  Serial.write(byte(cmd));
  Serial.write(byte(pitch));
  Serial.write(byte(0));
  digitalWrite(PIN_LED, LOW);
}

//Sends controller change to the specified controller
void controllerChange(int controller, int value) {
  Serial.write(byte(0xb0));
  Serial.write(byte(controller));
  Serial.write(byte(value));

  Serial.write(byte(0xb1));
  Serial.write(byte(controller));
  Serial.write(byte(value));

  Serial.write(byte(0xb2));
  Serial.write(byte(controller));
  Serial.write(byte(value));
}

// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
  WheelPos = 255 - WheelPos;
  if(WheelPos < 85) {
    return pixels.Color(255 - WheelPos * 3 + pitchBendLight, 0, WheelPos * 3 + pitchBendLight);
  }
  if(WheelPos < 170) {
    WheelPos -= 85;
    return pixels.Color(0, WheelPos * 3 + pitchBendLight , 255 - WheelPos * 3 + pitchBendLight);
  }
  else{
    WheelPos -= 170;
    return pixels.Color(WheelPos * 3 + pitchBendLight , 255 - WheelPos * 3 + pitchBendLight , 0 );
  }
  if(mod_final> 50){
  }
}

Are the right values in the EEPROM for the 'string' that didn't exist before?

This looks like it is going through frets 21 through 1 instead of 20 through 0 so they may be going off the end of some array.

I have assumed the correct values for the new softpot string have been saved into the EEPROM, because the new softpot registers just like the other two softpots during the calibration process.
It also performs and behaves like the other two softpots after I comment out the calibration portion of the code (except for the noted neopixel issue).

But please let me know if there's a better way to confirm the right values for the new softpot are being correctly stored to EEPROM.

Should I try changing: j > 0
to
j > -1

@Grumpy_Mike - When you have time, could you please take a quick look at the sketch and let me know if you see anything that might be causing the issue with the 3rd softpot not triggering the pixels?

Have you calibrated it?
If so could you please try printing the output you get from the EEPROM when you load in the EEPROM contents and posting that.

Also have you a proper schematic showing where everything is wired all in one diagram, not just fragments.

The code only controls 16 LEDs, is this how many you have?
Thanks

1 Like

Yes, and it seems that it has been calibrated correctly because all 21 frets are triggered properly on all 3 softpots (except there's about a centimeter at the far end of all 3 softpots that doesn't trigger - just before the first fret).

This hasn't bothered me much yet, but I would like to eventually figure out how to correct that. This spot that doesn't trigger notes is much larger when I try to use a 10k resistor from the softpots' wipers to ground as recommended in this hookup article - so far, they work best without a resistor

I will research how to do this and post the results

I will make one to my best ability today and post it as soon as possible

Yes, for now I'm just using two 8 pixel strips for a total of 16 pixels while everything is on the breadboard. I plan to switch to a longer adhesive strip when I build out the permanent enclosure.

Thanks for your help!

Nothing much to research, just use a Serial.println( value ) to print out each value you read from the EEPROM.

Could this be why your third sensor is not displaying anything on the LEDs?

1 Like

Oh, I've been working on that today. I inserted the same Serial.print values that was used in the same sketch that I got the neopixels bits of code from.

Below is what I get during the calibration process. Each 'Writing XX to address: XX is the 21st fret of the corresponding string, with each subsequent 'waiting' representing the other frets going down to fret 1.

I may not have inserted the Serial.print lines in the exact needed places, because I think it's supposed to give an address for each fret.

Softpot A0 fret 21 - Writing 28 to address: 42
Softpot A1 fret 21 - Writing 33 to address: 84
Softpot A2 fret 21 (the one not triggering pixels during normal use - Writing 91 to address: 126

The neopixels blink red each time I strike the piezo for a new fret on all 3 softpots. After calibration, I did notice that only the first softpot (A0) shows fret numbers in the Serial monitor - skips from fret 21 to fret 18 but all the other frets are there. The other softpots flash up seemingly random letters when touched. :man_shrugging:

New sketch with serial print bits added at the bottom of this post

calibrating...
waiting
Writing 28 to address: 42
waiting
waiting
waiting
waiting
waiting
waiting
waiting
waiting
waiting
waiting
waiting
waiting
waiting
waiting
waiting
waiting
waiting
waiting
waiting
waiting
waiting
Writing 33 to address: 84
waiting
waiting
waiting
waiting
waiting
waiting
waiting
waiting
waiting
waiting
waiting
waiting
waiting
waiting
waiting
waiting
waiting
waiting
waiting
waiting
waiting
Writing 91 to address: 126
waiting
waiting
waiting
waiting
waiting
waiting
waiting
waiting
waiting
waiting
waiting
waiting
waiting
waiting
waiting
waiting
waiting
waiting
waiting
waiting

/*
  Arduino Ribbon Synth MIDI controller
   ------------------------------------
   ©2014 Dean Miller
*/

#include <EEPROM.h>
#include <Wire.h>
#include <Adafruit_NeoPixel.h>
#include <QuickStats.h>
#ifdef __AVR__
#include <avr/power.h>
#endif

//------- Constants -------//
#define PIEZO_THRESHOLD_ON 12

#define PIN_LED 13
#define NEO_PIN   21
#define N_PIXELS  16      //# of LEDS

#define PIN_SOFTPOT_1 A0
#define PIN_SOFTPOT_2 A1
#define PIN_SOFTPOT_3 A2
#define PIN_PIEZO_1 A3
#define PIN_PIEZO_2 A4
#define PIN_PIEZO_3 A5
#define PIN_POT_1 8
#define PIN_POT_2 6

#define PIN_BUTTON_STICK 3

#define PIN_BUTTON_RIGHT 11
#define PIN_BUTTON_UP 5
#define PIN_BUTTON_DOWN 2
#define PIN_BUTTON_LEFT 7

//Joystick
#define PIN_JOYSTICK_X 9
#define PIN_JOYSTICK_Y 10

//Joystick
#define UP 0
#define RIGHT 1
#define DOWN 2
#define LEFT 3
#define STICK 4

#define PIEZO_SAMPLES 400
#define PADDING 2

#define THRESH    200
#define N_STR     3
#define N_FRET    21


//#define MOD_THRESHOLD 30  //Modulation is not send under this value

long noteDebounceTime = 0;
int noteDebounceDelay = 25;

long lastDebounceTime = 0;
int debounceDelay = 200;


long ledDebounceTime = 0;
int ledDebounceDelay = 20;

int mod_final;

//bool isPitchBend = false;
unsigned int pitchBendLight = 0;
//bool dim = false;


//------Note Class---------//
/*
  A note class that stores some info about each note played is necessary
   to ensure that open strings are held for the specified amount of time.
   That is a problem with using the piezos as triggers instead of FSRs, they
   only register momentary impact or vibration, creating a problem for open strings.
*/

class Note {
    int _number;
    int _velocity;
    int _startTime;
    int _fretted;

  public:
    void init(int number, int velocity, int startTime, int fretted) {
      _number = number;
      _velocity = velocity;
      _startTime = startTime;
      _fretted = fretted;
    }

    int number() {
      return _number;
    }

    int velocity() {
      return _velocity;
    }

    int fretted() {
      return _fretted;
    }

    int timeActive() {
      return millis() - _startTime;
    }
};

//------ Global Variables ---------//

/*
  fret defs stored in EEPROM for calibration purposes.
  Lower output voltages from USB ports result in different values read from
  SoftPots and wonky fret definitions.*/

int F0 = 248;  // all bigger softpot values will be treated as open string
int F21 = 0;

int fretDefs[N_STR][N_FRET];
//short fretDefs[N_STR][N_FRET];

int piezoVals[] = {
  0, 0, 0
};
int piezoPins[] = {
  PIN_PIEZO_1, PIN_PIEZO_2, PIN_PIEZO_3
};

int softPotVals[] = {
  0, 0, 0
};
int softPotPins[] = {
  PIN_SOFTPOT_1, PIN_SOFTPOT_2, PIN_SOFTPOT_3
};

int potVal1Old = -1;
int potVal2Old = -1;
int softPotValsOld[] = {
  0, 0, 0
};

int fretTouched[N_STR];
int noteFretted[N_STR];

int stringActive[] = {
  false, false, false
};
int stringPlucked[] = {
  false, false, false
};

Note *activeNotes[N_STR];

int calibrationMax[] = {
  0, 0, 0
};
int calibrationMin[N_STR];

//true for low strings, false for high strings
int stringSetLow = true;
int octave = 3;


//E A D
int offsets_low[] = {16, 21, 26};

//G B E
int offsets_high[] = {19, 23, 28};

//default offsets
int offsets[] = {52, 57, 62};

//states of control buttons
int buttonStates[] = {
  false, false, false, false, false
};
int stickActive = false;
int stickZeroX = 0;
int stickZeroY = 0;
int stickState = false;

int altControlSet = false;
int stickXY = false;
int fullLegatoMode = false;

int minDurationOpen = 75;
int minVelocity = 75;

Adafruit_NeoPixel pixels = Adafruit_NeoPixel(N_PIXELS, NEO_PIN, NEO_GRB + NEO_KHZ800);
int led_number[3] = {0, 0, 0};
int led_color;
int led;
int prev_led;

unsigned long last_read;

QuickStats stats;

//--------- Setup ----------------//
void setup() {

  //read fret definitions from EEPROM
  for (int i = 0; i < N_STR; i++) {
    fretDefs[i][0] = F0;
    for (int j = 1; j < 21; j++) {
      fretDefs[i][j] = EEPROM.read(j + (21 * i));
    }
    fretDefs[i][N_FRET] = 0;
    calibrationMin[i] = EEPROM.read(21 + (21 * i));
  }

  //For Calibration, use 9600
  Serial.begin(9600);
  //Serial.println("START");
  
  //Use 31250 MIDI spec baud rate during normal use
  //Serial.begin(31250);
  pinMode(PIN_SOFTPOT_1, INPUT);
  pinMode(PIN_SOFTPOT_2, INPUT);
  pinMode(PIN_SOFTPOT_3, INPUT);
  pinMode(PIN_PIEZO_1, INPUT);
  pinMode(PIN_PIEZO_2, INPUT);
  pinMode(PIN_PIEZO_3, INPUT);
  digitalWrite(PIN_SOFTPOT_1, HIGH);
  digitalWrite(PIN_SOFTPOT_2, HIGH);
  digitalWrite(PIN_SOFTPOT_3, HIGH);

  pinMode(PIN_BUTTON_RIGHT, INPUT);
  digitalWrite(PIN_BUTTON_RIGHT, HIGH);

  pinMode(PIN_BUTTON_LEFT, INPUT);
  digitalWrite(PIN_BUTTON_LEFT, HIGH);

  pinMode(PIN_BUTTON_UP, INPUT);
  digitalWrite(PIN_BUTTON_UP, HIGH);

  pinMode(PIN_BUTTON_DOWN, INPUT);
  digitalWrite(PIN_BUTTON_DOWN, HIGH);

  pinMode(PIN_BUTTON_STICK, INPUT);
  digitalWrite(PIN_BUTTON_STICK, HIGH);


  pinMode(PIN_LED, OUTPUT);

  pixels.begin();
  pixels.setBrightness(50);
  pixels.show();

  while (millis() < 500) {
    for (int i = 0; i < N_STR; i++) {
      int val = analogRead(softPotPins[i]);
      if (val > calibrationMax[i]) calibrationMax[i] = val;
    }

    //calibrate joystick
    stickZeroX = analogRead(PIN_JOYSTICK_X);
    stickZeroY = analogRead(PIN_JOYSTICK_Y);
  }
}

//----------Main Loop---------------//
void loop() {
  //reset
  for (int i = 0; i < N_STR; i++) {
    stringPlucked[i] = false;
    piezoVals[i] = false;
  }

  //read values of all sensors
  readSensors();

  determineFrets();

  //if we are in full legato mode, run the function
  if (fullLegatoMode) {
    fullLegato();
  }

  //otherwise just do the regular thing
  else {
    //test for legato action
    legatoTest();
   // onLED(N_PIXELS, 0, 0, 0);
    //use this info to determine which notes to pluck
    pickNotes();
    onLED(N_PIXELS, 0, 0, 0);
  }

  //send not off messages and reset necessary things
  cleanUp();

  //check for control changes
  readControls();
}

void readSensors() {
  for (int i = 0; i < N_STR; i++) {

    //read piezo vals
    int piezoVal = analogRead(piezoPins[i]);
    

    //if the value breaks the threshold read for max amplitude
    /* TODO: this is less than ideal. Have it determine which piezos were triggered and
       then sample them all at once for better support for polyphonic stuff.
    */

    if (piezoVal > PIEZO_THRESHOLD_ON) {
      int v_new = piezoVal;
      for (int sample = 0; sample < PIEZO_SAMPLES; sample++) {
        piezoVal = analogRead(piezoPins[i]);
        if (piezoVal > v_new) {
          v_new = piezoVal;
        }
      }
      piezoVals[i] = v_new;
      piezoVals[i] = map(piezoVals[i], 0, 500, 0, 127);
      piezoVals[i] = constrain(piezoVals[i], minVelocity, 127);
    }

    //read the value of all the softPots
    softPotVals[i] = analogRead(softPotPins[i]);
    softPotVals[i] = map(softPotVals[i], calibrationMin[i], calibrationMax[i], 0, 255);
    softPotVals[i] = constrain(softPotVals[i], 0, 255);
  }
}

void determineFrets () {
  static int count = 0;

  //---------Get Fret Numbers------
  for (int i = 0; i < N_STR; i++) {
  
    int softPotVal = softPotVals[i];

    //check for open strings
    if (softPotVal >= F0) {
      softPotValsOld[i] = softPotVal;
      fretTouched[i] = 0;
      led_number[i] = fretTouched[i];
    }

    //loop through the array of fret definitions
    for (int j = 1; j < N_FRET; j++) {

      int k = j - 1;
      if (softPotVal <= fretDefs[i][k] &&
          softPotVal > fretDefs[i][j] &&
          abs(softPotVal - softPotValsOld[i]) > PADDING) {

        softPotValsOld[i] = softPotVal;
        fretTouched[i] = j-2;
        led_number[i] = fretTouched[i];
        // Serial.print("string= ");  Serial.print(i);  Serial.print("  FretTouched= ");  Serial.print(j); Serial.print("  softpotval = ");  Serial.println(softPotVal);  //count=0;}
      }
    }
    if (softPotVal <= fretDefs[i][20]) {
      softPotValsOld[i] = softPotVal;
      fretTouched[i] = N_FRET;
      led_number[i] = fretTouched[i];
    }
  }
}

void pickNotes() {
  for (int i = 0; i < N_STR; i++) {

    //if the piezo was hit, play the fretted note
    if (piezoVals[i]) {
      switch (i) {
        case 0:
          noteFretted[i] = fretTouched[i] + offsets[i];
          break;
        case 1:
          noteFretted[i] = fretTouched[i] + offsets[i];
          break;
        case 2:
          noteFretted[i] = fretTouched[i] + offsets[i];
          break;
      }

      if (stringActive[i]) {

        //turn off the currently active note on that string
        noteOff(0x80 + i, activeNotes[i]->number(), 0);
        free(activeNotes[i]);
      }

      if (!stringActive[i]) {

        //mark string as active
        stringActive[i] = true;
      }
      //register with active notes
      activeNotes[i] = (Note *) malloc(sizeof(Note));

      if (fretTouched[i] > 0) activeNotes[i]->init(noteFretted[i], piezoVals[i], millis(), true);

      else activeNotes[i]->init(noteFretted[i], piezoVals[i], millis(), false);

      //turn on fretted note
      noteOn(0x90 + i, activeNotes[i]->number(), activeNotes[i]->velocity());

      //mark that the string was plucked
      stringPlucked[i] = true;
    }
  }
}

void legatoTest() {
  for (int i = 0; i < N_STR; i++) {
    if (stringActive[i]) {
      switch (i) {
        case 0:
          noteFretted[i] = fretTouched[i] + offsets[i];
          break;
        case 1:
          noteFretted[i] = fretTouched[i] + offsets[i];
          break;
        case 2:
          noteFretted[i] = fretTouched[i] + offsets[i];
          break;
      }

      if (noteFretted[i] != activeNotes[i]->number() && fretTouched[i]) {
        //turn on new note
        int vel = activeNotes[i]->velocity();
        noteOn(0x90 + i, noteFretted[i], vel);

        //turn off old note
        noteOff(0x80 + i, activeNotes[i]->number(), 0);
        free(activeNotes[i]);

        //register new note as the active one
        activeNotes[i] = (Note *) malloc(sizeof(Note));
        activeNotes[i]->init(noteFretted[i], vel, millis(), true);
      }
    }
  }
}
/*


*/
void fullLegato() {
  for (int i = 0; i < N_STR; i++) {
    if (fretTouched[i]) {

      switch (i) {
        case 0:
          noteFretted[i] = fretTouched[i] + offsets[i];
          break;
        case 1:
          noteFretted[i] = fretTouched[i] + offsets[i];
          break;
        case 2:
          noteFretted[i] = fretTouched[i] + offsets[i];
          break;
      }

      int vel = 127;

      if (!stringActive[i]) {
        noteOn(0x90 + i, noteFretted[i], vel);

        //register new note as the active one
        activeNotes[i] = (Note *) malloc(sizeof(Note));
        activeNotes[i]->init(noteFretted[i], vel, millis(), true);
        stringActive[i] = true;
      }
      else {

        if (noteFretted[i] != activeNotes[i]->number()) {
          int vel = 80;
          noteOn(0x90 + i, noteFretted[i], vel);

          //turn off old note
          noteOff(0x80 + i, activeNotes[i]->number(), 0);
          free(activeNotes[i]);

          //register new note as the active one
          activeNotes[i] = (Note *) malloc(sizeof(Note));
          activeNotes[i]->init(noteFretted[i], vel, millis(), true);
        }
      }
    }
  }
}

 
void cleanUp() {
  for (int i = 0; i < N_STR; i++) {

    //no fret is touched and the string is marked active
    if (!fretTouched[i] && stringActive[i]) {

      //if open string
      if (!activeNotes[i]->fretted()) {
        if (activeNotes[i]->timeActive() > minDurationOpen) {
          //turn off the active note
          noteOff(0x80 + i, activeNotes[i]->number(), 0);

          //mark as inactive
          stringActive[i] = false;
          free(activeNotes[i]);
        }
      }
      else {
        //turn off the active note
        noteOff(0x80 + i, activeNotes[i]->number(), 0);

        //mark as inactive
        stringActive[i] = false;
        free(activeNotes[i]);
      }
    }
  }
}

void readControls() {

  if (altControlSet) {
    minDurationOpen = analogRead(PIN_POT_1);
    minDurationOpen = map(minDurationOpen, 0, 1023, 0, 255 );

    minVelocity = analogRead(PIN_POT_2);
    minVelocity = map(minVelocity, 0, 1023, 0, 127);
  }

  //Potentiometers set the values for controllers 69 and 7
  else {
    int potVal1New = analogRead(PIN_POT_1);
    int potVal2New = analogRead(PIN_POT_2);

    if (abs(potVal1New - potVal1Old) > 30 || potVal1New == 0 || potVal1New == 1023 ) {
      if ((potVal1New - potVal1Old) != 0) {
        //Send MIDI control change message
        int val = map(potVal1New, 0, 1023, 0, 127);
        val = constrain(val, 0, 127);
        controllerChange(69, val);
        potVal1Old = potVal1New;
      }
    }

    if (abs(potVal2New - potVal2Old) > 30 || potVal2New == 0 || potVal2New == 1023) {
      if ((potVal2New - potVal2Old) != 0) {
        //Send MIDI control change message
        int val = map(potVal2New, 0, 1023, 0, 127);
        val = constrain(val, 0, 127);
        controllerChange(7, val);
        potVal2Old = potVal2New;
      }
    }
  }

  //-----ENGAGE FULL LEGATO MODE-----
  //removes the need for triggering with piezo for fretted notes
  if (digitalRead(PIN_BUTTON_LEFT) == LOW && !buttonStates[LEFT]) {
    fullLegatoMode = true;
    buttonStates[LEFT] = true;
  }
  if (digitalRead(PIN_BUTTON_LEFT) == HIGH && buttonStates[LEFT]) {
    fullLegatoMode = false;
    buttonStates[LEFT] = false;
  }

  led = max(led_number[0],led_number[1]);
  Serial.println(led_number[0]);
   //Serial.println(led_number[1]);
  if (led == -1){
    led = 0;
    onLED(N_PIXELS,0,0,0);
  }
    
  else{
    led = led + 1;
    led = map(led,0,N_FRET - 1,0,N_PIXELS);
    led_color = map(led,0,30,0,255);
    //Serial.println(led_number);
    if((millis() - ledDebounceTime ) > ledDebounceDelay){
      for(int i=0;i<led;i++){
        //pixels.Color takes RGB values, from 0,0,0 up to 255,255,255
        pixels.setPixelColor(i, Wheel(led_color));
      }
      if(prev_led > led)
        for(int i=led;i<N_PIXELS;i++){
          //pixels.Color takes RGB values, from 0,0,0 up to 255,255,255
          pixels.setPixelColor(i, (pixels.Color(0, 0, 0)));
        }
        pixels.show();
        ledDebounceTime = millis(); 
        prev_led = led ;
    }
  }

/*

  //switch to alt control set
  if (digitalRead(PIN_BUTTON_LEFT) == LOW && !buttonStates[LEFT]) {
    altControlSet = !altControlSet;
    buttonStates[LEFT] = true;
  }
  if (digitalRead(PIN_BUTTON_LEFT) == HIGH && buttonStates[LEFT]) buttonStates[LEFT] = false;


  //Right button triggers calibration
  if (digitalRead(PIN_BUTTON_RIGHT) == LOW && !buttonStates[RIGHT]) {
    buttonStates[RIGHT] = true;
    calibrate();
  }


*/
  //---- CHANGING THE OCTAVE -------//
  //UP and down buttons used to change offset/octave. Cycles through EAD and GBE like an infinite guitar neck.

  //---- UP BUTTON ----
  if (digitalRead(PIN_BUTTON_UP) == LOW) {
    //change the set of strings
    if (!buttonStates[UP]) {

      octave = octave - 1;

      stringSetLow = !stringSetLow;

      for (int i = 0; i < N_STR; i++) {
        //if low
        if (stringSetLow) {
          offsets[i] = offsets_low[i] + (12 * octave);
        }
        //if high
        if (!stringSetLow) {
          offsets[i] = offsets_high[i] + (12 * octave);
        }
      }
    }

    buttonStates[UP] = true;
  }
  //reset state once button is no longer being pressed
  if (digitalRead(PIN_BUTTON_UP) == HIGH && buttonStates[UP]) buttonStates[UP] = false;

  //----DOWN BUTTON----
  if (digitalRead(PIN_BUTTON_DOWN) == LOW) {
    //change the set of strings
    if (!buttonStates[DOWN]) {

      octave = octave + 1;

      stringSetLow = !stringSetLow;

      for (int i = 0; i < N_STR; i++) {
        //if low
        if (stringSetLow) {
          offsets[i] = offsets_low[i] + (12 * octave);
        }
        //if high
        if (!stringSetLow) {
          offsets[i] = offsets_high[i] + (12 * octave);
        }
      }
    }

    buttonStates[DOWN] = true;
  }
  //reset state once button is no longer being pressed
  if (digitalRead(PIN_BUTTON_DOWN) == HIGH && buttonStates[DOWN]) buttonStates[DOWN] = false;

  //switch stick to xy mode
  if (digitalRead(PIN_BUTTON_RIGHT) == LOW && !buttonStates[RIGHT]) {
    stickXY = !stickXY;
    buttonStates[RIGHT] = true;
  }
  if (digitalRead(PIN_BUTTON_RIGHT) == HIGH && buttonStates[RIGHT]) buttonStates[RIGHT] = false;

  //--------JOYSTICK-------//
  /* Click down the joystick to activate it. In regular mode it will read absolute position from the
     center in any direction (sends to modwheel, MIDI controller 1), and in XY mode the x axis
     sends to controller 2 (breath) and the y axis sends to controller 4 (foot).  */

  if (digitalRead(PIN_BUTTON_STICK) == LOW) {
    //activate joystick
    if (!buttonStates[STICK]) {
      //make sure modwheel value is set to 0 when stick is off
      if (stickActive) controllerChange(1, 0);
      stickActive = !stickActive;
    }
    buttonStates[STICK] = true;
  }
  //reset once stick is no longer being pressed
  if (digitalRead(PIN_BUTTON_STICK) == HIGH && buttonStates[STICK]) buttonStates[STICK] = false;

  if (stickActive) {
    //read positions from center
    float xPos = map(analogRead(PIN_JOYSTICK_X), stickZeroX, 1023, 0, 127);
    float yPos = map(analogRead(PIN_JOYSTICK_Y), stickZeroY, 1023, 0, 127);

    //get absolute position from center
    float z = sqrt(sq(xPos) + sq(yPos));
    int stickVal = (int)constrain(z, 0, 127);

    if (stickVal > 0) {
      stickState = true;
      if (stickXY) {
        controllerChange(2, abs(xPos));
        controllerChange(4, abs(yPos));
      }
      else controllerChange(1, stickVal);
    }
    else if (stickState && stickVal == 0) {
      stickState = false;
      if (stickXY) {
        controllerChange(2, 0);
        controllerChange(4, 0);
      }
      else controllerChange(1, 0);
    }
  }
}

//----CALIBRATION----//
/* Likely will only have to calibrate once. This is done by activating calibration mode and
   "plucking" each note on each string starting with the upper bound (after the 21st fret) and descending down
   to just after the 1st fret. Starts with the low E string, then the A string and then the D string.
   fret definitions are stored in EEPROM. Once it is calibrated for the voltage put out by the MIDI --> USB board
   you can just have the calibration button do something else because you probably won't need it again.
*/
void unset(int i){
  //this function doesn't even do anything!!
}

void calibrate() {
  
  Serial.println("calibrating...");
  
  for (int i = 0; i < N_STR; i++) {
    //Flash the LED too indicate calibration
    /*
      digitalWrite(PIN_LED, HIGH);
      delay(100);
      digitalWrite(PIN_LED, LOW);
      delay(100);
      digitalWrite(PIN_LED, HIGH);
      delay(100);
      digitalWrite(PIN_LED, LOW);
      delay(100);
      digitalWrite(PIN_LED, HIGH);
    */
    //Flash the LED too indicate calibration
    onLED(10, 250, 0, 0);
    delay(100);
    clrLED();
    onLED(10, 250, 0, 0);
    delay(100);
    clrLED();
    onLED(10, 250, 0, 0);
    delay(100);
    clrLED();
  
    int sensorMax = 0;
    int sensorMin = 1023;
    int val;

    //loop through the array of fret definitions
    for (int j = N_FRET; j > 0; j--) {

      int response = false;
      //wait for response
      Serial.println("waiting");
      //wait for response
      while (!response) {

        //read piezo val
        int piezoVal = analogRead(piezoPins[i]);

        //get the sensor min value (highest fret) on the first round
        if (j == N_FRET) {
          int fretVal = analogRead(softPotPins[i]);
          if (fretVal > sensorMax) (sensorMax = fretVal);
           clrLED();
          //if the piezo is hit, register this as the definition for this fret
          if (piezoVal > PIEZO_THRESHOLD_ON) {
            int fretVal = analogRead(softPotPins[i]);
            sensorMin = fretVal;
            val = fretVal;
            response = true;
            clrLED();
          int addr = j * sizeof(short) + (N_FRET*i*sizeof(short));
          Serial.print("Writing ");
          Serial.print(val);
          Serial.print(" to address: ");
          Serial.println(addr);
          EEPROM.write(addr, val);
          }
        }

        else {
          //get the rest of the fret definitions
          //if the piezo is hit, register this as the definition for this fret
          if (piezoVal > PIEZO_THRESHOLD_ON) {
            int fretVal = analogRead(softPotPins[i]);
            fretVal = map(fretVal, sensorMin, sensorMax, 0, 255);
            fretVal = constrain(fretVal, 0, 255);
            val = fretVal;
            response = true;
            clrLED();
          }
        }
      }

      //write to memory
      digitalWrite(PIN_LED, LOW);
      EEPROM.write(j + (N_FRET * i), val);

      delay(100);
      onLED(10, 250, 0, 0);
//      delay(100);
      digitalWrite(PIN_LED, HIGH);
    }


    //update global definitions
    calibrationMin[i] = EEPROM.read(N_FRET + (N_FRET * i));

    for (int j = 1; j < N_FRET; j++) {
      fretDefs[i][j] = EEPROM.read(j + (i * N_FRET));
    }
    clrLED();
  }
  buttonStates[RIGHT] = false;
  digitalWrite(PIN_LED, LOW);
}
void onLED(int led, int red, int green, int blue) {
  for (int i = 0; i < N_STR; i++) {
  
    //pixels.Color takes RGB values, from 0,0,0 up to 255,255,255
    pixels.setPixelColor(i, pixels.Color(red, green, blue));
  }
  pixels.show();
}

void clrLED() {
  for (int i = 0; i < N_PIXELS; i++){
    pixels.setPixelColor(i, pixels.Color(0, 0, 0)); // turn off
  }
  pixels.show();
}
//-------------MIDI functions-----------------

//note-on message
void noteOn(int cmd, int pitch, int velocity) {

  Serial.write(byte(cmd));
  Serial.write(byte(pitch));
  Serial.write(byte(velocity));
  digitalWrite(PIN_LED, HIGH);
}
//note-off message
void noteOff(int cmd, int pitch, int velocity) {

  Serial.write(byte(cmd));
  Serial.write(byte(pitch));
  Serial.write(byte(0));
  digitalWrite(PIN_LED, LOW);
}

//Sends controller change to the specified controller
void controllerChange(int controller, int value) {
  Serial.write(byte(0xb0));
  Serial.write(byte(controller));
  Serial.write(byte(value));

  Serial.write(byte(0xb1));
  Serial.write(byte(controller));
  Serial.write(byte(value));

  Serial.write(byte(0xb2));
  Serial.write(byte(controller));
  Serial.write(byte(value));
}

// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
  WheelPos = 255 - WheelPos;
  if(WheelPos < 85) {
    return pixels.Color(255 - WheelPos * 3 + pitchBendLight, 0, WheelPos * 3 + pitchBendLight);
  }
  if(WheelPos < 170) {
    WheelPos -= 85;
    return pixels.Color(0, WheelPos * 3 + pitchBendLight , 255 - WheelPos * 3 + pitchBendLight);
  }
  else{
    WheelPos -= 170;
    return pixels.Color(WheelPos * 3 + pitchBendLight , 255 - WheelPos * 3 + pitchBendLight , 0 );
  }
  if(mod_final> 50){
  }
}

I don't believe so, but I could definitely be wrong. I thought I could use the 16 pixels to represent all 21 frets, because it seems to work fine for the top 2 softpots that way (it's not using 8 pixels per softpot). But I will definitely try adding another strip and changing the N_PIXELS variable value to 24 and will try 21 as well. I'll post the results afterwards.

By the way, I'm still working on the schematic - trying to make it as clear and accurate to my setup as possible.

But you were not asked to do that were you? I asked:-

I think you are deluding yourself here. Without a resistor of sorts when not touching the soft pots the analogue inputs are not connected to anything, that is they float. A floating input when read can return anything. They are very susceptible to interference. That hook up guide is not specific to your project, I would try changing the value to 100K here, but you have to have something or you will get random notes as you play.

And you expect that to magically work without changing the code to accommodate the extra LEDs?

Tell me if I am wrong but it seems like you have found some code for a project and tried to extend it so you have three soft pots, without actually understanding what this code does.
Can you post a link to the original article you followed please, as I have not been able to find the 2014 variant of Dean Miller's work?

I wanted to show the odd readings during the calibration process, since you asked whether I calibrated it. I thought that's what you were asking me to do since the calibration question directly preceded the EEPROM print request below. My reading comprehension skills definitely leave a lot to be desired, apologies.

So are you asking me to touch each fret on each softpot and copy the values I get from the serial monitor - as I did with the calibration procedure? I just need some clarification.

Ok, I'll put resistors back in there, re-calibrate, and try to figure out why it shortens my 500mm softpots range by a third later.

No, that's why I've been modifying the code to try to make it work and that's why I've come here for help. Beforehand - I did think that it would be an arbitrary change in a variable such as the led_number[ ] array discussed in post #6 of this thread - which I did try changing before coming here for help. I also thought that it could be as easy as changing N_STR variable from 2 to 3 in the original neopixel sketch. I can plainly see now that it is more difficult than that.

I'm not good with understanding code, but I'm trying to learn. I even purchased your book Arduino Music and Audio Projects, but I'm having a tough time wrapping my head around the coding aspects.

Pretty much yes, I have not been able to decipher exactly how the sketch (with 2 softpots and neopixels) works. Actually I couldn't get that sketch variant to work properly - kept getting false triggers for notes not touched (even with resistors - could only get the neopixels working properly). That's why I added the neopixel bits to the other Dean Miller variant sketch that I was able to get working properly - instead of just working to extend the original 2 softpot code.

The primary difference between the two sketches that I used is that the 2 softpot/neopixel sketch is a variant on Dean Miller's sketch that uses force sensitive resistors for triggering notes and the 3 softpot variant is based on Dean's original sketch that implements piezo elements for triggering notes instead.

Links are in first post of this thread, but I'll embed the actual sketches into this post and the next post instead of linking.

First Dean Miller variant sketch - this is the primary sketch that I used:

/*
* Arduino Ribbon Synth MIDI controller
 * ------------------------------------
 * ©2014 Dean Miller
 */

#include <EEPROM.h>

//------- Constants -------//
#define PIEZO_THRESHOLD_ON 105

#define PIN_LED 13
#define PIN_SOFTPOT_1 A0
#define PIN_SOFTPOT_2 A1
#define PIN_SOFTPOT_3 A2
#define PIN_PIEZO_1 A3
#define PIN_PIEZO_2 A4
#define PIN_PIEZO_3 A5
#define PIN_POT_1 8
#define PIN_POT_2 6

#define PIN_BUTTON_STICK 3

#define PIN_BUTTON_RIGHT 11
#define PIN_BUTTON_UP 5
#define PIN_BUTTON_DOWN 2
#define PIN_BUTTON_LEFT 7

#define PIN_JOYSTICK_X 9
#define PIN_JOYSTICK_Y 10

#define UP 0
#define RIGHT 1
#define DOWN 2
#define LEFT 3
#define STICK 4

#define PIEZO_SAMPLES 400
#define NUM_STRINGS 3
#define PADDING 3

//------Note Class---------//
/*
* A note class that stores some info about each note played is necessary
 * to ensure that open strings are held for the specified amount of time.
 * That is a problem with using the piezos as triggers instead of FSRs, they
 * only register momentary impact or vibration, creating a problem for open strings.
 */

class Note {
  int _number;
  int _velocity;
  int _startTime;
  int _fretted;

public:
  void init(int number, int velocity, int startTime, int fretted) {
    _number = number;
    _velocity = velocity;
    _startTime = startTime;
    _fretted = fretted;
  }

  int number() {
    return _number;
  }

  int velocity() {
    return _velocity;
  }

  int fretted() {
    return _fretted;
  }

  int timeActive() {
    return millis() - _startTime;
  }
};

//------ Global Variables ---------//

/*
fret defs stored in EEPROM for calibration purposes.
 Lower output voltages from USB ports result in different values read from
 SoftPots and wonky fret definitions.*/

int F0 = 248;  // all bigger softpot values will be treated as open string
int F21 = 0;

int fretDefs[3][22];

int piezoVals[] = {
  0, 0, 0};
int piezoPins[] = {
  PIN_PIEZO_1, PIN_PIEZO_2, PIN_PIEZO_3};

int softPotVals[3];
int softPotPins[] = {
  PIN_SOFTPOT_1, PIN_SOFTPOT_2, PIN_SOFTPOT_3};

int potVal1Old = -1;
int potVal2Old = -1;
int softPotValsOld[] = {
  0, 0, 0};

int fretTouched[3];
int noteFretted[3];

int stringActive[] = {
  false, false, false};
int stringPlucked[] = {
  false, false, false};

Note *activeNotes[3];

int calibrationMax[] = {
  0, 0, 0};
int calibrationMin[3];

//true for low strings, false for high strings
int stringSetLow = true;
int octave = 3;

//E A D
int offsets_low[] = {
  7, 12, 17};

//G C F
int offsets_high[] = {
  7, 12, 17};

//default offsets
int offsets[] = {
  43, 48, 53};
//states of control buttons
int buttonStates[] = {
  false, false, false, false, false};
int stickActive = false;
int stickZeroX = 0;
int stickZeroY = 0;
int stickState = false;

int altControlSet = false;
int stickXY = false;
int fullLegatoMode = false;

int minDurationOpen = 300;
int minVelocity = 127;

//--------- Setup ----------------//
void setup() {

  //read fret definitions from EEPROM
  for (int i=0; i<NUM_STRINGS; i++){
    fretDefs[i][0] = F0;
    for (int j=1; j<21; j++){
      fretDefs[i][j] = EEPROM.read(j + (21*i));
    }
    fretDefs[i][21]=0;
    calibrationMin[i] = EEPROM.read(21 + (21*i));
  }

  //begin at MIDI spec baud rate
  Serial.begin(9600);
  Serial.println("START");
  Serial1.begin(31250);
  pinMode(PIN_SOFTPOT_1, INPUT);
  pinMode(PIN_SOFTPOT_2, INPUT);
  pinMode(PIN_SOFTPOT_3, INPUT);
  pinMode(PIN_PIEZO_1, INPUT);
  pinMode(PIN_PIEZO_2, INPUT);
  pinMode(PIN_PIEZO_3, INPUT);
  digitalWrite(PIN_SOFTPOT_1, HIGH);
  digitalWrite(PIN_SOFTPOT_2, HIGH); 
  digitalWrite(PIN_SOFTPOT_3, HIGH);

  pinMode(PIN_BUTTON_RIGHT, INPUT);  
  digitalWrite(PIN_BUTTON_RIGHT, HIGH);

  pinMode(PIN_BUTTON_LEFT, INPUT);  
  digitalWrite(PIN_BUTTON_LEFT, HIGH);

  pinMode(PIN_BUTTON_UP, INPUT);  
  digitalWrite(PIN_BUTTON_UP, HIGH);

  pinMode(PIN_BUTTON_DOWN, INPUT);  
  digitalWrite(PIN_BUTTON_DOWN, HIGH);

  pinMode(PIN_BUTTON_STICK, INPUT);  
  digitalWrite(PIN_BUTTON_STICK, HIGH);  


  pinMode(PIN_LED, OUTPUT);

  while(millis() < 500) {
    for (int i=0; i<NUM_STRINGS; i++){
      int val = analogRead(softPotPins[i]);
      if (val > calibrationMax[i]) calibrationMax[i] = val;
    }

    //calibrate joystick
    stickZeroX = analogRead(PIN_JOYSTICK_X);
    stickZeroY = analogRead(PIN_JOYSTICK_Y);
  }
}

//----------Main Loop---------------//
void loop() {
  //reset
  for (int i=0; i<NUM_STRINGS; i++) {
    stringPlucked[i] = false;
    piezoVals[i] = false;
  }

  //read values of all sensors 
  readSensors();

  determineFrets();

  //if we are in full legato mode, run the function
  if (fullLegatoMode) {
    fullLegato();
  }

  //otherwise just do the regular thing
  else {
    //test for legato action
    legatoTest();

    //use this info to determine which notes to pluck
    pickNotes();
  }

  //send not off messages and reset necessary things
  cleanUp();

  //check for control changes
  readControls();
}

void readSensors() {
  for (int i=0; i<NUM_STRINGS; i++) {

    //read piezo vals
    int piezoVal = analogRead(piezoPins[i]);

    //if the value breaks the threshold read for max amplitude
    /* TODO: this is less than ideal. Have it determine which piezos were triggered and
     * then sample them all at once for better support for polyphonic stuff.
     */

    if (piezoVal > PIEZO_THRESHOLD_ON) {
      int v_new = piezoVal;
      for (int sample=0; sample<PIEZO_SAMPLES; sample++){
        piezoVal = analogRead(piezoPins[i]);
        if (piezoVal > v_new){
          v_new = piezoVal;
        }
      }
      piezoVals[i] = v_new;
      piezoVals[i] = map(piezoVals[i], 0, 500, 0, 127);
      piezoVals[i] = constrain(piezoVals[i], minVelocity, 127);
    }

    //read the value of all the softPots
    softPotVals[i] = analogRead(softPotPins[i]);
    softPotVals[i] = map(softPotVals[i], calibrationMin[i], calibrationMax[i], 0, 255);
    softPotVals[i] = constrain(softPotVals[i], 0, 255);
  }
}

void determineFrets () {
  static int count=0;

  //---------Get Fret Numbers------
  for (int i=0; i< NUM_STRINGS; i++) {

    int softPotVal = softPotVals[i];

    //check for open strings
    if (softPotVal >= F0) {
      softPotValsOld[i] = softPotVal;
      fretTouched[i]=0;
    }

    //loop through the array of fret definitions
    for (int j=1; j<21; j++) {

      int k = j-1;
      if (softPotVal <= fretDefs[i][k] && 
        softPotVal > fretDefs[i][j] &&
        abs(softPotVal-softPotValsOld[i]) > PADDING) {

        softPotValsOld[i] = softPotVal;
        fretTouched[i] = j;
        // Serial.print("string= ");  Serial.print(i);  Serial.print("  FretTouched= ");  Serial.print(j); Serial.print("  softpotval = ");  Serial.println(softPotVal);  //count=0;}
      }
    }
    if (softPotVal <= fretDefs[i][20]) {
      softPotValsOld[i] = softPotVal;
      fretTouched[i]=21;
    }
  }
}

void pickNotes() {
  for (int i=0; i< NUM_STRINGS; i++) {

    //if the piezo was hit, play the fretted note
    if (piezoVals[i]){
      switch (i) {
      case 0:
        noteFretted[i] = fretTouched[i] + offsets[i];
        break;
      case 1:
        noteFretted[i] = fretTouched[i] + offsets[i];
        break;
      case 2:
        noteFretted[i] = fretTouched[i] + offsets[i];
        break;
      }

      if (stringActive[i]){

        //turn off the currently active note on that string
        noteOff(0x80 + i, activeNotes[i]->number(), 0);
        free(activeNotes[i]);
      }

      if (!stringActive[i]) {

        //mark string as active
        stringActive[i] = true;
      }
      //register with active notes
      activeNotes[i] = (Note *) malloc(sizeof(Note));

      if (fretTouched[i] > 0) activeNotes[i]->init(noteFretted[i], piezoVals[i], millis(), true);

      else activeNotes[i]->init(noteFretted[i], piezoVals[i], millis(), false);

      //turn on fretted note
      noteOn(0x90 + i, activeNotes[i]->number(), activeNotes[i]->velocity());

      //mark that the string was plucked
      stringPlucked[i] = true; 
    }
  }
}

void legatoTest() {
  for (int i=0; i< NUM_STRINGS; i++) {
    if (stringActive[i]) {
      switch (i) {
      case 0:
        noteFretted[i] = fretTouched[i] + offsets[i];
        break;
      case 1:
        noteFretted[i] = fretTouched[i] + offsets[i];
        break;
      case 2:
        noteFretted[i] = fretTouched[i] + offsets[i];
        break;
      }

      if (noteFretted[i] != activeNotes[i]->number() && fretTouched[i]) {
        //turn on new note
        int vel = activeNotes[i]->velocity();
        noteOn(0x90 + i, noteFretted[i], vel);

        //turn off old note
        noteOff(0x80 + i, activeNotes[i]->number(), 0);
        free(activeNotes[i]);

        //register new note as the active one
        activeNotes[i] = (Note *) malloc(sizeof(Note));
        activeNotes[i]->init(noteFretted[i], vel, millis(), true);
      }
    }
  }
}

void fullLegato() {
  for (int i=0; i<NUM_STRINGS; i++) {
    if (fretTouched[i]) {

      switch (i) {
      case 0:
        noteFretted[i] = fretTouched[i] + offsets[i];
        break;
      case 1:
        noteFretted[i] = fretTouched[i] + offsets[i];
        break;
      case 2:
        noteFretted[i] = fretTouched[i] + offsets[i];
        break;
      }

      int vel =127;

      if (!stringActive[i]) {
        noteOn(0x90 + i, noteFretted[i], vel);

        //register new note as the active one
        activeNotes[i] = (Note *) malloc(sizeof(Note));
        activeNotes[i]->init(noteFretted[i], vel, millis(), true);
        stringActive[i] = true;
      }
      else {

        if (noteFretted[i] != activeNotes[i]->number()) {
          int vel = 80;
          noteOn(0x90 + i, noteFretted[i], vel);

          //turn off old note
          noteOff(0x80 + i, activeNotes[i]->number(), 0);
          free(activeNotes[i]);

          //register new note as the active one
          activeNotes[i] = (Note *) malloc(sizeof(Note));
          activeNotes[i]->init(noteFretted[i], vel, millis(), true);
        }
      }
    }
  }
}

void cleanUp() {
  for (int i=0; i< NUM_STRINGS; i++) {

    //no fret is touched and the string is marked active
    if (!fretTouched[i] && stringActive[i]){

      //if open string
      if (!activeNotes[i]->fretted()) {
        if (activeNotes[i]->timeActive() > minDurationOpen) {
          //turn off the active note
          noteOff(0x80 + i, activeNotes[i]->number(), 0);

          //mark as inactive
          stringActive[i] = false;
          free(activeNotes[i]);
        }
      }
      else { 
        //turn off the active note
        noteOff(0x80 + i, activeNotes[i]->number(), 0);

        //mark as inactive
        stringActive[i] = false;
        free(activeNotes[i]);
      }
    }
  }
}

void readControls() {

  if (altControlSet){
    minDurationOpen = analogRead(PIN_POT_1);
    minDurationOpen = map(minDurationOpen, 0, 1023, 0, 255 );

    minVelocity = analogRead(PIN_POT_2);
    minVelocity = map(minVelocity, 0, 1023, 0, 127);
  }

  //Potentiometers set the values for controllers 69 and 7
  else {
    int potVal1New = analogRead(PIN_POT_1);
    int potVal2New = analogRead(PIN_POT_2);

    if (abs(potVal1New - potVal1Old) > 30 || potVal1New == 0 || potVal1New == 1023 ) {
      if ((potVal1New - potVal1Old) != 0){
        //Send MIDI control change message
        int val = map(potVal1New, 0, 1023, 0, 127);
        val = constrain(val, 0, 127);
        controllerChange(69, val); 

        potVal1Old = potVal1New;
      }
    }

    if (abs(potVal2New - potVal2Old) > 30 || potVal2New == 0 || potVal2New == 1023) {
      if ((potVal2New - potVal2Old) != 0){
        //Send MIDI control change message
        int val = map(potVal2New, 0, 1023, 0, 127);
        val = constrain(val, 0, 127);
        controllerChange(7, val);  
        potVal2Old = potVal2New;
      }
    }
  }

  //-----ENGAGE FULL LEGATO MODE-----
  //removes the need for triggering with piezo for fretted notes
  if (digitalRead(PIN_BUTTON_LEFT) == LOW && !buttonStates[LEFT]) {
    fullLegatoMode = true;
    buttonStates[LEFT] = true;
  }
  if (digitalRead(PIN_BUTTON_LEFT) == HIGH && buttonStates[LEFT]) {
    fullLegatoMode = false;
    buttonStates[LEFT] = false;
  }


  //switch to alt control set
  if (digitalRead(PIN_BUTTON_LEFT) == LOW && !buttonStates[LEFT]) {
    altControlSet = !altControlSet;
    buttonStates[LEFT] = true;
  }
  if (digitalRead(PIN_BUTTON_LEFT) == HIGH && buttonStates[LEFT]) buttonStates[LEFT] = false;

  //Right button triggers calibration
  /* if (digitalRead(PIN_BUTTON_RIGHT) == LOW && !buttonStates[RIGHT]) {
   buttonStates[RIGHT] = true;
   calibrate();*/



  //---- CHANGING THE OCTAVE -------//
  //UP and down buttons used to change offset/octave. Cycles through EAD and GBE like an infinite guitar neck.

  //---- UP BUTTON ----
  if (digitalRead(PIN_BUTTON_UP) == LOW) {
    //change the set of strings
    if (!buttonStates[UP]) {

      octave = octave - 1;

      stringSetLow = !stringSetLow;

      for (int i=0; i<NUM_STRINGS; i++) {
        //if low
        if (stringSetLow) {
          offsets[i] = offsets_low[i] + (12*octave);
        }
        //if high
        if (!stringSetLow) {
          offsets[i] = offsets_high[i] + (12*octave);
        }
      }
    }

    buttonStates[UP] = true;
  }
  //reset state once button is no longer being pressed
  if (digitalRead(PIN_BUTTON_UP) == HIGH && buttonStates[UP]) buttonStates[UP] = false;

  //----DOWN BUTTON----
  if (digitalRead(PIN_BUTTON_DOWN) == LOW) {
    //change the set of strings
    if (!buttonStates[DOWN]) {

      octave = octave + 1;

      stringSetLow = !stringSetLow;

      for (int i=0; i<NUM_STRINGS; i++) {
        //if low
        if (stringSetLow) {
          offsets[i] = offsets_low[i] + (12*octave);
        }
        //if high
        if (!stringSetLow) {
          offsets[i] = offsets_high[i] + (12*octave);
        }
      }
    }

    buttonStates[DOWN] = true;
  }
  //reset state once button is no longer being pressed
  if (digitalRead(PIN_BUTTON_DOWN) == HIGH && buttonStates[DOWN]) buttonStates[DOWN] = false;

  //switch stick to xy mode
  if (digitalRead(PIN_BUTTON_RIGHT) == LOW && !buttonStates[RIGHT]) {
    stickXY = !stickXY;
    buttonStates[RIGHT] = true;
  }
  if (digitalRead(PIN_BUTTON_RIGHT) == HIGH && buttonStates[RIGHT]) buttonStates[RIGHT] = false;


  //--------JOYSTICK-------//
  /* Click down the joystick to activate it. In regular mode it will read absolute position from the
   * center in any direction (sends to modwheel, MIDI controller 1), and in XY mode the x axis
   * sends to controller 2 (breath) and the y axis sends to controller 4 (foot).  */

   
   
   if (digitalRead(PIN_BUTTON_STICK) == LOW) {
   //activate joystick
   if (!buttonStates[STICK]) {
   //make sure modwheel value is set to 0 when stick is off
   if (stickActive) controllerChange(1, 0);
   stickActive = !stickActive;
   }
   buttonStates[STICK] = true;
   }
   //reset once stick is no longer being pressed
   if (digitalRead(PIN_BUTTON_STICK) == HIGH && buttonStates[STICK]) buttonStates[STICK] = false;
 
  if (stickActive) {
    //read positions from center
    float xPos = map(analogRead(PIN_JOYSTICK_X), stickZeroX, 1023, 0, 127);
    float yPos = map(analogRead(PIN_JOYSTICK_Y), stickZeroY, 1023, 0, 127);

    //get absolute position from center
    float z = sqrt(sq(xPos) + sq(yPos));
    int stickVal = (int)constrain(z, 0, 127);

    if (stickVal > 0) {
      stickState = true;
      if (stickXY) {
        controllerChange(2, abs(xPos));
        controllerChange(4, abs(yPos));
      }
      else controllerChange(1, stickVal);
    }
    else if (stickState && stickVal == 0) {
      stickState = false;
      if (stickXY) {
        controllerChange(2, 0);
        controllerChange(4, 0);
      }
      else controllerChange(1,0);
    }
  }
}

//----CALIBRATION----//
/* Likely will only have to calibrate once. This is done by activating calibration mode and
 * "plucking" each note on each string starting with the upper bound (after the 21st fret) and descending down
 * to just after the 1st fret. Starts with the low E string, then the A string and then the D string.
 * fret definitions are stored in EEPROM. Once it is calibrated for the voltage put out by the MIDI --> USB board
 * you can just have the calibration button do something else because you probably won't need it again.
 
 
 void calibrate() {
 
 for (int i=0; i<NUM_STRINGS; i++) {
 //Flash the LED too indicate calibration
 digitalWrite(PIN_LED, HIGH);
 delay(100);
 digitalWrite(PIN_LED, LOW);
 delay(100);
 digitalWrite(PIN_LED, HIGH);
 delay(100);
 digitalWrite(PIN_LED, LOW);
 delay(100);
 digitalWrite(PIN_LED, HIGH);
 
 int sensorMax = 0;
 int sensorMin = 1023;
 int val;
 
 //loop through the array of fret definitions
 for (int j=21; j>0; j--) {
 
 int response = false;
 
 //wait for response
 while (!response) {
 
 //read piezo val
 int piezoVal = analogRead(piezoPins[i]);
 
 //get the sensor min value (highest fret) on the first round
 if (j==21) {
 int fretVal = analogRead(softPotPins[i]);
 if (fretVal > sensorMax) (sensorMax = fretVal);
 
 //if the piezo is hit, register this as the definition for this fret
 if (piezoVal > PIEZO_THRESHOLD_ON) {
 int fretVal = analogRead(softPotPins[i]);
 sensorMin = fretVal;
 val = fretVal;
 response = true;
 }
 }
 
 else {
 //get the rest of the fret definitions
 //if the piezo is hit, register this as the definition for this fret
 if (piezoVal > PIEZO_THRESHOLD_ON) {
 int fretVal = analogRead(softPotPins[i]);
 fretVal = map(fretVal, sensorMin, sensorMax, 0, 255);
 fretVal = constrain(fretVal, 0, 255);
 val = fretVal;
 response = true;
 }
 }
 }
 
 //write to memory
 digitalWrite(PIN_LED, LOW);
 EEPROM.write(j + (21*i), val);
 
 delay(100);
 digitalWrite(PIN_LED, HIGH);
 }
 
 //update global definitions
 calibrationMin[i] = EEPROM.read(21 + (21*i));
 
 for (int j=1; j<21; j++) {
 fretDefs[i][j] = EEPROM.read(j + (i*21));
 }
 }
 
 buttonStates[RIGHT] = false;
 digitalWrite(PIN_LED, LOW);
 }
 */
//-------------MIDI functions-----------------

//note-on message
void noteOn(int cmd, int pitch, int velocity) {

  Serial1.write(byte(cmd));
  Serial1.write(byte(pitch));
  Serial1.write(byte(velocity));
  digitalWrite(PIN_LED, HIGH);
}
//note-off message
void noteOff(int cmd, int pitch, int velocity) {

  Serial1.write(byte(cmd));
  Serial1.write(byte(pitch));
  Serial1.write(byte(0));
  digitalWrite(PIN_LED, LOW);
}

//Sends controller change to the specified controller
void controllerChange(int controller, int value) {
  Serial1.write(byte(0xb0));
  Serial1.write(byte(controller));
  Serial1.write(byte(value));

  Serial1.write(byte(0xb1));
  Serial1.write(byte(controller));
  Serial1.write(byte(value));

  Serial1.write(byte(0xb2));
  Serial1.write(byte(controller));
  Serial1.write(byte(value));
}