Week 10: Keyboard Kover

← Back to main page

This week, I wanted to continue my piano project, officially started last week with servo and solenoid test boards. The eventual plan is to have 88 actuators over the keys of a piano (likely the grand piano in the MIT SAE library). Since this week’s assignment was to make something big with the CNC, I decided it was fitting to build a housing for these actuators to sit over the piano keys.

I began by measuring out the piano at the house, capturing all the details of its structure.

Next, I designed two 5-foot-long beams, each 3 inches tall, to maximize the "h" term in the \( \frac{bh^3}{12} \) formula. One beam is designated for white key actuators and the other for black key actuators. Here’s the design:

CNC design for keyboard cover

I cut the design on OSB wood using the CNC machine. The entire job took less than 10 minutes. Afterward, I used a bandsaw and file for some final touches.

Keyboard Kover Mid Cut Keyboard Kover Post Cut Keyboard Kover Assembled Keyboard Kover and Me

After assembling the pieces, I brought it to New Vassar's music room to test it on a piano. Here’s an image of the cover placed over the keys:

Keyboard Kover and Me

I glued the solenoid driver PCB and two solenoids (aligned with the F4 and Bb4 keys) onto the wooden cover. I programmed it to play "One-Note Samba." Spotify link to One-Note Samba.

Here’s the code I used:

                #define NUM_SOLENOIDS 5
                #define BPM 120
                
                int solenoidPins[NUM_SOLENOIDS] = {D0, D1, D2, D3, D4}; // solenoid GPIO pins
                
                enum noteTypes {
                  EIGHTH,   // Eighth note
                  QUARTER,  // Quarter note
                  HALF,
                  DOT_QUARTER,
                  EIGHTH_HALF, // 8th + 1/2
                  REST      // Rest note
                };
                
                void setup() {
                  for (int i = 0; i < NUM_SOLENOIDS; i++) {
                    pinMode(solenoidPins[i], OUTPUT);  // Set each solenoid pin as output
                  }
                }
                
                // Function to calculate the duration of a note based on BPM
                int calculateNoteDuration(enum noteTypes note) {
                  int beatDuration = 60000 / BPM;  // Calculate duration of a quarter note in milliseconds
                
                  switch (note) {
                    case EIGHTH:
                      return beatDuration / 2;     // Eighth note is half of a quarter note
                    case QUARTER:
                      return beatDuration;         // Quarter note is the full beat duration
                    case HALF:
                      return beatDuration * 2;
                    case DOT_QUARTER:
                      return beatDuration * 3 / 2;
                    case EIGHTH_HALF:
                      return beatDuration * 5 / 2;
                    case REST:
                      return beatDuration / 2;         // eighth note rest
                    default:
                      return 0;                    // Default to no duration if undefined
                  }
                }
                
                // Helper function to play a note on a specified solenoid pin
                void playNote(int solenoidPin, enum noteTypes note) {
                  int duration = calculateNoteDuration(note);
                
                  if (note != REST) {  // Activate only if it's not a rest
                    digitalWrite(solenoidPin, HIGH);  // Activate the solenoid
                    delay(duration/2);                  // Wait for the note duration
                    digitalWrite(solenoidPin, LOW);   // Deactivate the solenoid
                    delay(duration/2);
                  } else {
                    delay(duration);                  // Wait for the rest duration without activating the solenoid
                  }
                }
                
                void samba(int solenoidPin) {
                  playNote(solenoidPin, QUARTER);
                  playNote(solenoidPin, EIGHTH);
                  playNote(solenoidPin, QUARTER);
                  playNote(solenoidPin, EIGHTH);
                  playNote(solenoidPin, QUARTER);
                  playNote(solenoidPin, QUARTER);
                  playNote(solenoidPin, QUARTER);
                  playNote(solenoidPin, DOT_QUARTER);
                  playNote(solenoidPin, QUARTER);
                  playNote(solenoidPin, EIGHTH);
                  playNote(solenoidPin, QUARTER);
                  playNote(solenoidPin, EIGHTH);
                  playNote(solenoidPin, QUARTER);
                  playNote(solenoidPin, QUARTER);
                  playNote(solenoidPin, EIGHTH_HALF); // half + eighth
                }
                
                void loop() {
                  // One-note samba
                  for (int i = 0; i < 2; i++) {
                    samba(solenoidPins[0]);
                  }
                  samba(solenoidPins[1]);
                  playNote(solenoidPins[0], QUARTER);
                  playNote(solenoidPins[0], EIGHTH);
                  playNote(solenoidPins[0], QUARTER);
                  playNote(solenoidPins[0], EIGHTH);
                  playNote(solenoidPins[0], QUARTER);
                  playNote(solenoidPins[0], QUARTER);
                  playNote(solenoidPins[0], QUARTER);
                  playNote(solenoidPins[0], DOT_QUARTER);
                  playNote(solenoidPins[0], QUARTER);
                  playNote(solenoidPins[0], EIGHTH);
                  playNote(solenoidPins[0], QUARTER);
                  playNote(solenoidPins[0], EIGHTH);
                  playNote(solenoidPins[0], QUARTER);
                  playNote(solenoidPins[0], QUARTER);
                  playNote(solenoidPins[1], EIGHTH_HALF); // half + eighth
                  
                  delay(2000);
                }
                            

I used the 3D-printed piano finger beneath the solenoid rod to prevent damage to the keys. Here’s what the finger looked like after repetitive use:

3D-printed finger after use