Hi folks,

I’m working on a little project about detecting the most dominant frequency when music is playing. Using an Arduino Uno with a MAX9814 mic, also tied a KY-037 with the same results.

In theory, everything should be working just fine as I’ve tested different parts of the code individually, but I’m starting to think that the issue I have is hardware related.

I use the ArduinoFFT library for frequency analysis and with 128 samples and a sampling frequency of 10kHz, which I thought to be about the limits of an Arduino Uno. While playing around with the frequency generator (found here: Online Tone Generator - generate pure tones of any frequency ), I’ve noticed that below ~160Hz the frequency readings become useless. With a 64 sample size that threshold is at around 330Hz, which makes sense, i guess.

My question now is whether I can tweak the code in a way to make it accurately calculate bass frequencies from 50-300+Hz, use a different library or get a faster microcontroller - and if so which one.

```
#include "arduinoFFT.h"
#define SAMPLES 128 //SAMPLES-pt FFT. Must be a base 2 number. Max 128 for Arduino Uno.
#define SAMPLING_FREQUENCY 10000 //Ts = Based on Nyquist, must be 2 times the highest expected frequency.
arduinoFFT FFT = arduinoFFT();
//FFT related variable definition
unsigned int sampling_period_us;
unsigned long microseconds;
double vReal[SAMPLES]; //create vector of size SAMPLES to hold real values
double vImag[SAMPLES]; //create vector of size SAMPLES to hold imaginary values
//definition of microphone properties (from MicSetup)
const int Mic = A1; //Analog Input Pin on Arduino
const float AmpRange = 40; //value from MicSetup
//additional variables
double sampleCopy[SAMPLES];
double Peak;
float AvgAmp;
float HighAmp;
float LowAmp;
float DerivHigh;
float DerivLow;
float DerivMax;
void setup() {
Serial.begin(115200); //Baud rate for the Serial Monitor
sampling_period_us = round(1000000*(1.0/SAMPLING_FREQUENCY)); //Period in microseconds
}
void loop() {
//FFT gets samples
sampling();
copySamples();
Peak = hertz();
//checking if highest 10% amplitude is outside thresholds
AvgAmp = getAvgAmp();
bubbleSort(sampleCopy, SAMPLES);
HighAmp = getHighAmp();
LowAmp = getLowAmp();
DerivHigh = HighAmp - AvgAmp;
DerivLow = AvgAmp - LowAmp;
DerivMax = max(DerivHigh, DerivLow);
if(DerivMax < AmpRange){
Peak = 0;
}
Serial.println("Frequency: ");
Serial.println(Peak);
//Serial.println();
//delay(3000);
}
//Sampling
void sampling(){
for(int i=0; i<SAMPLES; i++){ /*SAMPLING*/
vReal[i] = analogRead(Mic);
vImag[i] = 0;
while(micros() - microseconds < sampling_period_us){
microseconds += sampling_period_us;
//Serial.println(vReal[i]); //debug
}
}
}
//copies samples for threshold check
void copySamples(){
for(int i=0; i<SAMPLES; i++){
sampleCopy[i] = vReal[i];
}
}
//FFT calculates frequency
double hertz(){
double peak;
FFT.Windowing(vReal, SAMPLES, FFT_WIN_TYP_HAMMING, FFT_FORWARD); /*FFT*/
FFT.Compute(vReal, vImag, SAMPLES, FFT_FORWARD);
FFT.ComplexToMagnitude(vReal, vImag, SAMPLES);
peak = FFT.MajorPeak(vReal, SAMPLES, SAMPLING_FREQUENCY);
peak = peak*0.825;
return peak;
}
//calculates median amplitude
float getAvgAmp(){
float sum = 0;
float avg;
for(int i = 0; i < SAMPLES; i++){
sum = sum + sampleCopy[i];
}
return avg = sum/SAMPLES;
}
//sorts samples from low to high
void bubbleSort(double a[], int size) {
for(int i=0; i<(size-1); i++) {
for(int o=0; o<(size-(i+1)); o++) {
if(a[o] > a[o+1]) {
int t = a[o];
a[o] = a[o+1];
a[o+1] = t;
}
}
}
}
//gets median amplitude from highest 10% of samples
float getHighAmp(){
int Highs = round(0.1*SAMPLES)+1;
//Serial.println(Highs);
float sum = 0;
float Avg;
for(int i = SAMPLES-1; i > SAMPLES-Highs-1; i--){
sum = sum + sampleCopy[i];
}
return Avg = sum/Highs;
}
//gets median amplitude from lowest 10% of samples
float getLowAmp(){
int Lows = round(0.1*SAMPLES)+1;
//Serial.println(Lows);
float sum = 0;
float Avg;
for(int i = 0; i < Lows; i++){
sum = sum + sampleCopy[i];
}
return Avg = sum/Lows;
}
```

Code explanation:

The important part for this issue is the sampling and FFT part. I implemented the rest so the frequency will be set to zero if the amplitude is too low. While causing a significant drop in speed, this part should not influence the frequency calculation part as i have tried it without threshold-checking. I use a separate program to determine the amplitude threshold. I got the FFT sample code from an example on the internet, sadly I don’t know the exact source.

My understanding is that to resolve lower frequencies, I need a high sampling rate and ideally a lot of samples and bins. When a 100Hz tone is being played, the frequency output isn’t stable, but repetitive, which is due to the resolution limitations?

I’m a newbie when it comes to Arduino and sound analysis, although I have previous experience with coding.

Thanks in advance!