I had some curiosity and interest with MIDI devices for some years and this small project came up after my Yamaha Portasound PSS-190 had a couple of burnt traces and the synthesizer IC was gone.
This particular instrument isn’t really high end, so there were no velocity sensing (velocity matters, try pressing a key on a piano slower and faster). And the keys were wired to form a matrix – thus known as matrix keyboards. This means each wont connect directly to the microprocessor/ synthesizer IC but the wiring arrangement is like a matrix/ table.

This means you only need 8 wires to access 16 keys. In case of the Yamaha keyboard I tried to fix, it had 7 + 6 wires and served 30+ keys. However the drawback is, you cannot all the keys at the same time (real time – I’m talking microseconds), the reason is that these needs “scanning”, enabling one row and read the values and so on. But this can be implemented to be fast enough for a human. For example, the standard PC keyboard is almost always a matrix keyboard with the internals scanning for key presses at least hundred times per second.
Back to the topic, since I couldn’t source the original synth IC, I decided to build a MIDI synth and install inside the keyboard! I had a STM32F4 Discovery board with me, so i went for the “Goom” (http://www.quinapalus.com/goom.html) ported to MidiBox (http://ucapps.de). Midibox is a platform to build various types of MIDI instruments. Will discuss about this in a later post.
With the synth running, what I needed was to get MIDI signals upon key presses of the keyboard. Therefore I did some googling, found out that MIDI is pretty much serial communication at 31250 baud rate. So to test, I used Arduino Mega.
// Pin Definitions
// Rows are connected to
const byte Mask = 255;
double oldtime;
uint8_t keyToMidiMap[32];
boolean keyPressed[50];
int command = 0x90;
int noteVelocity = 60;
//#define DEBUG
// use prepared bit vectors instead of shifting bit left everytime
byte bits[] = {
B00000001, B00000010, B00000100, B00001000, B00010000, B00100000, B01000000, B10000000 };
byte colVals[] = {
255,255, 255, 255, 255, 255, 255, 255 };
byte bits1[] = {
B11111110, B11111101, B11111011, B11110111, B11101111, B11011111, B10111111, B01111111 };
void scanColumn(int value) {
PORTA=value;
}
void setup() {
DDRA=B11111111;//output
DDRC=B00000000;//input
// Enable the pullups
PORTC = PORTC | Mask;
for(int i=0; i<50;i++){
keyPressed[i]=false;
}
Serial2.begin(31250);
Serial.begin(115200);
delay(500);
//noteOn(176,124,0);
for (int note = 0x1E; note < 0x5A; note ++) {
//Note on channel 1 (0x90), some note value (note), middle velocity (0x45):
noteOn(0x90, note, 0x45);
delay(100);
//Note on channel 1 (0x90), some note value (note), silent velocity (0x00):
noteOn(0x90, note, 0x00);
delay(100);
}
delay(100);
// noteOn(176,124,0);
}
void loop() {
for (int col = 0; col < 7; col++) {
// shift scan matrix to following column
scanColumn(bits1[col]); //enable all except one.
delayMicroseconds(3);
byte rowVal1 = PINC & Mask;
byte rowVal= ~rowVal1;//inverted rowVal => key press = 1
if(colVals[col] == rowVal1){
continue;
}
else{
colVals[col] = rowVal1;
}
for (int row = 0; row < 6; row++) {
if(col==0 &&row>0){
break;
}
int index =row + ((int)col *6) ;
int note= index + 48;
byte k =(bits[row] & rowVal);
if(k>0 && keyPressed[index]==false){ //and op. on each bit of rowval and determine note press.
keyPressed[index]=true;
noteOn(command,note,noteVelocity);
}
if(k==0 && keyPressed[index]==true){
keyPressed[index]=false;
noteOn(command,note,0);
}
}
}
}
void noteOn(int cmd, int pitch, int velocity) {
Serial2.write(cmd);
Serial2.write(pitch);
Serial2.write(velocity);
/**
* DEBUG stuff
*/
/*
Serial.print("Note: ");
Serial.print(pitch,DEC);
Serial.print(" Velocity :");
Serial.print(velocity,DEC);
Serial.println();
*/
}
First I setup the basics, enable internal pullups, then set the output port (PORT A) to a given arrangement – one pin turned OFF, others turned ON. The reason to do this than other way around is due to the usage of pullups instead of pull down resistors.
Then I read the input at PORT C. now this is where the rows are connected, so if a key is pressed, the corresponding pin would go LOW. For ease of processing I inverted this reading and I also keep track of “change of state” which means the code will proceed if an only if the previous state was changed.
Then depending whether it was a press down or releasing a key, the appropriate MIDI command is sent. – 0x91 means channel 1, note ON. Pitch is mapped as “48” = C3. (refer https://newt.phys.unsw.edu.au/jw/notes.html for detailed mapping information).
With the code tested, all that remains is to wire it up to the keyboard and test!