let's make an easy piano arduino interface!!!!!

so, i have an 88 weighted keyboard arranged in 8 x 11 array and i'm having trouble connecting it.
there are lot's of other project that seem to use shift registers o mux's, what i want to make is an easy to integrate, read and most importantly of all understandable code. there are lot's of people looking to modify their pianos and it's a really nice project. issue is every piano is arranged diferently.

ideally you would define your number of keys, rows and columns, and the code would do the rest.
so let's get at it

here is how the array is connected to the arduino:

int O1 = 52;
int O2 = 50;
int O3 = 48;
int O4 = 46;
int O5 = 44;
int O6 = 42;
int O7 = 40;
int O8 = 38;
int R1a = 36;
int R1b = 34;
int R2a = 32;
int R2b = 30;
int R3a = 28;
int R3b = 26;
int R4a = 24;
int R4b = 25;
int R5a = 27;
int R5b = 29;
int R6a = 31;
int R6b = 33;
int R7a = 35;
int R7b = 37;
int R8a = 39;
int R8b = 41;
int R9a = 43;
int R9b = 45;
int R10a = 47;
int R10b = 49;
int R11a = 51;
int R11b = 53;

so, basically what should happen is

  1. write O1 HIGH
  2. read each Ra and Rb
  3. if for example R3a is HIGH we initialize a timer that counts untill R3b is HIGH
  4. write O2 HIGH
    and so on untill you get back to O8 HIGH and repeat the cycle.

opinions? ideas? why do kids love the taste of cinnamon toast crunch?

taking shape

int O1 = 52;
int O2 = 50;
int O3 = 48;
int O4 = 46;
int O5 = 44;
int O6 = 42;
int O7 = 40;
int O8 = 38;
int R1a = 36;
int R1b = 34;
int R2a = 32;
int R2b = 30;
int R3a = 28;
int R3b = 26;
int R4a = 24;
int R4b = 25;
int R5a = 27;
int R5b = 29;
int R6a = 31;
int R6b = 33;
int R7a = 35;
int R7b = 37;
int R8a = 39;
int R8b = 41;
int R9a = 43;
int R9b = 45;
int R10a = 47;
int R10b = 49;
int R11a = 51;
int R11b = 53;

// The interval between the first and second switch while hitting keys with the
// highest velocity.
const float shortestInterval = 2000.0;

// Every time the interval between the first and second switch doubles, lower
// the midi velocity by this much:
const int velocityAttenuation = 20;

int numRow = 8;
int numCol = 22;

void setup() {
  Serial.begin(9600);
  pinMode(O1, OUTPUT);
  pinMode(O2, OUTPUT);
  pinMode(O3, OUTPUT);
  pinMode(O4, OUTPUT);
  pinMode(O5, OUTPUT);
  pinMode(O6, OUTPUT);
  pinMode(O7, OUTPUT);
  pinMode(O8, OUTPUT);
  pinMode(R1a, INPUT_PULLUP);
  pinMode(R1b, INPUT_PULLUP);
  pinMode(R2a, INPUT_PULLUP);
  pinMode(R2b, INPUT_PULLUP);
  pinMode(R3a, INPUT_PULLUP);
  pinMode(R3b, INPUT_PULLUP);
  pinMode(R4a, INPUT_PULLUP);
  pinMode(R4b, INPUT_PULLUP);
  pinMode(R5a, INPUT_PULLUP);
  pinMode(R5b, INPUT_PULLUP);
  pinMode(R6a, INPUT_PULLUP);
  pinMode(R6b, INPUT_PULLUP);
  pinMode(R7a, INPUT_PULLUP);
  pinMode(R7b, INPUT_PULLUP);
  pinMode(R8a, INPUT_PULLUP);
  pinMode(R8b, INPUT_PULLUP);
  pinMode(R9a, INPUT_PULLUP);
  pinMode(R9b, INPUT_PULLUP);
  pinMode(R10a, INPUT_PULLUP);
  pinMode(R10b, INPUT_PULLUP);
  pinMode(R11a, INPUT_PULLUP);
  pinMode(R11b, INPUT_PULLUP);
  delay(500);

}

void loop() {
  for (int row = 0; row <= numRow; row++) {
    for (int col = 0; col <= numCol; col+=2) {
        int key_index = row * col;
        bool switchA = digitalRead(col);
        bool switchB = digitalRead(col++);
        if (switchA){
          unsigned long tA = micros();
          if (switchB){
            unsigned long tB = micros();
            unsigned long ElapsedTime = tB - tA;
            int velocity = 127 - log(ElapsedTime/shortestInterval)/log(2) * velocityAttenuation;
            if (velocity < 0) {
              velocity = 0;
            }
          }
        }   
    }
    row = 0;
  }
}

Interesting project, Gary. I think I see where you're going. Some ideas and questions:

