Espectrómetro de Audio con Tiras LED RGB
Proyecto DIY · ESP32 + FFT + WS2812B
Espectrómetro de Audio
con Tiras LED RGB
Visualizador de frecuencias en tiempo real usando transformada de Fourier (FFT) en un ESP32. El color de cada barra cambia de verde a rojo según la potencia de cada banda de frecuencia.
Simulador en tiempo real FFT activa
5
8
· TIRAS WS2812B · 16 BANDAS × 20 LEDS ·
20Hz80Hz250Hz1kHz
4kHz8kHz16kHz20kHz
PICO: —
POT: —
BANDA: —
RMS: —
| Componente | Cant. | Notas | Precio aprox. |
|---|---|---|---|
| ESP32 DevKit v1 | 1 | 38 pines, dual core 240MHz | $4–6 USD |
| Micrófono INMP441 | 1 | I2S digital, 24 bits, mejor calidad | $3–5 USD |
| Micrófono MAX4466 | 1 | Analógico, más fácil, alternativa | $1–2 USD |
| Tiras WS2812B | 16 | 20 LEDs por tira = 320 LEDs total | $1–2 USD/m |
| Fuente 5V | 1 | Mínimo 10A. 320 LEDs × 60mA | $8–15 USD |
| Resistencias 330Ω | 16 | Una por pin de datos LED | $0.50 USD |
| Capacitor 1000µF 10V | 1 | Elimina parpadeo en las tiras | $0.50 USD |
Alimentación crítica: Nunca alimentes las tiras desde el pin 5V del ESP32. Usa siempre una fuente externa dedicada de 5V/10A mínimo. El GND de la fuente debe estar conectado al GND del ESP32 (GND común).
INMP441 → ESP32
VCC3.3V
GNDGND
SDGPIO 32
SCKGPIO 33
WSGPIO 25
L/RGND (mono)
MAX4466 → ESP32 (analógico)
VCC3.3V
GNDGND
OUTGPIO 34 (ADC)
Agrega divisor de tensión 2×100kΩ y capacitor 100nF para centrar la señal en 1.65V.
WS2812B → ESP32
DIN 0GPIO 2 + 330Ω
DIN 1GPIO 4 + 330Ω
DIN 2GPIO 5 + 330Ω
DIN 3–15GPIO 12–27 + 330Ω
5VFuente externa
GNDGND común
Condensador protector
Coloca el capacitor de 1000µF en paralelo entre el 5V y GND de la primera tira LED, lo más cerca posible al conector.
Elimina el pico de corriente que causa parpadeos al encender.
Versión con micrófono digital INMP441 (I2S). Mayor calidad de audio, 24 bits, hasta 20kHz estable.
esp32_spectrum_i2s.ino
// ═══════════════════════════════════════════════ // ESP32 Audio Spectrum Analyzer · I2S INMP441 // ═══════════════════════════════════════════════ #include <Arduino.h> #include <driver/i2s.h> #include <arduinoFFT.h> #include <FastLED.h> // ── I2S (INMP441) ───────────────────────────── #define I2S_WS 25 #define I2S_SCK 33 #define I2S_SD 32 #define SAMPLE_RATE 44100 #define BUFFER_SIZE 1024 // ── LEDs ────────────────────────────────────── #define NUM_BANDS 16 #define LEDS_PER_BAND 20 #define SENSITIVITY 2.5f #define NOISE_FLOOR 500 #define SMOOTHING 0.25f const uint8_t LED_PINS[NUM_BANDS] = { 2,4,5,12,13,14,15,16, 17,18,19,21,22,23,26,27 }; const float BAND_FREQ[NUM_BANDS+1] = { 20,40,80,120,200,315,500,800, 1250,2000,3150,5000,8000,12500,16000,20000,22050 }; CRGB leds[NUM_BANDS][LEDS_PER_BAND]; double vReal[BUFFER_SIZE], vImag[BUFFER_SIZE]; ArduinoFFT<double> FFT(vReal, vImag, BUFFER_SIZE, SAMPLE_RATE); float bandValues[NUM_BANDS]={}, peakValues[NUM_BANDS]={}; int peakTimer[NUM_BANDS]={}; void i2s_init() { i2s_config_t cfg = { .mode=(i2s_mode_t)(I2S_MODE_MASTER|I2S_MODE_RX), .sample_rate=SAMPLE_RATE, .bits_per_sample=I2S_BITS_PER_SAMPLE_32BIT, .channel_format=I2S_CHANNEL_FMT_ONLY_LEFT, .communication_format=I2S_COMM_FORMAT_STAND_I2S, .intr_alloc_flags=ESP_INTR_FLAG_LEVEL1, .dma_buf_count=4, .dma_buf_len=BUFFER_SIZE }; i2s_pin_config_t pins={.bck_io_num=I2S_SCK,.ws_io_num=I2S_WS, .data_out_num=I2S_PIN_NO_CHANGE,.data_in_num=I2S_SD}; i2s_driver_install(I2S_NUM_0,&cfg,0,NULL); i2s_set_pin(I2S_NUM_0,&pins); } CRGB powerToColor(float p) { p = constrain(p,0.0f,1.0f); if(p<0.4f) return CRGB(0, 50+(uint8_t)(p/0.4f*150), 0); if(p<0.7f){float t=(p-0.4f)/0.3f; return CRGB(t*255,200,0);} if(p<0.9f){float t=(p-0.7f)/0.2f; return CRGB(255,200-t*100,0);} float t=(p-0.9f)/0.1f; return CRGB(255,100-t*100,t*30); } void readMic() { int32_t raw[BUFFER_SIZE]; size_t b=0; i2s_read(I2S_NUM_0,raw,sizeof(raw),&b,portMAX_DELAY); for(int i=0;i<BUFFER_SIZE;i++){vReal[i]=(raw[i]>>8); vImag[i]=0;} } void computeFFT() { FFT.windowing(FFTWindow::Hamming,FFTDirection::Forward); FFT.compute(FFTDirection::Forward); FFT.complexToMagnitude(); for(int b=0;b<NUM_BANDS;b++){ int lo=BAND_FREQ[b]*BUFFER_SIZE/SAMPLE_RATE; int hi=min((int)(BAND_FREQ[b+1]*BUFFER_SIZE/SAMPLE_RATE),BUFFER_SIZE/2); float s=0; int n=0; for(int i=lo;i<hi;i++) if(vReal[i]>NOISE_FLOOR){s+=vReal[i];n++;} float v=n?constrain(s/n*SENSITIVITY/100000.f,0,1):0; float sp=v>bandValues[b]?SMOOTHING*2:SMOOTHING*0.5f; bandValues[b]+=(v-bandValues[b])*sp; if(bandValues[b]>peakValues[b]){peakValues[b]=bandValues[b];peakTimer[b]=30;} if(peakTimer[b]>0)peakTimer[b]--; else peakValues[b]=max(0.f,peakValues[b]-0.015f); } } void renderLEDs() { for(int b=0;b<NUM_BANDS;b++){ int lit=bandValues[b]*LEDS_PER_BAND; int pk=peakValues[b]*LEDS_PER_BAND; for(int l=0;l<LEDS_PER_BAND;l++){ if(l<lit) leds[b][l]=powerToColor((float)l/LEDS_PER_BAND+0.05f); else if(l==pk&&peakTimer[b]>0) leds[b][l]=CRGB::White; else leds[b][l]=CRGB::Black; } } FastLED.show(); } void setup() { FastLED.addLeds<WS2812B,2,GRB>(leds[0],LEDS_PER_BAND); FastLED.addLeds<WS2812B,4,GRB>(leds[1],LEDS_PER_BAND); // ... repetir para las 16 tiras ... FastLED.setBrightness(180); FastLED.clear(); FastLED.show(); i2s_init(); } void loop() { readMic(); computeFFT(); renderLEDs(); }
Versión con micrófono analógico MAX4466 o KY-038. Más fácil y barato. Reemplaza los bloques I2S.
esp32_spectrum_analog.ino
// ═══════════════════════════════════════════════ // ESP32 Audio Spectrum · Micrófono Analógico // MAX4466 o KY-038 en GPIO 34 // ═══════════════════════════════════════════════ #include <Arduino.h> #include <arduinoFFT.h> #include <FastLED.h> #define MIC_PIN 34 // ADC1_CH6 — solo entrada #define SAMPLE_RATE 10000 // Hz (max estable con ADC) #define BUFFER_SIZE 512 // Potencia de 2 #define ADC_CENTER 2048 // Centro 12 bits (0–4095) #define NUM_BANDS 16 #define LEDS_PER_BAND 20 CRGB leds[NUM_BANDS][LEDS_PER_BAND]; double vReal[BUFFER_SIZE], vImag[BUFFER_SIZE]; ArduinoFFT<double> FFT(vReal, vImag, BUFFER_SIZE, SAMPLE_RATE); float bandValues[NUM_BANDS]={}, peakValues[NUM_BANDS]={}; int peakTimer[NUM_BANDS]={}; const float BAND_FREQ[NUM_BANDS+1] = { 20,40,80,120,200,315,500,800, 1250,2000,3150,4500,5000,7000,9000,10000,10500 }; void readMicAnalog() { const uint32_t interval = 1000000 / SAMPLE_RATE; uint32_t t = micros(); for(int i=0; i<BUFFER_SIZE; i++){ while(micros() < t); t += interval; vReal[i] = analogRead(MIC_PIN) - ADC_CENTER; vImag[i] = 0; } } CRGB powerToColor(float p) { p = constrain(p,0,1); if(p<0.4f) return CRGB(0,50+p/0.4f*150,0); if(p<0.7f){float t=(p-0.4f)/0.3f; return CRGB(t*255,200,0);} float t=(p-0.7f)/0.3f; return CRGB(255,200-t*200,0); } void computeFFT() { FFT.windowing(FFTWindow::Hamming,FFTDirection::Forward); FFT.compute(FFTDirection::Forward); FFT.complexToMagnitude(); for(int b=0;b<NUM_BANDS;b++){ int lo=BAND_FREQ[b]*BUFFER_SIZE/SAMPLE_RATE; int hi=min((int)(BAND_FREQ[b+1]*BUFFER_SIZE/SAMPLE_RATE),BUFFER_SIZE/2); float s=0; int n=0; for(int i=lo;i<hi;i++) if(vReal[i]>300){s+=vReal[i];n++;} float v=n?constrain(s/n*3.0f/2048.f,0,1):0; float sp=v>bandValues[b]?0.5f:0.1f; bandValues[b]+=(v-bandValues[b])*sp; if(bandValues[b]>peakValues[b]){peakValues[b]=bandValues[b];peakTimer[b]=30;} if(peakTimer[b]>0)peakTimer[b]--; else peakValues[b]=max(0.f,peakValues[b]-0.02f); } } void renderLEDs() { for(int b=0;b<NUM_BANDS;b++){ int lit=bandValues[b]*LEDS_PER_BAND; int pk=peakValues[b]*LEDS_PER_BAND; for(int l=0;l<LEDS_PER_BAND;l++){ if(l<lit) leds[b][l]=powerToColor((float)l/LEDS_PER_BAND); else if(l==pk&&peakTimer[b]>0) leds[b][l]=CRGB::White; else leds[b][l]=CRGB::Black; } } FastLED.show(); } void setup() { analogReadResolution(12); analogSetAttenuation(ADC_11db); analogSetPinAttenuation(MIC_PIN, ADC_11db); FastLED.addLeds<WS2812B,2,GRB>(leds[0],LEDS_PER_BAND); // ... agregar las 16 tiras ... FastLED.setBrightness(180); FastLED.clear(); FastLED.show(); } void loop() { readMicAnalog(); computeFFT(); renderLEDs(); }
platformio.ini
; PlatformIO — pegar en platformio.ini
[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
lib_deps =
fastled/FastLED @ ^3.6.0
kosme/arduinoFFT @ ^2.0.2
monitor_speed = 115200
Arduino IDE — Library Manager
Herramientas → Administrar bibliotecas: FastLED by Daniel Garcia v3.6+ arduinoFFT by Enrique Condes v2.0+ Placa: "ESP32 Dev Module" Menú: Herramientas → Placa → esp32 → ESP32 Dev Module Partición: "Default 4MB with spiffs"
| Mic | Tipo | Freq max | Resolución | Dificultad |
|---|---|---|---|---|
| INMP441 | Digital I2S | 20 kHz | 24 bits | Media |
| MAX4466 | Analógico | ~10 kHz | 12 bits ADC | Fácil |
| KY-038 | Analógico | ~8 kHz | 12 bits ADC | Muy fácil |

Para el micrófono analógico

