Help Turn a toy Keyboard (piano) into a MIDI Device (Arduino uno)

hi guys!

searching on the web, i found some projects for turning a toy piano into a MIDI device, to send digital signals to the pc

i post the video link for let you know this project: https://www.youtube.com/watch?v=lXX8v-ZW6Tk

all is based on a scan matrix of the piano keys, so we need to program arduino for have a midi output when a piano key is pressed or relased

now i explain my problem:
i have an original arduino uno board, and a casio piano with 49 keys, divided by 15 wires (with a scan matrix of 6 columns and 9 rows - like this photo)

(consider only the number of wires written by pen, and the notes highlighted)

in the video tutorial, he use the shift register technique, using the 74hc595 chip, for demoltiply the columns number and use only the digital inputs of arduino

i have edited the code by adapting it to my piano, the output worked, and the pc receive the midi signals, but the problem is that the notes i press on the piano aren't the same that arrive on the pc (for exaple if i press the C note on piano, my pc receive a F note)

that's the code i used:

define NUM_ROWS 9
#define NUM_COLS 6

#define NOTE_ON_CMD 0x90
#define NOTE_OFF_CMD 0x80
#define NOTE_VELOCITY 127

//MIDI baud rate
#define SERIAL_RATE 31250

// Pin Definitions

// Row input pins
const int row1Pin = 2;
const int row2Pin = 3;
const int row3Pin = 4;
const int row4Pin = 5;
const int row5Pin = 6;
const int row12Pin = 7;
const int row13Pin = 8;
const int row14Pin = 9;
const int row15Pin = 10;

// 74HC595 pins
const int dataPin = 11;
const int latchPin = 12;
const int clockPin = 13;

boolean keyPressed[NUM_ROWS][NUM_COLS];
uint8_t keyToMidiMap[NUM_ROWS][NUM_COLS];

// bitmasks for scanning columns
int bits[] =
{
B11111110,
B11111101,
B11111011,
B11110111,
B11101111,
B11011111,
B10111111,
B01111111
};

void setup()
{
int note = 36;

for(int colCtr = 0; colCtr < NUM_COLS; ++colCtr)
{
for(int rowCtr = 0; rowCtr < NUM_ROWS; ++rowCtr)
{
keyPressed[rowCtr][colCtr] = false;
keyToMidiMap[rowCtr][colCtr] = note;
note++;
}
}

// setup pins output/input mode
pinMode(dataPin, OUTPUT);
pinMode(clockPin, OUTPUT);
pinMode(latchPin, OUTPUT);

pinMode(row1Pin, INPUT);
pinMode(row2Pin, INPUT);
pinMode(row3Pin, INPUT);
pinMode(row4Pin, INPUT);
pinMode(row5Pin, INPUT);
pinMode(row12Pin, INPUT);
pinMode(row13Pin, INPUT);
pinMode(row14Pin, INPUT);
pinMode(row15Pin, INPUT);

Serial.begin(SERIAL_RATE);
}

void loop()
{
for (int colCtr = 0; colCtr < NUM_COLS; ++colCtr)
{
//scan next column
scanColumn(colCtr);

//get row values at this column
int rowValue[NUM_ROWS];
rowValue[0] = !digitalRead(row1Pin);
rowValue[1] = !digitalRead(row2Pin);
rowValue[2] = !digitalRead(row3Pin);
rowValue[3] = !digitalRead(row4Pin);
rowValue[4] = !digitalRead(row5Pin);
rowValue[5] = !digitalRead(row12Pin);
rowValue[6] = !digitalRead(row13Pin);
rowValue[7] = !digitalRead(row14Pin);
rowValue[8] = !digitalRead(row15Pin);

// process keys pressed
for(int rowCtr=0; rowCtr<NUM_ROWS; ++rowCtr)
{
if(rowValue[rowCtr] != 0 && !keyPressed[rowCtr][colCtr])
{
keyPressed[rowCtr][colCtr] = true;
noteOn(rowCtr,colCtr);
}
}

// process keys released
for(int rowCtr=0; rowCtr<NUM_ROWS; ++rowCtr)
{
if(rowValue[rowCtr] == 0 && keyPressed[rowCtr][colCtr])
{
keyPressed[rowCtr][colCtr] = false;
noteOff(rowCtr,colCtr);
}
}
}
}

