Introduction: Arduino Wind Instrument

I wanted to create an Arduino instrument that was easy to play, but would sound better with practice. This "wind" instrument combines the Sparkfun Electret Microphone breakout board with the Tone library and 5 buttons on one analog pin. The musician must make a sound or blow into the microphone in order to enable the buttons. The buttons use the input from the microphone to determine what frequency to play. One of the buttons disables the buttons and the musician must make a sound or blow into the microphone to re-enable the instrument.

If you're blowing into the microphone, be careful, too much practice can make you dizzy! 

Step 1: Parts

1 Arduino Microcontroller
1 Breakout Board for Electret Microphone (BOB-09964 http://www.sparkfun.com/products/996 )
1  8-ohm speaker
5  1KΩ resistors
1  100Ω resistor#
5  6mm Tactile switch, flat top Model # SW-6mm-TACT-FLAT-01 
(http://www.allspectrum.com/store/product_info.php?products_id=298 )
1 Solderless breadboard
22-AWG hookup wire

Step 2: Many Buttons, But Too Few Pins


When you are running out of digial I/O pins for switches you can create a voltage divider and add a small amount of code to get many buttons working on one analog pin. The example below shows how 5 buttons can be identified using 5 1K Ω resistors and one analog pin. The resistor values must be small enough so that there isn't too much current through them but large enough to have a reliable and stable voltage divider circuit.

This circuit uses one analog pin and an equal number of resistors configured in series across +5V and GND. Each button connected to the analog input pin is at some fixed position in the series circuit. The code checks within a range of values and returns a button number if a button pushed was in that range.

You can connect more or less than five buttons to your analog pin. 

Step 3: Code for Buttons


1. Copy and paste the code below into a new sketch.

2. Compile, upload and then open the Serial Monitor.

3. Push the buttons and write down the values returned.

4. Replace my values with yours.

5. Comment out the line: #define DEBUG_ON


/*********************
// This code is based on:
// Code by Doug LaRue  
// from November 2008
// released under
// Creative Commons Attribution-Noncommercial-Share Alike 3.0
 **********************/
#define ERROR_WINDOW 50
#define INTERVAL 100
#define DEBUG_ON


int whichButton;


// variable to limit the button getting checked every cycle
long lastChecked = 0;

void setup(){
Serial.begin(9600);
}

void loop(){
if( millis() - lastChecked > INTERVAL ) {
if( int buttNum = buttonPushed(0) ) {
if(whichButton!=buttNum){
Serial.print("Button ");
Serial.println(buttNum);

switch (buttNum){
case 1:
Serial.println("Do something for Button 1");
break;
case 2:
Serial.println("Do something for Button 2");
break;
case 3:
Serial.println("Do something for Button 3");
break;
case 4:
Serial.println("Do something for Button 4");
break;
case 5:
Serial.println("Do something for Button 5");
break;
}
whichButton=buttNum;
}
}
lastChecked = millis(); // reset the lastChecked value
}
}

int buttonPushed(int pinNum) {
// variable to store the read value
int val = 0;

// enable the 20k internal pullup
digitalWrite((14+pinNum), HIGH);

val = analogRead(pinNum); // read the input pin

#ifdef DEBUG_ON
Serial.println(val);
#endif

/* don't use the upper position because that is the same as the all-open switch value when the internal 20K ohm pullup is enabled.
For 5 buttons, the ERROW_WINDOW is 50*/
//if( val >= 923 and val <= 1023 )
// Serial.println("switch 0 pressed/triggered");

if( val >= 780 and val <= 880 ) { // 830
return 1;
}
else if ( val >= (630-ERROR_WINDOW) and val <= (630+ERROR_WINDOW) ) { // 630
return 2;
}
else if ( val >= (430-ERROR_WINDOW) and val <= (430+ERROR_WINDOW) ) { // 430
return 3;
}
else if ( val >= (230-ERROR_WINDOW) and val <= (230+ERROR_WINDOW) ) { // 230
return 4;
}
else if( val >= 0 and val <= (20+ERROR_WINDOW) ) {
return 5;
}
else
return 0;
}

 

Step 4: The Microphone and Breakout Board


This breakout board couples a small electret microphone with a 100x op-amp to amplify the sounds of voice, door knocks, etc loud enough to be picked up by a microcontroller's Analog to Digital converter.

This was the code that I used to detect whispers and other noises: 

int micVal = 0;
int micPin = 1;

void setup(){
      Serial.begin(9600);
}
void loop(){
      micVal = analogRead(micPin);
      micVal = constrain(abs(micVal - 512)-25,0,512);
      Serial.println(micVal, DEC);
}

Step 5: The Tone Library


The Tone Library can be used to produce square-waves of the specified frequency on any Arduino pin. You can set the duration of the sound or use the method stop() to stop the sound.

To play a tone, connect a pin to a piezo buzzer or a speaker.

For this demo, I used a 8Ω speaker connected to pin 8 through a 100Ω resistor.



Step 6: More About the Tone Library


To use the library, you need to instantiate an instance of Tone:

Tone notePlayer;

void setup(void){
    //the number reflects the speaker pin
     notePlayer.begin(8);
}


void loop(){
     //To play a note:
     notePlayer.play(NOTE_B3);
}



These are the methods that you can use with this Library:
     begin() - prepares a pin for playing a tone.
     isPlaying() - returns true if tone is playing, false if not.
     play() - play a tone.
     stop() - stop playing a tone.




This is a list of constant values of frequencies for notes:

Constant Name Frequency (Hz)
NOTE_B2 ->123
NOTE_C3 ->131
NOTE_CS3 ->139
NOTE_D3 ->147
NOTE_DS3 ->156
NOTE_E3 ->165
NOTE_F3 ->175
NOTE_FS3 ->185
NOTE_G3 ->196
NOTE_GS3 ->208
NOTE_A3  ->220
NOTE_AS3 ->233
NOTE_B3 -> 247
NOTE_C4 ->262
NOTE_CS4 ->277
NOTE_D4 -> 294
NOTE_DS4 ->311
NOTE_E4 ->330
NOTE_F4 ->349
NOTE_FS4 ->370
NOTE_G4 ->392
NOTE_GS4 -> 415
NOTE_A4 -> 440
NOTE_AS4 -> 466
NOTE_B4 -> 494
NOTE_C5 -> 523
NOTE_CS5 -> 554
NOTE_D5 ->  587
NOTE_DS5  -> 622
NOTE_E5 -> 659
NOTE_F5 -> 698
NOTE_FS5 ->740
NOTE_G5 ->784
NOTE_GS5 ->831
NOTE_A5 ->880
NOTE_AS5 -> 932
NOTE_B5 ->988
NOTE_C6 ->1047
NOTE_CS6 ->1109
NOTE_D6 ->1175
NOTE_DS6 ->1245
NOTE_E6 ->1319
NOTE_F6 ->1397
NOTE_FS6 ->1480
NOTE_G6 ->1568
NOTE_GS6 ->1661
NOTE_A6 ->1760
NOTE_AS6 ->1865
NOTE_B6 ->1976
NOTE_C7 ->2093
NOTE_CS7 -> 2217
NOTE_D7 -> 2349
NOTE_DS7 -> 2489
NOTE_E7 ->2637
NOTE_F7 -> 2794
NOTE_FS7 -> 2960
NOTE_G7 ->3136
NOTE_GS7 ->3322
NOTE_A7 ->3520
NOTE_AS7 ->3729
NOTE_B7 ->3951
NOTE_C8 ->4186
NOTE_CS8 ->4435
NOTE_D8 ->4699
NOTE_DS8 ->4978

More information can be found here:
http://code.google.com/p/rogue-code/wiki/ToneLibraryDocumentation

Step 7: Putting It Altogether


Here's the circuit.


Step 8: The Code


#include <Tone.h>

#define ERROR_WINDOW 50
#define INTERVAL 100
//#define DEBUG_ON

int micVal = 0;
int micPin = 1;

Tone speaker;

int whichButton;


// variable to limit the button getting checked every cycle
long lastChecked = 0;


void setup() {
     Serial.begin(9600);
     speaker.begin(8);
}

void loop() {

      micVal = analogRead(micPin);
      micVal = constrain(abs(micVal - 512)-25,0,512);

      Serial.println(micVal, DEC);

     if( micVal>=50){
           if( millis() - lastChecked > INTERVAL ) {
                 if( int buttNum = buttonPushed(0) ) {
                      if(whichButton!=buttNum){

                      switch (buttNum){
                      case 1:
                            speaker.play(micVal/2);

                            break;
                      case 2:
                              speaker.play(micVal);

                              break;
                      case 3:
                              speaker.play(micVal+micVal/4);

                              break;
                      case 4:
                              speaker.play(micVal+micVal/2);

                               break;
                      case 5:
                              speaker.stop();

                              break;
                        }
                       whichButton=buttNum;
                     }

             }
            lastChecked = millis(); // reset the lastChecked value

       }
}
}

int buttonPushed(int pinNum) {
      // variable to store the read value
      int val = 0;

     // enable the 20k internal pullup
      digitalWrite((14+pinNum), HIGH);

      // read the input pin
      val = analogRead(pinNum);

      #ifdef DEBUG_ON
             Serial.println(val);
      #endif

/* don't use the upper position because that is the same as the all-open switch value when the internal 20K ohm pullup is enabled.

       //if( val >= 923 and val <= 1023 )
       // Serial.println("this is the upper limit");

       if( val >= (825-ERROR_WINDOW) and val <= (825+ERROR_WINDOW) ) { // 825
               return 1;
        } else if ( val >= (630-ERROR_WINDOW) and val <= (630+ERROR_WINDOW) ) { // 630
               return 2;
        } else if ( val >= (435-ERROR_WINDOW) and val <= (435+ERROR_WINDOW) ) { // 435
               return 3;
        } else if ( val >= (230-ERROR_WINDOW) and val <= (230+ERROR_WINDOW) ) { // 230
               return 4;
        } else if( val >= 0 and val <= (14+ERROR_WINDOW) ) {
               return 5;
        } else
               return 0;
}