Gram Piano Assembly Guide

This Tutorial is Retired!

This tutorial covers concepts or technologies that are no longer current. It's still here for you to read and enjoy, but may not be as useful as our newest tutorials.

Pages
Contributors: JordanDee
Favorited Favorite 0

Code Explanation

This part of the tutorial will go over the inner workings of the pre-installed program on your Gram Piano. It will help you understand how the code works and give you ideas on how you can modify it for your own needs. The entirety of the code won't be listed here, however the full program can be found on Github. It also requires Arduino's capacitive touch library. Follow this tutorial on installing an Arduino Library if you need help with that. You will also need a FTDI Basic to program the Gram Piano using the 6 pin header on the top edge of the board.

At the top of the program, we declare the variables we need. The most important to go over are the ones below:

language:cpp
// Keyboard variables
long keys[13]; // Contains latest capacitive sense reading for each key
int threshold = 25; // Threshold for key press readings, a key plays sound if its equal to or greater than the threshold
float octave = 1.0; // Stores what octave/multiplier the key presses use, change with potentiometer

// Declaring a capactive sensor for each key on the keyboard
CapacitiveSensor CapSensors[13] =
{
  CapacitiveSensor(2,3),
  CapacitiveSensor(2,4),
  ...
  ...

The variable array keys is used to store the latest capacitive touch reading for each key on the keyboard. The higher the number for a key, the harder it's being pressed. If a particular key exceeds the threshold value (found experimentally), then the corresponding note for that key is played (unless another key takes precedence, explained later). Each time a note is played, the frequency is multiplied by the octave variable, which is set by the potentiometer. CapSensors is an array that declares a capacitive touch sensor for each of the keys. All the keys use the same send pin, but each key's pad is connected to a different pin. CapacitiveSensor(2,3) indicates 2 is the "send pin," while 3 is the pin connected to the key's pad. This particular sensor is for the low C key, and all the keys are listed in order from lowest to highest frequency. We will use each sensor in this array to detect if a key is being pressed.

Our setup() function is quite simple:

language:cpp
void setup()                    
{  
  // Setup button pin (PB6) as input (not a default Arduino pin)
  DDRB &= ~(1 << 6); // Set bit six of the data direction register b to 0

  // Blink LED to indicate keyboard is ready for use
  pinMode(led,OUTPUT);
  digitalWrite(led,HIGH);
  delay(500);
  digitalWrite(led,LOW);
}

We initialize a button using AVR style code, since the button is not on an official Arduino digital pin. We then set the LED as an output and blink it for half a second to indicate to the user that the program is ready.

Our loop() function is where the core of the sketch lives.

language:cpp
void loop()                    
{
  // Measure each key press using capacitive sensing
  measureKeys();

  // Select one of three octaves based on position of potentiometer
  octave = readPot();

  // Play the note corresponding to the key press and octave
  // Higher pitch keys have priority if there were multiple presses
  playKeyPress();

  // Play melody and turn on LED if the button has been pressed
  if (buttonPressed())
  {
    digitalWrite(led,HIGH);
    playMelody();
  }
}

Each time through the loop, we first measure each key press with capacitive sensing to detect which ones are being pressed. We then check the potentiometer to select which octave of notes we will play. We then play the key press. If two or more keys are being pressed, we play the higher frequency one by default. Finally, we check if the button is pressed, and, if it is, we play a pre-programmed melody that's stored in the notes array.

Feel free to dig into the full source code to view each loop functions' implementation to understand how they work. We'll go over a few of the most important details here.

In the measureKeys() function, the most important line to understand is the following:

language:cpp
keys[i] = CapSensors[i].capacitiveSensor(5); // 5 samples per key

For each key, this takes 5 capacitive touch readings, averages them, and then stores them in our keys array. These values are then used afterward in the playKeyPress() function like this:

language:cpp
if (keys[12] > threshold) 
{ 
    tone(spkr,NOTE_C5 * octave,30); 
}

Each key is checked to see if its value is greater than the set threshold. If it is, we use Arduino's built-in tone() function to play the corresponding note for 30 milliseconds. Since the if statements check the keys from high to low pitch, higher pitch keys take priority. Only one note is ever played at a time.

The variable spkr is simply the pin to the on-board speaker. For the high C (right most note on the board), NOTE_C5 is the note/frequency to be played through the speaker by default. The variable octave can be set to .5, 1.0, or 2.0 depending on the position of the potentiometer that was read by the readPot() function. This value will be multiplied by the default NOTE_C5 value (found in pitches.h), and the speaker will play the note corresponding to the frequency just calculated.

Finally, if the push button is pressed, playMelody() is called. All this function does is go through each note stored in the array notes and plays each one for 300 milliseconds with the tone() function. After each note is played, it checks again for a button press, and the melody stops playing if a button press is detected. The LED is also turned on while the melody is playing.

These are the highlights of the core functionality of the default program. At this point, some exploring and experimenting is required to get a better grasp on the program. Always feel free to contact us if there is any confusion and we can do our best to improve the readability of the code and/or the above explanation.