Piano Playing Robot

This is a 3D printed modular piano playing robot designed to work with cheap servos.

Each module has 12 legs, each requiring a SG90 servo. Each module fits on an octave with 7 legs to play the white notes and 5 legs to play the black notes.

The modules fit together using bolts. Attach “A” type module to “B” type. The legs that press the white keys are flipped on the “B” type to fit the alternating pattern with 4 on the outside and 3 on the inside.

Can connect up to 4 modules/octaves together on one Arduino Mega (can support up to 48 servos) but can connect 2 Arduino Mega’s together (using I2C) for more octaves.

Downloads

STL files for 3D Printing

Piano Playing Robot:  Thingiverse

 

Parts Required (For 4 modules / octaves)

 (48x) SG90 Servos - AMAZON

(3x) Jumper Wires 120pcs - AMAZON

or combined kit: (5x) SG90 Servos + Jumper Wires - AMAZON

(96x) M3 x 8mm Bolts - AMAZON

(144x) M3 x 12mm Bolts - AMAZON

(8x) M3 x 20mm Bolts - AMAZON

(144x) M3 Washers - AMAZON

(248x) M3 Lock Nuts - AMAZON

(48x) Rubber Feet - AMAZON

Cable Ties - AMAZON

Header Pins - AMAZON

Arduino Mega 2560 - AMAZON

Equipment

5V Power Supply - AMAZON or Bench Power Supply - AMAZON

Glue Gun AMAZON

Extra Long 3mm Screwdriver - AMAZON

Gallery

Build Instructions

20180823_182650.jpg
demo1.jpg
demo2.jpg
20180827_060434.jpg
20180827_055506.jpg
20180912_164313.jpg
20180912_163606.jpg

Step 1

Print the following pieces: 12x Servo Arm, 12x Connector Arm, 12x Foot, 1x Octave Module, 2x Legs, 2x Leg Base.

 

Step 2

 Attach rubber feet to the “foot” pieces using M3 x 12mm bolts and M3 lock nuts.

 

 Step 3

 Attach Servo arm and connectors using M3 x 12mm bolts, M3 washers and M3 lock nuts. Half of the servo arms need to attach facing the opposite direction.

 

Step 4

Attach the SG90 servos to the main module using M3 x 8mm bolts. Use the extra long screwdriver to tighten the bolts.

 

Step 5

Insert the legs in the module and connect the servo arms to the servos. You may need to use a small servo screw to hold in place depending on print quality.

 

Step 6

Hold together all of the servo connectors and put a cable tie around them to hold in place. Using pin header cut to 14 pins wide and solder all the shorter ends together to create a bus bar. create 2 per module.

Step 7

Insert a bus bar to connect all the red cables on the servos. Cover the bus bar in electrical tape and add the second bus bar to the brown cables. You should have 2 pins overhanging, these are for daisy chaining power cables. Insert a 12 way ribbon cable to the yellow cables.

Step 8

Connect the ribbon to the Arduino and connect the power cables to a 5V power supply. Add a wire between the Arduino GND and the power supply GND.

Electronics

Here is a basic wiring diagram for each octave module. If the servos are juddering then add a capacitor in parallel in the 5V power supply (1000uF).

elec diagram.png

Code

Calibrating Movements:

Here is a basic example of how you would calibrate each servo to find the angle range for the leg to move:

#include 
Servo a1; Servo a2; Servo a3; Servo a4; Servo a5; Servo a6; Servo a7; Servo a8; Servo a9; Servo a10; Servo a11; Servo a12;
int w12 = 145; int w11 = 55; int w10 = 145; int w9 = 50; int w8 = 100; int w7 = 90; 
int w6 = 80; int w5 = 150; int w4 = 85; int w3 = 90; int w2 = 145; int w1 = 68;
int t = 500; int v = 30;
void setup() {
  a1.attach(13); a1.write(w1);
  a2.attach(12); a2.write(w2);
  a3.attach(11); a3.write(w3);
  a4.attach(10); a4.write(w4);
  a5.attach(9); a5.write(w5);
  a6.attach(7); a6.write(w6);
  a7.attach(8); a7.write(w7);
  a8.attach(6); a8.write(w8);
  a9.attach(5); a9.write(w9);
  a10.attach(4); a10.write(w10);
  a11.attach(3); a11.write(w11);
  a12.attach(2); a12.write(w12);
}
void loop() {
  a1.write(w1 + v); delay(t); a1.write(w1); 
  a2.write(w2 + v); delay(t); a2.write(w2); 
  a3.write(w3 + v); delay(t); a3.write(w3);
  a5.write(w5 + v); delay(t); a5.write(w5);
  a6.write(w6 + v); delay(t); a6.write(w6);
  a7.write(w7 + v); delay(t); a7.write(w7);
  a8.write(w8 + v); delay(t); a8.write(w8);
  a9.write(w9 + v); delay(t); a9.write(w9);
  a10.write(w10 + v); delay(t); a10.write(w10);
  a11.write(w11 + v); delay(t); a11.write(w11);
  a12.write(w12 + v); delay(t); a12.write(w12);
}

