Add Velocity & Aftertouch to 3xMPR121 with FSR with Control Surface Library

Hi!

I am trying to build a midi controller with a hex layout for playing microtonal scales and stuff.
I have a code which incorporates 3x Mpr121, so i get a total of 36 notes, which is fine. My plan is to use 2 Fsr ( i will build them out of Velostat and Coppertape) to get velocity and aftertouch.
Under the rows of buttons i plan to put a piece of foam, and beneath that the 2 fsr will be placed.

I am using PieterP´s Control Surface library.

I tried to write the arduino code for myself, but i just cant accomplish to incorporate velocity or aftertouch.
(CC´s or Pitchbend is no problem)

Right now the code sends fixed Note On´s with 127 and Note Off´s with 0, so i guess i would have to
change that to a variable and get the value from the Fsr?

here is my code (i also think that could be written way more elegant, but it works for me)

#include <Control_Surface.h>
#include <Adafruit_MPR121.h>
#include <Wire.h>
USBMIDI_Interface midi;

#ifndef _BV
#define _BV(bit) (1 << (bit))
#endif

Adafruit_MPR121 cap1 = Adafruit_MPR121();
Adafruit_MPR121 cap2 = Adafruit_MPR121();
Adafruit_MPR121 cap3 = Adafruit_MPR121();

// Keeps track of the last pins touched
// so we know when buttons are 'released'
uint16_t lasttouched = 0;
uint16_t currtouched = 0;
uint16_t lasttouched2 = 0;
uint16_t currtouched2 = 0;
uint16_t lasttouched3 = 0;
uint16_t currtouched3 = 0;

void setup() {

  if (!cap1.begin(0x5A));
  if (!cap2.begin(0x5B));
  if (!cap3.begin(0x5C));
  Control_Surface.begin();
}

