Chapter 11: Sound



In this chapter we will learn to make sounds using the Arduino. In order to make sounds we will also learn about arrays and interrupts.

You can download the Arduino source code for this chapter from:
a101_ch11_supplemental.zip

 

Chapter 11: Sound



In this chapter we will learn to make sounds using the Arduino. In order to make sounds we will also learn about arrays and interrupts.

You can download the Arduino source code for this chapter from:
a101_ch11_supplemental.zip

Doing things in the background: Interrupts



Microcontrollers, like the Atmel ATmega328 used in the Arduino UNO, are made of several internal sections on an IC (Integrated Circuit) that work together to provide intelligence, communications, sensing, and control. The intelligence is provided by the CPU (Central Processing Unit), a section that runs (processes) the code you write, one step at a time.

Peripheral devices do the communications, sensing and control. These are sections on the IC that can do things independent of the CPU. For example the Arduino Serial communications use a UART peripheral device to communicate with the PC. The CPU loads a byte of data into the UART and tells it to send the byte. The UART then, with no further help from the CPU, sends each of the 8-bits of the byte, one at a time to the PC. The UART also receives data from the PC, again one bit at a time until it has the required 8-bits for a byte of data. This sending and receiving of bytes of data happen at the same time that CPU is processing unrelated commands elsewhere on the IC. This work is done as background processing, meaning without requiring the CPU to do the work.

Another peripheral device we have used is a timer that we use for analog voltage output. We, via the CPU, use the analogOutput() function to tell this timer peripheral device to create a PWM (Pulse Width Modulation) signal and output it on a specific pin. The timer then generates the PWM signal on the pin, also in the background without further use of the CPU. Likewise we measure voltage on some pins by using the analogInput() function that reads the ADC (Analog to Digital Conversion) peripheral.

In early designs of microcontrollers, some of these sorts of tasks were done directly by the CPU, but they take up a lot of CPU time, so later designs added the peripheral devices so that they could do common repetitive tasks without burdening the CPU. You can think of a microcontroller by analogy as a small company with a boss and some employees. The boss is up front and in charge, while some employees laboring away in the background. Think of the CPU as the boss and the program you write as that bosses tasks. The boss may do some of those tasks, and may assign other tasks to employees, who are the equivalent or the microcontroller peripheral devices.

These separate sections are visible on the silicon in the IC as show for an Atmel chip in Figure 1. These sections correspond to those shown in the Block Diagram for an Arduino UNO's Atmega328p shown in Figure 2.

Figure 1: ATmega AVR silicon.

 

Figure 2: ATmega328p Block Diagram



Much of what is shown in these illustrations is well beyond the scope of this series, but they help understand the general concept that there are separate entities on the silicon that surround and work with the CPU.

So in this boss/employee analogy, how does the boss find out about the status of the employees work? There are two ways to do this. One is for the boss to stop working and go see what the employees are doing. In the computer world, when the boss CPU checks on the employee peripherals it is called polling.

The other way to do this is for the employees to knock on the boss's door and interrupts his workflow, which for computers is also called an interrupt. The boss responds to the racket by setting the current work aside, perhaps marking the task list so the work can be resumed when the interrupt is taken care of, then the boss deals with the interrupt. Once the interrupt is dealt with the boss looks back at the task list and resumes where the mark shows the interruption occurred. For a computer interrupt, the CPU marks where it was in the main code, deals with the interrupt, and then returns to the code where it left off.

There are several types of interrupts on the Arduino. One is a hardware interrupt that can notify the CPU when a voltage changes on a pin. Another is a timer interrupt that tells the CPU when a time interval has passed.

A hardware interrupt can be generated by a voltage change on either Arduino pin 2 or 3. We will use this to detect when our light sensor indicates that, after being relative dark, suddenly it has become relatively light. Imagine a design that is used only when the light comes on in a room. The Arduino could sit there and continuously check the light sensor (polling) or the light sensor could be connected to a pin with hardware interrupt capability and the Arduino could only deal with the light change when a light change actually happens and sends an interrupt to the CPU. We will use the Arduino attachInterrupt library to do this in Lab 1.

A timer interrupt is generated when a timer reaches a certain preset value. For example, you might program a timer to interrupt the CPU after one second has passed. We will use the Arduino TimerOne library to do this in Lab2. In later labs, we will use this kind of interrupt to generate sound in the background while the CPU does other things.

Making Sounds



A musical tone is characterized by its pitch, duration, intensity, and timbre. Pitch is a human perception based on the frequency of a sound. Duration at its simplest is the length of time for a musical note. Intensity is the loudness. And timbre refers to perceived qualities of the tone that differentiate between various musical sources - for instance a given pitch may be produced both a violin and a trumpet but the timbre is different.

Our piezo speaker has many problems associated with creating a musical tone. The intensity of each pitch varies significantly with far greater intensity for pitches near the resonant frequency (frequency that is physically amplified by the box containing the piezo element) and very diminished elsewhere. We do have good control over the duration, but the timbre can only be described as awful - certainly not HiFi (High Fidelity = really good sound). None the less, we can use a piezo element produce something that people will generally recognize as music, albeit awful sounding music, but still recognizable.

For our purposes in Arduino 101, music theory even in its most elementary form is beyond the scope of what we are trying to accomplish. We will thus just say that music involves tones played for discrete periods of time.

An example of this would be the tune Happy Birthday which may be played with 26 notes each held for a relative duration of 1, 2, 4 or 6 equal periods as shown below:

