Project tutorial
Midi Keypad

Midi Keypad © CC BY

Making music with a keypad? Seems like a weird idea! But we will see with the good software, we can even make an electric guitar out of it!

  • 1,500 views
  • 0 comments
  • 4 respects

Components and supplies

Necessary tools and machines

09507 01
Soldering iron (generic)

Apps and online services

About this project

In order to learn how to make MIDI instrument (musical instruments that can be used in music software), I tried to plug buttons on an Arduino Micro.

I figure out, I could simply plug a keypad instead to have 16 buttons! Keypads don't works well when you want to use multiples buttons at the same time, but it still a lot of fun.

Testing my prototype with Prominy V-Metal VST plugin

Wiring

Wiring the keypad and the display is pretty simple as it used pins from 2 to 11

Solder headers

First you need to cut two 17 pins female headers (for the arduino) and one 4 pins female headers (for the OLED display).

Here is how to do this: https://www.youtube.com/watch?v=qDG3VFSMSPQ

Solder the headers on a strip board, you can use the arduino micro to check the alignment. (5 spaces between the two headers)

I, personally, didn’t use a header for the OLED display, since I removed the pins on it.

Placing components

You can either hot glue the keypad or screw it by drilling holes on the strip board.

It’s better to fix the keypad before soldering the cable, as it will be easier to check the length of the cables.

Soldering the keypad

So let’s get to the point, here is how to solder the keypad.

Beware, you can easily make a mistake, when you check where the pins are plugged on your arduino, as it is reversed.

You should start with pins 11 and then solder the next one.

Solder the OLED Display

Here is how to solder the OLED display.

The display use the I2C protocol so it needs to be plugged on pins 2 (SDA) and 3 (SCL) (this is always the case on board with an ATmega32U4)

The ground and the 5v are in the same column

And voilà! our prototype is finished! Last thing, you need to plug the arduino micro this way (the USB port needs to be on the right)

The drawing used here are from usini cards: https://github.com/usini/usini-cards

Images are public domains, so feel free to use it!

Code

Now that our keypad is ready, we need to upload our sketch to the arduino micro.

The code is available here: https://github.com/maditnerd/keypad_midi

Libraries

The code used 3 libraries

If you forgot how to manage libraries, here is a guide:

https://www.arduino.cc/en/Guide/Libraries

  • MidiUSB by Gary Grewal (manage USB MIDI)
  • U8g2 by olikraus (for the OLED display)
  • Keypad by NullKraft (for the keypad)

MidiUSB and U8g2 can be installed using the library manager in the arduino software.

For the keypad, it’s a little bit tricky, this is a modified version of the keypad library by Mark Stanley and Alexander Brevig.

This one can manage multiple-key input.

Multiple keys pressed works more or less, since my goal was to make a monophonic instrument (aka that play one note at a time) it works fine, but if you want to play chords there will be a small delay between each note.

To install the keypad libraries, you need to

  • Click on the green button: Clone/Download
  • Extract Keypad-master.zip in your library folder inside your sketchbook, you should have a folder Keypad-Master in arduino/libraries.

Settings your preset

I tried to comment on the code as much as I could, so I won’t go into too much detail, feel free to ask me for more information on the comments.

Most parameters are at the beginning of the code, this is where we will set up the notes and velocity (velocity is the volume of the notes).

Memory issues

I had some issues with memory, primary due to the screen.

U8g2 library used a lot of dynamic memory (used by global variables).

I could have used u8x8 or the adafruit library for SSD1306 but the font was either too small or too big.

I had to limit the preset to 8, but you could probably increase the preset numbers with some optimisation.

You could for example, remove the possibilities to customize velocity on each notes for ex.

Preset name

Preset names are stored in this array:

String preset_name [PRESET] = {"Du Riechst So Gut", "FF - Replica", "DrumKit", "A", "4", "5", "6", "B"};

Notes

Notes are stored in a two-dimensional array, each note are in the order of the buttons (first one is 1, second one is 2, etc.).

int notes [PRESET] [NB_BUTTONS] = {
{38, 38, 39, 39, 43, 46, 38, 38, 48, 46, 45, 45, 57, 60, 60, 57}, //Preset 1
{37, 40, 42, 39, 40, 38, 41, 40, 48, 46, 45, 45, 57, 60, 60, 57}, // Preset 2
{35, 35, 38, 42, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 55, 57}, // Preset 3
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},  //Preset A
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},  //Preset 4
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},  //Preset 5
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},  //Preset 6
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}  //Preset B
};

Notes are stored as midi notes (it goes from 0 to 127), here is a spreadsheet to convert it from musical notes.

Velocity

Same thing for velocity

int velocity [PRESET] [NB_BUTTONS] = {
{70, 70, 70, 70, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100}, //Preset 1
{70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70}, //Preset 2
{100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100}, //Preset 3
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //Preset A
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //Preset 4
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //Preset 5
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //Preset 6
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //Preset B
};

Velocity changer

I create a way to change velocity when the D key is pressed.

You can choose to use it or not on any preset by changing the boolean value inside the velocity_changer array.