void loop() {

  currtouched  = cap1.touched();
  currtouched2 = cap2.touched();
  currtouched3 = cap3.touched();



  if ((currtouched & _BV(0)) && !(lasttouched & _BV(0)) ) {
    midi.sendNoteOn(42, 127);
  }
  if (!(currtouched & _BV(0)) && (lasttouched & _BV(0)) ) {
    midi.sendNoteOff(42, 0);
  }

  if ((currtouched & _BV(1)) && !(lasttouched & _BV(1)) ) {
    midi.sendNoteOn(43, 127);
  }
  if (!(currtouched & _BV(1)) && (lasttouched & _BV(1)) ) {
    midi.sendNoteOff(43, 0);
  }


  if ((currtouched & _BV(2)) && !(lasttouched & _BV(2)) ) {
    midi.sendNoteOn(44, 127);
  }
  if (!(currtouched & _BV(2)) && (lasttouched & _BV(2)) ) {
    midi.sendNoteOff(44, 0);
  }

  if ((currtouched & _BV(3)) && !(lasttouched & _BV(3)) ) {
    midi.sendNoteOn(45, 127);
  }
  if (!(currtouched & _BV(3)) && (lasttouched & _BV(3)) ) {
    midi.sendNoteOff(45, 0);
  }

  if ((currtouched & _BV(4)) && !(lasttouched & _BV(4)) ) {
    midi.sendNoteOn(46, 127);
  }
  if (!(currtouched & _BV(4)) && (lasttouched & _BV(4)) ) {
    midi.sendNoteOff(46, 0);
  }

  if ((currtouched & _BV(5)) && !(lasttouched & _BV(5)) ) {
    midi.sendNoteOn(47, 127);
  }
  if (!(currtouched & _BV(5)) && (lasttouched & _BV(5)) ) {
    midi.sendNoteOff(47, 0);
  }

  if ((currtouched & _BV(6)) && !(lasttouched & _BV(6)) ) {
    midi.sendNoteOn(48, 127);
  }
  if (!(currtouched & _BV(6)) && (lasttouched & _BV(6)) ) {
    midi.sendNoteOff(48, 0);
  }

  if ((currtouched & _BV(7)) && !(lasttouched & _BV(7)) ) {
    midi.sendNoteOn(49, 127);
  }
  if (!(currtouched & _BV(7)) && (lasttouched & _BV(7)) ) {
    midi.sendNoteOff(49, 0);
  }

  if ((currtouched & _BV(8)) && !(lasttouched & _BV(8)) ) {
    midi.sendNoteOn(50, 127);
  }
  if (!(currtouched & _BV(8)) && (lasttouched & _BV(8)) ) {
    midi.sendNoteOff(50, 0);
  }

  if ((currtouched & _BV(9)) && !(lasttouched & _BV(9)) ) {
    midi.sendNoteOn(51, 127);
  }
  if (!(currtouched & _BV(9)) && (lasttouched & _BV(9)) ) {
    midi.sendNoteOff(51, 0);
  }

  if ((currtouched & _BV(10)) && !(lasttouched & _BV(10)) ) {
    midi.sendNoteOn(52, 127);
  }
  if (!(currtouched & _BV(10)) && (lasttouched & _BV(10)) ) {
    midi.sendNoteOff(52, 0);
  }

  if ((currtouched & _BV(11)) && !(lasttouched & _BV(11)) ) {
    midi.sendNoteOn(53, 127);
  }
  if (!(currtouched & _BV(11)) && (lasttouched & _BV(11)) ) {
    midi.sendNoteOff(53, 0);
  }


  if ((currtouched2 & _BV(0)) && !(lasttouched2 & _BV(0)) ) {
    midi.sendNoteOn(54, 127);
  }
  if (!(currtouched2 & _BV(0)) && (lasttouched2 & _BV(0)) ) {
    midi.sendNoteOff(54, 0);
  }

  if ((currtouched2 & _BV(1)) && !(lasttouched2 & _BV(1)) ) {
    midi.sendNoteOn(55, 127);
  }
  if (!(currtouched2 & _BV(1)) && (lasttouched2 & _BV(1)) ) {
    midi.sendNoteOff(55, 0);
  }


  if ((currtouched2 & _BV(2)) && !(lasttouched2 & _BV(2)) ) {
    midi.sendNoteOn(56, 127);
  }
  if (!(currtouched2 & _BV(2)) && (lasttouched2 & _BV(2)) ) {
    midi.sendNoteOff(56, 0);
  }

  if ((currtouched2 & _BV(3)) && !(lasttouched2 & _BV(3)) ) {
    midi.sendNoteOn(57, 127);
  }
  if (!(currtouched2 & _BV(3)) && (lasttouched2 & _BV(3)) ) {
    midi.sendNoteOff(57, 0);
  }

  if ((currtouched2 & _BV(4)) && !(lasttouched2 & _BV(4)) ) {
    midi.sendNoteOn(58, 127);
  }
  if (!(currtouched2 & _BV(4)) && (lasttouched2 & _BV(4)) ) {
    midi.sendNoteOff(58, 0);
  }

  if ((currtouched2 & _BV(5)) && !(lasttouched2 & _BV(5)) ) {
    midi.sendNoteOn(59, 127);
  }
  if (!(currtouched2 & _BV(5)) && (lasttouched2 & _BV(5)) ) {
    midi.sendNoteOff(59, 0);
  }

  if ((currtouched2 & _BV(6)) && !(lasttouched2 & _BV(6)) ) {
    midi.sendNoteOn(60, 127);
  }
  if (!(currtouched2 & _BV(6)) && (lasttouched2 & _BV(6)) ) {
    midi.sendNoteOff(60, 0);
  }

  if ((currtouched2 & _BV(7)) && !(lasttouched2 & _BV(7)) ) {
    midi.sendNoteOn(61, 127);
  }
  if (!(currtouched2 & _BV(7)) && (lasttouched2 & _BV(7)) ) {
    midi.sendNoteOff(61, 0);
  }

  if ((currtouched2 & _BV(8)) && !(lasttouched2 & _BV(8)) ) {
    midi.sendNoteOn(62, 127);
  }
  if (!(currtouched2 & _BV(8)) && (lasttouched2 & _BV(8)) ) {
    midi.sendNoteOff(62, 0);
  }

  if ((currtouched2 & _BV(9)) && !(lasttouched2 & _BV(9)) ) {
    midi.sendNoteOn(63, 127);
  }
  if (!(currtouched2 & _BV(9)) && (lasttouched2 & _BV(9)) ) {
    midi.sendNoteOff(63, 0);
  }

  if ((currtouched2 & _BV(10)) && !(lasttouched2 & _BV(10)) ) {
    midi.sendNoteOn(64, 127);
  }
  if (!(currtouched2 & _BV(10)) && (lasttouched2 & _BV(10)) ) {
    midi.sendNoteOff(64, 0);
  }

  if ((currtouched2 & _BV(11)) && !(lasttouched2 & _BV(11)) ) {
    midi.sendNoteOn(65, 127);
  }
  if (!(currtouched2 & _BV(11)) && (lasttouched2 & _BV(11)) ) {
    midi.sendNoteOff(65, 0);
  }

  if ((currtouched3 & _BV(0)) && !(lasttouched3 & _BV(0)) ) {
    midi.sendNoteOn(66, 127);
  }
  if (!(currtouched3 & _BV(0)) && (lasttouched3 & _BV(0)) ) {
    midi.sendNoteOff(66, 0);
  }

  if ((currtouched3 & _BV(1)) && !(lasttouched3 & _BV(1)) ) {
    midi.sendNoteOn(67, 127);
  }
  if (!(currtouched3 & _BV(1)) && (lasttouched3 & _BV(1)) ) {
    midi.sendNoteOff(67, 0);
  }


  if ((currtouched3 & _BV(2)) && !(lasttouched3 & _BV(2)) ) {
    midi.sendNoteOn(68, 127);
  }
  if (!(currtouched3 & _BV(2)) && (lasttouched3 & _BV(2)) ) {
    midi.sendNoteOff(68, 0);
  }

  if ((currtouched3 & _BV(3)) && !(lasttouched3 & _BV(3)) ) {
    midi.sendNoteOn(69, 127);
  }
  if (!(currtouched3 & _BV(3)) && (lasttouched3 & _BV(3)) ) {
    midi.sendNoteOff(69, 0);
  }

  if ((currtouched3 & _BV(4)) && !(lasttouched3 & _BV(4)) ) {
    midi.sendNoteOn(70, 127);
  }
  if (!(currtouched3 & _BV(4)) && (lasttouched3 & _BV(4)) ) {
    midi.sendNoteOff(70, 0);
  }

  if ((currtouched3 & _BV(5)) && !(lasttouched3 & _BV(5)) ) {
    midi.sendNoteOn(71, 127);
  }
  if (!(currtouched3 & _BV(5)) && (lasttouched3 & _BV(5)) ) {
    midi.sendNoteOff(71, 0);
  }

  if ((currtouched3 & _BV(6)) && !(lasttouched3 & _BV(6)) ) {
    midi.sendNoteOn(72, 127);
  }
  if (!(currtouched3 & _BV(6)) && (lasttouched3 & _BV(6)) ) {
    midi.sendNoteOff(72, 0);
  }

  if ((currtouched3 & _BV(7)) && !(lasttouched3 & _BV(7)) ) {
    midi.sendNoteOn(73, 127);
  }
  if (!(currtouched3 & _BV(7)) && (lasttouched3 & _BV(7)) ) {
    midi.sendNoteOff(73, 0);
  }

  if ((currtouched3 & _BV(8)) && !(lasttouched3 & _BV(8)) ) {
    midi.sendNoteOn(74, 127);
  }
  if (!(currtouched3 & _BV(8)) && (lasttouched3 & _BV(8)) ) {
    midi.sendNoteOff(74, 0);
  }

  if ((currtouched3 & _BV(9)) && !(lasttouched3 & _BV(9)) ) {
    midi.sendNoteOn(75, 127);
  }
  if (!(currtouched3 & _BV(9)) && (lasttouched3 & _BV(9)) ) {
    midi.sendNoteOff(75, 0);
  }

  if ((currtouched3 & _BV(10)) && !(lasttouched3 & _BV(10)) ) {
    midi.sendNoteOn(76, 127);
  }
  if (!(currtouched3 & _BV(10)) && (lasttouched3 & _BV(10)) ) {
    midi.sendNoteOff(76, 0);
  }

  if ((currtouched3 & _BV(11)) && !(lasttouched3 & _BV(11)) ) {
    midi.sendNoteOn(77, 127);
  }
  if (!(currtouched3 & _BV(11)) && (lasttouched3 & _BV(11)) ) {
    midi.sendNoteOff(77, 0);
  }

  // reset our state
  lasttouched = currtouched;
  lasttouched2 = currtouched2;
  lasttouched3 = currtouched3;

  Control_Surface.loop();
}

