Visualizador de Espectro de Áudio RGB 32 Bandas – Projeto Matriz LED Arduino
Componentes e Materiais
- Arduino Nano R3
- Matriz RGB WS2812B 8x32
- Resistor 4.75k Ω – 3 unidades
- Resistor 100k Ω – 2 unidades
- Resistor 10k Ω – 1 unidade
- Resistor 390 Ω (furo passante) – 1 unidade
- Capacitor 100 nF – 2 unidades
- Capacitor 1000 µF – 1 unidade
- Chave Pushbutton SparkFun 12mm – 1 unidade
- Placa de prototipagem 4x6 cm multi-furos – 1 unidade
Ferramentas e Máquinas Necessárias
- Ferro de Solda (genérico)
- Cabo divisor Jack 3,5 mm
Sobre o Projeto
O Projeto
Visualizador de espectro de frequência de áudio (música) RGB 32-bandas usando Arduino Nano e matriz LED WS2812B 8x32.
Projeto Original que Inspirou Este
Agradecimentos a Shajeeb, autor do projeto original baseado em matriz MAX72xx. Modifiquei apenas a parte de pilotagem da matriz para adaptar à matriz RGB WS2812B.
Link original: 32-Band Audio Spectrum Visualizer Analyzer
A Matriz RGB WS2812B
Usando matriz LED RGB baseada em LEDs SMD 5050 alto brilho, fonte de alimentação externa é necessária: cada LED pode consumir >10mA, totalizando >2.5A com todos acesos no brilho máximo.
Inseri diodo em série no +5V para permitir alimentar Arduino standalone (sem cabo USB) e evitar que Arduino alimente a matriz RGB, sobrecarregando seus circuitos internos.
Adicionei também: resistor 390Ω em série entre pino D6 do Arduino e entrada Data (proteção contra picos), e capacitor 1000µF 12V para estabilizar tensão de alimentação do Arduino.
Montagem do Hardware
Primeiro protótipo em placa multi-furos 4x6cm com dois soquetes RCA (soldados direto na placa), substituíveis por jack 3.5mm fêmea. Importante: Use cabo blindado entre fonte de áudio e entrada da placa para evitar ruído. Mantenha conexão Arduino-matriz o mais curta possível.
O Código
Software baseado no excelente trabalho do autor do procedimento de amostragem via biblioteca FFT e realização final de Shajeeb.
Adicionei duas funções:
- GetLedFromMatrix(...): mapeia matriz em linhas/colunas para endereçar cada um dos 256 LEDs via coordenadas (x,y)
- SetColumn(...): acende LEDs de cada coluna baseado no valor de pico da digitalização de áudio (0-7) e cores pré-definidas em array bidimensional. Cores personalizáveis.
Usei sub-rotina Wheel() (do demo Adafruit Neopixel) que converte valor 0-255 em cor 32-bit para setPixelColor. Evite variáveis 32-bit para economizar memória.
Equalização de Áudio
Testei com áudio de placa-mãe de PC. Para melhorar resposta em frequência, adicionei array eq[32] com curva de equalização: atenua graves, realça agudos. Defina EQ_ON = false para desabilitar ou altere valores do array (100 = inalterado, <100 atenua, >100 realça).
Brilho dos LEDs
Brilho pré-definido no código: BRIGHTNESS = 32. Máximo teórico WS2812B é 255, mas >100 a luz branca fica amarelada (provavelmente precisa alimentar pela fiação central vermelha/preta, não conector lateral). Ainda testando...
Com brilho 64, fonte 1A provavelmente basta; caso contrário, 2A essencial.
Atualização Futura
Trabalhando em nova versão com biblioteca OpenMusicLabs FHT, muito mais rápida que Arduino FFT. Aguardem!
Peço desculpas pelo inglês ruim, usei tradutor automático.
Código
Spectrum_Analyzer_WS2812B.ino
/* Copyright (c) 2019 Shajeeb TM
* Licença MIT
* Versão WS2812B por Janux
*/
#include <arduinoFFT.h>
#include <SPI.h>
#include <Adafruit_NeoPixel.h>
#define SAMPLES 64 // Potência de 2
#define xres 32 // Colunas no display (<= SAMPLES/2)
#define yres 8 // Linhas no display
#define ledPIN 6 // Pino controle matriz LED
#define NUM_LEDS (xres * yres)
#define BRIGHTNESS 32
#define buttonPin 5 // Botão para mudar modo de cor
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);
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};
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);
ADCSRA = 0b11100101; // ADC free running, prescaler 32
ADMUX = 0b00000000; // Pino A0, referência externa
}
void loop() {
// Amostragem
for (int i = 0; i < SAMPLES; i++) {
while (!(ADCSRA & 0x10)); // espera ADIF
ADCSRA = 0b11110101; // limpa ADIF
int value = ADC - 512; // remove offset DC
vReal[i] = value / 8; // compressão
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);
// Rearranja FFT para colunas do display
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++;
}
// Envia para display
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] = peaks[i] - 1; // decaimento
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) {
byte led, i;
for (i = 0; i < yres; i++) {
led = GetLedFromMatrix(x, i);
if (peaks[x] > i) {
switch (displaycolor) {
case 4: // 0 no array = cor personalizada para pico
if (colors[displaycolor][i] > 0)
pixel.setPixelColor(led, Wheel(colors[displaycolor][i]));
else
pixel.setPixelColor(led, 255, 255, 255); // branco para pico
break;
case 5: // cor por coluna
pixel.setPixelColor(led, Wheel(x * 16)); break;
case 6: // cor por linha
pixel.setPixelColor(led, Wheel(i * 36)); break;
default: // modos 0-3: cores do array
pixel.setPixelColor(led, Wheel(colors[displaycolor][i]));
}
} else pixel.setPixelColor(led, 0);
}
}
byte GetLedFromMatrix(byte x, byte y) {
x = xres - x - 1;
if (x & 0x01) // colunas ímpares: sentido inverso
return ((x + 1) * yres - y - 1);
else // colunas pares: sentido normal
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;
}
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);
}
Peças Personalizadas e Gabinetes
Imagens do projeto e arquivo para download: arduino_rgb_audio_analyzer_lX4Dv4Pta2.zip
Esquemáticos
Esquemático do circuito.
Processo de manufatura
- Analisador de Espectro de Áudio Arduino 32 Bandas — Display Linear e Pseudo-Log
- Alerta Inteligente de Sede para Plantas – Sensor DIY para Hidratação Ideal
- Relógio de Palavras em Italiano com Arduino Nano, NeoPixel, LCD e RTC
- Medidor kWh Confiável com Sigfox e Arduino MKR Fox 1200
- Monitor de Temperatura com Bluetooth usando Arduino Nano
- Fechadura por Gestos Sem Toque: Acesso Seguro com APDS-9960 e Arduino
- Companion IC: Relógio, Ohmímetro, Capacímetro e Testador de Diodos com Arduino Nano
- Kit Adaptador USB-MIDI para Arduino Micro/Leonardo – Lista Completa de Peças
- Circuito de Entrada Analógica Isolada para Arduino
- Teste de Tempo de Reação Preciso com Arduino UNO