What are the rules around switch A and B? I assume A is closed when you first hit the key (or is it opened?) and B is closed when the key bottoms out. Does A remain closed until the key is released?

The listing will digitalRead() the same pin twice with col++. It will read 0, 0, 3, 3, 6, 6, ...

If we make that col+1, the listing reads pins 0 and 1, then 2 and 3, then 4 and 5... I don't think this is what you want either, since it disagrees with the list of pins earlier in the listing.

You need a layer of indirection here, like an array:

uint8_t start_pins[] = {36, 32, 28 ... };
uint8_t bottom_pins[] = {34, 30, 26 ... };

Then you can digitalRead(start_pins[col]);

Also, these arrays make the setup simpler - just a for loop.

Also need to energize or ground the row pin in the outer loop - same indirection concept.

Looking at the "if(switchA)" and "if(switchB) code, I think you want to detect transitions, not steady state of the switches. To detect transitions you need to store the state of the switch from the last scan and compare. The way it's written now, I think tA, tB and velocity keep getting updated while you're holding a key.

Maybe get this working with only one octave, only "A" switches. Then extend to "B", then to multi-octave. It's always easier to extend something that's working.

hey thanks for the answer!!!!!!!!
so the A switch is the first to be closed after that B is closed. it is not until the whole key is depresed that A is deactivated (maybe a "lift" effect could be added). from what i gather electric pianos initialize a timer when A is activated and interrupt when B is active. same principle i used. i will keep working at it and yes, you are right, it is better to start small.
i will update as soon as i can.

ok, im having some trouble here.
my console shows

1
1
1
1
1
1
1
1
terminado lince

this is the code what am i missing?

int column_pins[] = {52, 50, 48, 46, 44, 42, 40, 38};
int col = 8;
int row_pins[] = {36, 34};
/*/int O1 = 52;
int O2 = 50;
int O3 = 48;
int O4 = 46;
int O5 = 44;
int O6 = 42;
int O7 = 40;
int O8 = 38;
int R1a = 36;
int R1b = 34;
int R2a = 32;
int R2b = 30;
int R3a = 28;
int R3b = 26;
int R4a = 24;
int R4b = 25;
int R5a = 27;
int R5b = 29;
int R6a = 31;
int R6b = 33;
int R7a = 35;
int R7b = 37;
int R8a = 39;
int R8b = 41;
int R9a = 43;
int R9b = 45;
int R10a = 47;
int R10b = 49;
int R11a = 51;
int R11b = 53;
  pinMode(O1, OUTPUT);
  pinMode(O2, OUTPUT);
  pinMode(O3, OUTPUT);
  pinMode(O4, OUTPUT);
  pinMode(O5, OUTPUT);
  pinMode(O6, OUTPUT);
  pinMode(O7, OUTPUT);
  pinMode(O8, OUTPUT);
  pinMode(R1a, INPUT_PULLUP);
  pinMode(R1b, INPUT_PULLUP);
  pinMode(R2a, INPUT_PULLUP);
  pinMode(R2b, INPUT_PULLUP);
  pinMode(R3a, INPUT_PULLUP);
  pinMode(R3b, INPUT_PULLUP);
  pinMode(R4a, INPUT_PULLUP);
  pinMode(R4b, INPUT_PULLUP);
  pinMode(R5a, INPUT_PULLUP);
  pinMode(R5b, INPUT_PULLUP);
  pinMode(R6a, INPUT_PULLUP);
  pinMode(R6b, INPUT_PULLUP);
  pinMode(R7a, INPUT_PULLUP);
  pinMode(R7b, INPUT_PULLUP);
  pinMode(R8a, INPUT_PULLUP);
  pinMode(R8b, INPUT_PULLUP);
  pinMode(R9a, INPUT_PULLUP);
  pinMode(R9b, INPUT_PULLUP);
  pinMode(R10a, INPUT_PULLUP);
  pinMode(R10b, INPUT_PULLUP);
  pinMode(R11a, INPUT_PULLUP);
  pinMode(R11b, INPUT_PULLUP);
  delay(500);
*/


void setup() {
  Serial.begin(9600);
  for(int coln=0; coln<col; coln++)
   {
       pinMode(column_pins[coln], OUTPUT); // Set the mode to OUTPUT
   }
  

}

void loop() {
  for (int coln = 0; coln<col; coln++) {
      digitalWrite(column_pins[coln], HIGH);
      int estado = digitalRead(row_pins);
      Serial.println(estado);
      delay(500);   
      }  
  Serial.println("terminado lince");
}

Should definitely be using just arrays of pins, individual int declarations is not the way to go.
byte is plenty for a pin number:

byte column_pins[] = {52, 50, 48, 46, 44, 42, 40, 38};
byte row_start_pins[] = { 36, 32, 28, .... } ;
byte row_end_pins[] = { 34, 30, 26, ... } ;

