[Solved] Electric piano key matrix connections problem

Hi!
I am trying to turn an electric piano (jWIN MK-6140) to a MIDI piano using the Arduino Nano.
I already modified an old electric piano (Casio PT-100) to a MIDI piano using the Arduino Nano but I had to reverse all the diodes on the keys circuit.
I am trying to modify this new electric piano without removing the diodes and reversing them.

There are 61 keys on the jWIN piano and the keys circuit has 2x8 solder joints.
I concluded with the help of a multi-meter that the keys are an 8x8 key matrix.
I have removed the wires from the original controller and connected them to some headers so it would be easier connecting them to a breadboard.

I connected the 8 column wires to wires D5-D12 and the 8 row wires to A0-A7.

I tried using this code:

const byte ROWS = 8; //eight rows
const byte COLS = 8; //eight columns

char keys[COLS][ROWS] = {
  {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'},
  {'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P'},
  {'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X'},
  {'Y', 'Z', '0', '1', '2', '3', '4', '5'},
  {'6', '7', '8', '9', 'a', 'b', 'c', 'd'},
  {'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l'},
  {'m', 'n', 'o', 'p', 'q', 'r', 's', 't'},
  {'u', 'v', 'w', 'x', 'y', 'z', '!', '@'}
};
byte rowPins[ROWS] = {A0, A1, A2, A3, A4, A5, A6, A7};
byte colPins[COLS] = {5, 6, 7, 8, 9, 10, 11, 12};

void setup() {
  Serial.begin(9600);
  pinMode(rowPins, INPUT_PULLUP);
  pinMode(colPins, OUTPUT);
  digitalWrite(colPins, HIGH);
}

void loop() {
  for (int i = 0; i < COLS; i++) {
    digitalWrite(colPins[i], LOW);
    delay(10);
    for (int y = 0; y < ROWS; y++) {
      if (digitalRead(rowPins[y]) == LOW) {
        Serial.println(keys[y][i]);
      }
    }
    digitalWrite(colPins[i], HIGH);
  }
}

but the serial monitor was just filling with random letters from the keys array.
Also, I tried changing the rowPins with colPins to see if I reversed the pins but the output is still the same.
I tried using the keypad library code:

/* @file CustomKeypad.pde
  || @version 1.0
  || @author Alexander Brevig
  || @contact alexanderbrevig@gmail.com
  ||
  || @description
  || | Demonstrates changing the keypad size and key values.
  || #
*/
#include <Keypad.h>

const byte ROWS = 8; //eight rows
const byte COLS = 8; //eight columns

char hexaKeys[COLS][ROWS] = {
  {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'},
  {'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P'},
  {'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X'},
  {'Y', 'Z', '0', '1', '2', '3', '4', '5'},
  {'6', '7', '8', '9', 'a', 'b', 'c', 'd'},
  {'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l'},
  {'m', 'n', 'o', 'p', 'q', 'r', 's', 't'},
  {'u', 'v', 'w', 'x', 'y', 'z', '!', '@'}
};
byte colPins[ROWS] = {5, 6, 7, 8, 9, 10, 11, 12}; //connect to the row pinouts of the keypad
byte rowPins[COLS] = {A0, A1, A2, A3, A4, A5, A6, A7}; //connect to the column pinouts of the keypad

//initialize an instance of class NewKeypad
Keypad customKeypad = Keypad( makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS);

void setup() {
  Serial.begin(9600);
}

void loop() {
  char customKey = customKeypad.getKey();

  if (customKey) {
    Serial.println(customKey);
  }
}

The serial monitor first shows lowercase 'm'
but I got no output when pressing any key.
I tried reversing the connections but this time I got nothing on startup and nothing when pressing any key.
I am very frustrated with this I have been trying to fix this for 4 days with no luck.
Do I have to reverse all diodes with this? Or is it another problem?
I have tried this with two different Arduino Nanos and even tried using external pull-up/down resistors but no improvement.

Sorry if this was long but I tried giving you as much details as possible.

Any help is appreciated. Thanks.

PS: The keys circuit says MT MK-2085 if that helps somehow.

I guess that you missed to add the required diodes to the matrix.

Which way the diodes should point is a matter of how you run the key matrix.

My way sets all the pins to INPUT except for one row pin set INPUT_PULLUP and one column pin set OUTPUT LOW. The sensing and power is done on the row pin, the diodes should all block flow from column wires to row wires.

Another way sets the row pin OUTPUT HIGH and sets the column pin INPUT LOW and the diodes point the same way.

What would happen if I connect the row pins to the column wires and column pins to row wires? Diodes would be wrong way.

Perhaps some time with a meter could tell you more about your matrix? Is there any proprietary our-stuff-only-works-with-our-stuff twists in the keyboard?

DrDiettrich:
I guess that you missed to add the required diodes to the matrix.

The keys circuit already has diodes, I just want to see if it is really necessary to reverse their direction.

GoForSmoke:
My way sets all the pins to INPUT except for one row pin set INPUT_PULLUP and one column pin set OUTPUT LOW. The sensing and power is done on the row pin, the diodes should all block flow from column wires to row wires.

I just tried on only one column and I used analogRead to see if there is any issue, I found that there are 3 broken solder joints sigh.
Your way actually worked on that one column (!!!) I am going to try fixing the loose connections and update you guys if it works with all 8 columns

GoForSmoke:
What would happen if I connect the row pins to the column wires and column pins to row wires? Diodes would be wrong way.

That explains a LOT, thanks for the information.

GoForSmoke:
Perhaps some time with a meter could tell you more about your matrix? Is there any proprietary our-stuff-only-works-with-our-stuff twists in the keyboard?

I used the meter some more time and was able to figure out the correct way to connect the wires.
I don't think it has some manufacturer specific twists because it is cheap/ly made

The you're on the way.

An Arduino IO pin set to INPUT and not read will appear unconnected to the circuit, not LOW or HIGH.

This is my last matrix sketch that .. I don't have a matrix wired to test but did do my best with jumpers.

The debounce here is quick, 7 timed reads the same after 1 different = stable pin. The between reads time is 50 micros but any one button gets read after the rest so 50us x # of buttons, 7 of those in a row to call the button stable, in this 2x2 means 1400us debounce which may be a little quick but -- when I read buttons fast to see bounce times (recorded an array of reads and printed after collecting) I never got bounces more than 1000us apart so I used 2000us for my debounce interval before I had this bright idea of using bits to track button read history and read the button(s) less often than I had been, about 67K reads/sec in the lighter examples.
Note the Serial baud rate. 115200 will clear the output buffer > 10x as fast as 9600. If/when the output buffer fills up your fast code will bog until the buffer is no longer full.

// add-a-sketch_matrix_buttons 2018 by GoForSmoke @ Arduino.cc Forum
// Free for use, May 9/2018 by GFS. Compiled on Arduino IDE 1.6.9.
/*  Button Debounce Example

  This example requires a diode per button in the row&column matrix.
  Each row and column pin has its own wire. Where they cross a button and diode
  connect in series allowing current to flow from the row wire to th column wire
  only. The diode is to allow multiple buttons pressed at once detected correctly.

  OTOH for this test, jumper a row pin (2 or 3) to a column pin (4 or 5) while
  watching serial monitor.

  Yes I'm using a 16 bit micros timer to time fractions of millis as micros.
  The button reader only reads 1 button per call so as to not block void loop().
  Each button has a history byte that holds the last 8 reads with 256 possible
  states but only 4 of them being significant.
  0 is the button held down
  255 is the button left up
  127 is the buton transitioning from up to down, button just released.
  128 is the button transititioning from down to up, button just pressed.
  everything else is to be ignored as bounce.

  For multiple buttons the between-reads time is reduced and each pin is read in
  turn.
*/

// matrix_buttons vars --- 2D matrix
const byte rows = 2; // test with 4 buttons or jumper
const byte cols = 2;
byte rowIdx, colIdx = 255;
byte buttonRowPin[ rows ] = { 6, 7 };
byte buttonColPin[ cols ] = { 4, 5 };
byte buttonHistory[ rows ][ cols ];
word markButtonTime;        // 16-bit micros timers
const word waitButtonTime = 50; // micros, 50 us
// type word as micros can time across 65.535 millis before rollover, can be a few late


void matrixButtonsTask()
{
  if ( word( micros()) - markButtonTime >= waitButtonTime ) // read occaisoinally
  {
    // iterate the row and column indexes
    if ( ++colIdx >= cols ) // ++rowIdx pre-increments rowIdx before comparing to rows
    {
      colIdx = 0;
      if ( ++rowIdx >= rows ) // ++rowIdx pre-increments rowIdx before comparing to rows
      {
        rowIdx = 0;
      }
    }

    pinMode( buttonRowPin[ rowIdx ], INPUT_PULLUP ); // supplies weak 5V
    pinMode( buttonColPin[ colIdx ], OUTPUT ); // is LOW, grounds pressed buttons

    buttonHistory[ rowIdx ][ colIdx ] <<= 1; // shift history bits up 1 for the new read
    buttonHistory[ rowIdx ][ colIdx ] += digitalRead( buttonRowPin[ rowIdx ] ); // read history streams through buttonHistory
    markButtonTime = micros(); // gets the low 16 bits of micros(), time to 60 ms + margin

    pinMode( buttonRowPin[ rowIdx ], INPUT );
    pinMode( buttonColPin[ colIdx ], INPUT ); // was OUTPUT LOW
  }
}


void setup()
{
  Serial.begin( 115200 );
  Serial.println( F( "\n\n\n  Button Matrix Example, free by GoForSmoke\n" ));

  for ( byte i = 0; i < rows; i++ )
  {
    pinMode( buttonRowPin[ i ], INPUT ); // mode INPUT pin is electrically neutral
    for ( byte j = 0; j < cols; j++ )
    {
      if ( i == 0 ) // only set the column pins once
      {
        pinMode( buttonColPin[ j ], INPUT ); // mode INPUT pin is electrically neutral
      }

      buttonHistory[ i ][ j ] = 255; // all buttons start UP
    }
  }
};


void loop()
{
  matrixButtonsTask();
  /*
    0 is the button held down
    255 is the button left up
    127 is the buton changing from up to down, button just released.
    128 is the button changing from down to up, button just pressed.
    everything else is to be ignored as bounce.
  */
  static byte row, col;

  switch ( buttonHistory[ row ][ col ] )
  {
    case 128 : // pin is HIGH in bit 7, LOW for 7 reads, up to down detected
      buttonHistory[ row ][ col ] = 0; // change detected, make it into no change now
      Serial.print( F( "button " ));
      Serial.print( rowIdx );
      Serial.print( F( ", " ));
      Serial.print( colIdx );
      Serial.print( F( "  press detected     " ));
      Serial.println( millis());
      break;

    case 127 : // pin is LOW in bit 7, HIGH for 7 reads, down to up detected
      buttonHistory[ row ][ col ] = 255; // change detected, make it into no change now
      Serial.print( F( "button " ));
      Serial.print( rowIdx );
      Serial.print( F( ", " ));
      Serial.print( colIdx );
      Serial.print( F( "  release detected   " ));
      Serial.println( millis());
      break;
  }

  if ( ++col >= cols )
  {
    col = 0;;

    if ( ++row >= rows )
    {
      row = 0;;
    }
  }
}

GoForSmoke:
An Arduino IO pin set to INPUT and not read will appear unconnected to the circuit, not LOW or HIGH.

That is gonna save me a lot of headaches in the future. Thanks :slight_smile:

GoForSmoke:
The debounce here is quick, 7 timed reads the same after 1 different = stable pin. The between reads time is 50 micros but any one button gets read after the rest so 50us x # of buttons, 7 of those in a row to call the button stable, in this 2x2 means 1400us debounce which may be a little quick but -- when I read buttons fast to see bounce times (recorded an array of reads and printed after collecting) I never got bounces more than 1000us apart so I used 2000us for my debounce interval before I had this bright idea of using bits to track button read history and read the button(s) less often than I had been, about 67K reads/sec in the lighter examples.
Note the Serial baud rate. 115200 will clear the output buffer > 10x as fast as 9600. If/when the output buffer fills up your fast code will bog until the buffer is no longer full.

I am going to be honest with you, I didn't really understand much of this (Mainly because I am kinda average in Arduino and electronics but is what you're saying that I need to account for bounces in my code?
Also what is "Output buffer"?

  • I tried using your code and modifying it a little bit but:
    1- I didn't really understand some pieces of code.
    2- It read the wrong buttons being pressed (it read at when I pressed a button, the button it found was wrong).

So I tried tinkering around with a simpler piece of code and finally got it to work!
The Serial Plotter showed good results using this code.
Here is the code:

const byte ROWS = 8; //eight rows
const byte COLS = 8; //eight columns

/*char keys[COLS][ROWS] = {
  {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'},
  {'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P'},
  {'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X'},
  {'Y', 'Z', '0', '1', '2', '3', '4', '5'},
  {'6', '7', '8', '9', 'a', 'b', 'c', 'd'},
  {'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l'},
  {'m', 'n', 'o', 'p', 'q', 'r', 's', 't'},
  {'u', 'v', 'w', 'x', 'y', 'z', '!', '@'}
};*/
byte rowPins[ROWS] = {A0, A1, A2, A3, A4, A5, A6, A7};
byte colPins[COLS] = {12, 11, 10, 9, 8, 7, 6, 5};

void setup() {
  Serial.begin(115200);
  pinMode(rowPins, INPUT);
  pinMode(colPins, INPUT);
}

void loop() {
  for (int y = 0; y < COLS; y++) {
    pinMode(colPins[y], OUTPUT);
    digitalWrite(colPins[y], LOW);
    for (int i = 0; i < ROWS; i++) {
      pinMode(rowPins[i], INPUT_PULLUP);
      Serial.print(analogRead(rowPins[i]));
      Serial.print(", ");
      pinMode(rowPins[i], INPUT);
    }
    Serial.println();
    pinMode(colPins[y], INPUT);
  }
}

I will also attach how the serial plotter looks after adding a 10k resistor on A7 to 5V and A6 to 5V
I think the internal pull-up resistors are broken for those pins?

The spikes at the bottom of the serial plotter are just the code checking every column very fast so I will just need to add a pressed buttons array list and check if the button is already there so it wouldn't spam the serial.

Thank you so much for your help :slight_smile:

Guys i was working on this, i found you code

#include <Keypad.h>

const byte ROWS = 8; //eight rows
const byte COLS = 8; //eight columns

char hexaKeys[COLS][ROWS] = {
{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'},
{'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P'},
{'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X'},
{'Y', 'Z', '0', '1', '2', '3', '4', '5'},
{'6', '7', '8', '9', 'a', 'b', 'c', 'd'},
{'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l'},
{'m', 'n', 'o', 'p', 'q', 'r', 's', 't'},
{'u', 'v', 'w', 'x', 'y', 'z', '!', '@'}
};
byte colPins[ROWS] = {5, 6, 7, 8, 9, 10, 11, 12}; //connect to the row pinouts of the keypad
byte rowPins[COLS] = {A0, A1, A2, A3, A4, A5, A6, A7}; //connect to the column pinouts of the keypad

//initialize an instance of class NewKeypad
Keypad customKeypad = Keypad( makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS);

void setup() {
Serial.begin(9600);
}

void loop() {
char customKey = customKeypad.getKey();

if (customKey) {
Serial.println(customKey);
}
}

but i have 6 row and 8 cols: 6 in digital and 8 in analog,also i want to add release and MIDI USB code, i am trying to modify code but i can't. please can you guys help me out. thanks in advance

MG4mes:
I am going to be honest with you, I didn't really understand much of this (Mainly because I am kinda average in Arduino and electronics but is what you're saying that I need to account for bounces in my code?

You need to wait for the bouncing to stop or you will read each press as many presses as your code is fast, same with button release.

This all happens in 2 to 20 milliseconds depending on the button/switch used, it's usual to deal with in code. 1 ms is a long time to Arduino which takes getting used to by beginners, your code can be checking the pin 50 or more times each millisecond and still -do- a little processing every one of those times. Even tiny steps add up at 50000+/second.

So you read the pin and the button press changes the pin state and then the bouncing begins, the time is saved and the code looks for (now - start >= long enough to have quit bouncing) to declare a clean press done. If the pin changes before that, the start time becomes then --- wait for stable begins again which might be 20 micros since the last start time.

Also what is "Output buffer"?

Serial has 64 bytes of RAM to hold text/data to print/write. When there is bytes to send they are not sent in an instant though to human senses they are what really happens is the first to be sent is put in a serial port register then sent 1 bit at a time by making the TX pin HIGH or LOW for 0 or 1 in that order. And when that is finished the next byte is sent and the next until the text in that 64 bytes of RAM (the Serial Output Buffer) is sent, and everything else the code put in the buffer while the previous print was being sent is also sent.

Serial sending and receiving are automatic. Your code gets chars from the Serial Input Buffer and puts chars in the output buffer. Serial prints while (in between commands of) your sketch as it runs since Arduino code runs many times faster than serial. BIG exception is when the output buffer gets full and then your code becomes as slow as serial :(.

Running serial at higher rate empties the buffer quicker, less chance to overflow the buffer.

This is how to set a pin to INPUT_PULLUP.

void setup() {
  Serial.begin(115200);
  pinMode(rowPins, INPUT_PULLUP);
  pinMode(colPins, INPUT_PULLUP);
}