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.
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 voltage divider to select different voltages on an analog pin which is translated to variable pitch on the speaker.
// Variable Tone Generator Circuit for Arduino Uno or Circuit Playground // Anne 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:
// Fast Forier Transform Test Program for Circuit Playground // Anne 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.
Page last edited March 08, 2024
Text editor powered by tinymce.