This project involves creating a 32-band audio (music) frequency spectrum analyzer/viewer using Arduino.
Devices and components
Arduino-Nano
32x8 LED matrix display
100nF capacitor
10k ohm resistance
100k ohm resistance
Resistance 4.75k ohms
12mm push button switch
Materials and tools
Soldering iron (generic)
Project description
Main features of this frequency spectrum analyzer
Uses the easily installable libraries “arduinoFFT” and “MD_MAX72xx”
Five different display modes are supported and can be switched with the push button
The left and right channels of the audio signal are mixed so you don't miss a beat
The prototype uses a 32x8 LED matrix display, this can be changed and easily modified
Audio can be fed through the headphone output or line output of the music system/amplifier
Required Components
Arduino Nano or Uno (I tried with Nano and Uno, it should also work with other models)
LED matrix display 32 x 8 - 1 no
Push button switch - 1 no (normally comes with Arduino kit)
Capacitor 100nf - 2 ns
5 kilo ohm resistance - 3 nos
10 kilo ohm resistor - 1 no
Resistance of 100 kilo ohms - 2 no
5 volt power supply (a USB power supply will do)
Program flowchart
System Description
Frequency response
Additional Details
Spectrum analyzer in action
Connection entry
Source code
arduino
1/*
2Copyright (c) 2019 Shajeeb TM
3
4Permission is hereby granted, free of charge, to any person obtaining a copy
5of this software and associated documentation files (the "Software"), to deal
6in the Software without restriction, including without limitation the rights
7to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8copies of the Software, and to permit persons to whom the Software is
9furnished to do so, subject to the following conditions:
10The above copyright notice and this permission notice shall be included in all
11copies or substantial portions of the Software.
12THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
13IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
14FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
15AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
16LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
17OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
18SOFTWARE.
19*/
20
21#include <arduinoFFT.h>
22#include <MD_MAX72xx.h>
23#include <SPI.h>
24
25#define SAMPLES 64 //Must be a power of 2
26#define HARDWARE_TYPE MD_MAX72XX::FC16_HW // Set display type so that MD_MAX72xx library treets it properly
27#define MAX_DEVICES 4 // Total number display modules
28#define CLK_PIN 13 // Clock pin to communicate with display
29#define DATA_PIN 11 // Data pin to communicate with display
30#define CS_PIN 10 // Control pin to communicate with display
31#define xres 32 // Total number of columns in the display, must be <= SAMPLES/2
32#define yres 8 // Total number of rows in the display
33
34
35int MY_ARRAY[]={0, 128, 192, 224, 240, 248, 252, 254, 255}; // default = standard pattern
36int MY_MODE_1[]={0, 128, 192, 224, 240, 248, 252, 254, 255}; // standard pattern
37int MY_MODE_2[]={0, 128, 64, 32, 16, 8, 4, 2, 1}; // only peak pattern
38int MY_MODE_3[]={0, 128, 192, 160, 144, 136, 132, 130, 129}; // only peak + bottom point
39int MY_MODE_4[]={0, 128, 192, 160, 208, 232, 244, 250, 253}; // one gap in the top , 3rd light onwards
40int MY_MODE_5[]={0, 1, 3, 7, 15, 31, 63, 127, 255}; // standard pattern, mirrored vertically
41
42
43double vReal[SAMPLES];
44double vImag[SAMPLES];
45char data_avgs[xres];
46
47int yvalue;
48int displaycolumn , displayvalue;
49int peaks[xres];
50const int buttonPin = 5; // the number of the pushbutton pin
51int state = HIGH; // the current reading from the input pin
52int previousState = LOW; // the previous reading from the input pin
53int displaymode = 1;
54unsigned long lastDebounceTime = 0; // the last time the output pin was toggled
55unsigned long debounceDelay = 50; // the debounce time; increase if the output flickers
56
57
58MD_MAX72XX mx = MD_MAX72XX(HARDWARE_TYPE, CS_PIN, MAX_DEVICES); // display object
59arduinoFFT FFT = arduinoFFT(); // FFT object
60
61
62
63void setup() {
64
65 ADCSRA = 0b11100101; // set ADC to free running mode and set pre-scalar to 32 (0xe5)
66 ADMUX = 0b00000000; // use pin A0 and external voltage reference
67 pinMode(buttonPin, INPUT);
68 mx.begin(); // initialize display
69 delay(50); // wait to get reference voltage stabilized
70}
71
72void loop() {
73 // ++ Sampling
74 for(int i=0; i<SAMPLES; i++)
75 {
76 while(!(ADCSRA & 0x10)); // wait for ADC to complete current conversion ie ADIF bit set
77 ADCSRA = 0b11110101 ; // clear ADIF bit so that ADC can do next operation (0xf5)
78 int value = ADC - 512 ; // Read from ADC and subtract DC offset caused value
79 vReal[i]= value/8; // Copy to bins after compressing
80 vImag[i] = 0;
81 }
82 // -- Sampling
83
84
85 // ++ FFT
86 FFT.Windowing(vReal, SAMPLES, FFT_WIN_TYP_HAMMING, FFT_FORWARD);
87 FFT.Compute(vReal, vImag, SAMPLES, FFT_FORWARD);
88 FFT.ComplexToMagnitude(vReal, vImag, SAMPLES);
89 // -- FFT
90
91
92 // ++ re-arrange FFT result to match with no. of columns on display ( xres )
93 int step = (SAMPLES/2)/xres;
94 int c=0;
95 for(int i=0; i<(SAMPLES/2); i+=step)
96 {
97 data_avgs[c] = 0;
98 for (int k=0 ; k< step ; k++) {
99 data_avgs[c] = data_avgs[c] + vReal[i+k];
100 }
101 data_avgs[c] = data_avgs[c]/step;
102 c++;
103 }
104 // -- re-arrange FFT result to match with no. of columns on display ( xres )
105
106
107 // ++ send to display according measured value
108 for(int i=0; i<xres; i++)
109 {
110 data_avgs[i] = constrain(data_avgs[i],0,80); // set max & min values for buckets
111 data_avgs[i] = map(data_avgs[i], 0, 80, 0, yres); // remap averaged values to yres
112 yvalue=data_avgs[i];
113
114 peaks[i] = peaks[i]-1; // decay by one light
115 if (yvalue > peaks[i])
116 peaks[i] = yvalue ;
117 yvalue = peaks[i];
118 displayvalue=MY_ARRAY[yvalue];
119 displaycolumn=31-i;
120 mx.setColumn(displaycolumn, displayvalue); // for left to right
121 }
122 // -- send to display according measured value
123
124 displayModeChange (); // check if button pressed to change display mode
125}
126
127void displayModeChange() {
128 int reading = digitalRead(buttonPin);
129 if (reading == HIGH && previousState == LOW && millis() - lastDebounceTime > debounceDelay) // works only when pressed
130
131 {
132
133 switch (displaymode) {
134 case 1: // move from mode 1 to 2
135 displaymode = 2;
136 for (int i=0 ; i<=8 ; i++ ) {
137 MY_ARRAY[i]=MY_MODE_2[i];
138 }
139 break;
140 case 2: // move from mode 2 to 3
141 displaymode = 3;
142 for (int i=0 ; i<=8 ; i++ ) {
143 MY_ARRAY[i]=MY_MODE_3[i];
144 }
145 break;
146 case 3: // move from mode 3 to 4
147 displaymode = 4;
148 for (int i=0 ; i<=8 ; i++ ) {
149 MY_ARRAY[i]=MY_MODE_4[i];
150 }
151 break;
152 case 4: // move from mode 4 to 5
153 displaymode = 5;
154 for (int i=0 ; i<=8 ; i++ ) {
155 MY_ARRAY[i]=MY_MODE_5[i];
156 }
157 break;
158 case 5: // move from mode 5 to 1
159 displaymode = 1;
160 for (int i=0 ; i<=8 ; i++ ) {
161 MY_ARRAY[i]=MY_MODE_1[i];
162 }
163 break;
164 }
165
166 lastDebounceTime = millis();
167 }
168 previousState = reading;
169}
170
Source code
arduino
1/*
2Copyright (c) 2019 Shajeeb TM
3
4Permission is hereby granted,
5 free of charge, to any person obtaining a copy
6of this software and associated
7 documentation files (the "Software"), to deal
8in the Software without restriction,
9 including without limitation the rights
10to use, copy, modify, merge, publish,
11 distribute, sublicense, and/or sell
12copies of the Software, and to permit persons
13 to whom the Software is
14furnished to do so, subject to the following conditions:
15The
16 above copyright notice and this permission notice shall be included in all
17copies
18 or substantial portions of the Software.
19THE SOFTWARE IS PROVIDED "AS IS",
20 WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21IMPLIED, INCLUDING BUT NOT LIMITED TO
22 THE WARRANTIES OF MERCHANTABILITY,
23FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
24 IN NO EVENT SHALL THE
25AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
26 OR OTHER
27LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28 FROM,
29OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
30 IN THE
31SOFTWARE.
32*/
33
34#include <arduinoFFT.h>
35#include <MD_MAX72xx.h>
36#include
37 <SPI.h>
38
39#define SAMPLES 64 //Must be a power of 2
40#define HARDWARE_TYPE
41 MD_MAX72XX::FC16_HW // Set display type so that MD_MAX72xx library treets it
42 properly
43#define MAX_DEVICES 4 // Total number display modules
44#define
45 CLK_PIN 13 // Clock pin to communicate with display
46#define DATA_PIN 11 //
47 Data pin to communicate with display
48#define CS_PIN 10 // Control pin to
49 communicate with display
50#define xres 32 // Total number of columns in
51 the display, must be <= SAMPLES/2
52#define yres 8 // Total number of rows
53 in the display
54
55
56int MY_ARRAY[]={0, 128, 192, 224, 240, 248, 252, 254,
57 255}; // default = standard pattern
58int MY_MODE_1[]={0, 128, 192, 224, 240, 248,
59 252, 254, 255}; // standard pattern
60int MY_MODE_2[]={0, 128, 64, 32, 16, 8, 4,
61 2, 1}; // only peak pattern
62int MY_MODE_3[]={0, 128, 192, 160, 144, 136, 132,
63 130, 129}; // only peak + bottom point
64int MY_MODE_4[]={0, 128, 192, 160, 208,
65 232, 244, 250, 253}; // one gap in the top , 3rd light onwards
66int MY_MODE_5[]={0,
67 1, 3, 7, 15, 31, 63, 127, 255}; // standard pattern, mirrored vertically
68
69
70
71double vReal[SAMPLES];
72double vImag[SAMPLES];
73char data_avgs[xres];
74
75int
76 yvalue;
77int displaycolumn , displayvalue;
78int peaks[xres];
79const int buttonPin
80 = 5; // the number of the pushbutton pin
81int state = HIGH; //
82 the current reading from the input pin
83int previousState = LOW; // the previous
84 reading from the input pin
85int displaymode = 1;
86unsigned long lastDebounceTime
87 = 0; // the last time the output pin was toggled
88unsigned long debounceDelay
89 = 50; // the debounce time; increase if the output flickers
90
91
92MD_MAX72XX
93 mx = MD_MAX72XX(HARDWARE_TYPE, CS_PIN, MAX_DEVICES); // display object
94arduinoFFT
95 FFT = arduinoFFT(); // FFT object
96
97
98
99void
100 setup() {
101
102 ADCSRA = 0b11100101; // set ADC to free running mode
103 and set pre-scalar to 32 (0xe5)
104 ADMUX = 0b00000000; // use pin A0 and
105 external voltage reference
106 pinMode(buttonPin, INPUT);
107 mx.begin();
108 // initialize display
109 delay(50); // wait to get reference
110 voltage stabilized
111}
112
113void loop() {
114 // ++ Sampling
115 for(int
116 i=0; i<SAMPLES; i++)
117 {
118 while(!(ADCSRA & 0x10)); // wait for
119 ADC to complete current conversion ie ADIF bit set
120 ADCSRA = 0b11110101
121 ; // clear ADIF bit so that ADC can do next operation (0xf5)
122 int
123 value = ADC - 512 ; // Read from ADC and subtract DC offset caused
124 value
125 vReal[i]= value/8; // Copy to bins after compressing
126
127 vImag[i] = 0;
128 }
129 // -- Sampling
130
131
132
133 // ++ FFT
134 FFT.Windowing(vReal, SAMPLES, FFT_WIN_TYP_HAMMING, FFT_FORWARD);
135
136 FFT.Compute(vReal, vImag, SAMPLES, FFT_FORWARD);
137 FFT.ComplexToMagnitude(vReal,
138 vImag, SAMPLES);
139 // -- FFT
140
141
142 // ++ re-arrange FFT result
143 to match with no. of columns on display ( xres )
144 int step = (SAMPLES/2)/xres;
145
146 int c=0;
147 for(int i=0; i<(SAMPLES/2); i+=step)
148 {
149 data_avgs[c]
150 = 0;
151 for (int k=0 ; k< step ; k++) {
152 data_avgs[c] = data_avgs[c]
153 + vReal[i+k];
154 }
155 data_avgs[c] = data_avgs[c]/step;
156 c++;
157
158 }
159 // -- re-arrange FFT result to match with no. of columns on display
160 ( xres )
161
162
163 // ++ send to display according measured value
164
165 for(int i=0; i<xres; i++)
166 {
167 data_avgs[i] = constrain(data_avgs[i],0,80);
168 // set max & min values for buckets
169 data_avgs[i] = map(data_avgs[i],
170 0, 80, 0, yres); // remap averaged values to yres
171 yvalue=data_avgs[i];
172
173
174 peaks[i] = peaks[i]-1; // decay by one light
175 if (yvalue > peaks[i])
176
177 peaks[i] = yvalue ;
178 yvalue = peaks[i];
179 displayvalue=MY_ARRAY[yvalue];
180
181 displaycolumn=31-i;
182 mx.setColumn(displaycolumn, displayvalue); //
183 for left to right
184 }
185 // -- send to display according measured value
186
187
188 displayModeChange (); // check if button pressed to change
189 display mode
190}
191
192void displayModeChange() {
193 int reading = digitalRead(buttonPin);
194
195 if (reading == HIGH && previousState == LOW && millis() - lastDebounceTime > debounceDelay)
196 // works only when pressed
197
198 {
199
200 switch (displaymode) {
201 case
202 1: // move from mode 1 to 2
203 displaymode = 2;
204 for (int
205 i=0 ; i<=8 ; i++ ) {
206 MY_ARRAY[i]=MY_MODE_2[i];
207 }
208 break;
209
210 case 2: // move from mode 2 to 3
211 displaymode = 3;
212 for
213 (int i=0 ; i<=8 ; i++ ) {
214 MY_ARRAY[i]=MY_MODE_3[i];
215 }
216 break;
217
218 case 3: // move from mode 3 to 4
219 displaymode = 4;
220 for
221 (int i=0 ; i<=8 ; i++ ) {
222 MY_ARRAY[i]=MY_MODE_4[i];
223 }
224 break;
225
226 case 4: // move from mode 4 to 5
227 displaymode = 5;
228 for
229 (int i=0 ; i<=8 ; i++ ) {
230 MY_ARRAY[i]=MY_MODE_5[i];
231 }
232 break;
233
234 case 5: // move from mode 5 to 1
235 displaymode = 1;
236
237 for (int i=0 ; i<=8 ; i++ ) {
238 MY_ARRAY[i]=MY_MODE_1[i];
239 }
240
241 break;
242 }
243
244 lastDebounceTime = millis();
245 }
246 previousState
247 = reading;
248}
249
Downloadable files
Schematic diagram - updated
Schematic diagram - updated
Note: Content and images are from: https://projecthub.arduino.cc/, with some modifications.
If you want it removed due to copyright reasons, please leave a comment. Thank you.
I want to share this article more widely so that everyone knows about Arduino and your project.