MIDI on Arduino

And here is how to make the piano playing robot understand MIDI. I used a MIDI - CSV converter and opened the file on excel then wrote a function that converts it to an Arduino array in the format {Time, Direction, Power(0-10), Key}.

#include

Servo a1; Servo a2; Servo a3; Servo a4; Servo a5; Servo a6; Servo a7; Servo a8; Servo a9; Servo a10; Servo a11; Servo a12;

Servo b1; Servo b2; Servo b3; Servo b4; Servo b5; Servo b6; Servo b7; Servo b8; Servo b9; Servo b10; Servo b11; Servo b12;

Servo c1; Servo c2; Servo c3; Servo c4; Servo c5; Servo c6; Servo c7; Servo c8; Servo c9; Servo c10; Servo c11; Servo c12;

Servo d1; Servo d2; Servo d3; Servo d4; Servo d5; Servo d6; Servo d7; Servo d8; Servo d9; Servo d10; Servo d11; Servo d12;

int w1 = 135; int w2 = 54; int w3 = 135; int w4 = 50; int w5 = 95; int w6 = 70;

int w7 = 95; int w8 = 142; int w9 = 95; int w10 = 90; int w11 = 147; int w12 = 60;

int x1 = 135; int x2 = 80; int x3 = 90; int x4 = 85; int x5 = 50; int x6 = 115;

int x7 = 105; int x8 = 90; int x9 = 130; int x10 = 90; int x11 = 90; int x12 = 30;

int y1 = 80; int y2 = 80; int y3 = 80; int y4 = 80; int y5 = 80; int y6 = 80;

int y7 = 80; int y8 = 80; int y9 = 80; int y10 = 80; int y11 = 80; int y12 = 80;

int z1 = 80; int z2 = 80; int z3 = 80; int z4 = 80; int z5 = 80; int z6 = 80;

int z7 = 80; int z8 = 80; int z9 = 80; int z10 = 80; int z11 = 80; int z12 = 80;

int now = 0; int line = 0; int travel = 25;

