Making a system to monitor machinery and report anomalies for pre-failure detection

We use piezoelectric vibration transducers that have a wide bandwidth which includes the human audible range of frequencies which can be helpful when trying to synchronize signals. B&K and others make suitable ones.

Edit:
Also known as accelerometers.

Thank you very much, very useful information :slight_smile:

You're welcome. Glad to be of some help.

My 2c is Nobody ever learns anything by Not doing

I say jump in and see where you get to. It may well be that you end up needing a Raspberry Pi or similar, but get started with something.
There are many knowledgeable people on this forum that can help with specific questions, problems or issues, but if you ask for opinions......you'll get as many as there are watchers of this thread (me included :slight_smile: )

By the way, it does sound like a fascinating project

I have started by connecting my Uno to a liquidcrystal i2c display which is a learning experience in itself, especially since the official link has bad code. Then, last night I tried two different microphones that I had and spent a while learning how to connect them, what code to use to get set up, and how to troubleshoot the fact that it didn't work. I must say that chatgpt was very helpful and threw me test codes at one stage telling me to hook up a potentiometer and check the results in the serial monitor [something I never knew existed 2 days ago]. Then it threw me a code to install so we could read the numbers from the microphone. We narrowed it down to the fact that they were not the right microphones, simply sound detectors that were either low or high, so I have ordered a few max4466 microphones which should get me setting up a spectrum analyzer at least. I have a larger tft display on its way for my final project but while I'm waiting I want to accomplish a spectrum analyzer on my 1602 display as a learning experience. I know about 100X more than I did a week ago :smiley:

Not the same thing I realize, but I just completed a project using an Arduino Uno to monitor three temperature inputs and to use Fuzzy Logic AI to control a Hot Air Heater for PCB rework.

In addition, it displays a real-time graph of the monitored temps.
It's amazing what you can cram onto a UnoR3 if you try.

Details are here: PCB Preheater Hardware and Software with photos

ChatGPT actually knows nothing, and is likely to give you the wrong answer to a question. The skill in using it is to be able to spot where it starts giving you rubbish. So I would verify any information you get by searching the net for that piece of information before trusting it.

I do check up the advice it gives me, it really helped me a lot last night and, as mentioned, helped me diagnose why my setup wasn't working by providing me code to install for the tests, which all worked! Things that would have taken days, back and forth on the forum were answered in seconds.
Of course I will never trust it blindly but in this case it knew a lot more than I did and proved itself.

After reading this blog I'm wondering how easy it will be to create a "3D" display as in the image below. Maybe even FFT readings once per minute placed next to each other to give a 3D representation of the changes in sound levels over time.

So far I have managed to get an Adafruit 2.8 TFT display shield working with my Arduino Mega 2560 using the MCUFRIEND library. ChatGpt has done a good job of helping me write a code that records the initial reading and then only displays live data that is higher than the initial reading in red. In the image I am whistling which is causing a high frequency spike.

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]);
  }
}