const int velocity_button = 15; //The D key is used to change velocity if velocity changer is true
const int VELOCITY_CHANGE = 100; //Velocity when D is pressed (should probably be changeable)
bool velocity_changer [PRESET] = {false, true, false, false, false, false, false, false};

Rename your MIDI devices

By default, your MIDI keypad will be named Arduino Micro.

If you want to change the name, follow this tutorial:

http://liveelectronics.musinou.net/MIDIdeviceName.php

In a nutshell, you need to create a new board in boards.txt and rename it.

Using the MIDI Keypad

If you don’t have any music software, you should try LMMS which is free and open source.

https://lmms.io/

Each key plays a note define in the arduino code except *

* Open preset selection mode, press from 1 to B to choose a preset and press * again to exit the selection mode.

My Guitar Rig

Finally, here is a quick explanation of how I manage to use my keypad as an electric guitar.

In order to host my VST plugins (plugins to generate/modify sounds), I used Reason. https://www.propellerheads.com/fr/reason

However you can use any music software that is compatible with VST.

Here is the list of plugins, I used

I combined two guitars in order to have a heavier sound.

One play note by note (no legato) and the second one plays chords (power chords)

For the effects I used the Shredder Solo effects (which I have a bit modified)

I’m thinking of making a light version of it, that could work on LMMS https://lmms.io/ or samplerbox http://www.samplerbox.org/ by using samples, if you are interested, tell me about it in the comment or on my Twitter account (https://twitter.com/m4dnerd).

It will probably be a lot less cool. :-(

Code

Keypad MidiArduino
This is the code, at the moment I made this tutorial.
Please check the repository for the last version.
#include "MIDIUSB.h"

/* Settings for MIDI preset */
const byte ROWS = 4; //four rows
const byte COLS = 4; //four columns
const int NB_BUTTONS = ROWS * COLS;

const int PRESET = 8;
String preset_name[PRESET] = {"Du Riechst So Gut", "FF - Replica", "DrumKit", "A", "4", "5", "6", "B"};
int notes[PRESET][NB_BUTTONS] = {
  {38, 38, 39, 39, 43, 46, 38, 38, 48, 46, 45, 45, 57, 60, 60, 57}, //Preset 1
  {37, 40, 42, 39, 40, 38, 41, 40, 48, 46, 45, 45, 57, 60, 60, 57}, // Preset 2
  {35, 35, 38, 42, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 55, 57}, // Preset 3
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},  //Preset A
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},  //Preset 4
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},  //Preset 5
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},  //Preset 6
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}  //Preset B
};

int velocity[PRESET][NB_BUTTONS] = {
  {70, 70, 70, 70, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100}, //Preset 1
  {70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70}, //Preset 2
  {100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100}, //Preset 3
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //Preset A
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //Preset 4
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //Preset 5
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //Preset 6
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //Preset B
};

const int velocity_button = 15; // The D key is used to change velocity if velocity changer is true
const int VELOCITY_CHANGE = 100; //Velocity when D is pressed (should probably be changeable)
bool velocity_changer[PRESET] = {false, true, false, false, false, false, false, false};

/* Keypad Init */
//https://github.com/Nullkraft/Keypad
#include <Keypad.h>
char keys[ROWS][COLS] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};

//Keypad pins
byte rowPins[ROWS] = {8, 9, 10, 11}; //connect to the row pinouts of the kpd
byte colPins[COLS] = {4, 5, 6, 7}; //connect to the column pinouts of the kpd
Keypad kpd = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

