RGB 32-полосный аудио-спектрум анализатор на Arduino и матрице WS2812B 8x32
Компоненты и расходные материалы
- Arduino Nano R3 — 1 шт.
- WS2812B 8x32 RGB LED матрица — 1 шт.
- Резистор 4.75 кОм — 3 шт.
- Резистор 100 кОм — 2 шт.
- Резистор 10 кОм — 1 шт.
- Резистор 390 Ом (в отверстия) — 1 шт.
- Конденсатор 100 нФ — 2 шт.
- Конденсатор 1000 мкФ 12В — 1 шт.
- Тактильная кнопка 12 мм — 1 шт.
- Многоотверстная плата 4x6 см — 1 шт.
Необходимые инструменты и оборудование
- Паяльник
- Разъёмный кабель 3.5 мм Jack (splitter)
О проекте
Проект
Создание RGB 32-полосного визуализатора аудиочастот (музыкального спектра) на Arduino Nano и матрице 8x32 WS2812B RGB LED.
Оригинальный проект-вдохновитель
Большое спасибо Shajeeb — автору оригинального проекта на базе матриц MAX72xx. Я лишь адаптировал часть управления матрицей под RGB WS2812B.
Оригинал: 32-Band Audio Spectrum Visualizer Analyzer
Особенности WS2812B RGB матрицы
Матрица на 5050 SMD высокой яркости потребляет >10 мА на светодиод. При максимальной яркости всех 256 LED — более 2.5 А. Необходимо внешнее питание.
Добавлен диод в разрыв +5V для возможности автономной работы Arduino от USB (когда USB отключён) и защиты Arduino от перегрузки током матрицы. Также: резистор 390 Ом в линию данных (D6 → Data In) для защиты входа матрицы от пиковых напряжений, и конденсатор 1000 мкФ 12В для стабилизации питания Arduino.
Сборка железа
Первый прототип на многоотверстной плате 4x6 см с двумя RCA-розетками (можно заменить на 3.5 мм Jack). Важно: используйте экранированный кабель для аудиовхода, чтобы избежать гудения. Соединение Arduino ↔ матрица делайте максимально коротким.
Код
База — отличная работа автора библиотеки FFT и финальная реализация Shajeeb. Добавлены две функции:
- GetLedFromMatrix(x, y) — маппинг матрицы в строки/столбцы для адресации каждого из 256 LED по координатам.
- SetColumn(col, height) — зажигает LED в столбце до высоты пика (0–7) с цветами из двумерного массива. Цвета можно менять по вкусу. Используется подпрограмма
Wheel()(из демо Adafruit Neopixel) — преобразует 0–255 в 32-битный цвет дляsetPixelColor(). Учитывайте ограничения памяти Arduino — избегайте 32-битных переменных для хранения цветов где возможно.
Аудио-эквалайзация
Тесты проводились со встроенной звуковой картой ПК — для улучшения АЧХ добавлен байтовый массив eq[32] — кривая эквалайзации: подавление басов, усиление высоких. Значение 100 = без изменений, <100 = подавление, >100 = усиление. Отключается флагом EQ_ON = false.
Яркость матрицы
Задана константой BRIGHTNESS = 32 (макс. 255). При >100 белый становится жёлтоватым — вероятно, нужно питать матрицу через центральные красный/чёрный провода, а не правый коннектор. На яркости 64 достаточно 1А БП, иначе нужен 2А.
Будущие обновления
Работаю над версией с библиотекой OpenMusicLabs FHT — в разы быстрее Arduino FFT. Stay tuned! :)
Простите мой английский, использовал Google Translate.
Код: Spectrum_Analyzer_WS2812B.ino
/* Copyright (c) 2019 Shajeeb TM
* MIT License
* WS2812B Led Matrix version by Janux
*/
#include <arduinoFFT.h>
#include <SPI.h>
#include <Adafruit_NeoPixel.h>
#define SAMPLES 64 // степень 2
#define xres 32 // столбцов на дисплее (<= SAMPLES/2)
#define yres 8 // строк на дисплее
#define ledPIN 6 // пин управления матрицей
#define NUM_LEDS (xres * yres)
#define BRIGHTNESS 32
#define buttonPin 5 // кнопка смены цветовой схемы
byte yvalue, displaycolumn, displayvalue;
int peaks[xres];
byte state = HIGH, previousState = LOW;
byte displaycolor = 0;
unsigned long lastDebounceTime = 0;
unsigned long debounceDelay = 100;
double vReal[SAMPLES];
double vImag[SAMPLES];
byte data_avgs[xres];
arduinoFFT FFT = arduinoFFT();
Adafruit_NeoPixel pixel = Adafruit_NeoPixel(NUM_LEDS, ledPIN, NEO_GRB + NEO_KHZ800);
// EQ фильтр: подавление басов, усиление высоких (для встроенного звука ПК)
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 };
// Цветовые схемы [схема][строка] — значения для Wheel()
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}, // 0 = кастомный цвет пика
{170, 160, 150, 140, 130, 120, 1, 1},
{170, 160, 150, 140, 130, 120, 1, 1}
};
void setup() {
pixel.begin();
pixel.setBrightness(BRIGHTNESS);
// Настройка АЦП: free running mode, прескалер 32
ADCSRA = 0b11100101; // 0xE5
ADMUX = 0b00000000; // A0, внешний опорный
}
void loop() {
// Сэмплирование
for (int i = 0; i < SAMPLES; i++) {
while (!(ADCSRA & 0x10)); // ждём ADIF
ADCSRA = 0b11110101; // сброс ADIF (0xF5)
int value = ADC - 512; // чтение АЦП, снятие DC смещения
vReal[i] = value / 8; // сжатие
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 результата под 32 столбца
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++;
}
// Вывод на матрицу
for (int i = 0; i < xres; i++) {
if (EQ_ON) data_avgs[i] = data_avgs[i] * (float)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]--; // затухание пика
if (yvalue > peaks[i]) peaks[i] = yvalue;
yvalue = peaks[i];
displaycolumn = i; displayvalue = yvalue;
setColumn(displaycolumn, displayvalue);
}
pixel.show();
displaycolorChange();
}
// Зажигает LED столбца x до высоты y
void setColumn(byte x, byte y) {
byte led, i;
for (i = 0; i < yres; i++) {
led = GetLedFromMatrix(x, i);
if (peaks[x] > i) {
switch (displaycolor) {
case 4: // кастомный цвет пика (0 в массиве)
if (colors[displaycolor][i] > 0)
pixel.setPixelColor(led, Wheel(colors[displaycolor][i]));
else
pixel.setPixelColor(led, 255, 255, 255); // белый пик
break;
case 5: // цвет по столбцу
pixel.setPixelColor(led, Wheel(x * 16));
break;
case 6: // цвет по строке
pixel.setPixelColor(led, Wheel(i * 36));
break;
default: // схемы 0-3 из массива colors
pixel.setPixelColor(led, Wheel(colors[displaycolor][i]));
}
} else {
pixel.setPixelColor(led, 0);
}
}
}
// Расчёт номера LED по координатам (x=0-31, y=0-7)
// Змеиная разводка, нулевой LED в правом нижнем углу (вход справа)
byte GetLedFromMatrix(byte x, byte y) {
x = xres - x - 1;
if (x & 0x01) // нечётные столбцы — обратный порядок
return ((x + 1) * yres - y - 1);
else // чётные — прямой
return ((x + 1) * yres - yres + y);
}
// Смена цветовой схемы кнопкой с антидребезгом
void displaycolorChange() {
int reading = digitalRead(buttonPin);
if (reading == HIGH && previousState == LOW && millis() - lastDebounceTime > debounceDelay) {
displaycolor++;
if (displaycolor > 6) displaycolor = 0;
lastDebounceTime = millis();
}
previousState = reading;
}
// Цветовое колесо 0-255 → RGB (из Adafruit Neopixel demo)
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-модели и дополнительные файлы
Изображения схем и архив с 3D-частьми: arduino_rgb_audio_analyzer_lX4Dv4Pta2.zip
Схемы
Схемотехнический диаграмм подключения.
Производственный процесс
- 32-полосный Arduino аудио-спектральный анализатор — линейный и псевдологарфмический дисплей
- Умный датчик полива «Thirst Alert» на Arduino — DIY сигнализация засухи для растений
- Итальянские словесные часы на Arduino Nano, NeoPixel, LCD и RTC
- Надежный счетчик кВт·ч на Sigfox с Arduino MKR Fox 1200
- Bluetooth-монитор температуры на Arduino Nano с NTC-термистором
- Бесконтактный жестовый замок: APDS-9960 + Arduino для безопасного доступа
- Набор-компаньон для Arduino Nano — необходимые компоненты и инструменты для DIY-электроники
- Набор USB-MIDI адаптера для Arduino Micro / Leonardo — полный список деталей
- Гальванически разделённый аналоговый вход для Arduino на LM331 и оптопаре 4N25
- Точный тест времени реакции на Arduino UNO