Happy Birthday
Notes: ccdcfeccdcgfccCafedbbafgf
Duration for each note: 1, 1, 2, 2, 2, 4, 1, 1, 2, 2, 2, 4, 1, 1, 2, 2, 2, 2, 6, 1, 1, 2, 2, 2, 2, 4

Try singing the song while looking at the duration for each note and this should make more sense. The first six durations are 1, 1, 2, 2, 2, 4, which correspond to "Hap py birth day to you". You'll notice that you divide Happy into two instance of the same note as you distinctly separate it into "Hap" and "py". Next you sing "birth" but for the same duration as the time to do the two notes for "Hap" and "py." Likewise for "day" and "to", but when you sing "you" you hold that note for four times as long as "Hap" and "py" and twice as long "birth", "day" and "to" so the timing is 1, 1, 2, 2, 2, 4. You may also notice that as you sang each note you inserted a brief quiet pause between each note and that this pause was about equal to the time of the shortest note: 1. You might ask "1" what? And that depends on the pace you want your tune to have. If you set 1 to 1 second then the tune would sound like it was being played in slow motion, if you set it to 1/4 a second (250 ms) then you'd find the pace to sound more like what you would sing. So if we wanted to play this. A program looking at the first six notes and their associated duration would then play the c note for 250ms, pause for 250 ms, c note for 250ms, pause for 250 ms, d note for 500ms, pause for 250 ms, f note for 500ms, pause for 250 ms, e note for 1000ms and pause for 250 ms.

The Arduino toneMelody example (that you can find in the Arduino IDE under File/examples/0.2Digital/toneMelody) does something similar to what we discussed in that it reads the note for the tune array, turns on the tone and then delays for the duration. That's cool and we will do this in a lab, but there is one very serious caveat for playing tunes this way - when you are playing a tune this way, you can't be doing anything else because the delay() function completely occupies the CPU while delaying for the indicated duration! Fortunately, there is a better way. We can use an interrupt to play the tune in the background. For this we will keep our tune in a single array and have the interrupt check that array every 250 ms to play the tone in the background for 250 ms until the next interrupt occurs. A quarter of a second, 250ms, may not seem like much to us, but to a CPU processing instructions at roughly 16,000,000 per second, it can do 4,000,000 instructions while that tone is being generated in the background by one of the peripheral devices. The CPU can get a lot of other work done while humming a tune if you use interrupts and let a peripheral do the humming.

But wait, what's this 'array' of which you speak?


Yikes, we are in the middle of learning about making music and suddenly we are using arrays - what the heck is an array?

An array is a way of storing some related data in a location in memory so that it is easy to get to. An example of 'some related data' would be that list of notes for the tune Happy Birthday. There are 25 notes and we need to store them somewhere in the computer memory so that we can pull them out one at a time when it comes time to play that note. An array is a way of defining blocks of memory with each byte located in a continuous sequence of memory locations. So for our song we will set aside 25 memory locations and then we can access each byte in that array by getting the byte from the first location, then adding one to that location to get the next byte, and so on till we have added 25 to the first memory location and gotten the 25th note. Arrays are a data type and the compiler knows how to handle them when you define them correctly.

The toneMelody example that comes with the Arduino IDE (File\Examples\02.Digital\toneMeldoy), plays the simple tune for shave and a haircut, two bits. In this code, you'll see before the setup() function:

#include "pitches.h"

// notes in the melody:
int melody[] = {
NOTE_C4, NOTE_G3,NOTE_G3, NOTE_A3, NOTE_G3,0, NOTE_B3, NOTE_C4};

// note durations: 4 = quarter note, 8 = eighth note, etc.:
int noteDurations[] = { 4,8,8,4,4,4,4,4 };

First we see that we include the pitches.h file which has definitions for musical note frequencies. For example it defines:

#define NOTE_C4 262

Providing the actually frequency for the musical note C4. A timer peripheral is used to generate a tone at the indicated frequency. We will discuss in more detail later, but for the moment we want to understand the array data type that the notes are stored in:
// notes in the melody:
int melody[] = { NOTE_C4, NOTE_G3, NOTE_G3, NOTE_A3, NOTE_G3,0, NOTE_B3, NOTE_C4};

This statement defines an array named melody that stores integers (remember that NOTE_C4 is an alias for the integer 262).

In order to play the tune we will want to extract each element in sequence one at a time to play each note. We do this as follows:

// define the variable to hold a note
int myNote = 0;

// set myNote equal to the number 3 element of the array
myNote = melody[3];

Arrays use computer style numbering so the first element of the array is stored at the 0, location, the second at 1, the third at 2 and so on. In the above example, myNote will equal the number 3 position in the array which is the third note: NOTE_A3. The first note in the array, NOTE_C4, is in the number 0 position.

Let's review this. Arrays are blocks of memory with the first memory location in that block identified by the array name. (In our example the 'melody' term is an alias for the location of the first element in the array - NOTE_C4). Arrays are numbered beginning with 0, so when you request an element of an array you must remember this and request the element by subtracting 1 from the location of the element if you use normal arithmetic counting. (In our example, the third element in the array is indicated by melody[3], NOTE_A3) There is no magic in this - it is just the way that arrays are done.

We might use that melody array as follows:

int i = 0;
int myNote = 0;

for(i = 0 ; i < 7 ; i++)
{
myNote = melody[i];
playTone(myNote);
}

In that case we know that the array has 7 elements, so we use a for-loop to move through the array beginning at element 0 and continuing through element 6 (the first through seventh elements of the array). The actually toneMelody program uses a more efficient, but less clear way to play the melody, but the underlying principle for using an array is the same.

