I'm working on a project with Arduino where I need to detect frequencies between 19kHz and about 25kHz from an audio input.
Is there a shield or something that can help me do this easily? I notice that things get a lot more complicated when you need more than a 8928 Hz sampling rate. If it's easier for Arduino to record less than 5 seconds of audio, analyze the frequencies in the audio clip, and from there determine if the desired frequencies were reached, that would work for me as well.
In case something I said didn't already give it away, I am very new to Arduino and circuits so thank you in advance for your patience. With that in mind, here's what I've done so far:
I've read/watched a few guides relating to this subject already:
I've read into more, but these are the main ones I've drawn from.
The first source gave me the best results, but could only detect frequencies up to approximately 19kHz (my guess is because of the sampling frequency set in the original code). I tried increasing the sampling rate with information from the second source but I still had the same problem. I'm sure I made mistakes in the code, but I also noticed I was experiencing a lot of clipping so I was wondering if my circuit needed to be set up differently.
Currently my circuit is identical to the one shown in source 3 but to a 3.5mm audio jack instead of directly to a microphone and with the clipping indicator LED hooked up to 12. However, I noticed a few guides showed a more complicated circuit for audio input (http://www.instructables.com/id/Arduino-Audio-Input/). Being new to circuits I had trouble following it and was unsure what I'd be getting from the more complicated circuit. At this point I thought maybe there was a shield that could take the place of that more complicated circuit.
Here is the code I got from the first source including the changes I made to try to get the sampling rate I need. What I found with this version is that it often spikes up to its new maximum of 38.4 kHz in the same way the previous version would spike to 19 kHz. Note that I didn't bother to change all the references to 38.4 kHz in the comments.
//generalized wave freq detection with sampling rate and interrupts
//by Amanda Ghassaei
//https://www.instructables.com/id/Arduino-Frequency-Detection/
//Sept 2012
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
*/
//clipping indicator variables
boolean clipping = 0;
//data storage variables
byte newData = 0;
byte prevData = 0;
unsigned int time = 0;//keeps time and sends vales to store in timer[] occasionally
int timer[10];//sstorage for timing of events
int slope[10];//storage for slope of events
unsigned int totalTimer;//used to calculate period
unsigned int period;//storage for period of wave
byte index = 0;//current storage index
float frequency;//storage for frequency calculations
int maxSlope = 0;//used to calculate max slope as trigger point
int newSlope;//storage for incoming slope data
//variables for decided whether you have a match
byte noMatch = 0;//counts how many non-matches you've received to reset variables if it's been too long
byte slopeTol = 3;//slope tolerance- adjust this if you need
int timerTol = 10;//timer tolerance- adjust this if you need
//variables for amp detection
unsigned int ampTimer = 0;
byte maxAmp = 0;
byte checkMaxAmp;
byte ampThreshold = 30;//raise if you have a very noisy signal
void setup(){
Serial.begin(9600);
pinMode(13,OUTPUT);//led indicator pin
pinMode(12,OUTPUT);//output pin
cli();//diable interrupts
//set up continuous sampling of analog pin 0 at 38.5kHz
//clear ADCSRA and ADCSRB registers
ADCSRA = 0;
ADCSRB = 0;
ADMUX |= (0 & 0x07); // set A0 analog input pin
ADMUX |= (1 << REFS0); //set reference voltage
ADMUX |= (1 << ADLAR); //left align the ADC value- so we can read highest 8 bits from ADCH register only
ADCSRA |= (1 << ADPS2); //set ADC clock with 32 prescaler- 16mHz/32=500kHz
ADCSRA |= (1 << ADATE); //enabble auto trigger
ADCSRA |= (1 << ADIE); //enable interrupts when measurement complete
ADCSRA |= (1 << ADEN); //enable ADC
ADCSRA |= (1 << ADSC); //start ADC measurements
sei();//enable interrupts
}
ISR(ADC_vect) {//when new ADC value ready
PORTB &= B11101111;//set pin 12 low
prevData = newData;//store previous value
newData = ADCH;//get value from A0
if (prevData < 127 && newData >=127){//if increasing and crossing midpoint
newSlope = newData - prevData;//calculate slope
if (abs(newSlope-maxSlope)<slopeTol){//if slopes are ==
//record new data and reset time
slope[index] = newSlope;
timer[index] = time;
time = 0;
if (index == 0){//new max slope just reset
PORTB |= B00010000;//set pin 12 high
noMatch = 0;
index++;//increment index
}
else if (abs(timer[0]-timer[index])<timerTol && abs(slope[0]-newSlope)<slopeTol){//if timer duration and slopes match
//sum timer values
totalTimer = 0;
for (byte i=0;i<index;i++){
totalTimer+=timer[i];
}
period = totalTimer;//set period
//reset new zero index values to compare with
timer[0] = timer[index];
slope[0] = slope[index];
index = 1;//set index to 1
PORTB |= B00010000;//set pin 12 high
noMatch = 0;
}
else{//crossing midpoint but not match
index++;//increment index
if (index > 9){
reset();
}
}
}
else if (newSlope>maxSlope){//if new slope is much larger than max slope
maxSlope = newSlope;
time = 0;//reset clock
noMatch = 0;
index = 0;//reset index
}
else{//slope not steep enough
noMatch++;//increment no match counter
if (noMatch>9){
reset();
}
}
}
if (newData == 0 || newData == 1023){//if clipping
PORTB |= B00100000;//set pin 13 high- turn on clipping indicator led
clipping = 1;//currently clipping
}
time++;//increment timer at rate of 38.5kHz
ampTimer++;//increment amplitude timer
if (abs(127-ADCH)>maxAmp){
maxAmp = abs(127-ADCH);
}
if (ampTimer==1000){
ampTimer = 0;
checkMaxAmp = maxAmp;
maxAmp = 0;
}
}
void reset(){//clea out some variables
index = 0;//reset index
noMatch = 0;//reset match couner
maxSlope = 0;//reset slope
}
void checkClipping(){//manage clipping indicator LED
if (clipping){//if currently clipping
PORTB &= B11011111;//turn off clipping indicator led
clipping = 0;
}
}
void loop(){
checkClipping();
if (checkMaxAmp>ampThreshold){
frequency = 76924/float(period);//calculate frequency timer rate/period
//print results
Serial.print(frequency);
Serial.println(" hz");
}
}
If I need to provide any more information, let me know, and I'll gladly post it. Any and all help would be appreciated, thanks.