Frequency Spectrum Analyzer – Overview

     
     The purpose of our project was to create a Frequency Spectrum Analyzer that receives an analog input via a 3.5mm audio jack and shows the frequencies of the signal on a board of 60 LEDs. The fourier transform converts audio output frequencies back into the original input frequencies and was implemented via the fix_fft library for Arduino.
     The first step of programming was to test the LEDs with example code from Adafruit, the company that manufactures them. This strandtest program includes functions such as color flush which lights each LED in sequence to a given color with a delay between each illumination of the LEDs. Once the LEDs were proven to be functional, the LED enclosure was built (here is the strand test with the enclosure) and analog signal testing was begun. First, the 3.5mm auxiliary jack’s right channel was connected through a 3.5mm breakout adapter to the analog 0 port, then the jack’s sleeve was connected to the ground of the board. To process the signal, the 1024 analog samples were sent as an array through the fix_fft function within the fix_fft library. 10 samples were taken from the output of the fft and sent to the terminal to verify functionality. Here an issue was discovered. If the analog reference value is not set, the output will show signal noise due to floating values. This problem was fixed by adding analogReference(DEFAULT) to the setup function.
     To display the fft output values, a setBar function was created. This function receives the value for which frequency column to modify and the data for that frequency set. If the input data value is above the threshold for a given column, the LEDs will light up vertically indicating how strong the frequency is at that position. For instance, if the threshold is 15 and the column is 1, the third LED will light up with a value of data greater than 45 (15*3). To set the color of the LEDs, the setPixelColor(n,color) function was used. The bottom row’s color is set to green with a gradient reaching red at the top row. After each iteration through all received analog data, all LEDs are cleared until new data is received. This completes the cycle for spectrum conversion and displaying the data.

Frequency Spectrum Analyzer – Schematics


To display the fft output values, a setBar function was created. This function receives the value for which frequency column to modify and the data for that frequency set. Six strip color variables are set corresponding to the six rows of the LED grid along with a variable threshold for modifying gain. Because the LEDs follow a zig-zag pattern, the LED position values increase for every other column and decrease for the rest as you count from the base of the enclosure to the top. This can be illustrated through a table.
54 53 . . . . . .             18 17 6 5
55 52 . . . . .               19 16 7 4
56 51 . . . . .               20 15 8 3
57 50 . . . . . .             21 14 9 2
58 49 . . . . . .           22 13 10 1
59 48 47 36 35 24 23 12 11 0

An issue arises if you set the LED n value to the column number multiplied by 6. This will invert the direction of the column lighting in every odd numbered column. To address this, the odd numbered columns have a bottom LED value of 6*column+5 which correctly sets the second column base LED n value to 11 (1*6+5). Then you count down as you increase in the column.

Frequency Spectrum Analyzer – Code

Below is the entirety of the source code, but the magic happens within this segment.

// Loop for the number of samples to take from the analog signal
for (int i = 0; i < 128; i++) {
// Store the value from the from analog pin 0
val = analogRead(A0);
// Store a modified value into the arrays
data[i] = val / 4 - 128;
im[i] = 0;
}

// Run the fixed FFT function on the arrays
fix_fft(data, im, 7, 0);

Here you can see that the loop runs 128 times and this corresponds to reading the analog input 128 times. This data is then stored inside a data array which gets passed to the fix_fft library function. Within this function, it converts each of the analog values into their corresponding frequency levels. Almost all of the rest of the code is to control the LEDs themselves

/* This section of code includes the necessary libraries to run our code properly
// Adafruit_NeoPixel.h – Gives us commands to initialize and operate the NeoPixel LEDs
// fix_fft.h – Gives us commands to run the Fast Fourier Transform (FFT)
// avr/power.h – Reduces power consumption if the board runs AVR architecture */
#include <Adafruit_NeoPixel.h>
#include “fix_fft.h”
#ifdef __AVR__
#include <avr/power.h>
#endif

// Defines a global variable to store what pin the LED information will be sent from
#define PIN 0

// Variable arrays to store data from the analog signals and run through the FFT
char im[128], data[128];