You can use midi.sendKP to send MIDI Key Pressure events.

You might find some useful examples in #55 and #151.

(As an aside, your code could really use some for loops.)

Pieter

Thank you!

I wil work on it!

How do i put these functions in loops? Have you maybe some useful links?
I know this is maybe the most basic stuff, but i get totally lost because there
always seem to be new variables, new functions and stuff...

thanks in advance!

Ok, i will study the documentation of your control surface library!!
Its all very well explained there, thx!!

I just cant get it to work.
I think i am quite close, but i just dont know where to look. i looked at the Control Surface master documentation and tried to come up with something working, but i am not able to.

I "just" want to add velocity to my controller. From a Filtered Analog Pot.
But I can not figure out how to map the velocity read from it to the mpr121.....

here is a (shorter) version of my code so far, which does not work, as " setVelocity" was not declared in the scope.
Please can anyone give me a hint, would be greatly appreciated.

#include <Control_Surface.h>
#include <Adafruit_MPR121.h>
#include <Wire.h>

USBMIDI_Interface midi;


FilteredAnalog<7> velocityPot = A0;

uint8_t velocity = 0;



#ifndef _BV
#define _BV(bit) (1 << (bit))
#endif

Adafruit_MPR121 cap1 = Adafruit_MPR121();



// Keeps track of the last pins touched
// so we know when buttons are 'released'
uint16_t lasttouched = 0;
uint16_t currtouched = 0;