int midi[96][4] = {

{ 0, 1, 5, 36 },

{ 500, 0, 5, 36 },

{ 1000, 1, 5, 37 },

{ 1500, 0, 5, 37 },

{ 2000, 1, 5, 38 },

{ 2500, 0, 5, 38 },

{ 3000, 1, 5, 39 },

{ 3500, 0, 5, 39 },

{ 4000, 1, 5, 40 },

{ 4500, 0, 5, 40 },

{ 5000, 1, 5, 41 },

{ 5500, 0, 5, 41 },

{ 6000, 1, 5, 42 },

{ 6500, 0, 5, 42 },

{ 7000, 1, 5, 43 },

{ 7500, 0, 5, 43 },

{ 8000, 1, 5, 44 },

{ 8500, 0, 5, 44 },

{ 9000, 1, 5, 45 },

{ 9500, 0, 5, 45 },

{ 10000, 1, 5, 46 },

{ 10500, 0, 5, 46 },

{ 11000, 1, 5, 47 },

{ 11500, 0, 5, 47 },

{ 12000, 1, 5, 48 },

{ 12500, 0, 5, 48 },

{ 13000, 1, 5, 49 },

{ 13500, 0, 5, 49 },

{ 14000, 1, 5, 50 },

{ 14500, 0, 5, 50 },

{ 15000, 1, 5, 51 },

{ 15500, 0, 5, 51 },

{ 16000, 1, 5, 52 },

{ 16500, 0, 5, 52 },

{ 17000, 1, 5, 53 },

{ 17500, 0, 5, 53 },

{ 18000, 1, 5, 54 },

{ 18500, 0, 5, 54 },

{ 19000, 1, 5, 55 },

{ 19500, 0, 5, 55 },

{ 20000, 1, 5, 56 },

{ 20500, 0, 5, 56 },

{ 21000, 1, 5, 57 },

{ 21500, 0, 5, 57 },

{ 22000, 1, 5, 58 },

{ 22500, 0, 5, 58 },

{ 23000, 1, 5, 59 },

{ 23500, 0, 5, 59 },

{ 24000, 1, 5, 60 },

{ 24500, 0, 5, 60 },

{ 25000, 1, 5, 61 },

{ 25500, 0, 5, 61 },

{ 26000, 1, 5, 62 },

{ 26500, 0, 5, 62 },

{ 27000, 1, 5, 63 },

{ 27500, 0, 5, 63 },

{ 28000, 1, 5, 64 },

{ 28500, 0, 5, 64 },

{ 29000, 1, 5, 65 },

{ 29500, 0, 5, 65 },

{ 30000, 1, 5, 66 },

{ 30500, 0, 5, 66 },

{ 31000, 1, 5, 67 },

{ 31500, 0, 5, 67 },

{ 32000, 1, 5, 68 },

{ 32500, 0, 5, 68 },

{ 33000, 1, 5, 69 },

{ 33500, 0, 5, 69 },

{ 34000, 1, 5, 70 },

{ 34500, 0, 5, 70 },

{ 35000, 1, 5, 71 },

{ 35500, 0, 5, 71 },

{ 36000, 1, 5, 72 },

{ 36500, 0, 5, 72 },

{ 37000, 1, 5, 73 },

{ 37500, 0, 5, 73 },

{ 38000, 1, 5, 74 },

{ 38500, 0, 5, 74 },

{ 39000, 1, 5, 75 },

{ 39500, 0, 5, 75 },

{ 40000, 1, 5, 76 },

{ 40500, 0, 5, 76 },

{ 41000, 1, 5, 77 },

{ 41500, 0, 5, 77 },

{ 42000, 1, 5, 78 },

{ 42500, 0, 5, 78 },

{ 43000, 1, 5, 79 },

{ 43500, 0, 5, 79 },

{ 44000, 1, 5, 80 },

{ 44500, 0, 5, 80 },

{ 45000, 1, 5, 81 },

{ 45500, 0, 5, 81 },

{ 46000, 1, 5, 82 },

{ 46500, 0, 5, 82 },

{ 47000, 1, 5, 83 },

{ 47500, 0, 5, 83 }};

void setup() {

a1.attach(2); a2.attach(3); a3.attach(4); a4.attach(5); a5.attach(6); a6.attach(7);

a7.attach(8); a8.attach(9); a9.attach(10); a10.attach(11); a11.attach(12); a12.attach(13);

b1.attach(14); b2.attach(15); b3.attach(16); b4.attach(17); b5.attach(18); b6.attach(19);

b7.attach(20); b8.attach(21); b9.attach(22); b10.attach(23); b11.attach(24); b12.attach(25);

c1.attach(26); c2.attach(27); c3.attach(28); c4.attach(29); c5.attach(30); c6.attach(31);

c7.attach(32); c8.attach(33); c9.attach(34); c10.attach(35); c11.attach(36); c12.attach(37);

d1.attach(38); d2.attach(39); d3.attach(40); d4.attach(41); d5.attach(42); d6.attach(43);

d7.attach(44); d8.attach(45); d9.attach(46); d10.attach(47); d11.attach(48); d12.attach(49);

a1.write(w1); a2.write(w2); a3.write(w3); a4.write(w4); a5.write(w5); a6.write(w6);

a7.write(w7); a8.write(w8); a9.write(w9); a10.write(w10); a11.write(w11); a12.write(w12);

b1.write(x1); b2.write(x2); b3.write(x3); b4.write(x4); b5.write(x5); b6.write(x6);

b7.write(x7); b8.write(x8); b9.write(x9); b10.write(x10); b11.write(x11); b12.write(x12);

c1.write(y1); c2.write(y2); c3.write(y3); c4.write(y4); c5.write(y5); c6.write(y6);

c7.write(y7); c8.write(y8); c9.write(y9); c10.write(y10); c11.write(y11); c12.write(y12);

d1.write(z1); d2.write(z2); d3.write(z3); d4.write(z4); d5.write(z5); d6.write(z6);

d7.write(z7); d8.write(z8); d9.write(z9); d10.write(z10); d11.write(z11); d12.write(z12);

}

