Help Needed with MIDI Foot Controller with Keypad KeyEvent Approach

Hi all,

I am new to Arduino community and the programming language.

So, my first Arduino project is to build a MIDI foot pedal that can play an entire octave.

I know there are many examples out there but the problem is I have to use Keypad library in order to interface the pedal with my Arduino Nano.

The octave pedal I am using was extracted from a Technics organ and it came with an onboard matrix circuitry with 6 rows and 3 columns.

I managed to wire them correctly however I couldn’t get these Keypad “PRESSED/HOLD/RELEASE” commands to work.

Whenever I hit a note on the pedal, my synth will produce the correct note but I can’t make it stop :slight_smile: so obviously the code is messed up in “RELEASE” part.

Also, it goes polyphonic if I hit other notes on the pedal.

In short, all I want is:
key PRESSED → Play the note as long as the key is pressed (constant velocity).
key RELEASED → Stop playing (Send note off command?)

Do I need to use “HOLD” command or maybe “IDLE”?

Down below is my Frankenstein code so let me know if you guys have any suggestions.

Thanks

EDIT: Code now compiles.

/* @file HelloKeypad.pde
|| @version 1.0
|| @author Alexander Brevig
|| @contact alexanderbrevig@gmail.com
||
|| @description
|| | Demonstrates the simplest use of the matrix Keypad library.
|| #
*/
#include <Keypad.h>

// Keypad 
const byte ROWS = 6; //six rows
const byte COLS = 3; //three columns
char keys[ROWS][COLS] = {
  {'1', 47, 41},
  {'2', 46, 40},
  {'3', 45, 39},
  {'4', 44, 38},
  {'5', 43, 37},
  {48, 42, 36}
};
byte rowPins[ROWS] = {7, 6, 5, 4, 3, 2}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {10, 9, 8}; //connect to the column pinouts of the keypad

Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );


void setup(){
  Serial.begin(31250);
 // keypad.setHoldTime(20);                   // Default is 1000mS
 keypad.addEventListener(keypadEvent); //add an event listener for this keypad
}



void loop(){
  char key = keypad.getKey();
   if (key) {
    noteOn(0x90, key, 0x45);
  }
}


  void keypadEvent(KeypadEvent key){
  switch (keypad.getState()){
    case IDLE:
      switch (key){
        noteOn(0x80, key, 0x45);
      }
      
    break;
    
    case PRESSED:
      switch (key){
        noteOn(0x90, key, 0x45);
      }
    
    break;

  }
}




// plays a MIDI note. Doesn't check to see that cmd is greater than 127, or that
// data values are less than 127:
void noteOn(int cmd, int pitch, int velocity) {
  Serial.write(cmd);
  Serial.write(pitch);
  Serial.write(velocity);
}

If I undrstand you correctly, when a key is pressed on a keypad (actually a set of foot pedals) a midi command corresponding to the key pressed should be sent.

Is that correct ?

By the way, I assume that the code you posted does not compile

Yes, correct. To me, MIDI part is easier to deal with. So, i want it to work like a midi keyborad.

Also, the code compiled good last time. I'll try it again on a different pc.

the code compiled good last time. I'll try it again on a different pc.

You can try it on as many PCs as you like, but it will not compile as it has fatal flaws such as an attempt to use two loop() functions.

Let's go from the point of view that the first time loop() shows up in the code is a typo. then

   if (key) {
    noteOn(0x90, key, 0x00);
  }

this is actually sending a 'note-off' command, but you do that from loop() and you use IDLE as a key event to do this as well, which obviously isn't working is there a 'RELEASE' key event ? then use that instead

I've updated the code and now it compiles again. NoteOff or "RELEASED/IDLE" cases don't work, though...

The approach that I would take is to define the keyboard with digits from 1 to 18 (see below) as their key values. Read the keypad and if there is a key value between 1 and 18 then subtract 1 from it and use the value as the index to an array of MIDI values to be output.

The reason for using 1 to 18 instead of 0 to 17 is because when no key value is available its value will be zero.

Hi UKHeliBob,

Thank you for the suggestion but I don't feel skilled enough to write the array. :) Not really sure where to start.

I don't feel skilled enough to write the array

I can pretty much guarantee that you know more about MIDI than I do.

Not really sure where to start.

Start by writing down in English what you want to happen when each key/pedal is pressed. Then convert them to MIDI commands. You can include more than one MIDI command per key press if required and each list of commands does not have to be the same length or have the same number of commands if that helps.

I see some problem here.

You use keypad.getKey() to read a key in your loop routine, but you register a key listener too

keypad.addEventListener(keypadEvent);

aka a function (keypadEvent) called when something happens with a key, including a key is pressed.

My advice is: use only the key in your listener function.

Now the problem with your key listener function. You used

switch (key){
        noteOn(0x90, key, 0x45);
      }

This is a switch with no cases, and does nothing.

So, a modified version using keylistener function (renamed keypadEventListener), and the RELEASE event to end the midi note, may look like

/* @file HelloKeypad.pde
|| @version 1.0
|| @author Alexander Brevig
|| @contact alexanderbrevig@gmail.com
||
|| @description
|| | Demonstrates the simplest use of the matrix Keypad library.
|| #
*/
#include <Keypad.h>

// Keypad 
const byte ROWS = 6; //six rows
const byte COLS = 3; //three columns
char keys[ROWS][COLS] = {
  {'1', 47, 41},
  {'2', 46, 40},
  {'3', 45, 39},
  {'4', 44, 38},
  {'5', 43, 37},
  {48, 42, 36}
};
byte rowPins[ROWS] = {7, 6, 5, 4, 3, 2}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {10, 9, 8}; //connect to the column pinouts of the keypad

Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );


void setup(){
  Serial.begin(31250);
 // keypad.setHoldTime(20);                   // Default is 1000mS
 keypad.addEventListener(keypadEventListener); //add an event listener for this keypad
}



void loop(){
  keypad.getKey();
}


void keypadEventListener(KeypadEvent key)
{
  switch (keypad.getState())
  {
    case RELEASED:
        // end note
        noteOn(0x80, key, 0x45);
    	break;
    
    case PRESSED:
        // start note
        noteOn(0x90, key, 0x45);
    	break;
  }
}




// plays a MIDI note. Doesn't check to see that cmd is greater than 127, or that
// data values are less than 127:
void noteOn(int cmd, int pitch, int velocity) {
  Serial.write(cmd);
  Serial.write(pitch);
  Serial.write(velocity);
}

Awesome! Thanks vlc0617. The code works like charm.

BUMP I want to use MIDI Channel 3. Any ideas??

noteOn(0x80, key, 0x45);This is actually a tad confusing, you are sending a noteOff, but if you just want to send that on channel 3 use noteOn(0x82, key, 0x45); It think.. check this document, the channel is the least significant nibble of the command byte. There used to be better documentation on Midi.org, but that requires some kind of login these days.

This worked great. Thanks

void keypadEventListener(KeypadEvent key)
{
  switch (keypad.getState())
  {
    case RELEASED:
        // end note
        noteOn(0x82, key, 0x45, 3);
      break;
   
    case PRESSED:
        // start note
        noteOn(0x92, key, 0x45, 3);
      break;
  }
}