Detecting Frequencies and their Magnitude

Hey guys, I’m attempting to use an MAX9814 microphone, an arduino uno r3, a L293D motor driver and dc motor to detect a frequency and then start a motor for a given amount of time. With the image of the setup and following code below I’ve been able to pick up noise and implement the goertzel algorithm to isolate the desired frequency of the tuning fork I’ve been using. However I’ve found that this process is rather inconsistent in loud environments which I need it to operate in, upwards of 90dB. False positives where the magnitude threshold has been met by things other than the tuning fork have been happening frequently. Also it seems like the tuning fork gets drowned out sometimes. It also is affected by crashes around it so advice on how to mount it to reduce this would also be helpful.

Overall I’m looking for any suggestions on

Code improvements
Tuning fork amplification
Noise reduction of environment
Proper mounting of microphone
Possible use of resonance?
Anything else you think may be useful

Thanks!

#include <Arduino.h>

// Motor driver pins
#define ENA 5 // PWM speed control (L293D)
#define IN1 9 // Motor direction control
#define IN2 10 // Motor direction control

// Microphone input pin
#define MIC_PIN A0

// Frequency detection settings
const float targetFreq = 506.5; // Change this to your desired frequency (e.g., 293.66 Hz for D4)
const int sampleRate = 9000; // Sampling rate in Hz (must be at least 2x target frequency)
const int numSamples = 100; // Number of samples (adjust for accuracy)
const int motorRunTime = 3000; // Motor run time in milliseconds (2 seconds)
const int detectionThreshold = 3000; // Magnitude threshold for detection

// Noise Filtering Settings
const int noiseThreshold = 200; // Ignore low-magnitude signals

// Function to run Goertzel algorithm for frequency detection
float detectFrequency(float targetFrequency) {
float coeff, q0, q1, q2, magnitude;
float omega = (2.0 * PI * targetFrequency) / sampleRate;
coeff = 2.0 * cos(omega);

q1 = 0;
q2 = 0;

for (int i = 0; i < numSamples; i++) {
    int sample = analogRead(MIC_PIN) - 512; // Normalize around 0
  if (abs(sample) < noiseThreshold) continue; // Ignore weak signals 
    q0 = coeff * q1 - q2 + sample;
    q2 = q1;
    q1 = q0;
}

magnitude = sqrt(q1 * q1 + q2 * q2 - q1 * q2 * coeff);
return magnitude;

}

// Function to turn the motor on
void turnMotorOn() {
digitalWrite(IN1, HIGH);
digitalWrite(IN2, LOW);
analogWrite(ENA, 200); // Set motor speed (0-255)
}

// Function to turn the motor off
void turnMotorOff() {
digitalWrite(IN1, LOW);
digitalWrite(IN2, LOW);
}

void setup() {
pinMode(ENA, OUTPUT);
pinMode(IN1, OUTPUT);
pinMode(IN2, OUTPUT);
Serial.begin(9600);
}

void loop() {
float magnitude = detectFrequency(targetFreq);
Serial.print("Detected Magnitude: ");
Serial.println(magnitude);

if (magnitude > detectionThreshold) {
    Serial.println("Frequency detected! Turning on motor.");
    turnMotorOn();
    delay(motorRunTime);
    turnMotorOff();
    Serial.println("Motor off.");
}

delay(200); // Small delay to avoid constant triggering

}

Please edit your post to add code tags (<code> editor button).

One major problem with your approach is that the audio sample frequency must be twice the value of the highest frequency in the input signal. Otherwise, sampling artifacts known as aliasing can make the signal analysis difficult to impossible.

In very loud environments, distortion in the microphone and input amplifier can actually create high frequency signals not otherwise present.

For the Uno R3 with default analog sample rate of about 9 kHz, a good low pass filter with reasonably sharp cutoff of 4.5 kHz is required between the microphone and the analog input.

Wrap that rascal!

1 Like

You need what is called a "band pass filter". That would be designed to pass the frequencies of the tuning forks with little attenuation, but attenuate all other frequencies as much as possible.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.