arduinoFFT and Adafruit OLED Display

If I Upload this code:

#include <arduinoFFT.h>

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "math.h"

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define IN_PIN        A0 // Mic read pin
#define OLED_RESET     -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C // 0x3C for 128x64

#define SAMPLES 64  //N for N-point FFT         
#define SAMPLING_FREQUENCY 20000  //Fs, Make sure satisifies Nyquist Criteria

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);


unsigned int sampling_period_us;  //Time period
unsigned long microseconds; //for storing Time

double vReal[SAMPLES];
double vImag[SAMPLES];
arduinoFFT FFT = arduinoFFT(vReal, vImag, SAMPLES, SAMPLING_FREQUENCY);

void setup() {
  Serial.begin(115200);
  pinMode(IN_PIN, INPUT);

  //SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
  }

  sampling_period_us = round(1000000 * (1.0 / SAMPLING_FREQUENCY));  //Time period is inverse of Frequency

  display.clearDisplay();

  text_displayer("0kHz", 0, 0, 1);
  text_displayer("5kHz", 54, 0, 1);
  text_displayer("10kHz", 94, 0, 1);  
  text_invert_displayer("audioFFT ", 0, 56, 1);
  text_displayer("313hz/u", 85, 56, 1);
}

void loop() {

  //Reading 64 samples at Fs frequency
  for(int i = 0; i < SAMPLES; i += 1){
    microseconds = micros();

    vReal[i] = analogRead(IN_PIN);
    vImag[i] = 0;
    
    while(micros()-microseconds < sampling_period_us){}
  }

  FFT.DCRemoval();
  FFT.Windowing(FFT_WIN_TYP_HAMMING, FFT_FORWARD);
  FFT.Compute(FFT_FORWARD);
  FFT.ComplexToMagnitude();

  //Finding Max frequency in FFT result for Normalization
  int peak = 0.0;
  for(int i = 0; i < SAMPLES; i += 1){
    if( peak < vReal[i] ){
      peak = vReal[i];
    }
  }

  //Displaying (N/2) bins as Final result
  double invPeak = 44.0/(peak*1.0);
  display.fillRect(0, 7, 127, 48, BLACK);
  for(int j = 0; j < SAMPLES; j += 1){
    display.fillRect(j*4, (56.0-(vReal[j]*invPeak)), 3, (vReal[j]*invPeak), WHITE);  
  }

  display.display();
  delay(1);
}

//Function for displaying Text on OLED display
void text_displayer(String text, int x, int y, int s){
  display.setTextSize(s);
  display.setTextColor(WHITE);
  display.setCursor(x, y);
  display.println(text);

  display.display();
}

//Function for displaying InverseBackground Text on OLED display
void text_invert_displayer(String text, int x, int y, int s){
  display.setTextSize(s);
  display.setTextColor(BLACK, WHITE);
  display.setCursor(x, y);
  display.println(text);

  display.display();
}

I obtain this message in Serial Monitor:

SSD1306 allocation failed
SSD1306 allocation failed
...

But if I use this code:

//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 >> 2) - 128; // 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();
  
  display.display();
  delay(1);
}
/* 
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 text_displayer(String text, int x, int y, int s){
  display.setTextSize(s);
  display.setTextColor(WHITE);
  display.setCursor(x, y);
  display.println(text);

  //display.display();
}

//Function for displaying InverseBackground Text on OLED display
void text_invert_displayer(String text, int x, int y, int s){
  display.setTextSize(s);
  display.setTextColor(BLACK, WHITE);
  display.setCursor(x, y);
  display.println(text);

  //display.display();
}

void display_spectrum()
{
  display.clearDisplay();
  int yOffset = 12;
  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 >= 63 ) dat = 63;
    display.drawLine(i * 2, yaxis - yOffset, i * 2, yaxis - dat - yOffset, WHITE);// draw bars for each frequency bin from 70 Hz to 4.5 KHz
  }
  // display.setTextSize(2);
  // display.setCursor(16,8);
  // display.print("SPECTRUM"); 

  text_displayer("0kHz", 0, 0, 1);
  text_displayer("5kHz", 104, 0, 1);
  text_invert_displayer("audioFFT", 0, 56, 1);
     
  display.display(); 
}

It all works!

However, in my opinion, the implementation of the arduinoFFT.h library is easier than the implementation of the fix_fft.h library. Is there any way to fix this so that I can use the arduinoFFT library? Thank you all

After compiling your first sketch, the following memory usages are displayed:

Global variables use 1080 bytes (52%) of dynamic memory, leaving 968 bytes for local variables. Maximum is 2048 bytes.

The two really important bits are these:

  1. Global variables use 1080 bytes
  2. Maximum is 2048 bytes.

That means you have 968 bytes of RAM left for local variables and stack requirements.

The Adafruit_SSD1306 library wants to allocate 1024 bytes of RAM for the display. Clearly, that won't work. You don't have 1024 bytes left. And even if you did, the sketch still wouldn't run, because you'd have nothing left over for the stack or local variables.

So you either have to reduce your RAM usage dramatically, or use a board with more RAM.

If you don't need graphics on the display, use the text only SSD1306 library, which does not require or allocate that large display buffer.

Hi thank you for your reply! I tried it with the ESP32 card and now it actually works! However, I still have problems with my KY-037 KY-038 microphone, in fact it is not accurate at all. This mic is probably not suitable and I should consider a MAX4466. Can you advise me?

Hi thank you for your reply! Unfortunately, I need to create a spectrum analyzer that is as good as possible. However, I will consider the library you recommended for other projects! P.S. I've made a bit of a mess with previous posts :slight_smile:

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