MIDI keyboard using leonardo keys overlaping

Hello world,

I'm new to arduino and i recently started this project where i took an old casio keyboard and made it a midi controller using arduino Leonardo. It uses an 8X8 matrix and its connected to a 74HC595 shift register, and the project seems to be working fine except for one thing, the key connected to the 2nd digital pin overlaps with the next note connected to that same pin.

I had the same issue with other keys, however i fixed it by assigning the right pin for each (i did'nt notice i assigned the same pin to some of them), but in this case i dont know what am I doing wrong.`

I can show you my schematics and the code im using.

Thanks in advance.

#define NUM_ROWS 8
#define NUM_COLS 8

#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; //2
const int row2Pin = 3; //3
const int row3Pin = 4; //4
const int row4Pin = 5; //5
const int row5Pin = 6; //6
const int row6Pin = 7; //7
const int row7Pin = 8; //8
const int row8Pin = 9; //9

// 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[] =
{
  B00000001,
  B00000010,
  B00000100,
  B00001000,
  B00010000,
  B00100000,
  B01000000,
  B10000000
};

void setup()
{
  int note = 24;

  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);

  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);

    // 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 <= 8)
  {
    shiftOut(dataPin, clockPin, MSBFIRST, B00000000); //right sr
    shiftOut(dataPin, clockPin, MSBFIRST, bits[colNum]); //left sr
  }
  else
  {
    shiftOut(dataPin, clockPin, MSBFIRST, bits[colNum-8]); //right sr
    shiftOut(dataPin, clockPin, MSBFIRST, B00000000); //left sr
  }
  digitalWrite(latchPin, HIGH);
}

void noteOn(int row, int col)
{
  Serial.write(NOTE_ON_CMD);
  Serial.write(keyToMidiMap[row][col]);
  Serial.write(NOTE_VELOCITY);
  noteOnMIDI(0, keyToMidiMap[row][col], NOTE_VELOCITY);   // Nueva Linea Leonardo
  MidiUSB.flush();// Nueva Linea Leonardo



}

void noteOff(int row, int col)
{
  noteOffMIDI(0, keyToMidiMap[row][col], NOTE_VELOCITY);  // Channel 0, middle C, normal velocity
  MidiUSB.flush();
 
  
}

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

void noteOffMIDI(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);
}

