Learn Arduino with Olympia Circuits
Learn Arduino
  • Home
    • Get Started
    • How to Use This Site
  • Electronics
    • The Basics
    • Electricity Flows like Water
    • Electronic Components
    • The Arno Board
  • Programming
    • The Basics
    • setup and loop Blocks
    • Variables and Arrays
    • Connecting with the Pins
    • Flow Control >
      • if Statement
      • Loops
      • Delays
    • Functions
    • Serial Communication
    • USB
    • Some Thoughts
  • Projects
    • Projects 1 >
      • 1.01: Blink
      • 1.02 Blink x2
      • 1.03 Blink Faster
      • 1.04 LED Chase!
      • 1.05 Wait To Blink
      • 1.06 Blink a Little Faster Now
      • 1.07 LED Fade
      • 1.08 RGB Blink
      • 1.09 Change RGB Color with SW1
      • 1.10 Fade RGB Colors
      • 1.11 Reaction Time Game
    • Projects 2 >
      • 2.01 Hello World
      • 2.02 Talk Back
      • 2.03 ASCII Values
      • 2.04 Ski Game
      • 2.05 Demonstration of the String Object
    • Projects 3 >
      • 3.01 Read the Potentiometer
      • 3.02 ASCIIbet Soup
      • 3.03 Potentiometer sets LED Brightness
      • 3.04 Potentiometer Sets Blink Rate
      • 3.05 LED Chase, Part II
    • Projects 4 >
      • 4.01 Bringing the Piezo to Life
      • 4.02 Controlling the Piezo with a Function
      • 4.03 Piezo C Major
      • 4.04 Piezo Greensleaves
      • 4.05 Piezo Metronome
      • 4.06 Piezo as an Input
      • 4.07 Piezo as an Input 2
      • 4.08 Metronome II
      • 4.09 Piezo Playback
      • 4.10 Piezo Fireworks
      • 4.11 Piezo Mosquito
    • Projects 5 >
      • 5.01 The Phototransistor
      • 5.02 Light and Sound
      • 5.03 Light and Sound II
    • Projects 6 >
      • 6.01 EEPROM
      • 6.02 I2C Address Scan
      • 6.03 Read the I2C Temperature Sensor
      • 6.04 High Temperature Alarm
    • Projects 7 >
      • 7.01 Arno Phone Home
      • 7.02 Keyboard Alphabet
      • 7.03 Move Mouse
      • 7.04 Draw Squares
    • Special Projects >
      • Bike Light Demo
  • References
    • Arno Pin Key
    • Arno Schematic
    • Project Index

Project 4.04 Piezo Greensleaves

 We developed the Arno’s ability for playing music in Project 4.03. In this project, we give it a tune to play.

Concepts:  arrays, functions