void setup() {


  if (!cap1.begin(0x5A));
 
  
  Control_Surface.begin();
}

void loop()  {

  currtouched  = cap1.touched();



if ((currtouched & _BV(0)) && !(lasttouched & _BV(0)) ) {
 midi.sendNoteOn(42, velocity);
 }
 if (!(currtouched & _BV(0)) && (lasttouched & _BV(0)) ) {
  midi.sendNoteOff(42, 0);
  }
 
 
  if (velocityPot.update()) // if the analog value changed
    setVelocity(velocityPot.getValue());

  // reset our state
  lasttouched = currtouched;



  Control_Surface.loop();
}

I think it would be best to start with a more beginner-friendly Arduino/C++ tutorial first.

You're getting the error because you're calling a function "setVelocity", but the function doesn't exist, you haven't declared it, and it's not part of the Control Surface library either.

The MPR121 gives you access to the "filteredData", see Adafruit_MPR121/MPR121test.ino at 6509b41a8dc7574c3e7e4d2617d420a24dfb2b8e · adafruit/Adafruit_MPR121 · GitHub.

#include <Control_Surface.h>
#include <Adafruit_MPR121.h>
#include <Wire.h>

USBMIDI_Interface midi;

FilteredAnalog<7> velocityPot = A0;

uint8_t BV(uint8_t bit) { return 1 << bit; }

Adafruit_MPR121 cap1 = {};

void setup() {
  Control_Surface.begin();
  if (!cap1.begin(0x5A))
    FATAL_ERROR(F("Couldn't initialize MPR121"), 0xF121);
}

void loop()  {
  // Keeps track of the last pins touched
  // so we know when buttons are 'released'
  static uint16_t lasttouched = 0;
  // Read the current state from the MPR121
  uint16_t currtouched = cap1.touched();

  // Read the value of the velocity potentiometer
  velocityPot.update();
  
  if ((currtouched & BV(0)) && !(lasttouched & BV(0)) ) {
    midi.sendNoteOn(42, velocityPot.getValue());
  }
  if (!(currtouched & BV(0)) && (lasttouched & BV(0)) ) {
    midi.sendNoteOff(42, 0);
  }

  // remember the current state
  lasttouched = currtouched;

  Control_Surface.loop();
}

But I can not figure out how to map the velocity read from it to the mpr121

Can you get velocity sensitivity from an MPR121?
If so I would be glad to here how.

Thanks Pieter, i will try that code as soon as i am home!.
@ Mike: that would be awesome, but i plan to read velocity from an fsr!

I am trying to build a midi controller with a hex layout for playing microtonal scales and stuff.

I made this one about 10 years ago and used OSC instead of MIDI.

http://www.thebox.myzen.co.uk/Hardware/Hexome.html