void scanColumn(int colNum)
{
digitalWrite(latchPin, LOW);

if(0 <= colNum && colNum <= 7)
{
shiftOut(dataPin, clockPin, MSBFIRST, bits[colNum]); //left sr
}
else
{
shiftOut(dataPin, clockPin, MSBFIRST, bits[colNum-8]); //right sr
}

digitalWrite(latchPin, HIGH);
}

void noteOn(int row, int col)
{
Serial.write(NOTE_ON_CMD);
Serial.write(keyToMidiMap[row][col]);
Serial.write(NOTE_VELOCITY);
}

void noteOff(int row, int col)
{
Serial.write(NOTE_OFF_CMD);
Serial.write(keyToMidiMap[row][col]);
Serial.write(NOTE_VELOCITY);
}

in my opinion the problem is in the managment of the shift register (i use only one, unlike twos used in the video project, and probably i do something wrong in the code)

please can you help me to find the error?

this a photo of the circuit:

the electrical connections are all correct, the scan matrix is correct because i checked it on the piano user manual and i have tested it with a multimeter

PS:
i found that the analog inputs of arduino can be used as digital one, simply call him in this mode:
A0 = pin14
A1 = pin15
A2 = pin16
A3 = pin 17
A4 = pin18
A5 = pin19

so, is possible to create a program without using the shift register, but using only the arduino imputs (in this case are enough for all the 15 wires of my piano)

by you, in this mode, how i can program arduino for send midi signals to the pc?

That is very very poor code. It can be made substantially shorter.

That said there is a bit missing, it is the keyToMidiMap array.
Sure you dimension it in the line:-

uint8_t keyToMidiMap[NUM_ROWS][NUM_COLS];

But you never put any values in it!

This is a look up table that will translate your key press value into a MIDI note on number. As it stands you are just sending MIDI notes that are in the random bit of uninitialised memory.

USE THE CODE ICON NOT THE QUOTE ICON NEXT TO IT!!

Grumpy_Mike:
That is very very poor code. It can be made substantially shorter.

That said there is a bit missing, it is the keyToMidiMap array.
Sure you dimension it in the line:-

uint8_t keyToMidiMap[NUM_ROWS][NUM_COLS];

But you never put any values in it!

This is a look up table that will translate your key press value into a MIDI note on number. As it stands you are just sending MIDI notes that are in the random bit of uninitialised memory.

USE THE CODE ICON NOT THE QUOTE ICON NEXT TO IT!!

thanks for the aswer.
i edited the code but still no works

i have re-checked the wiring and still ok

the notes aren't random, because every time i initialize the midi port, the note output still the same

i edited the code but still no works

So what you do here is to repost the new code and ask again.

Have you created that look up table?

Grumpy_Mike:
So what you do here is to repost the new code and ask again.

Have you created that look up table?

No i don't!

I dont know how to create a look up table!

This sketch is not Made by me, i just only adapt it to my keyboard

I'm a newbie with arduino and i don't have enough knowledge for This sophisticated sketches

I'm a newbie with arduino and i don't have enough knowledge for This sophisticated sketches

That is not a sophisticated sketch, it is very crude.

the notes aren't random, because every time i initialize the midi port, the note output still the same

They are random in that your code has no control over what is produced because your look up table is just using junk in the memory, that is uninitialised memory locations. You change your code and the notes it produces will probably change.

I dont know how to create a look up table!

Well it looks like your project comes to a full stop then because without one nothing can work. The problem is that you have not supplied enough information about the hardware that you have. In code terms the lookup table will look something like this:-

uint8_t keyToMidiMap[9][6] =   {  // note that these are not real values to use
                          30,31,32,33,34,35,       
                          45,44,43,42,41,40,      
                          50,51,52,53,54,55,
                          65,64,63,62,61,60,                          
                          71,72,74,73,71,72,        
                          82,83,85,84,82,85,
                          91,94,95,92,94,90,
                          100,104,102,103,105,106,                         
                          107,108,109,110,111,113,        
                         };

But those are not the real numbers to use in the body of that matrix. You have to work out what the real numbers are, either from examining and understanding your circuit, or from printing out the row and column numbers for each key and putting the MIDI note number you want in each corresponding matrix location.