//Keypad state : 1 = DOWN 3 = UP (there are other state which are unused here)
unsigned int buttons_state[NB_BUTTONS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
bool preset_selector = false;

//Oled Display
#include <Wire.h> //I2C
#include <U8g2lib.h>
U8G2_SSD1306_128X32_UNIVISION_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
String oled_line1;
String oled_line2;

//Preset
int selected_preset = 0;

void setup() {
  Serial.begin(115200);
  u8g2.begin();
  change_preset(selected_preset);
}

//Change preset and display it on screen
//You need to press "*" again to validate the preset
void change_preset(int preset) {
  if (preset < PRESET) {
    selected_preset = preset;
    oled_line1 = "-----Preset------";
    oled_line2 = preset_name[preset];
    printScreen();
    Serial.println(oled_line1);
    Serial.println(oled_line2);
  }
}

//Change what is written on the Oled screen using global variable
//oled_line1 and oled_line2
void printScreen() {
  //https://github.com/olikraus/u8g2/wiki/fntlist8x8
  u8g2.setFont(u8g2_font_7x14_mr);
  u8g2.firstPage();
  do {
    u8g2.setCursor(0, 14);
    u8g2.println(oled_line1);  // write something to the internal memory
    u8g2.setCursor(0, 30);
    u8g2.println(oled_line2);  // write something to the internal memory
  }
  while ( u8g2.nextPage() );
}

void loop() {
  if (kpd.getKeys()) {
    for (int i = 0; i < LIST_MAX; i++) // Scan the whole key list.
    {
      if ( kpd.key[i].stateChanged )   // Only find keys that have changed state.
      {
        buttons_state[kpd.key[i].kcode] = kpd.key[i].kstate;

        if (kpd.key[i].kstate == 1) { //DOWN
          Serial.print(kpd.key[i].kcode);
          Serial.print(":");
          Serial.println("1");

          //If key "#" is pressed
          if (kpd.key[i].kcode == 12) {
            //If preset selection is not active, we go to selection mode
            if (!preset_selector) {
              Serial.println("Selector");
              preset_selector = true;
              oled_line1 = "Selector";
              oled_line2 = "--------";
              printScreen();
            } else {
              //If preset selection is active, we return to midi mode
              Serial.println("Deselect");
              oled_line1 = preset_name[selected_preset];
              oled_line2 = "";
              printScreen();
              preset_selector = false;
            }
          } else {
            //If in selection mode we change preset
            if (preset_selector) {
              Serial.println("Change preset");
              change_preset(kpd.key[i].kcode);
            } else {
              //If in midi mode we play midi note
              key_down(kpd.key[i].kcode);
            }
          }
        }
        if (kpd.key[i].kstate == 3) { //UP
          Serial.print(kpd.key[i].kcode);
          Serial.print(":");
          Serial.println("0");
          // If key is up we send a midi note off
          key_up(kpd.key[i].kcode);
        }
      }
    }
  }
}

//Key down (pressed)
void key_down(int key) {
  int current_velocity = 100;
  //Serial.print(buttons_state[velocity_button]);

  //If velocity_changer (D) active
  if (velocity_changer[selected_preset]) {
    //If velocity changer pressed check for state 1 (down) or 2 (long down)
    if ((buttons_state[velocity_button] == 1) || (buttons_state[velocity_button] == 2)) {
      current_velocity = VELOCITY_CHANGE;
    } else {
      //If not pressed use velocity from preset
      current_velocity = velocity[selected_preset][key];
    }
  } else {
    //If velocity_changer not active, use velocity from preset
    current_velocity = velocity[selected_preset][key];
  }


  if (!velocity_changer[selected_preset]) {
    //Play note if velocity changer not set
    noteOn(0, notes[selected_preset][key], current_velocity);
  } else {
    //Else do not play note on velocity changer button (D)
    if (key != velocity_button) {
      noteOn(0, notes[selected_preset][key], current_velocity);
    }
  }

  //Display note and velocity on screen
  oled_line1 = preset_name[selected_preset];
  oled_line2 = "Note:" + String(notes[selected_preset][key]) + " " "V:" + String(current_velocity);
  printScreen();
  MidiUSB.flush();
}

//Key up (unpressed)
void key_up(int key) {
  //If velocity_changer
  if (!velocity_changer[selected_preset]) {
    noteOff(0, notes[selected_preset][key], 127);
  } else {
    if (key != velocity_button) {
      noteOff(0, notes[selected_preset][key], 127);
    }
  }
  MidiUSB.flush();
}

void noteOn(byte channel, byte pitch, byte velocity) {
  midiEventPacket_t noteOn = {0x09, 0x90 | channel, pitch, velocity};
  MidiUSB.sendMIDI(noteOn);
}

void noteOff(byte channel, byte pitch, byte velocity) {
  midiEventPacket_t noteOff = {0x08, 0x80 | channel, pitch, velocity};
  MidiUSB.sendMIDI(noteOff);
}

void controlChange(byte channel, byte control, byte value) {
  midiEventPacket_t event = {0x0B, 0xB0 | channel, control, value};
  MidiUSB.sendMIDI(event);
}
Keypad Midi code
This repository contains the arduino code, and svg schematics.

Schematics

Keypad Schematic
Solder keypad nhwro4jzup
OLED Schematic
Solder oled aegrjigwqs

Comments

Team labsud

Labsud
labsud
  • 3 projects
  • 1 follower
Madnerd
madnerd
  • 3 projects
  • 1 follower

Additional contributors

Published on

February 11, 2019

Members who respect this project

Photo974490 ic83bpyyzzDefaultDefault
See similar projects
you might like

Similar projects you might like

Minimal MIDI Drum Kit with 3D Printer

Project tutorial by ryokosaka

  • 9,881 views
  • 1 comment
  • 29 respects

Turn an Arduino Uno into a MIDI Controller: Guitar Pedals

Project tutorial by Johan van Vugt

  • 1,869 views
  • 0 comments
  • 7 respects

MIDI Slide Whistle "MEMIDION" Next Stage

Project tutorial by HomeMadeGarbage

  • 1,999 views
  • 0 comments
  • 14 respects

Arduino + LEDs + MIDI Keyboard + MuseScore = Piano Tutor

Project tutorial by tcucinotta

  • 6,884 views
  • 3 comments
  • 19 respects

USB-BLE Wireless MIDI Adapters

Project tutorial by Joe Bowbeer

  • 2,915 views
  • 0 comments
  • 4 respects

Arduino LED MIDI Controller with FL Studio

Project tutorial by reyadeetopee

  • 2,036 views
  • 0 comments
  • 11 respects
Add projectSign up / Login