This may feel a bit iffy, but the labs will help reinforce the array concept.

Making Sounds with a Piezo Element



We are advised to make a joyful noise, and what better way than a piezo element? Well, honestly, just about anything would be better – you don’t get much more low-fidelity than this. Even calling the sound it makes ‘noise’ is being generous - so lets ask ‘what's cheaper’? And now we are getting somewhere, since these things are cheap and don’t require any external amplification circuitry.

Sound Components, Schematic, Layout



The piezo speaker included in the Arduino 101 projects kit comes with long straight legs. You should bend its legs bent as shown in Figure 3 so that it can be used conveniently on the Arduino Proto Shield breadboard (also shown in Figure 3.)

Figure 3: Piezo with legs bent for breadboard



We use the PWM peripheral that we discussed for using to control the angle of servomotors. You can set these PWMs so that they generate an audio frequency associated with a musical note. For instance, to generate the ‘c’ musical note we create an output waveform (see Figure 4: ‘c’ note waveform) that turns on and off with a frequency of 261 cycles per second. Each of these on/off cycles occurs in 1/261 of a second or 0.003831 seconds. Since we are dealing with microseconds we multiply this by 1,000,000 to get 3831 microseconds per cycle. And since we need to cycle the pin (turn the pin on and off) in that time, we turn it on for 3831/2 = 1915 microseconds (throwing away the fractional part) and off for 1915 microseconds giving us a total of 3830 - we lost 1 due to our not wanting to use fractions, but who is going to miss a microsecond? And, yeah that sounds like a lot of work for the computer, but fortunately the CPU can simply tell the PWM to do this and it does it in the background with no further intervention by the CPU until time to stop outputting that sound.

Figure 4: note c waveform

 

Sound Using Interrupts



Earlier in this chapter we discussed the Arduino example program toneMelody the plays a sequence of notes and uses the delay() function to time the notes. The main problem with the methods shown in the toneMelody example from the Arduino IDE is that it uses the delay() function to time the duration of the tone in the melody. The delay() blocks the CPU while playing a tone, so it can do nothing but wait for the tone to finish before it does the next thing. That doesn't matter in the toneMelody example since all the Arduino is doing is playing a tune. But if you want to do anything else while you play a tune, you'll need to use interrupts. As discussed above, by using interrupts the CPU can do millions of operations while each tone of a tune plays in the background. We want to have the CPU set the duration in a peripheral timer and have that timer interrupt the CPU when the duration is passed so that it can stop the current tone and then instruct the PWM to play the next tone.

We will generate tones using the PWM and we will keep track of the duration of the tone using a timer interrupt. The duration of each tone will vary depending on the tune being played, but each duration will be in common units. For instance, we might have a melody with 100 mS units of duration. The shortest time we can play a tone will be 100 mS and each longer interval will be some number of those periods. We then keep an array of durations which tells us how many of these periods to play a tone. For instance, if in my tone array at position 10 we find a 'c' note, we then look in the duration array at position 10 and see that it is 5. Since we know that the minimum duration is 100 mS we will then start that 'c' note on the PWM and set the timer to interrupt the CPU with 5*100 mS have passed, that is 500mS or half a second.

In order to play a melody this way we will need the tone array, the duration array, the number of tones we are going to play and the minimum duration of a tone. For example, we use the following data to play Happy Birthday:

  
int timerMS = 40;
int dataCount = 50;
int freq[] = {587,0,587,0,659,0,587,0,784,0,740,0,587,0,587,0,659,0,587,0,440,0,784,0,587,0,587,0,1175,0,988,0,1568,0,1480,0,1319,0,1047,0,1047,0,988,0,1568,0,880,0,1568,0,};
int duration[] = {3,1,2,1,8,1,8,1,8,1,16,1,3,1,2,1,8,1,8,1,8,1,16,1,3,1,2,1,8,1,8,1,8,1,8,1,16,1,3,1,2,1,8,1,8,1,8,1,16,1};


We read each note and duration until we have read all the data as indicated by dataCount indicating that we have 50 tones to generate. For each element in the freq[] and duration[] arrays we set the PWM to play the tone and then the timer to interrupt after a certain number of base durations have elapsed. For instance, the first tone is 587 and the first duration is 3, so we set the PWM to output a square wave with a frequency of 587 Hz and we set the timer to 3*40mS = 120 mS. The 587 Hz tone will sound on the piezo element for 120 mS. The next tone is 0 which is a silent space between tones and it plays for 1 duration. The third element is also 587 but this time we only play it for 2*40 mS. We do this process 50 times as indicated by dataCount and the tune is finished.

The code used in Lab 6 allows us to play renditions of 14 tunes.

Lab 1: Sensor Hardware Interrupt - light