Grumpy_Mike:
That is not a sophisticated sketch, it is very crude.
They are random in that your code has no control over what is produced because your look up table is just using junk in the memory, that is uninitialised memory locations. You change your code and the notes it produces will probably change.
Well it looks like your project comes to a full stop then because without one nothing can work. The problem is that you have not supplied enough information about the hardware that you have. In code terms the lookup table will look something like this:-

uint8_t keyToMidiMap[9][6] =   {  // note that these are not real values to use

30,31,32,33,34,35,     
                          45,44,43,42,41,40,     
                          50,51,52,53,54,55,
                          65,64,63,62,61,60,                         
                          71,72,74,73,71,72,       
                          82,83,85,84,82,85,
                          91,94,95,92,94,90,
                          100,104,102,103,105,106,                       
                          107,108,109,110,111,113,       
                        };



But those are not the real numbers to use in the body of that matrix. You have to work out what the real numbers are, either from examining and understanding your circuit, or from printing out the row and column numbers for each key and putting the MIDI note number you want in each corresponding matrix location.

thank you for your help, but it still no work!
here is the code:

#define NUM_ROWS 9
#define NUM_COLS 6

#define NOTE_ON_CMD 0x90
#define NOTE_OFF_CMD 0x80
#define NOTE_VELOCITY 127

//MIDI baud rate
#define SERIAL_RATE 31250

// Pin Definitions

// Row input pins
const int row1Pin = 2;
const int row2Pin = 3;
const int row3Pin = 4;
const int row4Pin = 5;
const int row5Pin = 6;
const int row6Pin = 7;
const int row7Pin = 8;
const int row8Pin = 9;
const int row9Pin = 10;

// 74HC595 pins
const int dataPin = 11;
const int latchPin = 12;
const int clockPin = 13;

boolean keyPressed[NUM_ROWS][NUM_COLS];
uint8_t keyToMidiMap[9][6] = {
                                        36, 37, 38, 39, 40, 41,
                                        42, 43, 44, 45, 46, 47,
                                        48, 49, 50, 51, 52, 53,
                                        54, 55, 56, 57, 58, 59,
                                        60, 61, 62, 63, 64, 65,
                                        66, 67, 68, 69, 70, 71,
                                        72, 73, 74, 75, 76, 77,
                                        78, 79, 80, 81, 82, 83,
                                        84,
                                          };

// bitmasks for scanning columns
int bits[] =
{ 
  B11111110,
  B11111101,
  B11111011,
  B11110111,
  B11101111,
  B11011111,
  B10111111,
  B01111111
};