Circuits:
  • Circuit 4


  • Select Sketch

    ///////////////////////////////////////////////////
    //Project 4.04 Piezo Greensleeves
    int piezo = 12;
    int octave = 3;
    char notes[] = {
    C,d,D,e,E,F,g,G,a,A,b,B};
    float freqs[] = {
    16.352,17.324,18.354,19.445,20.602,21.827,23.125,24.500,25.957,27.500,29.135,30.868};
    char music[] = {
    E,G,A,B,+,d,-,B,A,F,D,E,F,G,E,E,e,E,F,e,B,E,G,A,B,+,d,-,B,A,F,D,E,F,G,F,E,e,f,F,E,E,E,+,D,
    D,d,-,B,A,F,D,E,F,G,E,E,e,E,F,e,B,+,D,D,d,-,B,A,F,D,E,F,G,F,E,
    e,f,F,E,E,E,E};
    byte beats[] = {
    2,4,2,3,1,2,4,2,3,1,2,4,2,3,1,2,4,2,4,2,4,2,3,1,2,4,2,2,1,2,3,1,2,3,2,2,4,2,6,6,3,1,2,4,2,3,1,2,4,2,3,1,2,4,2,6,6,3,1,2,4,2,3,1,2,3,1,2,3,1,2,6,4,2,6};
    long thisNote = 0;
    long thisBeat = 0;
    int skips = 0;
    void setup(){
    pinMode(piezo,OUTPUT);
    }
    void loop(){
    octave = 4;
    skips = 0;
    for(int i=0;i<83;i++){
    //check if octave is changed
    if(music[i] == +){
    octave = octave + 1;
    skips++;
    continue;
    }
    if(music[i] == -){
    octave = octave - 1;
    skips++;
    continue;
    }
    //otherwise find the note;
    for(int k=0;k<12;k++){
    if(music[i]==notes[k]){
    thisNote = (long) freqs[k]*pow(2,octave);
    break;
    }
    }
    thisBeat = (long) beats[i-skips];
    thisBeat = thisBeat * 200;
    piezoTone(thisNote,thisBeat);
    } //end loop through notes
    delay(1000);
    }
    void piezoTone(long freq, long duration){
    long aSecond = 1000000;
    long period = aSecond/freq;
    duration = duration*1000;
    duration = duration/period;
    for(long k = 0; k < duration; k++){
    digitalWrite(piezo,HIGH);
    delayMicroseconds(period/2);
    digitalWrite(piezo,LOW);
    delayMicroseconds(period/2);
    }
    }
    ///////////////////////////////////////////////////

    To play music we need two pieces of information: the notes to play and the duration of each note.  There aren’t any shortcuts here.  We need to write out each note. The music array contains the notes and the beats array contains the duration of each note (1 beat = a quarter note).

    We created a system in Project 4.03 to link the names of each note with its frequency (e.g., C = 16.352).  This works within an octave, but our music goes across several octaves.  We include that additional information in the music array.  The character ‘+’ means to move up an octave and ‘-‘ moves down an octave:

    char music[] = { 'E','G','A','B','+','d','-','B','A','F','D' (the array keeps going…)

    Since the music array contains elements that aren’t actually notes, we need to make a decision: do we keep the length of the beats array the same as the music array (which makes it easier to read) or do we skip the beats for the elements in music that move up or down an octave (which makes it harder to match the note and beat later)?  All of this information can push the limit of the Arnos memory, so we decided to only provide a beat when we play a note, which makes the beats array shorter.  We also use the byte variable type to save on space (an int array would take up twice the space since each int value needs two bytes):

    byte beats[] = {2,4,2,3, (and so on)

    We begin the loop() block by setting the initial octave and setting the variable skips. We use skips to keep track of non-note elements in music so that we can keep the music and beats arrays coordinated:

      octave = 4;

      skips = 0;

    Next, we begin a loop to go through the 83 characters in the music array:

      for(int i=0;i<83;i++){

    As we read each element of music we need to identify those elements that are not played but change the octave.  When we encounter one of these elements, we use the continue statement that tells the sketch to move back to the top of the for loop and read the next element of music. This saves a little time since we don’t run through the rest of the loop. The variable skips keeps track of these changes, too:

        //check if octave is changed

        if(music[i] == '+'){

           octave = octave + 1;

           skips++;

           continue;

        }

        if(music[i] == '-'){

          octave = octave - 1;

          skips++;

          continue;

        }

    If we make it to this point in the loop, then it’s time to play a note.  First, we need to look up the frequency of the note.  We loop through the notes array until we find a match with the current element of music.  Once a match is found, the break statement ends the loop.   There’s no need to keep looking and it saves a little time (and time is important in music!):

    //otherwise find the note;

        for(int k=0;k<12;k++){

          if(music[i]==notes[k]){

            thisNote = (long) freqs[k]*pow(2,octave);

            break;

          }

        }

    Next, we find the beat for the current note.  The octave changes mean the the elements of music and beats don’t directly correspond.  But we kept track of this with skips.  The second line sets the tempo (if you replace 200 with 100, the music will play twice the speed):

        thisBeat = (long) beats[i-skips];

        thisBeat = thisBeat * 200;

    Finally, we call piezoTone to play the note:

    piezoTone(thisNote,thisBeat);

    After playing the entire piece, we wait a second and then start again:

      } //end loop through notes

      delay(1000);

    Back to Projects 4

    Copyright Olympia Circuits LLC 2014. All Rights Reserved.