This project is nothing more than an adaptation to the WS2812B LED matrix of the original MAX72xx based project published by Shajeeb.
Devices and components
Arduino-Nano
1000 µF capacitor
RGB LED MATRIX WS2812B 8x32
100k ohm resistance
10k ohm resistance
100nF capacitor
Resistance 4.75k ohms
12mm push button switch
Multi-hole prototype card 4x6 cm
Through-hole resistor, 390 ohm
Materials and tools
3.5mm Jack splitter cable
Soldering iron (generic)
Project description
The project
The original project that inspired this
The WS2812B RGB LED matrix
Hardware assembly
The code
Future update
Spectrum_Analyzer_WS2812B.ino
arduino
1/*
2 Copyright (c) 2019 Shajeeb TM
3
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
10 The above copyright notice and this permission notice shall be included in all
11 copies or substantial portions of the Software.
12 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
13 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
14 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
15 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
16 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
17 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
18 SOFTWARE.
19
20 WS2812B Led Matrix vesion by Janux
21*/
22
23#include <arduinoFFT.h>
24#include <SPI.h>
25#include <Adafruit_NeoPixel.h>
26
27#define SAMPLES 64 //Must be a power of 2
28#define xres 32 // Total number of columns in the display, must be <= SAMPLES/2
29#define yres 8 // Total number of rows in the display
30#define ledPIN 6 // pint to control Led Matrix
31#define NUM_LEDS (xres * yres)
32#define BRIGHTNESS 32
33#define buttonPin 5 // the number of the pushbutton pin to change displaycolor
34
35byte yvalue;
36byte displaycolumn, displayvalue;
37int peaks[xres];
38byte state = HIGH; // the current reading from the input pin
39byte previousState = LOW; // the previous reading from the input pin
40byte displaycolor = 0;
41
42//Arrays for samplig
43double vReal[SAMPLES];
44double vImag[SAMPLES];
45byte data_avgs[xres];
46arduinoFFT FFT = arduinoFFT(); // FFT object
47
48unsigned long lastDebounceTime = 0; // the last time the output pin was toggled
49unsigned long debounceDelay = 100; // the debounce time; increase if the output flickers
50
51// Parameter 1 = number of leds in matrix
52// Parameter 2 = pin number (most are valid)
53// Parameter 3 = pixel type flags, add together as needed:
54// NEO_KHZ800 800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
55// NEO_KHZ400 400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
56// NEO_GRB Pixels are wired for GRB bitstream (most NeoPixel products)
57// NEO_RGB Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
58Adafruit_NeoPixel pixel = Adafruit_NeoPixel(NUM_LEDS, ledPIN, NEO_GRB + NEO_KHZ800);
59
60// EQ filter to attenuates bass and improves treble
61// Useful on PC sound card which usually has many bass and poor high frequency
62bool EQ_ON = true; // set to false to disable eq
63byte eq[32] = {50, 55, 60, 70, 75, 80, 85, 95,
64 100, 100, 100, 100, 100, 100, 100, 100,
65 100, 100, 100, 100, 100, 100, 100, 100,
66 115, 125, 140, 160, 185, 200, 225, 255
67 };
68
69//Define color for single led, used in setColumn function, 0 for custom color
70//Color are calculated by Wheel function, see below
71byte colors[][8] = {
72 {170, 160, 150, 140, 130, 120, 1, 1},
73 {1, 5, 10, 15, 20, 25, 90, 90},
74 {90, 85, 80, 75, 70, 65, 1, 1},
75 {90, 90, 90, 30, 30, 30, 1, 1},
76 {170, 160, 150, 140, 130, 120, 0, 0},
77 {170, 160, 150, 140, 130, 120, 1, 1},
78 {170, 160, 150, 140, 130, 120, 1, 1}
79};
80
81void setup() {
82
83 pixel.begin();
84 pixel.setBrightness(BRIGHTNESS);
85
86 // Begin FFT operations
87 ADCSRA = 0b11100101; // set ADC to free running mode and set pre-scalar to 32 (0xe5)
88 ADMUX = 0b00000000; // use pin A0 and external voltage reference
89}
90
91void loop() {
92
93 // ++ Sampling
94 for (int i = 0; i < SAMPLES; i++) {
95 while (!(ADCSRA & 0x10)); // wait for ADC to complete current conversion ie ADIF bit set
96 ADCSRA = 0b11110101 ; // clear ADIF bit so that ADC can do next operation (0xf5)
97 int value = ADC - 512 ; // Read from ADC and subtract DC offset caused value
98 vReal[i] = value / 8; // Copy to bins after compressing
99 vImag[i] = 0;
100 }
101
102 FFT.Windowing(vReal, SAMPLES, FFT_WIN_TYP_HAMMING, FFT_FORWARD);
103 FFT.Compute(vReal, vImag, SAMPLES, FFT_FORWARD);
104 FFT.ComplexToMagnitude(vReal, vImag, SAMPLES);
105
106 // -- FFT
107 // ++ re-arrange FFT result to match with no. of columns on display (xres)
108 int step = (SAMPLES / 2) / xres;
109 int c = 0;
110 for (int i = 0; i < (SAMPLES / 2); i += step) {
111 data_avgs[c] = 0;
112 for (int k = 0 ; k < step ; k++) {
113 data_avgs[c] = data_avgs[c] + vReal[i + k];
114 }
115 data_avgs[c] = data_avgs[c] / step;
116 c++;
117 }
118
119 // ++ send to display according measured value
120 for (int i = 0; i <xres; i++) {
121 if (EQ_ON)
122 data_avgs[i] = data_avgs[i] * (float)(eq[i]) / 100; //apply eq filter
123 data_avgs[i] = constrain(data_avgs[i], 0, 80); // set max & min values for buckets to 0-80
124 data_avgs[i] = map(data_avgs[i], 0, 80, 0, yres); // remap averaged values to yres 0-8
125 yvalue = data_avgs[i];
126 peaks[i] = peaks[i] - 1; // decay by one light
127 if (yvalue > peaks[i]) peaks[i] = yvalue; //save peak if > previuos peak
128 yvalue = peaks[i];
129 displaycolumn = i;
130 displayvalue = yvalue;
131 setColumn(displaycolumn, displayvalue); // draw buckets
132 }
133 pixel.show(); // show buckets
134 displaycolorChange(); // check if button pressed to change color mode
135}
136
137//-----------------------------------------------------------------
138// Light leds of x column according to y value
139void setColumn(byte x, byte y) {
140 byte led, i;
141
142 for (i = 0; i < yres; i++) {
143 led = GetLedFromMatrix(x, i); //retrieve current led by x,y coordinates
144 if (peaks[x] > i) {
145 switch (displaycolor) {
146 case 4:
147 //put zero 0 on array value to customize peaks color
148 if (colors[displaycolor][i] > 0) {
149 //normal color defined on color array
150 pixel.setPixelColor(led, Wheel(colors[displaycolor][i]));
151 }
152 else {
153 //custom color for peaks only with 0 on array value
154 pixel.setPixelColor(led, 255, 255, 255); //Led number, R, G, B values
155 }
156 break;
157
158 case 5:
159 //change color by column
160 pixel.setPixelColor(led, Wheel(x * 16));
161 break;
162
163 case 6:
164 //change color by row
165 pixel.setPixelColor(led, Wheel(i * 36));
166 break;
167
168 default:
169 //display color set -> displaycolor from 0 to 3
170 //color are defined on color array
171 pixel.setPixelColor(led, Wheel(colors[displaycolor][i]));
172 }//END SWITCH
173 }
174 else {
175 pixel.setPixelColor(led, 0);
176 }
177 }
178}
179
180//======================================================================
181// Calculate a led number by x,y coordinates
182// valid for WS2812B with serpentine layout placed in horizzontal
183// and zero led at bottom right (input connector on the right side)
184// input value: x=0-31, y=0-7, return a led number from 0 to 255
185//========================================================================
186byte GetLedFromMatrix(byte x, byte y) {
187 x = xres - x - 1;
188 if (x & 0x01) {
189 //Odd columns increase backwards
190 return ((x + 1) * yres - y - 1);
191 }
192 else {
193 //Even columns increase normally
194 return ((x + 1) * yres - yres + y);
195 }
196}
197//========================================================================
198
199void displaycolorChange() {
200 int reading = digitalRead(buttonPin);
201 if (reading == HIGH && previousState == LOW && millis() - lastDebounceTime > debounceDelay) // works only when pressed
202 {
203 displaycolor++;
204 if (displaycolor > 6) displaycolor = 0;
205 lastDebounceTime = millis();
206 }
207 previousState = reading;
208}
209
210/* Utility from Adafruit Neopixel demo sketch
211 Input a value 0 to 255 to get a color value.
212 The colours are a transition R - G - B - back to R.*/
213unsigned long Wheel(byte WheelPos) {
214 WheelPos = 255 - WheelPos;
215 if (WheelPos < 85) {
216 return pixel.Color(255 - WheelPos * 3, 0, WheelPos * 3);
217 }
218 if (WheelPos < 170) {
219 WheelPos -= 85;
220 return pixel.Color(0, WheelPos * 3, 255 - WheelPos * 3);
221 }
222 WheelPos -= 170;
223 return pixel.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
224}
225
Spectrum_Analyzer_WS2812B.ino
arduino
1/*
2 Copyright (c) 2019 Shajeeb TM
3
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
10 The above copyright notice and this permission notice shall be included in all
11 copies or substantial portions of the Software.
12 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
13 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
14 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
15 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
16 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
17 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
18 SOFTWARE.
19
20 WS2812B Led Matrix vesion by Janux
21*/
22
23#include <arduinoFFT.h>
24#include <SPI.h>
25#include <Adafruit_NeoPixel.h>
26
27#define SAMPLES 64 //Must be a power of 2
28#define xres 32 // Total number of columns in the display, must be <= SAMPLES/2
29#define yres 8 // Total number of rows in the display
30#define ledPIN 6 // pint to control Led Matrix
31#define NUM_LEDS (xres * yres)
32#define BRIGHTNESS 32
33#define buttonPin 5 // the number of the pushbutton pin to change displaycolor
34
35byte yvalue;
36byte displaycolumn, displayvalue;
37int peaks[xres];
38byte state = HIGH; // the current reading from the input pin
39byte previousState = LOW; // the previous reading from the input pin
40byte displaycolor = 0;
41
42//Arrays for samplig
43double vReal[SAMPLES];
44double vImag[SAMPLES];
45byte data_avgs[xres];
46arduinoFFT FFT = arduinoFFT(); // FFT object
47
48unsigned long lastDebounceTime = 0; // the last time the output pin was toggled
49unsigned long debounceDelay = 100; // the debounce time; increase if the output flickers
50
51// Parameter 1 = number of leds in matrix
52// Parameter 2 = pin number (most are valid)
53// Parameter 3 = pixel type flags, add together as needed:
54// NEO_KHZ800 800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
55// NEO_KHZ400 400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
56// NEO_GRB Pixels are wired for GRB bitstream (most NeoPixel products)
57// NEO_RGB Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
58Adafruit_NeoPixel pixel = Adafruit_NeoPixel(NUM_LEDS, ledPIN, NEO_GRB + NEO_KHZ800);
59
60// EQ filter to attenuates bass and improves treble
61// Useful on PC sound card which usually has many bass and poor high frequency
62bool EQ_ON = true; // set to false to disable eq
63byte eq[32] = {50, 55, 60, 70, 75, 80, 85, 95,
64 100, 100, 100, 100, 100, 100, 100, 100,
65 100, 100, 100, 100, 100, 100, 100, 100,
66 115, 125, 140, 160, 185, 200, 225, 255
67 };
68
69//Define color for single led, used in setColumn function, 0 for custom color
70//Color are calculated by Wheel function, see below
71byte colors[][8] = {
72 {170, 160, 150, 140, 130, 120, 1, 1},
73 {1, 5, 10, 15, 20, 25, 90, 90},
74 {90, 85, 80, 75, 70, 65, 1, 1},
75 {90, 90, 90, 30, 30, 30, 1, 1},
76 {170, 160, 150, 140, 130, 120, 0, 0},
77 {170, 160, 150, 140, 130, 120, 1, 1},
78 {170, 160, 150, 140, 130, 120, 1, 1}
79};
80
81void setup() {
82
83 pixel.begin();
84 pixel.setBrightness(BRIGHTNESS);
85
86 // Begin FFT operations
87 ADCSRA = 0b11100101; // set ADC to free running mode and set pre-scalar to 32 (0xe5)
88 ADMUX = 0b00000000; // use pin A0 and external voltage reference
89}
90
91void loop() {
92
93 // ++ Sampling
94 for (int i = 0; i < SAMPLES; i++) {
95 while (!(ADCSRA & 0x10)); // wait for ADC to complete current conversion ie ADIF bit set
96 ADCSRA = 0b11110101 ; // clear ADIF bit so that ADC can do next operation (0xf5)
97 int value = ADC - 512 ; // Read from ADC and subtract DC offset caused value
98 vReal[i] = value / 8; // Copy to bins after compressing
99 vImag[i] = 0;
100 }
101
102 FFT.Windowing(vReal, SAMPLES, FFT_WIN_TYP_HAMMING, FFT_FORWARD);
103 FFT.Compute(vReal, vImag, SAMPLES, FFT_FORWARD);
104 FFT.ComplexToMagnitude(vReal, vImag, SAMPLES);
105
106 // -- FFT
107 // ++ re-arrange FFT result to match with no. of columns on display (xres)
108 int step = (SAMPLES / 2) / xres;
109 int c = 0;
110 for (int i = 0; i < (SAMPLES / 2); i += step) {
111 data_avgs[c] = 0;
112 for (int k = 0 ; k < step ; k++) {
113 data_avgs[c] = data_avgs[c] + vReal[i + k];
114 }
115 data_avgs[c] = data_avgs[c] / step;
116 c++;
117 }
118
119 // ++ send to display according measured value
120 for (int i = 0; i <xres; i++) {
121 if (EQ_ON)
122 data_avgs[i] = data_avgs[i] * (float)(eq[i]) / 100; //apply eq filter
123 data_avgs[i] = constrain(data_avgs[i], 0, 80); // set max & min values for buckets to 0-80
124 data_avgs[i] = map(data_avgs[i], 0, 80, 0, yres); // remap averaged values to yres 0-8
125 yvalue = data_avgs[i];
126 peaks[i] = peaks[i] - 1; // decay by one light
127 if (yvalue > peaks[i]) peaks[i] = yvalue; //save peak if > previuos peak
128 yvalue = peaks[i];
129 displaycolumn = i;
130 displayvalue = yvalue;
131 setColumn(displaycolumn, displayvalue); // draw buckets
132 }
133 pixel.show(); // show buckets
134 displaycolorChange(); // check if button pressed to change color mode
135}
136
137//-----------------------------------------------------------------
138// Light leds of x column according to y value
139void setColumn(byte x, byte y) {
140 byte led, i;
141
142 for (i = 0; i < yres; i++) {
143 led = GetLedFromMatrix(x, i); //retrieve current led by x,y coordinates
144 if (peaks[x] > i) {
145 switch (displaycolor) {
146 case 4:
147 //put zero 0 on array value to customize peaks color
148 if (colors[displaycolor][i] > 0) {
149 //normal color defined on color array
150 pixel.setPixelColor(led, Wheel(colors[displaycolor][i]));
151 }
152 else {
153 //custom color for peaks only with 0 on array value
154 pixel.setPixelColor(led, 255, 255, 255); //Led number, R, G, B values
155 }
156 break;
157
158 case 5:
159 //change color by column
160 pixel.setPixelColor(led, Wheel(x * 16));
161 break;
162
163 case 6:
164 //change color by row
165 pixel.setPixelColor(led, Wheel(i * 36));
166 break;
167
168 default:
169 //display color set -> displaycolor from 0 to 3
170 //color are defined on color array
171 pixel.setPixelColor(led, Wheel(colors[displaycolor][i]));
172 }//END SWITCH
173 }
174 else {
175 pixel.setPixelColor(led, 0);
176 }
177 }
178}
179
180//======================================================================
181// Calculate a led number by x,y coordinates
182// valid for WS2812B with serpentine layout placed in horizzontal
183// and zero led at bottom right (input connector on the right side)
184// input value: x=0-31, y=0-7, return a led number from 0 to 255
185//========================================================================
186byte GetLedFromMatrix(byte x, byte y) {
187 x = xres - x - 1;
188 if (x & 0x01) {
189 //Odd columns increase backwards
190 return ((x + 1) * yres - y - 1);
191 }
192 else {
193 //Even columns increase normally
194 return ((x + 1) * yres - yres + y);
195 }
196}
197//========================================================================
198
199void displaycolorChange() {
200 int reading = digitalRead(buttonPin);
201 if (reading == HIGH && previousState == LOW && millis() - lastDebounceTime > debounceDelay) // works only when pressed
202 {
203 displaycolor++;
204 if (displaycolor > 6) displaycolor = 0;
205 lastDebounceTime = millis();
206 }
207 previousState = reading;
208}
209
210/* Utility from Adafruit Neopixel demo sketch
211 Input a value 0 to 255 to get a color value.
212 The colours are a transition R - G - B - back to R.*/
213unsigned long Wheel(byte WheelPos) {
214 WheelPos = 255 - WheelPos;
215 if (WheelPos < 85) {
216 return pixel.Color(255 - WheelPos * 3, 0, WheelPos * 3);
217 }
218 if (WheelPos < 170) {
219 WheelPos -= 85;
220 return pixel.Color(0, WheelPos * 3, 255 - WheelPos * 3);
221 }
222 WheelPos -= 170;
223 return pixel.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
224}
225
Downloadable files
Wiring
Wiring
Wiring
Wiring
Documentation
Eagle Project
Eagle Project
PCB sample
PCB sample
PCB sample
PCB sample
Example schematic image of Eagle
Example schematic image of Eagle
Eagle Project
Eagle Project
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.