void loop() {

if ( now == midi[line][0]) {

int onoff = midi[line][1]; int vel = midi[line][2]; int key = midi[line][3]; //set variables to current line of MIDI file

switch (key) {

case 36: a1.write(w1 + ((travel + vel) * onoff)); line++; break;

case 37: a2.write(w2 + ((travel + vel) * onoff)); line++; break;

case 38: a3.write(w3 + ((travel + vel) * onoff)); line++; break;

case 39: a4.write(w4 + ((travel + vel) * onoff)); line++; break;

case 40: a5.write(w5 + ((travel + vel) * onoff)); line++; break;

case 41: a6.write(w6 + ((travel + vel) * onoff)); line++; break;

case 42: a7.write(w7 + ((travel + vel) * onoff)); line++; break;

case 43: a8.write(w8 + ((travel + vel) * onoff)); line++; break;

case 44: a9.write(w9 + ((travel + vel) * onoff)); line++; break;

case 45: a10.write(w10 + ((travel + vel) * onoff)); line++; break;

case 46: a11.write(w11 + ((travel + vel) * onoff)); line++; break;

case 47: a12.write(w12 + ((travel + vel) * onoff)); line++; break;

case 48: b1.write(x1 + ((travel + vel) * onoff)); line++; break;

case 49: b2.write(x2 + ((travel + vel) * onoff)); line++; break;

case 50: b3.write(x3 + ((travel + vel) * onoff)); line++; break;

case 51: b4.write(x4 + ((travel + vel) * onoff)); line++; break;

case 52: b5.write(x5 + ((travel + vel) * onoff)); line++; break;

case 53: b6.write(x6 + ((travel + vel) * onoff)); line++; break;

case 54: b7.write(x7 + ((travel + vel) * onoff)); line++; break;

case 55: b8.write(x8 + ((travel + vel) * onoff)); line++; break;

case 56: b9.write(x9 + ((travel + vel) * onoff)); line++; break;

case 57: b10.write(x10 + ((travel + vel) * onoff)); line++; break;

case 58: b11.write(x11 + ((travel + vel) * onoff)); line++; break;

case 59: b12.write(x12 + ((travel + vel) * onoff)); line++; break;

case 60: c1.write(y1 + ((travel + vel) * onoff)); line++; break;

case 61: c2.write(y2 + ((travel + vel) * onoff)); line++; break;

case 62: c3.write(y3 + ((travel + vel) * onoff)); line++; break;

case 63: c4.write(y4 + ((travel + vel) * onoff)); line++; break;

case 64: c5.write(y5 + ((travel + vel) * onoff)); line++; break;

case 65: c6.write(y6 + ((travel + vel) * onoff)); line++; break;

case 66: c7.write(y7 + ((travel + vel) * onoff)); line++; break;

case 67: c8.write(y8 + ((travel + vel) * onoff)); line++; break;

case 68: c9.write(y9 + ((travel + vel) * onoff)); line++; break;

case 69: c10.write(y10 + ((travel + vel) * onoff)); line++; break;

case 70: c11.write(y11 + ((travel + vel) * onoff)); line++; break;

case 71: c12.write(y12 + ((travel + vel) * onoff)); line++; break;

case 72: d1.write(z1 + ((travel + vel) * onoff)); line++; break;

case 73: d2.write(z2 + ((travel + vel) * onoff)); line++; break;

case 74: d3.write(z3 + ((travel + vel) * onoff)); line++; break;

case 75: d4.write(z4 + ((travel + vel) * onoff)); line++;break;

case 76: d5.write(z5 + ((travel + vel) * onoff)); line++; break;

case 77: d6.write(z6 + ((travel + vel) * onoff)); line++; break;

case 78: d7.write(z7 + ((travel + vel) * onoff)); line++; break;

case 79: d8.write(z8 + ((travel + vel) * onoff)); line++; break;

case 80: d9.write(z9 + ((travel + vel) * onoff)); line++; break;

case 81: d10.write(z10 + ((travel + vel) * onoff)); line++; break;

case 82: d11.write(z11 + ((travel + vel) * onoff)); line++; break;

case 83: d12.write(z12 + ((travel + vel) * onoff)); line++; break;

default: line++; break;

}

} else {delay(1); now++;}

}