MPR121 touch button to latch button with Control_Surface library?

Hi!

So i also made a midi controller out of an old casio keyboard (seems to be a common project here;) ).
Now i got a little stuck, because i can not figure out how to convert the Mpr121´s capacitive buttons
into latch buttons inside my DAW (Reason)
is there an easy way?
If i touch one of the twelve capacitive sensors now, i get the cc value 127, if i release it, it sends 0, exactly like the code shows. But i would prefer: first touch 127, second touch 0. Without it going back to 0 as soon as i release it.

I use the Control_Surface library by Pieter P… here is my code:

//a non velocity sensitive midikeyboard made out of an old Casio keyboard
//made with the Control_Surface library.
//Keyboard Matrix and Adafruit_MPR121.
//MPR121 used for 12 CC Buttons
//velocity may be added in the future with a FSR resitor underneath the keys




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

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


// You can have up to 4 on one i2c bus but one is enough for testing!
Adafruit_MPR121 cap = Adafruit_MPR121();

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



// A transposer that can transpose the keyboard one octave lower or higher
Transposer<-12, +12> transposer;

//Buttons to transpose the keyboard
//IncrementDecrementSelector<transposer.N> transposebuttons = {
//  transposer,
//}
//   {2, 3},
//    Wrap::NoWrap,
//};

using namespace MIDI_Notes;
Bankable::NoteButtonMatrix<6, 9> keyboard = {
    transposer,
   {A0, A1, 4, 5, 6, 7}, // row pins   
   {8, 9, 10, 11, 12, A2, A3, A4, A5},    // column pins                   
    
    // All notes
    {{
        { note(C,2), note(Gb,2),note(C,3), note(Gb,3),note(C,4), note(Gb,4),note(C,5), note(Gb,5),note(C,6),},
        { note(Db,2),note(G,2), note(Db,3),note(G,3), note(Db,4),note(G,4), note(Db,5),note(G,5),           },
        { note(D,2), note(Ab,2),note(D,3), note(Ab,3),note(D,4), note(Ab,4),note(D,5), note(Ab,5),          },
        { note(Eb,2),note(A,2), note(Eb,3),note(A,3), note(Eb,4),note(A,4), note(Eb,5),note(A,5),           },
        { note(E,2), note(Bb,2),note(E,3), note(Bb,3),note(E,4), note(Bb,4),note(E,5), note(Bb,5),          },
        { note(F,2), note(B,2), note(F,3), note(B,3), note(F,4), note(B,4), note(F,5), note(B,5),           },
            
}},
};

// 7-bit analog input
//FilteredAnalog<7> velocityPot = A0;

void setup() { 
 
 Control_Surface.begin(); 
  if (!cap.begin(0x5A));
  }

