Arduino OLED Spectrum Analyzer



Today I am make an Arduino OLED Spectrum Analyzer its work with 0 to 3.3v Analog input. This is very smooth and clear Visualization of audio frequencies. This is very important to make an audio spectrum analyser for DVD Music player, IPod, MP3 player and other Music player not only its look good it also represents any kind of music. You can make a box to fit it and attached in car or anywhere you want but I make just simple in breadboard.

Note: This project is only for simple audio device, a Real Spectrum Analyser devices are use in Microwave signal, Radar Signal, Dish Antenna Signal, Cable frequencies Signal, etc. Do not try to use for these proposes your Arduino will burn or crash permanently.

In this blog you are learning about a Arduino Spectrum Analyzer which follows:

>>SSD1306 Oled Display

>>Needed Parts

>>Libraries in we need to use

>>Super Easy Breadboard connection

>>Complete Code

SSD1306 Oled Display:

This very common and simple OLED Display for Arduino that very less count of Pin to connect in arduino that called I2C two wire interface the I2C stands for (I2C=IIC=Inter-Integrated Circuit) and OLED stands for Organic Light-Emitting Diodes. The size of this oled display is 0.96 inch that make it too much small and the total pixel is 128X64 px resolution you can convert a pic to bitmap and program to show in this display. In 16X2 LCD Display We need a backlight pin but in OLED this pin not required.

This is very simple to connect an OLED to Arduino Just follow these wiring:
OLED Pins  :    Arduino Pins
Vin             |        5V     //this is power Pin
GND           |        GND //this is GND Pin
SCL             |          A5     // this is I2C Pins
SDA            |          A4     //this is I2C Pins
Varieties of Arduino Pin If you use:
 Arduino Uno: SDA (A4), SCL (A5)
Arduino Nano: SDA (A4), SCL (A5)
Arduino Leonardo: SDA (20), SCL (21)
Arduino MEGA: SDA (20), SCL (21)

Needed Parts:

Arduino Nano or
Arduino Uno
128x68 I2C OLED Display
Jumper Wires
Breadboard

Libraries Used in This Project:

If you are beginner for this project, you need to download the Adafruit SSD1306 Master zip Library From SSD1306 available in Github and also need one more Adafruit GFX Library master.zip that is also available in Github. Open Arduino IDE Software go to the sketch tab, then here click include library and click Add Zip Library here you can add your downloaded library you can also download and install from Manage Library Option just search these library in this box.



And Now for This Project need to download FIX_FFT.h master zip from fix_fft.h and nano_engine.h library here Nano Engine (This is a built in header file of SSD1306).

Breadboard Connection:

This is very simple connection for Arduino Nano and oled. Here I am using 10k variable for adjust the volume of Music it connected with A0 pin and Audio input plug for connect with audio devices.


Audio Input:

Make sure always check voltage and current level in Analog Input of Arduino Board as Music or Sound Input, some amplifiers and Sound Devices are too much high Current as Output, do not plug on it. This Device needs only 3.8 to 4V and 40mA to 300mA Max voltage and current that come from mini portable amplifier, IPod, Portable Speaker, Mp3 Players, etc.

Complete Code:

Here you can copy and paste the code in Arduino IDE
This is a copyright code from ColoneWatch Github

// Copyright [2019] [colonelwatch]

#include <fix_fft.h>                  // 8-bit FFT library modified for Arduino
#include <ssd1306.h>                  // library for OLED
#include <nano_engine.h>              // library for nanoengine (which draws sector-by-sector on OLED because the ATtiny85 can't hold a full buffer)

// To get this program to operate, the SDA and SCL pins must be reassigned to 0 and 2 respectively in the library header file
// The file is located in libraries\ssd1306\src\intf\i2c\ssd1306_i2c_conf.h
// Make sure to undo this if the library will be used again in the future

// These are user-adjustable
//#define LOG_OUTPUT              // Uncomment to enable logarithmic output (exchanges absolute resoluton for more readable output; may require different below params)
#define SAMPLING_FREQUENCY 15000  // Sampling frequency (Actual max measured frequency captured is half)
#define TIME_FACTOR 3             // Smoothing factor (lower is more dynamic, higher is smoother) ranging from 1 to 10+
#define SCALE_FACTOR 12           // Direct scaling factor (raise for higher bars, lower for shorter bars)

#ifdef LOG_OUTPUT
const float log_scale = 64./log(64./SCALE_FACTOR + 1.);                              // Attempts to create an equivalent to SCALE_FACTOR for log function
#endif
const float coeff = 1./TIME_FACTOR;                                                  // Time smoothing coefficients (used to factor in previous data)
const float anti_coeff = (TIME_FACTOR-1.)/TIME_FACTOR;
const unsigned int sampling_period_us = round(1000000 * (2.0 / SAMPLING_FREQUENCY)); // Sampling period (doubled to account for overclock)

int8_t data[64], buff[32];                                     // used to store FFT input/output and past data
unsigned long microseconds;                                    // used for timekeeping
int summ, avg;                                                 // used for DC bias elimination

NanoEngine<TILE_32x32_MONO> engine;                            // declares nanoengine

