RGB 32-Band Audio-Spektrum-Visualisierer: Arduino Nano & WS2812B LED-Matrix
Komponenten und Zubehör
| Arduino Nano R3 | 1 |
| WS2812B 8×32 RGB LED-Matrix (256 LEDs) | 1 |
| Widerstand 4,75 kΩ | 3 |
| Widerstand 100 kΩ | 2 |
| Widerstand 10 kΩ | 1 |
| Widerstand 390 Ω (Durchsteck) | 1 |
| Keramikkondensator 100 nF | 2 |
| Elko 1000 µF / 12 V | 1 |
| Taster 12 mm | 1 |
| Lochrasterplatine 4×6 cm | 1 |
Benötigte Werkzeuge
- Lötkolben
- 3,5-mm-Klinken-Splitterkabel (Audio-Eingang)
Projektbeschreibung
Das Projekt
Ein 32-Band-Audiofrequenz-Spektrum-Visualisierer (Musik-Analyzer) mit Arduino Nano und 8×32 WS2812B-RGB-LED-Matrix (256 LEDs).
Ursprungsprojekt
Basiert auf der Arbeit von Shajeeb (MAX72xx-Matrix-Version). Ich habe nur den LED-Matrix-Treiberteil für WS2812B angepasst. Original: „32-Band Audio Spectrum Visualizer Analyzer“.
Die WS2812B-RGB-LED-Matrix
Jede 5050-SMD-LED kann >10 mA ziehen → bei voller Helligkeit alle LEDs: >2,5 A. Externes Netzteil zwingend!
Schutzmaßnahmen:
- Diode in +5-V-Zuleitung: Verhindert Rückspeisung in Arduino bei USB-Betrieb; ermöglicht Standalone-Betrieb mit Ext. Netzteil.
- 390-Ω-Widerstand in Data-Leitung (D6 → Matrix): Schutz vor Spannungsspitzen.
- 1000-µF-Elko (12 V) parallel zur Arduino-Versorgung: Stabilisierung.
Hardware-Aufbau
Erster Prototyp auf 4×6-cm-Lochraster mit zwei CINCH-Buchsen (oder 3,5-mm-Klinke). Wichtig: Geschirmtes Kabel für Audio-Eingang gegen Brummen. Verbindung Arduino → Matrix so kurz wie möglich halten.
Der Code
Basiert auf Shajeebs FFT-Sampling und Visualisierung. Zwei Erweiterungen:
- GetLedFromMatrix(x, y): Mappt serpentinenförmige Matrix (Eingangsstecker rechts, horizontale Ausrichtung) auf Zeile/Spalte → LED-Index 0–255.
- SetColumn(x, y): Setzt LEDs einer Spalte basierend auf Peak-Wert (0–7) und Farbpalette. Nutzt
Wheel()(Adafruit NeoPixel Demo) für HSV-ähnliche Farbübergänge.
Audio-Equalisierung: Byte-Array eq[32] dämpft Bässe, hebt Höhen an (PC-Onboard-Sound oft basslastig). EQ_ON = true aktiviert; Wert 100 = neutral, <100 = Dämpfung, >100 = Anhebung.
Helligkeit: Konstant BRIGHTNESS = 32 (max. 255). Ab ~100 wird Weiß gelblich – wahrscheinlich Spannungseinbruch am Stecker. Empfehlung: Matrix mittig einspeisen (rote/schwarze Mittelkabel). Bei 64 Helligkeit reicht 1 A, sonst 2 A Netzteil.
Zukünftige Erweiterung
Arbeit an Version mit OpenMusicLabs FHT (Fast Hartley Transform) – vielfach schneller als Arduino-FFT.
Quellcode: Spectrum_Analyzer_WS2812B.ino
/* Copyright (c) 2019 Shajeeb TM, MIT License
* WS2812B Version by Janux
*/
#include <arduinoFFT.h>
#include <SPI.h>
#include <Adafruit_NeoPixel.h>
#define SAMPLES 64 // 2er-Potenz
#define xres 32 // Spalten (≤ SAMPLES/2)
#define yres 8 // Zeilen
#define ledPIN 6 // Data-Pin Matrix
#define NUM_LEDS (xres * yres)
#define BRIGHTNESS 32
#define buttonPin 5 // Farbmodus-Wechsel
byte yvalue, displaycolumn, displayvalue;
int peaks[xres];
byte state = HIGH, previousState = LOW, displaycolor = 0;
double vReal[SAMPLES], vImag[SAMPLES];
byte data_avgs[xres];
arduinoFFT FFT = arduinoFFT();
unsigned long lastDebounceTime = 0;
unsigned long debounceDelay = 100;
Adafruit_NeoPixel pixel = Adafruit_NeoPixel(NUM_LEDS, ledPIN, NEO_GRB + NEO_KHZ800);
// EQ-Filter: Bässe dämpfen, Höhen anheben
bool EQ_ON = true;
byte eq[32] = {
50, 55, 60, 70, 75, 80, 85, 95,
100, 100, 100, 100, 100, 100, 100, 100,
100, 100, 100, 100, 100, 100, 100, 100,
115, 125, 140, 160, 185, 200, 225, 255
};
// Farbpaletten [Modus][Zeile] – 0 = benutzerdefinierte Peak-Farbe (weiß)
byte colors[][8] = {
{170, 160, 150, 140, 130, 120, 1, 1},
{1, 5, 10, 15, 20, 25, 90, 90},
{90, 85, 80, 75, 70, 65, 1, 1},
{90, 90, 90, 30, 30, 30, 1, 1},
{170, 160, 150, 140, 130, 120, 0, 0},
{170, 160, 150, 140, 130, 120, 1, 1},
{170, 160, 150, 140, 130, 120, 1, 1}
};
void setup() {
pixel.begin();
pixel.setBrightness(BRIGHTNESS);
// ADC Free-Running, Prescaler 32, A0, externe Referenz
ADCSRA = 0b11100101; // 0xE5
ADMUX = 0b00000000;
}
void loop() {
// Sampling
for (int i = 0; i < SAMPLES; i++) {
while (!(ADCSRA & 0x10)); // ADIF warten
ADCSRA = 0b11110101; // ADIF clear (0xF5)
int value = ADC - 512; // DC-Offset entfernen
vReal[i] = value / 8; // Komprimieren
vImag[i] = 0;
}
FFT.Windowing(vReal, SAMPLES, FFT_WIN_TYP_HAMMING, FFT_FORWARD);
FFT.Compute(vReal, vImag, SAMPLES, FFT_FORWARD);
FFT.ComplexToMagnitude(vReal, vImag, SAMPLES);
// FFT-Bins auf 32 Spalten mappen
int step = (SAMPLES / 2) / xres;
int c = 0;
for (int i = 0; i < (SAMPLES / 2); i += step) {
data_avgs[c] = 0;
for (int k = 0; k < step; k++) data_avgs[c] += vReal[i + k];
data_avgs[c] /= step;
c++;
}
// Anzeige
for (int i = 0; i < xres; i++) {
if (EQ_ON) data_avgs[i] = data_avgs[i] * eq[i] / 100;
data_avgs[i] = constrain(data_avgs[i], 0, 80);
data_avgs[i] = map(data_avgs[i], 0, 80, 0, yres);
yvalue = data_avgs[i];
peaks[i] = max(peaks[i] - 1, 0); // Decay
if (yvalue > peaks[i]) peaks[i] = yvalue;
yvalue = peaks[i];
displaycolumn = i;
displayvalue = yvalue;
setColumn(displaycolumn, displayvalue);
}
pixel.show();
displaycolorChange();
}
void setColumn(byte x, byte y) {
for (byte i = 0; i < yres; i++) {
byte led = GetLedFromMatrix(x, i);
if (peaks[x] > i) {
switch (displaycolor) {
case 4: // Peak-Farbe anpassbar (0 im Array = weiß)
if (colors[displaycolor][i] > 0)
pixel.setPixelColor(led, Wheel(colors[displaycolor][i]));
else
pixel.setPixelColor(led, 255, 255, 255);
break;
case 5: // Farbe nach Spalte
pixel.setPixelColor(led, Wheel(x * 16));
break;
case 6: // Farbe nach Zeile
pixel.setPixelColor(led, Wheel(i * 36));
break;
default: // Modi 0–3: feste Paletten
pixel.setPixelColor(led, Wheel(colors[displaycolor][i]));
}
} else {
pixel.setPixelColor(led, 0);
}
}
}
byte GetLedFromMatrix(byte x, byte y) {
x = xres - x - 1; // Spiegelung (Eingang rechts)
if (x & 0x01) // Ungerade Spalten: rückwärts
return ((x + 1) * yres - y - 1);
else // Gerade Spalten: vorwärts
return ((x + 1) * yres - yres + y);
}
void displaycolorChange() {
int reading = digitalRead(buttonPin);
if (reading == HIGH && previousState == LOW &&
millis() - lastDebounceTime > debounceDelay) {
displaycolor = (displaycolor + 1) % 7;
lastDebounceTime = millis();
}
previousState = reading;
}
// Adafruit Wheel: 0–255 → RGB-Farbkreis
unsigned long Wheel(byte WheelPos) {
WheelPos = 255 - WheelPos;
if (WheelPos < 85) return pixel.Color(255 - WheelPos * 3, 0, WheelPos * 3);
if (WheelPos < 170) { WheelPos -= 85; return pixel.Color(0, WheelPos * 3, 255 - WheelPos * 3); }
WheelPos -= 170; return pixel.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}
3D-Druck & Gehäuse
STL-Dateien im Originalprojekt: arduino_rgb_audio_analyzer_lX4Dv4Pta2.zip.
Schaltplan
Siehe Originalprojekt (Bilddatei).
Herstellungsprozess
- 32-Band Arduino Audio-Spektrumanalysator – Linear & Pseudo-Log Darstellung
- Smart Thirst Alert: DIY Pflanzen-Alarm für optimale Bewässerung
- Italienische Word Clock mit Arduino Nano, NeoPixel, LCD & RTC
- Zuverlässiger Sigfox-kWh-Zähler mit Arduino MKR Fox 1200
- Bluetooth-Temperaturmonitor mit Arduino Nano
- Berührungsloses Gesten-Schloss: Sicherer Zugang mit APDS-9960 & Arduino
- Arduino Nano Begleiter-Set – Essentielle Bauteile & Werkzeuge für DIY-Elektronik
- USB-MIDI-Adapter für Arduino Micro / Leonardo – Komplettes Bauteil-Kit & Code
- Isolierter Analogeingang für Arduino: Spannung/Strom galvanisch trennen
- Präziser Reaktionszeit-Test mit Arduino UNO