We will use what we learned in Chapter 10 about light sensors and use a light sensor to generate a hardware interrupt. With this we can interrupt the Arduino CPU by simply waving our hand over the board - no need to touch anything! [You may want to refer back to Chapter 10 Lab 1 if the circuit isn't clear.]

Parts Required:
1 Arduino
1 USB cable
1 Arduino Proto Shield and jumper wires
1 CdS light sensor
1 10000 Ω resistor

Estimated time for this lab: 30 minutes

Check off when complete:
Build the circuit shown in Figures 5 and 6. This circuit is nearly identical to the one we built in Chapter 10 except that instead of having the sensor connected to the A0 pin for doing ADC, we connect it to pin 2 for doing an external hardware interrupt.

Figure 5: Light sensor interrupt breadboard

 

Figure 6: Light sensor interrupt schematic



Either copy and paste, or type in the following program into the Arduino IDE. [Or open the A101_ch11_light_sensor_interrupt.ino program - note this file is located in A101_ch11_supplemental.zip that you can find on the Nuts&Volts web site page for this article and on the www.arduinoclassroom.com Chapter 11 web page.]

  
// A101_ch9_light_sensor_voltage 9/28/14 Joe Pardue

#define INT0 0 // INT0 is on pin 2 in the UNO

// You must use volatile when using variables in interrupts
volatile int state = 0;

unsigned long time = 0;
int count = 0;

void setup() {
  Serial.begin(57600);
  Serial.println("Measure light sensor voltage rev 1.0");
  attachInterrupt(INT0,lightInterrupt,RISING);
}

void loop() {
  // The time statements are used to prevent multiple
  // interrupts caused by a bouncing signal on the pin
  if(state)
  {    
    if((time+5) < millis())  
    {      
      Serial.print("Light interrupt #:");
      Serial.println(count++);
      time = millis();
    }    
    state = 0;    // reset state to 0    
  } 
}

// This function is called when an external interrupt
// occurs on the pin indicated by attachInterrupt
void lightInterrupt()
{
  state = 1;//!state; 
}


Compile and run the program, then open the Serial Monitor.
Make sure the board is lit by a bright overhead light and then pass your hand back and forth over the sensor. You should see output similar to that shown in Figure 7.

Figure 7: Light sensor interrupt Serial Monitor output

 

Lab 2: Timer Interrupt


Parts Required:
1 Arduino
1 USB cable

Estimated time for this lab: 15 minutes

Check off when complete:
This lab requires no additional circuitry and be run on a bare Arduino.
Either copy and paste, or type in the following program into the Arduino IDE. [Or open the A101_ch11_timer1_interrupt.ino program - note this file is located in A101_ch11_supplemental.zip that you can find on the Nuts&Volts web site page for this article and on the www.arduinoclassroom.com Chapter 11 web page.]
Take special note of the logic being used in this program. We have a variable set aside to act as a flag to tell the loop() funtion when an interrupt has happened. Once each second, the myTimer1 interrupt function is called and sets the flag. The loop() checks the flag and when it sees the flag is set one second has passed and it checks to see if the tick flag is set, if it is, then it writes 'tick' to the Serial Monitor and sets the flag to 0, so that next time a second passes it will see that tick is 0, print 'tock' and set the flag to one.

  
// A101_ch11_timer_interrupt 9/28/14 Joe Pardue

#include <TimerOne.h>  // Timer 1 library

volatile int oneSecondFlag = 0;
int tick = 1;

void setup(){
  // Set up the serial port
  Serial.begin(57600);
  
  // identify yourself
  Serial.println(F("A101_ch11_timer_interrupt rev. 0.01"));  
  
  // initialize timer1 interrupt
  Timer1.initialize(1000000); // call it once per second
  Timer1.attachInterrupt(myTimer1);  
}

void loop(){
  // if one second has passed
  if(oneSecondFlag) 
  {
    oneSecondFlag = 0; // set the flag to 0        
    if(tick)
    { 
      Serial.println("tick");
      tick = 0;
    } 
    else
    { 
      Serial.println("tock");
      tick = 1;
    }  
  }
}

// once per second this is called and sets the flag to 1
void myTimer1()
{
  oneSecondFlag = 1; 
}


Compile and run this program and you should see the Serial Monitor output shown in Figure 8.

Figure 8: Timer1 interrupt output

 

Lab 3: Finding the Piezo Resonant Frequency


We learned that these piezo elements are contained in a plastic container the will vibrate most readily at a specific resonant frequency so that at that frequency the loudness of the sound is vastly magnified from sounds made at other frequencies. The resonant frequency is used when you want to create a very loud alarm sound.

Parts Required:
1 Arduino
1 USB cable
1 Arduino Proto Shield and jumper wires
1 Piezo element

Estimated time for this lab: 30 minutes

Check off when complete:
Build the circuit shown in Figures 9 and 10. We attach one leg of the piezo element to pin 6 of the Arduino, which we will use to generate PWM to create tones.

Figure 9: Piezo element breadboard

 

Figure 10: Piezo element schematic



Either copy and paste, or type in the following program into the Arduino IDE. [Or open the A101_ch11_resonant_frequency.ino program - note this file is located in A101_ch11_supplemental.zip that you can find on the Nuts&Volts web site page for this article and on the www.arduinoclassroom.com Chapter 11 web page.]

  
/* A101_ch11_resonant_frequency 9/30/14 Joe Pardue */

#define tonePin 6

void setup() {
 Serial.begin(57600);
 Serial.println("A101_ch11_resonant_frequency rev 1.0");
}

void loop() {
  
  for(int i = 0; i < 7000; i+=100)
  {

    tone(tonePin,i);
    delay(250);
    Serial.println("freq: ");
    Serial.println(i);
    tone(tonePin,0);
  }   
}

Compile and run the program. Open the Arduino Serial Monitor as shown in Figure 11 and observe the frequencies to judge the loudest sound.

Figure 11: First frequency observation


I observed that the loudest sound was around 4500 - you may find a different frequency.
Modify your code to bracket the observed frequency with 500 below and 500 above and step through it in 50 Hz increments as shown below. Note that my use of 4500 is not necessarily what you will observe.

  
void loop() {
  
  for(int i = 4000; i < 5000; i+=50)
  {
    tone(tonePin,i);
    delay(250);
    Serial.println("freq: ");
    Serial.println(i);
    tone(tonePin,0);
  }   
}

Compile and run the code to observe the loudest freq.
In my case, I observed again that the loudest frequency seemed to be around 4500.
Next bracket 200Hz around your loudest frequency in 10 Hz increments. I used the following code

  
void loop() {
  
  for(int i = 4400; i < 46000; i+=10)
  {
    tone(tonePin,i);
    delay(250);
    Serial.println("freq: ");
    Serial.println(i);
    tone(tonePin,0);
  }   
}


I observed that, to me, 4530 Hz seemed loudest. Differences in piezo elements and your hearing acuity may cause you to find a different frequency.

Lab 4: Alarms


Now that we have found the resonant frequency for our piezo element, we can use that frequency to generate various alarm sound patterns. This lab uses the concepts for timers and arrays learned earlier in the chapter.

Parts Required:
1 Arduino
1 USB cable
1 Arduino Proto Shield and jumper wires
1 Piezo element

Estimated time for this lab: 30 minutes

Use the piezo circuit shown in Lab 3.
Either copy and paste, or type in the following program into the Arduino IDE. [Or open the A101_ch11_alarms.ino program - note this file is located in A101_ch11_supplemental.zip that you can find on the Nuts&Volts web site page for this article and on the www.arduinoclassroom.com Chapter 11 web page.]

  
// A101_ch11_alarms 9/30/14 Joe Pardue
#include  // Timer 1 library

#define highTone 4530
#define lowTone 3500

int speaker = 6;   // the number of the speaker driver pin
int alarmArray[100]; // array to hold the arlarm tones
int arrayCount = 0; // initialize the array count

void setup(){
  // Set up the serial port
  Serial.begin(57600);
  
  // identify yourself
  Serial.println(F("a101_ch11_alarm rev. 0.01"));  
  
  // initialize timer1
  Timer1.initialize(125000); // interrupts 8 times a second
  Timer1.attachInterrupt(myTimer1); 
}

void loop(){
  threeBeep();
  delay(3000); 
  threeBeepWarbleUp();
  delay(3000);
  threeBeepWarbleDown();
  delay(3000);  
  threeBeepWarbleUpDown();
  delay(3000);
  threeBeepWarbleDownUp();
  delay(3000);
  sos();
  delay(5000); 
}

void sos(){
 int sos[] = {0, 0, highTone, 0, highTone, 0, highTone, 0, highTone, highTone, highTone, 0, highTone, highTone, highTone, 0, highTone, highTone, highTone, 0, highTone, 0, highTone, 0, highTone,0};  
  arrayCount = 25;
  for(int i = 0; i<=arrayCount; i++)
  {
    alarmArray[i] = sos[i];
  }
}

void threeBeep(){
 int threeBeep[] = {0, 0, highTone, 0, highTone, 0, highTone};  
  arrayCount = 6;
  for(int i = 0; i<=arrayCount; i++)
  {
    alarmArray[i] = threeBeep[i];
  }
}

void threeBeepWarbleUp(){
 int threeBeepWarble[] = {0, 0, highTone, lowTone, 0, highTone, lowTone, 0, highTone, lowTone};  
  arrayCount = 9;
  for(int i = 0; i<=arrayCount; i++)
  {
    alarmArray[i] = threeBeepWarble[i];
  }
}

void threeBeepWarbleUpDown(){
 int threeBeepWarble[] = {0, 0, highTone, lowTone, highTone, 0, highTone, lowTone, highTone, 0, highTone, lowTone, highTone};  
  arrayCount = 12;
  for(int i = 0; i<=arrayCount; i++)
  {
    alarmArray[i] = threeBeepWarble[i];
  }
}

void threeBeepWarbleDownUp(){
 int threeBeepWarble[] = {0, 0, lowTone, highTone, lowTone, 0, lowTone, highTone, lowTone, 0, lowTone, highTone, lowTone};  
  arrayCount = 12;
  for(int i = 0; i<=arrayCount; i++)
  {
    alarmArray[i] = threeBeepWarble[i];
  }
}

void threeBeepWarbleDown(){
 int threeBeepWarble[] = {0, 0, lowTone, highTone,  0, lowTone, highTone, 0, lowTone, highTone};  
  arrayCount = 9;
  for(int i = 0; i<=arrayCount; i++)
  {
    alarmArray[i] = threeBeepWarble[i];
  }
}

// Called 8 times per second
// Checks alarm array count is > 0
// if so it plays the tone in the alarm array 
// and decrements the count.
void myTimer1()
{  
  if((arrayCount > 0)){
   if(alarmArray[arrayCount] == 0) noTone(speaker);
   else tone(speaker,alarmArray[arrayCount]);
   arrayCount--;
  }  
}


Compile and run the program.
Verify that the alarms play according to the program directions.

Lab 5: Light Theremin


A Theremin is an electronic audio instrument that was often used in early science fiction and horror films to make spooky UFO or ghostly sounds. Our version uses the light sensor to provide a way to input a continuously varying data from relative dark to relative light and to map those sensed variations into tones output on the piezo element.

Parts Required:
1 Arduino
1 USB cable
1 Arduino Proto Shield and jumper wires
1 Piezo element
1 CdS light sensor
1 10000 Ω resistor

Estimated time for this lab: 30 minutes

Build a circuit that combines the light sensor circuit shown in Lab 1 and the piezo speaker circuit shown in Lab 3. We see these circuits combined in Figures 12 and 13

Figure 12: Theremin light sensor breadboard

 

Figure 13: Theremin light sensor schematic



Either copy and paste, or type in the following program into the Arduino IDE. [Or open the A101_ch11_light_sensor_theremin.ino program - note this file is located in A101_ch11_supplemental.zip that you can find on the Nuts&Volts web site page for this article and on the www.arduinoclassroom.com Chapter 11 web page.]

  
// A101_ch11_light_sensor_theremin 10/5/14 Joe Pardue

int sensorPin = A0;  // analog input pin
unsigned int sensorValue = 0;  // store the analog input value

int speaker = 6;
unsigned int freq = 0;;

void setup() {
  Serial.begin(57600);
  Serial.println("Light sensor LED Theremin rev 1.0");
}

void loop() {
  // read the value from the sensor:
  freq = analogRead(sensorPin);
  
  // map the light level to the frequency
  freq = map(freq, 1024, 0, 0, 5000); 
  // play the tone
  tone(speaker,freq);
}

Compile and run the program.
Verify that the tone varies according to the light intensity.

Lab 6: Making Music

Making music with an Arduino requires a bit of understanding of how music is written and produced that is beyond the scope of this lab. Basically you produce musical tones (constant frequencies defined for specific musical notes) for short periods of time according to a defined pattern (the melody). I transcribed a number of tunes so that there are two arrays for each tune, one for the tones and one for the times to play the tone. The program to read these arrays and produce the melodies is based on concepts we have already visited, but be forewarned - this is a very long program. Don't let that scare you though; you should understand each aspect of the program. While there are a bunch of melodies available, the program only plays one that you select before compiling the code. You select the melody you want to play and remove the comment symbols: /* and */ from the program and make sure that all the other melodies are blocked in with those comment symbols. Parts Required:
1 Arduino 1 USB cable 1 Arduino Proto Shield and jumper wires 1 Piezo element Estimated time for this lab: 30 minutes Use the piezo circuit shown in Lab 3.
Either copy and paste, or type in the following program into the Arduino IDE. [Or open the A101_ch11_tunes.ino program - note this file is located in A101_ch11_supplemental.zip that you can find on the Nuts&Volts web site page for this article and on the www.arduinoclassroom.com Chapter 11 web page.]

  
// A101_ch11_tunes 9/24/14 Joe Pardue

#include  // Timer 1 library
/*
//HBday.ardplay
int timerMS = 40;
int dataCount = 50;
int freq[] = {587,0,587,0,659,0,587,0,784,0,740,0,587,0,587,0,659,0,587,0,440,0,784,0,587,0,587,0,1175,0,988,0,1568,0,1480,0,1319,0,1047,0,1047,0,988,0,1568,0,880,0,1568,0,};
int duration[] = {3,1,2,1,8,1,8,1,8,1,16,1,3,1,2,1,8,1,8,1,8,1,16,1,3,1,2,1,8,1,8,1,8,1,8,1,16,1,3,1,2,1,8,1,8,1,8,1,16,1};
*/

/*
//GoodBad.ardplay
int timerMS = 154;
int dataCount = 33;
int freq[] = {0,466,1244,932,1244,932,1480,1660,1244,932,1244,932,1244,932,1480,1660,1109,932,1244,932,1244,932,1480,1397,1244,1109,932,1244,932,1244,932,1660,1244,};
int duration[] = {1,1,1,1,1,3,2,2,8,1,1,1,1,3,2,2,8,1,1,1,1,3,2,0,1,8,1,1,1,1,3,2,8,};
*/

/*
//lullaby.ardplay
int timerMS = 60;
int dataCount = 109;
int freq[] = {659,0,659,0,784,0,659,0,659,0,784,0,0,0,659,0,784,0,1047,0,494,0,440,0,440,0,784,0,587,0,659,0,698,0,587,0,587,0,659,0,698,0,0,0,587,0,698,0,494,0,440,0,784,0,494,0,1047,0,523,0,523,0,1047,0,440,0,698,0,784,0,659,0,523,0,698,0,784,0,440,0,784,0,523,0,523,0,1047,0,440,0,698,0,784,0,659,0,523,0,698,0,784,0,698,0,659,0,587,0,523,};
int duration[] = {4,1,4,1,5,1,4,1,8,1,8,1,8,1,4,1,4,1,8,1,5,1,4,1,8,1,8,1,4,1,4,1,8,1,8,1,4,1,4,1,8,1,8,1,4,1,4,1,4,1,4,1,8,1,8,1,16,1,4,1,4,1,16,1,4,1,4,1,16,1,4,1,4,1,8,1,8,1,8,1,16,1,4,1,4,1,16,1,4,1,4,1,16,1,4,1,4,1,4,1,2,1,2,1,8,1,8,1,16,};
*/

/*
//BOLERO.ardplay
int timerMS = 94;
int dataCount = 47;
int freq[] = {1047,1047,494,1047,1175,1047,988,880,1047,1047,880,1047,1047,988,1047,880,1568,1319,1397,1568,1568,1397,1319,1175,1319,1397,1568,880,1568,1568,1568,880,988,880,1568,1397,1319,1175,1319,1175,1047,1047,1047,1175,1319,1397,1175,1568,};
int duration[] = {8,4,2,2,2,2,2,2,4,2,2,8,4,2,2,2,2,2,2,16,2,2,2,2,2,2,2,2,8,8,2,2,2,2,2,2,2,2,2,2,4,4,2,2,4,4,8,16,};
*/


/*
//ode.ardplay - note duration is off 
int timerMS = 77;
int dataCount = 59;
int freq[] = {659,0, 659,0 ,698,0, 784,0, 784,0, 698,0, 659,0, 587,0, 523,0, 523,0, 659,0, 659,0, 659,0, 587,0, 587,0, 659,0, 659,0, 698,0, 784,0, 784,0, 698,0, 659,0, 587,0, 523,0, 523,0, 587,0, 659,0, 587,0, 523,0, 523,0,};
//int freq[] = {659,659,698,784,784,698,659,587,523,523,659,659,659,587,587,659,659,698,784,784,698,659,587,523,523,587,659,587,523,523,0,};
int duration[] = {3,1, 3,1, 4,1 ,5,1, 5,1, 4,1, 3,1 ,2,1, 2,1, 2,1 ,2,1 ,3,1, 3,1, 2,1, 2,1 ,3,1, 3,1, 4,1, 5,1, 5,1, 4,1 ,3,1 ,2,1, 1,1, 1,1, 2,1, 3,1, 2,1, 1,1,};
*/

/*
//jinglebells.ardplay
int timerMS = 34;
int dataCount = 96;
int freq[] = {659,0,659,0,659,0,659,0,659,0,659,0,659,0,784,0,523,0,587,0,659,0,698,0,698,0,698,0,698,0,698,0,659,0,659,0,659,0,659,0,587,0,587,0,659,0,587,784,659,0,659,0,659,0,659,0,659,0,659,0,659,0,784,0,523,0,587,0,659,0,698,0,698,0,698,0,698,0,698,0,659,0,659,0,659,0,784,0,784,0,698,0,587,0,523,0,};
int duration[] = {8,1,8,1,16,1,8,1,8,1,16,1,8,1,8,1,8,1,8,1,32,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,16,16,8,1,8,1,16,1,8,1,8,1,16,1,8,1,8,1,8,1,8,1,32,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,32,8,};
*/

/*
//amazing.ardplay
int timerMS = 34;
int dataCount = 38;
int freq[] = {391,261,329,261,329,293,261,2200,391,391,261,329,261,329,293,391,391,391,329,391,329,391,329,261,391,2200,261,2200,391,391,261,329,261,329,293,261,261,261,};
int duration[] = {8,32,8,8,32,16,32,16,32,16,32,8,8,32,16,32,16,32,16,16,8,8,8,32,16,32,8,8,32,16,16,8,8,32,16,32,16,32,};
*/

/*
//smokey.ardplay
int timerMS = 54;
int dataCount = 35;
int freq[] = {523,0,523,659,784,1047,880,0,0,1397,1397,1568,880,1568,0,1568,0,0,1047,1047,1319,1568,0,1568,1175,0,0,1319,1397,1175,1047,0,1047,0,1047,};
int duration[] = {8,1,8,8,8,8,16,8,8,8,8,8,8,8,1,8,8,8,8,8,8,8,1,8,8,8,8,8,8,8,8,1,8,1,16,};
*/

/*
//america.ardplay
int timerMS = 55;
int dataCount = 111;
int freq[] = {391,0,391,0,329,0,329,0,391,0,391,0,293,0,293,0,329,0,349,0,391,0,440,0,493,0,391,0,391,0,391,0,329,0,329,0,391,0,391,0,293,0,293,0,587,0,554,0,587,0,659,0,440,0,587,0,391,0,659,0,659,0,587,0,523,0,493,0,493,0,493,0,523,0,587,0,493,0,440,0,391,0,523,0,523,0,523,0,440,0,440,0,523,0,523,0,391,0,391,0,391,0,440,0,523,0,391,0,587,0,523,};
int duration[] = {8,1,10,1,4,1,8,1,8,1,10,1,4,1,8,1,8,1,8,1,8,1,8,1,8,1,16,1,8,1,10,1,4,1,8,1,8,1,10,1,4,1,8,1,8,1,8,1,8,1,8,1,8,1,16,1,8,1,10,1,4,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,16,1,8,1,10,1,4,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,16,};
*/
/*
//furelise.ardplay
int timerMS = 28;
int dataCount = 73;
int freq[] = {659,0,622,0,659,0,622,0,659,0,493,0,587,0,523,0,440,0,261,0,329,0,440,0,493,0,0,329,0,415,0,493,0,523,0,0,329,0,0,659,0,622,0,659,0,622,0,659,0,493,0,587,0,523,0,440,0,261,0,0,329,0,440,0,493,0,329,0,523,0,493,0,440,};
int duration[] = {8,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,16,1,8,1,8,1,8,1,16,1,1,8,1,8,1,8,1,16,1,1,8,1,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,16,1,8,1,1,8,1,8,1,16,1,8,1,8,1,8,1,16,};
*/
/*
//ode_to_joy.ardplay
int timerMS = 30;
int dataCount = 122;
int freq[] = {329,0,329,0,349,0,391,0,391,0,349,0,329,0,293,0,261,0,261,0,293,0,329,0,329,0,293,0,293,0,329,0,329,0,349,0,391,0,391,0,349,0,329,0,293,0,261,0,261,0,293,0,329,0,293,0,261,0,261,0,293,0,293,0,329,0,261,0,293,0,329,349,0,329,0,261,0,293,0,329,349,0,329,0,293,0,261,0,293,0,196,0,329,0,329,0,349,0,391,0,391,0,349,0,329,0,293,0,261,0,261,0,293,0,329,0,293,0,261,0,261,0,};
int duration[] = {8,2,8,2,8,2,8,2,8,2,8,2,8,2,8,2,8,2,8,2,8,2,8,2,10,2,4,2,16,2,8,2,8,2,8,2,8,2,8,2,8,2,8,2,8,2,8,2,8,2,8,2,8,2,10,2,4,2,16,2,8,2,8,2,8,2,8,2,8,2,4,4,2,8,2,8,2,8,2,4,4,2,8,2,8,2,8,2,8,2,16,2,8,2,8,2,8,2,8,2,8,2,8,2,8,2,8,2,8,2,8,2,8,2,8,2,10,2,4,2,16,2,};
*/
/*
//indiana_jones_long.ardplay
int timerMS = 25;
int dataCount = 157;
int freq[] = {329,0,349,0,391,0,523,0,293,0,329,0,349,0,391,0,440,0,493,0,698,0,440,0,493,0,523,0,587,0,659,0,329,0,349,0,391,0,523,0,587,0,659,0,698,0,391,0,440,0,659,0,587,0,391,0,659,0,587,0,391,0,659,0,587,0,391,0,698,0,659,0,587,0,523,0,329,0,391,0,349,0,293,0,349,0,329,0,391,0,523,0,523,0,329,0,391,0,349,0,293,0,391,0,329,0,293,0,261,0,261,0,329,0,391,0,349,0,293,0,349,0,329,0,523,0,523,0,391,0,391,0,659,0,587,0,391,0,659,0,587,0,391,0,659,0,587,0,391,0,698,0,659,0,587,0,523};
int duration[] = {8,1,8,1,8,1,32,1,10,1,8,1,32,1,10,1,8,1,8,1,32,1,10,1,8,1,8,1,8,1,8,8,10,1,8,1,8,1,16,1,10,1,8,1,32,1,10,1,8,1,8,1,10,1,8,1,8,1,10,1,8,1,8,1,10,1,8,1,8,1,10,1,8,1,32,1,10,1,8,1,32,1,10,1,8,1,8,1,8,1,8,1,32,1,10,1,8,1,32,1,10,1,8,1,8,1,8,1,8,1,32,1,10,1,8,1,32,1,10,1,8,1,8,1,8,1,32,1,10,1,8,1,8,1,10,1,8,1,8,1,10,1,8,1,8,1,10,1,8,1,8,1,10,1,8,1,32};
*/

//indiana_short.ardplay
int timerMS = 26;
int dataCount = 76;
int freq[] = {329,0,349,0,391,0,523,0,293,0,329,0,349,0,391,0,440,0,493,0,698,0,440,0,493,0,523,0,587,0,659,0,329,0,349,0,391,0,523,0,587,0,659,0,698,0,391,0,440,0,659,0,587,0,391,0,659,0,587,0,391,0,659,0,587,0,391,0,698,0,659,0,587,0,523,0,};
int duration[] = {8,1,8,1,8,1,32,1,10,1,8,1,32,1,10,1,8,1,8,1,32,1,10,1,8,1,8,1,8,1,8,8,10,1,8,1,8,1,16,1,10,1,8,1,32,1,10,1,8,1,8,1,10,1,8,1,8,1,10,1,8,1,8,1,10,1,8,1,8,1,10,1,8,1,32,1,};

/*
//star_wars.ardplay
int timerMS = 18;
int dataCount = 74;
int freq[] = {261,0,261,0,261,329,0,523,0,493,0,440,0,391,0,698,0,523,0,493,0,440,0,391,0,698,0,523,0,493,0,440,0,493,0,391,0,261,0,261,0,261,0,329,0,523,0,493,0,440,0,391,0,698,0,523,0,493,0,440,0,391,0,698,0,523,0,493,0,440,0,493,0,391,0};
int duration[] = {8,2,8,2,8,32,2,32,2,8,2,8,2,8,2,32,2,16,2,8,2,8,2,8,2,32,2,16,2,8,2,8,2,8,2,32,2,8,2,8,2,8,2,32,2,32,2,8,2,8,2,8,2,32,2,16,2,8,2,8,2,8,2,32,2,16,2,8,2,8,2,8,2,32};
*/

const int speaker = 6;   // the number of the speaker driver pin
int playIt = 0;
int i = 0;
int count = dataCount;
int myDuration = 0;
int finished = 1;
  
void setup(){
  // Set up the serial port
  Serial.begin(57600);
  
  // identify yourself
  Serial.println(F("a101_ch11_tunes rev. 0.01"));  
  
  // initialize timer1 interrupt
  Timer1.initialize(timerMS*1000); 
  Timer1.attachInterrupt(myTimer1);  
}

void loop(){ 
  // if the tune is finished playing, play it again
  if(finished) 
  {
    playTune();    
  }
}

void playTune()
{
  playIt = 1;
  i = 0;
  count = dataCount;
  myDuration = 0;
  finished = 0;
}

void myTimer1()
{   
  if(playIt)
  {
    // First see if you can get it to send out a single tone for the duration in the array
    if(myDuration <= 0) 
    { 
      myDuration = duration[i];      
      if(freq[i] == 0)noTone(speaker);
      else tone(speaker,freq[i]); 
      i++;
    }   
    if(myDuration >= 0)
    {
      myDuration--; 
    }
    
    if(i > dataCount)
    {
     i = 0;
     playIt = 0; 
     noTone(speaker);
     finished = 1;
    } 
  }
}


Compile and run the program.
Verify that the Indiana Jones tune plays on the piezo element.
Add comments to the Indiana Jones tune and remove comments from other tunes to play them.