void setup()
{
  OSCCAL = 240; // Overclocks the MCU to around 30 MHz, set lower if this causes instability, raise if you can/want
  
  ADCSRA &= ~(bit (ADPS0) | bit (ADPS1) | bit (ADPS2));       // clear ADC prescaler bits
  ADCSRA |= bit (ADPS2);                                      // sets ADC clock in excess of 10kHz
  ADCSRA |= bit (ADPS0);

  ssd1306_128x64_i2c_init();                                  // initializes OLED
  ssd1306_clearScreen();                                      // clears OLED
  
  engine.begin();                                             // inititalizes nanoengine
};

void loop()
{
  summ = 0;
  for (int i = 0; i < 64; i++) {
    microseconds = micros();
    
    data[i] = ((analogRead(A0)) >> 2) - 128;                        // Fitting analogRead data (range:0 - 1023) to int8_t array (range:-128 - 127)
    summ += data[i];
    while (micros() < (microseconds + sampling_period_us)) {        // Timing out uC ADC to fulfill sampling frequency requirement
    }
  }

  // Eliminating remaining DC component (produces usable data in FFT bin #0, which is usually swamped by DC bias)
  avg = summ/64;
  for (int i = 0; i < 64; i++){
    data[i] -= avg;
  }
    
  fix_fftr(data, 6, 0);                             // Performing real FFT
  
  // Time smoothing by user-determined factor and user-determined scaling
  for(int count = 0; count < 32; count++){
  if(data[count] < 0) data[count] = 0;                                          // Eliminating negative output of fix_fftr
  #ifdef LOG_OUTPUT
  else data[count] = log_scale*log((float)(data[count]+1));                     // Logarithmic function equivalent to SCALING_FACTOR*log2(x+1)
  #else
  else data[count] *= SCALE_FACTOR;                                             // Linear scaling up according to SCALE_FACTOR
  #endif
  data[count] = (float)buff[count] * anti_coeff + (float)data[count] * coeff;   // Smoothing by factoring in past data
  buff[count] = data[count];                                                    // Storing current output as next frame's past data
  if(data[count] > 63) data[count] = 63;                                        // Capping output at screen height
  }
  
  // Output to SSD1306 using nanoengine canvas from library
  engine.refresh();                                               // Mark entire screen to be refreshed
  engine.canvas.clear();                                          // Clear canvas as previous data
  for(int i = 0; i < 8; i++){
    engine.canvas.drawVLine(i*4,31-(data[i]+1),31);          // Draw to canvas data for lower-leftest sector (FFT bins 0 - 7, lower half)
  }
  engine.canvas.blt(0,32);                                        // Outputs canvas to OLED with an offset (x pixels, y pixels)
  engine.canvas.clear();
  for(int i = 0; i < 8; i++){
    if(data[i] > 31) engine.canvas.drawVLine(i*4,31-(data[i]-31),31);     // Draw to canvas data for upper-leftest sector (FFT bins 0 - 7, upper half)
  }
  engine.canvas.blt(0,0);
  engine.canvas.clear();
  for(int i = 8; i < 16; i++){
    engine.canvas.drawVLine((i-8)*4,31-(data[i]+1),31);      // FFT bins 8 - 15, lower half
  }
  engine.canvas.blt(32,32);  
  engine.canvas.clear();
  for(int i = 8; i < 16; i++){
    if(data[i] > 31) engine.canvas.drawVLine((i-8)*4,31-(data[i]-31),31);   // FFT bins 9 - 15, upper half
  }
  engine.canvas.blt(32,0);
  engine.canvas.clear();
  for(int i = 16; i < 24; i++){
    engine.canvas.drawVLine((i-16)*4,31-(data[i]+1),31);     // FFT bins 16 - 23, lower half
  }
  engine.canvas.blt(64,32);
  engine.canvas.clear();
  for(int i = 16; i < 24; i++){
    if(data[i] > 31) engine.canvas.drawVLine((i-16)*4,31-(data[i]-31),31);  // FFT bins 16 - 23, upper half 
  }
  engine.canvas.blt(64,0);
  engine.canvas.clear();
  for(int i = 24; i < 32; i++){
    engine.canvas.drawVLine((i-24)*4,31-(data[i]+1),31);     // FFT bins 24 - 31, lower half
  }
  engine.canvas.blt(96,32);
  engine.canvas.clear();
  for(int i = 24; i < 32; i++){
    if(data[i] > 31) engine.canvas.drawVLine((i-24)*4,31-(data[i]-31),31);  // FFT bins 24 - 31, upper half
  }
  engine.canvas.blt(96,0);
}

This is not much hard to build just need some simple stuff. That all for guys.

Comments

  1. Hi I need some help ? Can I get your email ID? By just sending a mail to iamsasi@gmail.com please

    ReplyDelete
  2. What a crap instruction what is wrong with you,

    ReplyDelete
  3. Is it possible to connect 2 OLED at one Nano? I want to display the Spectrum for the left and right channel. Can you help me?

    ReplyDelete
    Replies
    1. Yes it is possible. Your OLEDS need to have different I2C Adresses

      Delete

Post a Comment