Starting as an Entrepreneurship Ambassador at the MakerHub, I was assigned with creating one of my own projects using the resources available to me. Looking around the room I had more than enough at my arsenal: a big MakerBot 3D printer, a couple of arduino boards, some tools and wires, and an idea.
Planning
Naturally, with my affection for sketching and music, I was excited to get started on my ambitious endeavor to make this idea of a digital piano come to life. Taking a pen and a post-it note, I started to put it on paper. When it came to determining the size of my project, I had to keep in mind some of the limitations of my resources. The printing plate for the 3D printer we had was only 8in x 12in, so I couldn’t make a full length piano base, and I also had to determine how many keys I wanted to be on the board.
After scaling the size of the base I wanted, I moved on to designing evenly-sized keys, making sure that I would be able to fit at least a whole octave in C major within the base. Afterwards I moved onto my favorite part of the design process: 3D modeling.
3D Modeling & Printing
Inventor, my old friend… I had not opened that lovely AutoCAD application since high school, so when I did I felt rather nostalgic and excited.
Through a series of sketches and subtractive extrusions on Inventor, I turned what was originally an 8.5×6″ block into. I measured the maximum size for each of the keys if I wanted 17 total (C1 to E2, ten white keys, seven black keys) given the block size I set for the base.
I also had to keep in mind the size of the Arduino and circuit boards inside, with even spacing between the buttons and enough distance for the printed keys to sit on top of the circuit while still giving the “clicking” aspect of the piano playing. I also had to keep in mind the size of the buzzer and LED screen, and how I wanted them to be displayed. Originally I wanted the buzzer to emit the note frequencies from INSIDE of the piano base, and give the authentic acoustic sound, but after trying it I found that PLA is not really reverb friendly…
There was a heavy back and forth process between the computer and my trusty MakerBot printer, with the hook around the keys not really fitting around the hanging cylinder on the piano base for the keys to attach to, but with trial and error I was able to print out all keys in a way that would all them to fit nicely within the base and lying on top of the circuit board that would fit inside.
Below are a few images of the finalized models:











Programming
#include <Wire.h>
// declare and define array of button pins; ports are declared
// in specific order according to key note position; A3 is
// button port for key C, pin 8 is button port
// for key C#, pin A2 is button port for key D, etc.
float myPins[] = {A3, 8, A2, 2, 1, 3, A1, 4, 5, 6, A0, 7, 9, 10, 11, 12, 13};
void setup()
{
// initialize pins
for(int i = 0; i<=16; i++)
{
pinMode(myPins[i], INPUT);
}
Wire.begin();
}
void loop()
{
// function checks continuously if a button is being
// pressed; if program detects input signal from any
// of the buttons, that item # (or pin being activated)
// in array myPins[] gets sent to slave i2c bus, for
// the following commands to be executed. Function ends.
for(int i=0; i<=16; i++)
{
if (digitalRead(myPins[i]) == HIGH)
{
Wire.beginTransmission(1);
Wire.write(i);
Wire.endTransmission();
delay(50);
break;
}
}
}
It took a while to get readjusted to C++ in arduino. It had been a while since I last used it but was relatively straightforward once I remembered how to implement arrays and I2C communications.
My objective for the program was to have a buzzer that emits a unique frequency according to the pitch assigned to each button. For example, given a set of 17 buttons, each representing the notes from C1 to E2 and every black key in between, the right sound should be outputted when its key was pressed.
The program above initializes each button port according to chromatic order (pretty manual on my end, but it is what it is), continuously check whether or not a button is pressed. The position number of the port signal detected will correspond to its position on the octave of keys, so the program will send the array position # to the other Arduino board.
The program below, that is, the slave bus, receives the communicated value and identifies the corresponding tone and note identification, and calls the buzzer to emit that frequency and the LED screen to write that note.
// Slave bus
// Installs library necessary for I2C communication
#include <Wire.h>
// Declares variables and arrays; Array tones[] contain buzzer
// frequencies assigned to each corresponding key name found
// in array keys[]. For example, item 6 in tones[] would be
// the buzzer frequency matched with item 6 in keys[].
float tones[] = {262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494, 523, 554, 587, 622, 659};
const char* keys[] = {"C ", "C#", "D ", "D#", "E ", "F ", "F#", "G ", "G#", "A ", "A#", "B ", "C ", "C#", "D ", "D#", "E "};
char key;
// Installs library for LCD display
#include <LiquidCrystal.h>
// Assigning LCD pins to Arduino board pins
const int RS = 11, EN = 12, D4 = 2, D5 = 3, D6 = 4, D7 = 5;
LiquidCrystal lcd(RS, EN, D4, D5, D6, D7);
void setup()
{
lcd.begin(16, 2); // set up number of columns and rows
pinMode(A3, OUTPUT);
pinMode(A2, OUTPUT);
// move cursor to (0, 0), print message at (0, 0)
// move cursor to (0, 1), print message at (0, 1)
lcd.setCursor(0, 0);
lcd.print("beep boop piano :)");
lcd.setCursor (0, 1);
lcd.print("Key: ");
Wire.begin(1); // join i2c bus
Wire.onReceive(receiveEvent); // register event
}
void loop()
{
delay(100);
}
// function that executes whenever data is received from
// master this function is registered as an event, see setup()
void receiveEvent(int howMany)
{
// variable declared and set equal to value received from
// master (e.g. if button for note D is pressed, then
// respective frequency is emitted
int noteNum = Wire.read();
float ton = tones[noteNum];
tone(A3, ton, 100);
tone(A2, ton, 100);
lcd.setCursor(5, 1);
lcd.print(keys[noteNum]);
}
Circuit Building
Building the circuit was the hardest part of this process. Arduino components tend to get a bit finicky, so certain positioning and buttons would one day decide to work with you, and other days they would not.
Having 17 buttons and a single Arduino board, or at least what started as one, was no simple task. Some buttons were more uncooperative than others, and many times I would be left staring at my breadboard questioning my life choices. I would break apart some of the circuit and rewire at times only to be left with the buzzer detecting a HIGH signal from one of the buttons and then outputting an uninterrupted frequency at some pitch. Experience with ear training had me battling with the G note at an inconsiderably annoying rate.
To solve this problem, I had to isolate and mess around with the pin schematics. I wouldn’t change anything internal with the board, but instead I would reassign buttons to a different port and see if the input would work that way. Sometimes that method would work, other times it wouldn’t, and if the latter were the case then I would assume that button was not working and swap it with a new one. The trial and error process with circuit building was very heavy with this project, and therefore it took a lot of patience and the necessity to do things one at a time to avoid making mistakes.
I came to the conclusion that it was usually the internal clock of the arduino that was messing up with what I aimed to be a continuous stream of detecting and releasing signals. If I touched port 1, or had anything connected to it, the program would only output in pulses, for example.
As I ran out of ports, I decided I needed to implement I2C communication and put the circuit together with two Arduino boards, as seen in the code. I had one board dedicated for detecting the button signals, and the other for outputting its respective frequency through the buzzer and the LED screen.
The final product was a result of around three cycles of rebuilding and deconstructing the circuit. But alas, that is the life of an electrical engineer.