`

Please do that. Hopefully it will make more sense than the wiring diagram you posted.

Here it is, hope it makes more sense


This is what it does by the way, im pressing just one note and it register as if im pressing the next note but 8 places forward.

Does writing to Serial interfere with the Midi, since both are using the same Serial over USB?

scanColumn() looks weird, the if clause handles columns 0..8 (9 columns), since you don't seem to call it with column number equal to 8, the else clause looks like it never gets used.

But that's probably not an issue, just a distraction.

If one key exhibits this behaviour, then it points to a wiring error, or a broken something.

Keeping careful track of your experiments, try to see if the problem ones around or changes as you

  • exchange two column lines
  • exchange two row lines

Also, probably not a problem, but I don't see exactly what handles contact bounce. Try temporarily placing a delay in you loop(), a simple way to globally eliminate the problem, however inelegant and ultimately unsatisfactory. Just for now

// very last line in your loop function

  delay(50);    // poor man's debounce
}

Am I right to think that MIDI is hogging up the serial port? If so, it deprives you of the cheapest easiest tool, printing to the serial monitor to verify the values of key variables and "see" the flow through the code.

TBH it all looks good, so I am on the wiring/broken idea first if Imdo get to the lab and have time to build it.

BTW, something even as simple as this would benefit from a dev path that would first have gotten the keyboard matrix working and just printing, and adding the MIDI later. In fact I have done some helping by providing my own mini-MIDI functions, or ripping them out of the true sketch.

I call your attention to the wokwi simulator, which has all those parts, where you could build this, at least the MIDI-free version. It's an excellent way to eliminate real-world problems - if it won't run good in simulation, there's no chance it will work IRL. Conversely, if it does work in the wokwi, you will have energy to focus on what makes real life different.

I'll check L8R to see if you've learned anything from the debugging suggestions above.

a7

Oh dear, good eye and good point. It looks like that may have been added to gather information, thus after the problem has manifested, but…

a7

Thank you David and Alto,

I'm really new to programming and its kinda hard for me to understand all of the terminology, however I'm willing to investigate further in order to fully understand.

First of all, do you mean the wiring from the 14th pin of the 74hc595 to the Arduino? Cause in every other schematic i've seen it seems to work fine as it is.

And yeah, im also inclined by the bad wiring idea, however, i´ve tried to change wires and nothing seems to happen or change.

Also thank you for the wokwi simulator, I'll take a look into it.

Greetings from Mexico.

#include <MIDI.h>
MIDI_CREATE_DEFAULT_INSTANCE();
#define NOTE_VELOCITY 127

const byte rowPin = 2; //2// Row input pins
const byte rowCount = 8; //8
const byte colCount = 8; //8

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

byte keyPressed[colCount ];
uint8_t keyToMidiMap[rowCount][colCount ];

void setup() {
  //Serial.begin(31250);
  MIDI.begin(1); 

  // setup pins output/input mode
  pinMode(dataPin,  OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(latchPin, OUTPUT);
  uint8_t note = 24;

  for (uint8_t colCtr = 0; colCtr < colCount; ++colCtr)  {
    for (uint8_t rowCtr = 0; rowCtr < rowCount; ++rowCtr)    {
      keyToMidiMap[rowCtr][colCtr] = note;
      note++;
    }
  }
}

void loop() {
  for (uint8_t c = 0; c < colCount; c++)  {
    SetColumn(c);
    byte rowValue = 0;

    //get row values at this column
    for (int p = 0; p < rowCount; p++)rowValue |= (digitalRead(rowPin + p) << p);

    // process keys
    for (int k = 0; k < rowCount; k++)    {
      if ((rowValue & (1 << k)) > 0 && (keyPressed[c] & (1 << k) )== 0)note(k, c, NOTE_VELOCITY);
      if ((rowValue & (1 << k)) == 0 && (keyPressed[c] & (1 << k)) > 0)note(k, c, 0);
    }
    keyPressed[c] = rowValue;
  }
}

void SetColumn(byte colCur) {
  digitalWrite(latchPin, LOW);
  byte bits = 1 << colCur;
  shiftOut(dataPin, clockPin, MSBFIRST, bits); //left sr
  digitalWrite(latchPin, HIGH);
}

void note(byte row, byte col, byte velo) {
  MIDI.sendNoteOn(keyToMidiMap[row][col], velo, 1)
//  Serial.write(0x90);
//  Serial.write(keyToMidiMap[row][col]);
//  Serial.write(velo);
//  Serial.flush();
}

last update for native USB boards

to avoid ghosting use diodes in series with each switch/key

@smarq - conceptually the keys are in a matrix of rows and columns, despite the physical,layout of the keyboard.

With 8 rows and 8 columns, there are 64 switch points. Each switch (key) connects one row to one column.

Your rows are Arduino pins 2 to 9.

Your columns are the '595 output pins labeled Q0 to Q7, according to your diagram those are 15, 1, 2, 3, 4, 5, 6 and 7.

Find the row and column that is misbehaving and swap that row and any working row, similarly swap that column and any working column.

The other thing shou,d be clear, just put a delay at the end of the loop() function.

Where did you get this code or the inspiration for it? It is relatively sophisticated, chances are if you've taken it in its entirety is you, not the code.

a7

Another idea - neither depiction of your hardware shows the actual keyboard.

If you can really remove the keyboard, then using a jump wire between any row pin as isted above and any column pin listed above would be the same as hittong some key on the keyboard.

If this

happens consistently and is the only key that shows this flaw, find the row and column for that key and use the jumper to mimic what the keyboard should do.

Have you used the keyboard elsewhere and found it to be 100 percent?

a7

Thanks, however it doesn't work as expected

how is expected?

Thank you, I tried to switch up the cables and noticed that wherever i placed the cable which had the problem, it went up the next cable. however i dont really know what makes it do that thing

What is the model number of the Casio keyboard that you used the keyboard from?

When you press the problematic key, do you get a midi note for both the desired key and the key eight positions farther up, or just a single key that is eight positions off from the desired key?

That when i plug it into my hardware tester it doesnt recognise it as a midi device

what is it?

Both keys, the desired one and the one eight positions up, and on the last group of notes, it plays the desired key and the one on the beginning. I an using a Casio CTK-1300ES.

Im using this one Hardware Tester

do you know this ?
зображення