I'm still on a mission to create my project that will detect audio anomalies in machinery for early warning of bearing failure. I've included the code that I have admittedly had 'external help' with and am still very much a beginner. In my code the initial idea was to:
1.) Take a 5 second "average" reading of the audio using an Esp32 with a max4466 mic and save that pattern for reference.
2.) Run a 'live' audio analysis with smoothing to try and measure only constant audio, not intermittent sounds, and then display the difference between the two, so that I can detect 'new' sounds developing.
I have concluded that having two different types of measuring only complicates matters and I would like to change the code so that I do a 5 second average and save the first pattern, and then keep doing the exact same reading and compare it with the first to show any increases of an frequency. In time I will change the amount of time that the readings are done once I get an idea of how well it is working.
I know it's a long bit of code, with possibly many mistakes, all I'm asking is if someone could have a look at the first 5 second reading that is taken and tell me whether that will actually return an average reading over the first 5 seconds of monitoring so I know that I can proceed from there.
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <arduinoFFT.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
#define SAMPLES 256
#define SAMPLING_FREQUENCY 32000 // Placeholder, actual sampling rate will be measured
#define SCALING_FACTOR 0.0001
#define AVERAGE_TIME 5000
#define CONSECUTIVE_DETECTIONS_REQUIRED 3
#define MIN_FREQUENCY 3500 // Minimum frequency to consider (500 Hz)
#define LED_PIN 23
#define POTENTIOMETER_PIN 13
arduinoFFT FFT = arduinoFFT();
double vReal[SAMPLES];
double vImag[SAMPLES];
double initialFFT[SAMPLES / 2];
double liveFFT[SAMPLES / 2];
double previousFFT[SAMPLES / 2] = {0}; // Buffer for previous FFT results
unsigned long lastHighestFrequencyUpdate = 0;
const unsigned long HIGHEST_FREQUENCY_UPDATE_INTERVAL = 1000;
double highestFrequency = -1;
bool frequencyUpdated = false;
int consecutiveDetections = 0;
unsigned long frequencyStartTime = 0;
unsigned long constantFrequencyTime = 2000; // 2 seconds for constant detection
int detectionThreshold = 250; // Default threshold value
void setup() {
Serial.begin(115200);
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("SSD1306 allocation failed"));
for (;;);
}
display.setRotation(0); // Rotate the display upside down
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.println(F("Initializing..."));
display.display();
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW);
pinMode(POTENTIOMETER_PIN, INPUT); // Set potentiometer pin as input
// Measure actual sampling frequency
unsigned long actualSamplingFrequency = measureSamplingFrequency();
// Capture and average the initial FFT data over 5 seconds
captureFFT(initialFFT, true, actualSamplingFrequency);
display.clearDisplay();
display.setTextSize(1);
display.setCursor(0, 0);
display.print("Freq: ");
display.display();
}
void loop() {
detectionThreshold = analogRead(POTENTIOMETER_PIN); // Read value from potentiometer
detectionThreshold = map(detectionThreshold, 0, 4095, 10, 1000); // Map value to threshold range
// Round the threshold to the nearest multiple of 10
detectionThreshold = (detectionThreshold / 10) * 10;
unsigned long actualSamplingFrequency = measureSamplingFrequency();
captureFFT(liveFFT, false, actualSamplingFrequency);
displayFFT(liveFFT);
double newHighestFrequency = getFrequencyOfHighestSpike(liveFFT, actualSamplingFrequency);
if (newHighestFrequency > MIN_FREQUENCY) {
if (millis() - lastHighestFrequencyUpdate >= HIGHEST_FREQUENCY_UPDATE_INTERVAL) {
if (newHighestFrequency == highestFrequency) {
consecutiveDetections++;
} else {
highestFrequency = newHighestFrequency;
consecutiveDetections = 1;
}
if (consecutiveDetections >= CONSECUTIVE_DETECTIONS_REQUIRED) {
if (millis() - frequencyStartTime >= constantFrequencyTime) {
digitalWrite(LED_PIN, HIGH);
}
} else {
frequencyStartTime = millis();
digitalWrite(LED_PIN, LOW);
}
lastHighestFrequencyUpdate = millis();
}
} else {
digitalWrite(LED_PIN, LOW);
consecutiveDetections = 0;
highestFrequency = -1;
}
displayFrequency(newHighestFrequency);
delay(100);
}
unsigned long measureSamplingFrequency() {
unsigned long startMicros = micros();
for (int i = 0; i < SAMPLES; i++) {
vReal[i] = analogRead(34); // Reading microphone data
}
unsigned long elapsedMicros = micros() - startMicros;
double actualSamplingRate = (1000000.0 / (elapsedMicros / SAMPLES));
Serial.print("Actual Sampling Rate: ");
Serial.println(actualSamplingRate);
return actualSamplingRate;
}
void captureFFT(double* fftResults, bool initial, unsigned long samplingFrequency) {
double maxFrequencies[SAMPLES / 2] = {0};
unsigned long startTime = millis();
double previousAverage[SAMPLES / 2] = {0}; // Buffer for smoothing
if (initial) {
int count = 0;
while (millis() - startTime < AVERAGE_TIME) {
for (int i = 0; i < SAMPLES; i++) {
vReal[i] = analogRead(34);
vImag[i] = 0;
}
FFT.Windowing(vReal, SAMPLES, FFT_WIN_TYP_HAMMING, FFT_FORWARD);
FFT.Compute(vReal, vImag, SAMPLES, FFT_FORWARD);
FFT.ComplexToMagnitude(vReal, vImag, SAMPLES);
for (int i = 2; i < SAMPLES / 2; i++) {
maxFrequencies[i] += vReal[i];
}
count++;
}
for (int i = 0; i < SAMPLES / 2; i++) {
double averageValue = maxFrequencies[i] / count;
fftResults[i] = (previousAverage[i] * 0.9) + (averageValue * 0.1);
previousAverage[i] = fftResults[i];
}
} else {
for (int i = 0; i < SAMPLES; i++) {
vReal[i] = analogRead(34);
vImag[i] = 0;
}
FFT.Windowing(vReal, SAMPLES, FFT_WIN_TYP_HAMMING, FFT_FORWARD);
FFT.Compute(vReal, vImag, SAMPLES, FFT_FORWARD);
FFT.ComplexToMagnitude(vReal, vImag, SAMPLES);
for (int i = 2; i < SAMPLES / 2; i++) {
fftResults[i] = (previousFFT[i] * 0.9) + (vReal[i] * 0.1);
previousFFT[i] = fftResults[i];
}
}
// Debugging output for FFT results
debugFFTResults(fftResults, samplingFrequency);
}
double getFrequencyOfHighestSpike(double* fftData, unsigned long samplingFrequency) {
int highestIndex = 2;
double highestValue = 0;
for (int i = 2; i < SAMPLES / 2; i++) {
double frequency = (i * samplingFrequency) / SAMPLES;
if (frequency > MIN_FREQUENCY) {
double value = fftData[i];
if (value > highestValue && value > detectionThreshold) { // Use variable detectionThreshold
highestValue = value;
highestIndex = i;
}
}
}
double frequency = highestIndex * ((double)samplingFrequency / (double)SAMPLES);
return frequency;
}
void displayFFT(double* fftData) {
display.clearDisplay();
for (int i = 2; i < SAMPLES / 2; i++) {
double normValue = (fftData[i] - initialFFT[i]) * SCALING_FACTOR;
int barHeight = normValue * (SCREEN_HEIGHT / 2);
if (barHeight > SCREEN_HEIGHT / 2) barHeight = SCREEN_HEIGHT / 2;
display.drawLine(i * (SCREEN_WIDTH / (SAMPLES / 2)), SCREEN_HEIGHT, i * (SCREEN_WIDTH / (SAMPLES / 2)), SCREEN_HEIGHT - barHeight, SSD1306_WHITE);
}
display.display();
}
void displayFrequency(double frequency) {
display.clearDisplay(); // Clear the display to prevent overlapping text
display.setTextSize(1);
// Display threshold value at the top
display.setCursor(0, 0);
display.print("T:");
display.print(detectionThreshold);
// Display frequency value on the same line
display.setCursor(40, 0);
if (frequency > MIN_FREQUENCY) {
display.print("Frq:");
display.print(frequency);
display.print(" Hz");
} else {
display.print("Freq: No Freq");
}
// Calculate the arrow position based on the detectionThreshold
int arrowPosition = map(detectionThreshold, 10, 1000, SCREEN_HEIGHT - 10, 0);
// Draw the arrow on the right-hand side of the display
display.setCursor(SCREEN_WIDTH - 8, arrowPosition);
display.print("<"); // Draw arrow
display.display();
}
void debugFFTResults(double* fftData, unsigned long samplingFrequency) {
for (int i = 2; i < SAMPLES / 2; i++) {
double frequency = (i * samplingFrequency) / SAMPLES;
Serial.print("Frequency: ");
Serial.print(frequency);
Serial.print(" Hz, Magnitude: ");
Serial.println(fftData[i]);
}
}