void loop() {

   currtouched = cap.touched();

   if ((currtouched & _BV(0)) && !(lasttouched & _BV(0)) ) {
     midi.sendCC(0, 127); }
     if (!(currtouched & _BV(0)) && (lasttouched & _BV(0)) ) {
     midi.sendCC(0, 0); }
   
    if ((currtouched & _BV(1)) && !(lasttouched & _BV(1)) ) {
     midi.sendCC(1, 127); }
     if (!(currtouched & _BV(1)) && (lasttouched & _BV(1)) ) {
     midi.sendCC(1, 0); }
   
      if ((currtouched & _BV(2)) && !(lasttouched & _BV(2)) ) {
      midi.sendCC(2, 127); }
    // if it *was* touched and now *isnt*, alert!
     if (!(currtouched & _BV(2)) && (lasttouched & _BV(2)) ) {
      midi.sendCC(2, 0); }

     if ((currtouched & _BV(3)) && !(lasttouched & _BV(3)) ) {
     midi.sendCC(3, 127); }
     if (!(currtouched & _BV(3)) && (lasttouched & _BV(3)) ) {
      midi.sendCC(3, 0); }
     
   if ((currtouched & _BV(4)) && !(lasttouched & _BV(4)) ) {
     midi.sendCC(4, 127); }
     if (!(currtouched & _BV(4)) && (lasttouched & _BV(4)) ) {
      midi.sendCC(4, 0); }

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

          if ((currtouched & _BV(6)) && !(lasttouched & _BV(6)) ) {
      midi.sendCC(6, 127); }
      if (!(currtouched & _BV(6)) && (lasttouched & _BV(6)) ) {
      midi.sendCC(6, 0); }
      
          if ((currtouched & _BV(7)) && !(lasttouched & _BV(7)) ) {
      midi.sendCC(7, 127); }
      if (!(currtouched & _BV(7)) && (lasttouched & _BV(7)) ) {
      midi.sendCC(7, 0); }
      
          if ((currtouched & _BV(8)) && !(lasttouched & _BV(8)) ) {
      midi.sendCC(8, 127); }
      if (!(currtouched & _BV(8)) && (lasttouched & _BV(8)) ) {
      midi.sendCC(8, 0); }
      
          if ((currtouched & _BV(9)) && !(lasttouched & _BV(9)) ) {
      midi.sendCC(9, 127); }
      if (!(currtouched & _BV(9)) && (lasttouched & _BV(9)) ) {
      midi.sendCC(9, 0); }
      
          if ((currtouched & _BV(10)) && !(lasttouched & _BV(10)) ) {
      midi.sendCC(10, 127); }
      if (!(currtouched & _BV(10)) && (lasttouched & _BV(10)) ) {
      midi.sendCC(10, 0); }

          if ((currtouched & _BV(11)) && !(lasttouched & _BV(11)) ) {
      midi.sendCC(11, 127); }
      if (!(currtouched & _BV(11)) && (lasttouched & _BV(11)) ) {
      midi.sendCC(11, 0); }
      
          if ((currtouched & _BV(12)) && !(lasttouched & _BV(12)) ) {
      midi.sendCC(12, 127); }
      if (!(currtouched & _BV(12)) && (lasttouched & _BV(12)) ) {
      midi.sendCC(12, 0); }
   

//  if (velocityPot.update()) // if the analog value changed
//    keyboard.setVelocity(velocityPot.getValue());

// reset our state
  lasttouched = currtouched;

  Control_Surface.loop();
}

You could try something like this (untested, I don’t have the specific hardware):

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

class MIDIMPR121Buttons : public MIDIOutputElement {
  public:
    MIDIMPR121Buttons(Adafruit_MPR121 &mpr, const Array<MIDIAddress, 12> &addresses)
      : mpr(&mpr), addresses(addresses) {}

    // Initializing the MPR121 is left to the user
    void begin() override {}

    // Update is called by Control Surface once every loop iteration
    void update() override {
      uint16_t newTouchStates = mpr->touched();
      uint16_t oldTouchStates = this->touchStates;
      this->touchStates = newTouchStates;

      if (newTouchStates == oldTouchStates)
        return;

      for (uint8_t i = 0; i < 12; ++i) {
        // if this pad just became touched
        if ((newTouchStates & 1) == 1 && (oldTouchStates & 1) == 0) {
          uint16_t mask = 1u << i;
          latchStates ^= mask; // toggle the latched state
          bool latchState = latchStates & mask;
          // Send the appropriate MIDI message
          latchState ? Control_Surface.sendCC(addresses[i], onValue)
                     : Control_Surface.sendCC(addresses[i], offValue);
        }
        newTouchStates >>= 1; // Shift over the states to read the next pad
        oldTouchStates >>= 1;
      }
    }

  private:
    Adafruit_MPR121 *mpr;
    Array<MIDIAddress, 12> addresses;
    uint16_t latchStates = 0;
    uint16_t touchStates = 0;
    constexpr static uint8_t onValue = 0x7F;
    constexpr static uint8_t offValue = 0x00;
};

Adafruit_MPR121 mpr;

MIDIMPR121Buttons midibuttons = {
  mpr,
  {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, // addresses
};

USBDebugMIDI_Interface midi;

void setup() {
  if(!mpr.begin())
    FATAL_ERROR(F("MPR121 not found, check wiring?"), 0x0121);
  Control_Surface.begin();
}

void loop() {
  Control_Surface.loop();
}

Pieter

Hi Pieter!!!

Thank you very much! As this code is quite different i need some time to check out what is going on ;) Right now it puts out values on the serial monitor, but no midi cc´s. Serial monitor looks like the latching function is implemented!

To send actual MIDI messages, just replace USBDebugMIDI_Interface by USBMIDI_Interface.

ok!

That works like a charm now, thank you Pieter! You are a great help.

I will try to merge this new code with my keyboard matrix, and will come back here..