void loop() {
  for (byte coln = 0; coln<sizeof(column_pins); coln++) {
    digitalWrite(column_pins[coln], HIGH);
    for (byte rown = 0 ; rown < sizeof (row_start_pins) ; rown++)  // need to scan rows somehow here
    {
       boolean estado = digitalRead(row_start_pins [rown]
       Serial.println(estado);
    }
    delay(500);    // why?
    digitalWrite (column_pins[coln], LOW) ; // mustn't leave them all high!
  } 
  Serial.println("terminado lince");
}

I think the scanning code is going to have to loop many times checking the time elapsed and
looking for the row_end_pins changing state.

ok so i'm trying to achieve right now is to get the row / key and state

byte column_pins[] = {52, 50, 48, 46, 44, 42, 40, 38};
byte row_a_pins[] = { 36, 32, 28, 24, 27, 31, 35, 39, 43, 47, 51} ;
byte row_b_pins[] = { 34, 30, 26, 25, 29, 33, 37, 41, 45, 49, 53} ;

void setup() {
  Serial.begin(115200);
  for(int coln=0; coln<sizeof(column_pins); coln++)
   {
       pinMode(column_pins[coln], OUTPUT); // Set the mode to OUTPUT
   }
}

void loop() {
  for (byte coln = 0; coln<sizeof(column_pins); coln++) {
    digitalWrite(column_pins[coln], HIGH);
    for (byte rown = 0 ; rown < sizeof (row_a_pins) ; rown++)  // need to scan rows somehow here
    {
        boolean state = digitalRead(row_a_pins [rown]);
        Serial.print(coln);
        Serial.print(" = ");
        Serial.print(rown);
        Serial.print(" = ");
        Serial.println(state);
        delay(500); 
        
    }
    digitalWrite (column_pins[coln], LOW) ; // mustn't leave them all high!
  } 
  Serial.println("terminado lince");
}

issue is even though i'm not pressing any keys i still get random keys on high state.
any ideas?

VincentGoodman:
Great job.Thanks for this.

are you working on a similar project?

updated code

byte column_pins[] = {52, 50, 48, 46, 44, 42, 40, 38};
byte row_a_pins[] = { 36, 32, 28, 24, 27, 31, 35, 39, 43, 47, 51} ;
byte row_b_pins[] = { 34, 30, 26, 25, 29, 33, 37, 41, 45, 49, 53} ;

void setup() {
  Serial.begin(115200);
  for(int coln=0; coln<sizeof(column_pins); coln++)
   {
       pinMode(column_pins[coln], OUTPUT); // Set the mode to OUTPUT
   }
   for(int rowNa=0; rowNa<sizeof(row_a_pins); rowNa++)
   {
       pinMode(row_a_pins[rowNa], INPUT_PULLUP); // Set the mode to INPUT
   }
   for(int rowNb=0; rowNb<sizeof(row_b_pins); rowNb++)
   {
       pinMode(row_b_pins[rowNb], INPUT_PULLUP); // Set the mode to INPUT
   }
}

void loop() {
  for (byte coln = 0; coln<sizeof(column_pins); coln++) {
    digitalWrite(column_pins[coln], LOW);
    for (byte rown = 0 ; rown < sizeof (row_a_pins) ; rown++)  
    {
        boolean state = digitalRead(row_a_pins [rown]);
        Serial.print(coln);
        Serial.print(" = ");
        Serial.print(rown);
        Serial.print(" = ");
        Serial.println(state);
        delay(500); //this is so i can see when a key is pressed ideally it wouldn't be here
        
    }
    digitalWrite (column_pins[coln], HIGH) ; // mustn't leave them all high!
  } 
  Serial.println("terminado lince");
}

is there any way i can have a double for loop?
like

 for(int rowNa=0; rowNa<sizeof(row_a_pins); rowNa++) && (int rowNb=0; rowNb<sizeof(row_b_pins); rowNb++)
   {
       pinMode(row_a_pins[rowNa], INPUT_PULLUP); // Set the mode to INPUT
   }

still no luck detecting transitions, does anyone have any ideas?

I'm trying to build a midi interface for a regular piano (I think that's what you're talking about here?) and so, like above, I divided the 88 keys into a matrix, (except I did 10x9 instead of 8x11), so I have 19 pins. The idea was to have a midi input on pin 0 (serial rx), and use the other 13 digital pins and 6 analog pins (acting as digital i/o) to control the matrix (I have an uno). When the arduino receives a byte (note #, on or off), it take that note number and pulls high the two pins for that note. Those two lead to an AND gate, which goes to a switching transistor to drive a solenoid.
My only problem is that I have almost no idea how to code anything (except in BASIC on an apple II, really). My experience with arduino is copying other people's projects, and that was several years ago, I haven't done anything for some time. All I really need to pay attention to in the midi data is note on and off, and note number (intensity doesn't really matter). If anyone could help me get something like this off the ground, it would be much appreciated.
Thanks!