Oh wow, that is next level!!! Nice page too, lots of awesome projects Mike!

Ok, so here is the working code if anybody is interested.
It has 3 MPR121 for the midi notes, velocity and aftertouch via potentiometer or fsr.
A mapping function for velocity is also included, and cc potis...
Thanks for your help!!!

#include <Control_Surface.h>
#include <Adafruit_MPR121.h>


USBMIDI_Interface midi;

FilteredAnalog<7> velocityPot = A0;

//FilteredAnalog<7> aftertouchPot = A1;

// The filtered value read when potentiometer is at the 0% position
constexpr analog_t minimumValue = 0;
// The filtered value read when potentiometer is at the 100% position
constexpr analog_t maximumValue = 16000 - 0;

// A mapping function to eliminate the dead zones of the potentiometer:
// Some potentiometers don't output a perfect zero signal when you move them to
// the zero position, they will still output a value of 1 or 2, and the same
// goes for the maximum position.
analog_t mappingFunction(analog_t raw) {
  // make sure that the analog value is between the minimum and maximum
  raw = constrain(raw, minimumValue, maximumValue);
  // map the value from [minimumValue, maximumValue] to [0, 16383]
  return map(raw, minimumValue, maximumValue, 0, 16383);
  // Note: 16383 = 2¹⁴ - 1 (the maximum value that can be represented by
  // a 14-bit unsigned number

}


#ifndef _BV
#define _BV(bit) (1 << (bit))
#endif

Adafruit_MPR121 cap1 = Adafruit_MPR121();
Adafruit_MPR121 cap2 = Adafruit_MPR121();
Adafruit_MPR121 cap3 = Adafruit_MPR121();


// Keeps track of the last pins touched
// so we know when buttons are 'released'
uint16_t lasttouched = 0;
uint16_t currtouched = 0;
uint16_t lasttouched2 = 0;
uint16_t currtouched2 = 0;
uint16_t lasttouched3 = 0;
uint16_t currtouched3 = 0;


// Instantiate a CCPotentiometer object
//CCPotentiometer potentiometer = {
//  A3,                                   // Analog pin connected to potentiometer
// {MIDI_CC::Channel_Volume, CHANNEL_1}, // Channel volume of channel 1
//};
//Instantiate a Pitchbend poti
//PBPotentiometer  potentiometer = { A2,};


void setup() {

  if (!cap1.begin(0x5A));
  if (!cap2.begin(0x5B));
  if (!cap3.begin(0x5C));

  velocityPot.map(mappingFunction);

  Control_Surface.begin();
}

void loop()  {

  currtouched  = cap1.touched();
  currtouched2 = cap2.touched();
  currtouched3 = cap3.touched();

  velocityPot.update();

  // if (aftertouchPot.update()) {
  //    uint8_t newValue = aftertouchPot.getValue();
  //    if (newValue > 0)
  //     {
  //        midi.sendCP (CHANNEL_1, newValue);
  //      }

  //   }




  for (uint8_t i = 0; i < 12; i++) {
    if ((currtouched & _BV(i)) && !(lasttouched & _BV(i)) ) { 
        midi.sendNoteOn(i + 48, velocityPot.getValue()); 
        }
    if (!(currtouched & _BV(i)) && (lasttouched & _BV(i)) ) {
        midi.sendNoteOff(i + 48, 0);
        }
        
    if ((currtouched2 & _BV(i)) && !(lasttouched2 & _BV(i)) ) {
        midi.sendNoteOn(i + 60, velocityPot.getValue()); 
        }
    if (!(currtouched2 & _BV(i)) && (lasttouched2 & _BV(i)) ) {
        midi.sendNoteOff(i + 60, 0); 
        }
    
    if ((currtouched3 & _BV(i)) && !(lasttouched3 & _BV(i)) ) {
        midi.sendNoteOn(i + 72, velocityPot.getValue()); 
        }
    if (!(currtouched3 & _BV(i)) && (lasttouched3 & _BV(i)) ) { 
        midi.sendNoteOff(i + 72, 0);
        }
       
      }

  // reset our state
  lasttouched = currtouched;
  lasttouched2 = currtouched2;
  lasttouched3 = currtouched3;


  Control_Surface.loop();
}
1 Like