void setup()
{
  int note = 36;

  for(int colCtr = 0; colCtr < NUM_COLS; ++colCtr)
  {
    for(int rowCtr = 0; rowCtr < NUM_ROWS; ++rowCtr)
    {
      keyPressed[rowCtr][colCtr] = false;
      keyToMidiMap[rowCtr][colCtr] = note;
      note++;
    }
  }

  // setup pins output/input mode
  pinMode(dataPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(latchPin, OUTPUT);

  pinMode(row1Pin, INPUT);
  pinMode(row2Pin, INPUT);
  pinMode(row3Pin, INPUT);
  pinMode(row4Pin, INPUT);
  pinMode(row5Pin, INPUT);
  pinMode(row6Pin, INPUT);
  pinMode(row7Pin, INPUT);
  pinMode(row8Pin, INPUT);
  pinMode(row9Pin, INPUT);

  Serial.begin(SERIAL_RATE);
}

void loop()
{
  for (int colCtr = 0; colCtr < NUM_COLS; ++colCtr)
  {
    //scan next column
    scanColumn(colCtr);

    //get row values at this column
    int rowValue[NUM_ROWS];
    rowValue[0] = !digitalRead(row1Pin);
    rowValue[1] = !digitalRead(row2Pin);
    rowValue[2] = !digitalRead(row3Pin);
    rowValue[3] = !digitalRead(row4Pin);
    rowValue[4] = !digitalRead(row5Pin);
    rowValue[5] = !digitalRead(row6Pin);
    rowValue[6] = !digitalRead(row7Pin);
    rowValue[7] = !digitalRead(row8Pin);
    rowValue[8] = !digitalRead(row9Pin);

    // process keys pressed
    for(int rowCtr=0; rowCtr<NUM_ROWS; ++rowCtr)
    {
      if(rowValue[rowCtr] != 0 && !keyPressed[rowCtr][colCtr])
      {
        keyPressed[rowCtr][colCtr] = true;
        noteOn(rowCtr,colCtr);
      }
    }

    // process keys released
    for(int rowCtr=0; rowCtr<NUM_ROWS; ++rowCtr)
    {
      if(rowValue[rowCtr] == 0 && keyPressed[rowCtr][colCtr])
      {
        keyPressed[rowCtr][colCtr] = false;
        noteOff(rowCtr,colCtr);
      }
    }
  }
}

void scanColumn(int colNum)
{
  digitalWrite(latchPin, LOW);
  
  if(0 <= colNum && colNum <= 7)
  {
    shiftOut(dataPin, clockPin, MSBFIRST, bits[colNum]); //left sr
  }
  else
  {
    shiftOut(dataPin, clockPin, MSBFIRST, bits[colNum-8]); //right sr
  }
  
  digitalWrite(latchPin, HIGH);
}

void noteOn(int row, int col)
{
  Serial.write(NOTE_ON_CMD);
  Serial.write(keyToMidiMap[row][col]);
  Serial.write(NOTE_VELOCITY);
}

void noteOff(int row, int col)
{
  Serial.write(NOTE_OFF_CMD);
  Serial.write(keyToMidiMap[row][col]);
  Serial.write(NOTE_VELOCITY);
}

i have the same results as before, with the same wrong notes.

I've tried to make a different sketch, using all the arduino inputs, but i don't have any example to follow for make it.

my idea was:
for row 1 HIGH, and col 1 HIGH, play note midi 36... and so on

#include <MIDI.h>
#include <midi_Defs.h>
#include <midi_Message.h>
#include <midi_Namespace.h>
#include <midi_Settings.h>

MIDI_CREATE_DEFAULT_INSTANCE();

void setup()
{
  //  Set MIDI baud rate:
  Serial.begin(31250);
  
  //initialize MIDI
  MIDI.begin();
  
 pinMode(2, INPUT);   
 pinMode(3, INPUT);  
 pinMode(4, INPUT);   
 pinMode(5, INPUT);  
 pinMode(6, INPUT);  
 pinMode(7, INPUT);   
 pinMode(8, INPUT);  
 pinMode(9, INPUT);   
 pinMode(10, INPUT);  
 pinMode(14, INPUT);  
 pinMode(15, INPUT);   
 pinMode(16, INPUT);  
 pinMode(17, INPUT);   
 pinMode(18, INPUT);  
 pinMode(19, INPUT); 
}
 

void loop() 
{
    //   9 Rows connected on digital pins 2 to 10
    //   6 Cols connected on digital pins 14 to 19 - A0 to A5
  int row1 = digitalRead(2);
  int row2 = digitalRead(3);
  int row3 = digitalRead(4);
  int row4 = digitalRead(5);
  int row5 = digitalRead(6);
  int row6 = digitalRead(7);
  int row7 = digitalRead(8);
  int row8 = digitalRead(9);
  int row9 = digitalRead(10);
  int col1 = digitalRead(14);
  int col2 = digitalRead(15);
  int col3 = digitalRead(16);
  int col4 = digitalRead(17);
  int col5 = digitalRead(18);
  int col6 = digitalRead(19);
  
  {
    
    if(row1==HIGH and col1==HIGH)
    {
    MIDI.sendNoteOn(36,127,1); // note number, velocity, midi channel
    }
  }
}


void noteOn(int cmd, int pitch, int velocity) 
{
  Serial.write(cmd);
  Serial.write(pitch);
  Serial.write(velocity);
}

this is my attempt, but i cant' compile it

EDIT just looking through your code. Please remove this line:-

      keyToMidiMap[rowCtr][colCtr] = note;

i have the same results as before, with the same wrong notes.

How did you work out your lookup table. The numbers look right but the order they are in do not.

So time to do some debugging.
You need to forget the MIDI stuff and see what note you get with what key.
Change :-
#define SERIAL_RATE 31250
to
#define SERIAL_RATE 9600

Change your note on and off functions to:-

void noteOn(int row, int col)
{
  Serial.print("note on number ");
  Serial.println(keyToMidiMap[row][col]);
}

void noteOff(int row, int col)
{
  Serial.print("Note off number ");
  Serial.println(keyToMidiMap[row][col]);
}

Now remove all MIDI connections and open the serial monitor in the Arduino IDE and see what note number you get with each key. Use that information to adjust the numbers in your look up table.

I've tried to make a different sketch, using all the arduino inputs,

You can not drive a matrix with all inputs.

my idea was:
for row 1 HIGH, and col 1 HIGH, play note midi 36... and so on

Not only an appallingly bad idea but it will not work either. This is because you are not scanning the matrix.

As you continue to refuse to post the schematic of what you have built then help can only be limited.

You might find these two application notes helpful, they describes how a scanning keyboard matrix works:-

01081a.pdf (479 KB)

doc2532.pdf (105 KB)

Grumpy_Mike:
EDIT just looking through your code. Please remove this line:-

      keyToMidiMap[rowCtr][colCtr] = note;

How did you work out your lookup table. The numbers look right but the order they are in do not.

So time to do some debugging.
You need to forget the MIDI stuff and see what note you get with what key.
Change :-
#define SERIAL_RATE 31250
to
#define SERIAL_RATE 9600

Change your note on and off functions to:-

void noteOn(int row, int col)

{
  Serial.print("note on number ");
  Serial.println(keyToMidiMap[row][col]);
}

void noteOff(int row, int col)
{
  Serial.print("Note off number ");
  Serial.println(keyToMidiMap[row][col]);
}




Now remove all MIDI connections and open the serial monitor in the Arduino IDE and see what note number you get with each key. Use that information to adjust the numbers in your look up table.
You can not drive a matrix with all inputs.
Not only an appallingly bad idea but it will not work either. This is because you are not scanning the matrix.

As you continue to refuse to post the schematic of what you have built then help can only be limited.

thank you for your gentle help mate!

i have done the debugging (pressing every piano key one by one, from left to right) and i have obtained this result:

note on number 36
Note off number 36
note on number 37
Note off number 37
note on number 38
Note off number 38
note on number 39
Note off number 39
note on number 40
Note off number 40
note on number 41
Note off number 41
note on number 42
Note off number 42
note on number 43
Note off number 43
note on number 44
Note off number 44
note on number 45
Note off number 45
note on number 46
Note off number 46
note on number 47
Note off number 47
note on number 48
Note off number 48
note on number 49
Note off number 49
note on number 50
Note off number 50
note on number 51
Note off number 51
note on number 52
Note off number 52
note on number 53
Note off number 53
note on number 54
Note off number 54
note on number 55
Note off number 55
note on number 56
Note off number 56
note on number 57
Note off number 57
note on number 58
Note off number 58
note on number 59
Note off number 59
note on number 60
Note off number 60
note on number 61
Note off number 61
note on number 62
Note off number 62
note on number 63
Note off number 63
note on number 64
Note off number 64
note on number 65
Note off number 65
note on number 66
Note off number 66
note on number 67
Note off number 67
note on number 68
Note off number 68
note on number 69
Note off number 69
note on number 70
Note off number 70
note on number 71
Note off number 71
note on number 72
Note off number 72
note on number 73
Note off number 73
note on number 74
Note off number 74
note on number 75
Note off number 75
note on number 76
Note off number 76
note on number 77
Note off number 77
note on number 78
Note off number 78
note on number 79
Note off number 79
note on number 80
Note off number 80
note on number 81
Note off number 81
note on number 82
Note off number 82
note on number 83
Note off number 83
note on number 84
Note off number 84

and this is correct!

i don't know why, i have wrong midi outputs!

EDIT:
i have tried removing only this line: keyToMidiMap[rowCtr][colCtr] = note;

and finally IT WORKSS!!!

thank you very much grumpy_mike! you are a genius!

Can you please share your sketch and wiring schematic? I'd love to make this.

yess, Please share the project, I'm having issues with the shift registers on a 61 keys keyboard, 2x 32 sensitive notes 8x8 matrix. I'm a newbie too.