Using the Microphone

It sounds like it would be an easy task: make a certain sound and have the microphone detect it and some software makes a decision based on what the microphone just heard. But most sounds are polyphonic, made up of many different sounds at multiple frequencies. This is very hard to match up in software. 

But our microcontroller can match monophonic, single frequency sounds. It still is not so easy. You need to take into account a sound occuring over time. So to simplify a sound's identification, we want to convert the microphone input to the frequency domain. If we know the likely frequency of a sound, we can more easily compare it to the sound frequency you expect.

What is the method we transform sound to frequency? There are several methematical transformations that take intensities sensed over time and calculate a set of frequencies. The main method is called the Fast Fourier Transform (commonly abbreviated as FFT). For more in-depth information, see this Adafruit Learning Center tutorial and Wikipedia.

XKCD-style graph generated by http://matplotlib.org/users/whats_new.html#xkcd-style-sketch-plotting by Tony DiCola.

In the diagrams, you can see a continuous line graph transforms to a bar graph. That's part of the math. We look for the tallest bar and see where it lands on the x-axis which is the frequency of the sound. 

The Circuit Playground library has a FFT routine built-in for our use. The routine CircuitPlayground.mic.fft takes in sound samples and puts the frequencies it finds into 32 frequency bins using some math. To make sure we find the sound we are looking for (as things can change over time), several samples are taken and averaged.

Which of the 32 frequency bins corresponds to sounds we might want to make? I used the list of musical notes in the tutorial Circuit Playground Sound and Music as a start. But I found I was not getting good results.

The best frequencies to use are shown in the chart below. If you would like to perform sound tests or just have fun, here are the simple circuits and program you can use.

I grabbed an Arduino Uno and created one of my "go to" circuits which reads a variable resistor and maps the values to a range of frequency tones to output to a piezo speaker. You can also use a second Circuit Playground as shown in the center diagram, either using an external piezo speaker or the internal speaker. The value of the potentiometer is not critical, any one from 1 kiloohms to 100 kiloohms or more will work fine as it acts as a volage divider to selectdifferent voltages on an analog pin which we translate to variable pitch on the speaker.

Download: file
// Variable Tone Generator Circuit for Arduino Uno or Circuit Playground
// Mike Barela for Adafruit Industries    September, 2016

#define speakerPin 11 // For Circuit Playground change 11 to 9 for
                      //  external Piezo, change to 5 for onboard speaker
#define potPin A0     // For Circuit Playground change A0 to A7 

void setup() { 
  pinMode(potPin, INPUT);
  pinMode(speakerPin, OUTPUT);  
  Serial.begin(9600);
}

void loop() {
  uint16_t potValue;
  uint16_t freq;
  
  potValue = analogRead(potPin);
  freq = map(potValue,0,1023,100,8000);
  tone(speakerPin,freq);
  Serial.println(freq);
}

Test out your tone generator by turning the potentiometer and listening to the different tones. Hooking up a serial monitor to the USB port, you can see the frequency you have dialed in.

On the "listening" Circuit Playground, you use the onboard microphone and see which frequency bins "max out" (like the peak in the above bar graph) as it "hears" the sound from the sound generator circuit.  

For the "listening" Circuit Playground, download the following code: 

Download: file
// Fast Forier Transform Test Program for Circuit Playground
// Mike Barela for Adafruit Industries    September, 2016

#include <Adafruit_CircuitPlayground.h>

#define BINS   32          // The number of FFT frequency bins
#define FRAMES 4           // This many FFT cycles are averaged 

void setup() {
  CircuitPlayground.begin();  // Set up the board library and serial
  Serial.begin(9600);
}

void loop() {
  uint8_t i,j;
  uint16_t spectrum[BINS];     // FFT spectrum output buffer
  uint16_t avg[BINS];          // The average of FRAME "listens"

  for(j=1; j <= FRAMES; j++) {             // We gather data FRAMES times and average it
     CircuitPlayground.mic.fft(spectrum);  // Here is the CP listen and FFT the data routine
     for(i=0; i < BINS; i++) {             // Add for an average
       if(spectrum[i] > 255) spectrum[i] = 255; // limit outlier data
       if(i == 0)
         avg[i] = spectrum[i];
       else
         avg[i] = avg[i] + spectrum[i];
     }
  } 
  for(i=0; i < BINS; i++) {               // For each output bin average
     avg[i] = avg[i] / FRAMES;            //  divide about the number of values aaveraged
  }
  int maxVal = 0, maxIndex = 0;
  for(i=0; i < BINS; i++) {               // For each output bin average
     if(avg[i] >= maxVal) {               //  find the peak value
       maxVal = avg[i];
       maxIndex = i;                      //  and the bin that max value is in
     }
  }
  for(j=0; j < 32; j++) {           // print spectrum 32 bins
     Serial.print(avg[j]);
     Serial.print(" ");
  }
  Serial.println("");              // and print the highest value and the bin it is in
  Serial.print("Max Value = "); Serial.print(maxVal);
  Serial.print(", Index of Max Value = "); Serial.println(maxIndex);
}

Find a room where people will not be annoyed by squeeky sounds and is quiet. Let the far right "listening" Circuit Playground listen to the ambient sounds with the second sketch and a computer to monitor the serial output. Then use your tone generator to make tones.

You can use the same computer with a separate USB port and serial terminal program to monitor the frequency you chose dial in. Adjust the potentiometer and note where a FFT bin reaches a maximum (hopefully 255, but maybe greater than 220 or so) and write that frequency and that bin number as a data point. Continue through the full range of the potentiometer. I recorded the following values and made a quick plot.

Several different frequencies maxed out an FFT bin. The data shows that you can get the same bin filling at different frequencies. Those frequencies are about 3 times a lower value (a third harmonic in musical/frequency terms). But not all frequencies will max out a bin. That's why my first inclination to pick nice musical notes didn't work like I thought. Several candidate frequencies (#1 to #5) are chosen to use for the robot. Those bins are spaced out so one tone that might be close to another does not get so close that the code cannot differentiate one for another.

This guide was first published on Oct 12, 2016. It was last updated on Oct 12, 2016. This page (Using the Microphone) was last updated on Nov 13, 2019.