/*********************************************************************************************************************************************
Nitin William Arduino Audio FFT with OLED
Adapted from learnelectronics:https://www.youtube.com/watch?v=5RmQJtE61zE
Aadapted from cbm80amiga: https://www.youtube.com/watch?v=EnvhEgjrHsw
https://github.com/GadgetReboot/Arduino/blob/master/Uno/Spectrum_Analyzer/Spectrum_Analyzer.ino
https://sites.google.com/site/myscratchbooks/home/projects/proejct-20-oled-spectrum-analizer-using-max9812-microphone-amplifier
**********************************************************************************************************************************************/
//Libraries
#include <Wire.h> //IDE Standard
#include <Adafruit_GFX.h> //Adafruit GFX https://github.com/adafruit/Adafruit-GFX-Library
#include <Adafruit_SSD1306.h> //Adafruit SSD1306 https://github.com/adafruit/Adafruit_SSD1306
#include <fix_fft.h> //https://www.arduinolibraries.info/libraries/fix_fft
const byte audioIn = A0; //Define analog pin for FFT
char re[128], im[128]; //Real and Imaginary FFT result arrays
byte yaxis = 63; //OLED y-axis drawing boundary limit
byte xaxis = 0; //OLED x-axis limit
Adafruit_SSD1306 display = Adafruit_SSD1306(128, 64, &Wire,-1);
void setup()
{
Wire.begin(); //I2C Master
pinMode(audioIn,INPUT); //Set A0 as analog input pin
analogReference(DEFAULT);
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.setTextColor(WHITE);
display.clearDisplay();
display.display();
}
/*
The FFT real/imaginary data are stored in a char data type as a signed -128 to 127 number
This allows a waveform to swing centered around a 0 reference data point
The ADC returns data between 0-1023 so it is scaled to fit within a char by dividing by 4 and subtracting 128.
eg (0 / 4) - 128 = -128 and (1023 / 4) - 128 = 127
*/
void loop()
{
for (byte i = 0; i < 128; i++) //Read 128 analog input samples from ADC
{
int sample = analogRead(audioIn);
re[i] = sample / 4 - 128; //Scale the samples to fit within a char variable
im[i] = 0; //There are no imaginary samples associated with the time domain so set to 0
}
fix_fft(re, im, 7, 0); //Send the samples for FFT conversion, returning the real/imaginary results in the same arrays
display_spectrum();
}
/*
For a 16 MHz Arduino the ADC clock is set to 16 MHz/128 = 125 KHz. Each conversion in AVR takes 13 ADC clocks so 125 KHz /13 = 9615 Hz.
The data array will contain frequency bin data in locations 0..127 for samples up to the sampling frequency of approx. 9 KHz
Each frequency bin will represent a center frequency of approximately (9 KHz / 128 samples) = 70 Hz
Due to Nyquist sampling requirements, we can only consider sampled frequency data up to (sampling rate / 2) or (9 KHz / 2) = 4.5 KHz
Therefore we only acknowledge the first 64 frequency bins [0..63] = [0..4.5KHz]
*/
void display_spectrum()
{
display.clearDisplay();
for (byte i = 1; i < 64; i++)
{
int dat = sqrt(re[i] * re[i] + im[i] * im[i]); //Frequency magnitude is the square root of the sum of the squares of the real and imaginary parts of a vector
if (dat >= 32 ) dat = 32;
display.drawLine(i * 2, yaxis, i * 2, yaxis - dat, WHITE);// draw bars for each frequency bin from 70 Hz to 4.5 KHz
}
display.setTextSize(2);
display.setCursor(16,8);
display.print("SPECTRUM");
display.display();
}