/* Creates an instance of our strip of LEDs to run operations on
// Parameter 1 = Number of pixels in strip
// Parameter 2 = Arduino pin number to send data (most are valid)
// Parameter 3 = pixel type flags, add together as needed:*/
Adafruit_NeoPixel strip = Adafruit_NeoPixel(60, PIN, NEO_GRB + NEO_KHZ800);

// The method that is run on start up for the board
void setup(){
// Sets the reference for the analog signals to the 5V default
analogReference(DEFAULT);

// Initializes the LED strip
strip.begin();

// Display the latest colors on the LED strip
strip.show();
};

// The method that runs repeated until the board shuts off
void loop(){
// Sets the minimum and maximum ADC values
int min=1024, max=0,val=0;

// Loop for the number of samples to take from the analog signal
for (int i = 0; i < 128; i++) {
// Store the value from the from analog pin 0
val = analogRead(A0);
// Store a modified value into the arrays
data[i] = val / 4 – 128;
im[i] = 0;
// If the incoming value exceeds the min or max, replace them
if(val>max)
max=val;
if(val<min)
min=val;
}

// Run the fixed FFT function on the arrays
fix_fft(data, im, 7, 0);

// Run the clearBars function to turn all LEDs off before adjusting levels
clearBars();

// Loop for each column of the visualizer (Indexed starting at 1 to avoid FFT errors)
for (i = 1; i <= 10; i++) {
// Store a modified version of the data values to filter noise and hum
double dat = sqrt(data[i] * data[i] + im[i] * im[i]);
// Send the data to the respective bar the visualizer to set its height
setBar(i-1, dat);
}
};

// A method that sets the height of a given column to represent the given data value
void setBar(int bar, double dat){
// Stores the bottom LED ID for the column
int bottom;
// Stores the threshold that is modified to compare all levels
int threshold = 15;
// Save six preset colors for each row of the visualizer
uint32_t row6 = strip.Color(255, 0, 0);
uint32_t row5 = strip.Color(255, 30, 0);
uint32_t row4 = strip.Color(255, 63, 0);
uint32_t row3 = strip.Color(45, 255, 20);
uint32_t row2 = strip.Color(10, 255, 10);
uint32_t row1 = strip.Color(0, 255, 0);

// Checks if given bar is an even number
if(bar % 2 == 0){
// Set bottom to the address of the first LED in the column
bottom = 6*bar;

/* Checks if the given data value is above certain thresholds, and if its true // // enable the corresponding LED with the rows color. */
if((dat*10) >= threshold*5){
strip.setPixelColor(bottom+5, col6);
}
if((dat*10) >= threshold*4){
strip.setPixelColor(bottom+4, col5);
}
if((dat*10) >= threshold*3){
strip.setPixelColor(bottom+3, col4);
}
if((dat*10) >= threshold*2){
strip.setPixelColor(bottom+2, col3);
}
if((dat*10) >= threshold){
strip.setPixelColor(bottom+1, col2);
}
strip.setPixelColor(bottom, col1);

// Run the show function of the LED strip to show the intensity change
strip.show();
}
else{
// Set bottom to the address of the first LED in the column
bottom = 6*bar+5;

/* Checks if the given data value is above certain thresholds, and if its true // // enable the corresponding LED with the rows color. */
if((dat*10) >= threshold*5){
strip.setPixelColor(bottom-5,col6);
}
if((dat*10) >= threshold*4){
strip.setPixelColor(bottom-4, col5);
}
if((dat*10) >= threshold*3){
strip.setPixelColor(bottom-3, col4);
}
if((dat*10) >= threshold*2){
strip.setPixelColor(bottom-2, col3);
}
if((dat*10) >= threshold){
strip.setPixelColor(bottom-1, col2);
}
strip.setPixelColor(bottom, col1);

// Run the show function of the LED strip to show the intensity change
strip.show();
}
};

// A method to clear the colors of all LEDs
void clearBars(){
// Loop for every LED in the LED strip
for(int i = 0; i < 60; i++){
// Set the LEDs color to zeros to turn them off
strip.setPixelColor(i, 0, 0, 0);
}
};