LedB

Proyecto abierto · Autorregulación sonora

LedB: el nivel de sonido a la vista de todos

Un indicador luminoso que mide la intensidad sonora en dB(A) y la muestra en una tira de LED: verde, ámbar, rojo. Pensado para aulas y comedores escolares acústicamente mejorados, con micrófono digital MEMS, ESP32 y código abierto.

Mejorar la acústica de una sala no cambia por sí solo cómo se habla dentro de ella. Hace falta que quien habla tome conciencia de su propio nivel sonoro, y para eso necesita verlo. LedB convierte los decibelios en una señal luminosa que está ahí para todo el grupo, no en un juicio del adulto que lo dirige.

LedB, indicador luminoso de nivel de sonido

LedB en funcionamiento: cada LED encendido equivale a un escalón de nivel sonoro medido en dB(A).

Por qué un indicador visual

La taxonomía de objetivos de aprendizaje de Marzano y Kendall (2007) incorpora, en el máximo nivel, el Sistema de Conciencia del Ser: la autorregulación.1 Modificar los hábitos de habla en un espacio mejorado acústicamente no es automático: requiere facilitar esa autorregulación evaluando la importancia, la eficacia, las emociones y la motivación.

El objetivo, entonces, es que los hablantes se autorregulen tomando conciencia del nivel de logro en la mejora. Para eso hemos creado un dispositivo colectivo que mide y muestra visualmente el nivel de intensidad sonora.

La idea en una línea

Más allá de una herramienta de aprendizaje STEM, este sonómetro visual es un agente de cambio conductual: devuelve el dato en el mismo momento en que se produce y lo hace visible para todo el grupo a la vez.

Qué muestra en el aula

40 – 55 dB(A)
Concentración óptima
  • Lectura individual fluida
  • Trabajo en grupos pequeños
  • Explicaciones del profesorado claras
55 – 70 dB(A)
Alerta preventiva
  • Actividades colaborativas moderadas
  • Discusiones dirigidas
  • Transiciones entre actividades
más de 70 dB(A)
Acción necesaria
  • Ruido excesivo que interfiere
  • Pérdida de concentración
  • Necesidad de intervención inmediata

Lo que hemos observado

  • Autorregulación inmediata: el alumnado ajusta su volumen al ver los LED.
  • Menos desgaste docente: menos necesidad de intervenciones verbales.
  • Mejor ambiente de trabajo: un entorno más propicio para el aprendizaje.
  • Conciencia social: respeto por el espacio compartido.

En el comedor escolar

El comedor es el otro espacio donde el indicador rinde de inmediato, porque ahí el ruido no solo dificulta la conversación: también cambia cómo se come. Los tramos de referencia son distintos a los del aula.

50 – 65 dB(A)
Comida tranquila
  • Digestión óptima y conversación relajada
  • Socialización saludable
  • Experiencia gastronómica positiva
65 – 75 dB(A)
Socialización activa
  • Interacción social normal
  • Celebraciones moderadas
  • Equilibrio entre lo social y la salud
más de 75 dB(A)
Sobrecarga sensorial
  • Estrés alimentario
  • Comunicación dificultada
  • Ambiente poco saludable

Cómo se usa bien

El color no es un premio ni un castigo: es información. Cada tramo tiene una función pedagógica distinta, y conviene explicarla al grupo antes de encender el aparato por primera vez.

Verde
Refuerzo positivo inmediato
  • Satisfacción inmediata al mantenerlo
  • «Mantener el verde» funciona como juego natural
  • El grupo se autorregula colectivamente
Ámbar
Atención sin castigo
  • Alerta temprana, sin consecuencias negativas
  • Tiempo para autoajustarse antes del problema
  • Desarrollo gradual de la conciencia acústica
Rojo
Llamada de atención
  • Señal clara, pero no punitiva
  • Responsabilidad compartida por todo el grupo
  • Conexión visible entre ruido y bienestar

Retroalimentación inmediata frente a intervención tardía

Sistema tradicional
Ruido ↓ Molestia ↓ Intervención adulta ↓ Corrección

Reactivo: genera conflicto.

Con indicador luminoso
Ruido ↓ Visualización ↓ Autorregulación ↓ Mantenimiento

Proactivo: genera conciencia.

Competencias que se trabajan

Competencia Desarrollo Evidencia observable
Empatía acústica Consideración hacia otros Reducción voluntaria del volumen
Autorregulación Control interno del comportamiento Ajuste proactivo sin supervisión
Responsabilidad colectiva Cuidado del ambiente común Intervención positiva entre iguales
Conciencia ambiental Comprensión de causa y efecto Correlación entre ruido y bienestar

Y un proyecto STEM completo

Ámbito Componente Aplicación
Ciencia Análisis FFT y ponderación A Física del sonido y psicoacústica
Tecnología Micrófono I2S y ESP32 Procesamiento digital de señales
Ingeniería Diseño de circuitos y LED Sistemas embebidos y visualización
Matemáticas Algoritmos DSP y estadística Transformadas, promedios y mapeo

Medir el sonido no es sencillo

Medir bien la intensidad sonora tiene más dificultades de las que parece, y merece la pena conocerlas antes de elegir componentes: hay un buen repaso en este enlace.2

Queremos una sensibilidad y una banda de frecuencia suficientes para medir desde 40 dB hasta 90 dB con ponderación dB(A), que es la que se corresponde con la percepción de nuestro oído. Nuestra elección, por relación coste-rendimiento, ha sido el micrófono digital MEMS INMP441. Como segunda opción, el MEMS ICS-43434. Ambos trabajan con el interfaz I2S que encontramos en el ESP32.

Por qué no un micrófono analógico

Algunos prototipos habituales de semáforos de ruido utilizan micrófonos analógicos electret. Por lo general el resultado está más cerca de unas luces de fiesta que de un auténtico medidor de intensidad del sonido.

Placa controladora

Si necesitamos disponer de interfaz I2S, hay que verificar que esté disponible en nuestra controladora. Nuestro prototipo utiliza una ESP32-S3 Supermini. También ha funcionado con la AZ-Delivery Lolin32 y con la ESP32 D1 R32.

Visualización

Para visualizar los dB medidos utilizamos una tira Neopixel. El número de LED iluminados corresponde al nivel de dB(A) medido, y el color va variando: los primeros LED en verde, después ámbar y los niveles máximos en rojo. Es un híbrido de semáforo y vúmetro: un vúmetro semafórico.

40–90 dB(A)
Rango de medida
11 LED
Prototipo: 5 dB por LED
25 LED
Versión ampliada: 2 dB por LED

El prototipo

Plano general del prototipo de LedB Primer plano del prototipo de LedB

Plano general y detalle del montaje: ESP32-S3 Supermini, micrófono MEMS I2S y tira WS2812B.

1. Micrófono I2S al ESP32

Micrófono I2S ESP32
VDD (3.3 V)3.3V
GNDGND
SD (DATA)GPIO 1
SCK (BCLK)GPIO 2
WS (LRCLK)GPIO 7
L/RGND (para canal izquierdo)

2. Tira LED WS2812B al ESP32

WS2812B ESP32 Fuente externa
DIN (Data)GPIO 8—
5V—5V
GNDGNDGND
Alimentación y protecciones
  • Con pocos LED, como en este caso, puede usarse la salida de 5 V del propio ESP32.
  • A partir de 8-10 LED, usar una fuente externa de 5 V y conectar la masa (GND) común.
  • Condensador de filtrado de 1000 μF entre 5 V y GND, cerca de la tira: estabiliza la alimentación y previene picos de corriente.
  • Resistencia de 330-470 Ω entre el GPIO 8 y la entrada de datos de la tira: protege el primer LED y el GPIO del microcontrolador.

Test de medición

Para la prueba hemos añadido una pantalla OLED que muestra los dB medidos, y así poder comparar con la lectura de un sonómetro comercial. Nuestro visualizador mide entre 40 y 90 dB.

Comparación en directo entre la lectura de LedB y la de un sonómetro comercial.

Límite del micrófono MEMS

Los micrófonos MEMS tienen una limitación: no miden por debajo de 30 dB. En nuestro caso es irrelevante, porque en un contexto de actividad escolar casi siempre se dan lecturas por encima de 50 dB.

Lecturas del prototipo a 60, 70 y 80 dB Versión funcional de mayor tamaño de LedB

Comprobación de precisión a 60, 70 y 80 dB. A la derecha, la versión funcional de mayor tamaño (40 × 10 cm), con cuatro tiras LED para asegurar la visualización con luz de día y a distancia.

En el prototipo trabajamos con una tira de 11 LED Neopixel, lo que equivale a 5 dB por LED. Vista la precisión, podemos incrementar el número de LED por tira hasta 25: equivaldría a 2 dB por LED.

Listo para el aula

Listo para trabajar la autorregulación de los hablantes en nuevos espacios de aprendizaje. También en comedores escolares acústicamente mejorados.

Qué mide y qué no

LedB mide lo que genera el grupo, no lo que devuelve la sala. No mide el tiempo de reverberación ni el ruido de las instalaciones, y no sustituye a una medición acústica del espacio: para eso está el protocolo de medición del blog.

Código Arduino

Estructura y componentes principales

Hardware utilizado: microcontrolador ESP32, micrófono digital I2S INMP441 y tira de LED WS2812B (11 LED configurados).

Biblioteca Función
Arduino.hFramework base
driver/i2s.hInterfaz de audio digital
FastLED.hControl de LED
CircularBuffer.hppGestión de buffer circular para datos
arduinoFFT.hAnálisis de frecuencias

Flujo de funcionamiento

El sistema opera en un ciclo continuo con cuatro etapas.

1 · setup()
Configuración inicial
  • Inicialización de los LED, apagados
  • Configuración del bus I2S para el micrófono
  • Preparación de las estructuras de datos para la FFT
2 · loop()
Ciclo principal
  • Captura constante de audio a través de I2S
  • Actualización de la visualización cada 50 ms, configurable
3 · processAudio()
Procesamiento de audio
  • Cálculo del RMS de la señal
  • FFT para el análisis espectral
  • División del espectro en 12 bandas de frecuencia
  • Ponderación A, que simula la respuesta del oído humano
  • Conversión a decibelios y almacenamiento en buffer circular
4 · updateLEDDisplay()
Visualización
  • Mapeo del nivel sonoro al número de LED activos
  • Verde en niveles bajos, ámbar en medios, rojo en altos

Características técnicas

Bloque Parámetros
Muestreo de audio 44,1 kHz (calidad CD) · resolución de 32 bits · buffer DMA de 8 buffers de 1024 muestras
Análisis espectral FFT de 1024 puntos · ventana Hanning/Hamming · 12 bandas de frecuencia en escala logarítmica
Procesamiento acústico RMS verdadero · ponderación A (dBA) · rango configurable de 40 a 90 dB

Flujo de datos

Micrófono I2S → captura de muestras → cálculo RMS → FFT
→ análisis por bandas → ponderación A → conversión a dB
→ buffer circular → promediado → mapeo a LED → visualización

Optimizaciones implementadas

Rendimiento
  • DMA para captura de audio sin bloqueo
  • Ventana FFT para reducir efectos de borde
  • Buffer circular para estabilizar las lecturas
Precisión acústica
  • Ponderación A para representar mejor la percepción humana
  • Escala logarítmica para las bandas de frecuencia
  • Promediado temporal para reducir fluctuaciones
ledb.ino · ESP32 Sound Level Meter
/**
 * ESP32 Sound Level Meter with LED Display
 *
 * Este sistema captura audio mediante un micrófono I2S digital,
 * procesa la señal para calcular niveles de sonido en dB(A),
 * y visualiza los resultados en una tira de LEDs WS2812B.
 *
 * Características:
 * - Captura de audio digital mediante interfaz I2S
 * - Análisis FFT para procesamiento en dominio de frecuencia
 * - Ponderación A para simular la respuesta del oído humano
 * - Visualización mediante LEDs con escala de colores según intensidad
 *
 * Hardware recomendado:
 * - ESP32 (cualquier variante)
 * - Micrófono digital I2S (INMP441, SPH0645, etc.)
 * - Tira de LEDs WS2812B
 *
 * Conexiones:
 * - I2S WS (Word Select/LRCLK): GPIO 7
 * - I2S SD (Serial Data): GPIO 1
 * - I2S SCK (Serial Clock/BCLK): GPIO 2
 * - LED Data: GPIO 8
 *
 * Autor: Vicent Villena Serrano
 * Herramienta de refinamiento: Claude AI
 * Fecha: Mayo 2025
 */

#include <Arduino.h>
#include <driver/i2s.h>
#include <FastLED.h>
#include <CircularBuffer.hpp>
#include <arduinoFFT.h>

// ==================== CONFIGURACIÓN DE CONSTANTES ====================

// Configuración de LED
#define LED_PIN     8       // El pin GPIO conectado a la entrada de datos de la tira WS2812
#define NUM_LEDS    11      // Número de LEDs en la tira (ajustable según configuración)
#define LED_TYPE    WS2812B // Tipo de LED (WS2812, WS2812B, WS2811, etc.)
#define COLOR_ORDER GRB     // Orden de color (GRB para WS2812, puede variar según modelo)
#define BRIGHTNESS  150     // Brillo global (0-255)
#define STRIPS      1       // Número de tiras LED conectadas en zigzag (para configuraciones avanzadas)
#define STEP        (254/NUM_LEDS/STRIPS) // Incremento de color entre LEDs sucesivos

// Configuración de I2S (protocolo de audio digital)
#define I2S_WS      7       // Word Select (LRCLK) - Selecciona canal I/D
#define I2S_SD      1       // Serial Data - Datos del micrófono
#define I2S_SCK     2       // Serial Clock (BCLK) - Reloj de bits
#define I2S_PORT    I2S_NUM_0 // Puerto I2S del ESP32 (0 o 1)

// Parámetros de audio mejorados
#define SAMPLE_RATE       44100   // Frecuencia de muestreo en Hz (calidad CD)
#define SAMPLE_BITS       32      // Bits por muestra
#define DMA_BUFFER_COUNT  8       // Número de buffers DMA para I2S
#define DMA_BUFFER_LENGTH 1024    // Tamaño de cada buffer DMA en samples
#define UPDATE_INTERVAL   50      // Intervalo de actualización visual en ms
#define BUFFER_SIZE       50      // Tamaño del buffer circular para promediar lecturas
#define DB_MIN            40      // Nivel mínimo de dB para mapeo a LEDs
#define DB_MAX            90      // Nivel máximo de dB para mapeo a LEDs

// Configuración de FFT (Transformada Rápida de Fourier)
#define FFT_SAMPLES       1024    // Número de muestras para FFT (potencia de 2)
#define NUM_BANDS         12      // Número de bandas de frecuencia para el análisis espectral

// ==================== VARIABLES GLOBALES ====================

// Buffer circular para almacenar niveles de sonido y reducir fluctuaciones
CircularBuffer<float, BUFFER_SIZE> buffer;
unsigned long lastUpdateTime = 0;  // Timestamp de la última actualización de LEDs

// Array de LEDs para control mediante FastLED
CRGB leds[NUM_LEDS];

// Variables para cálculo de FFT
ArduinoFFT<double> FFT;        // Objeto FFT
double vReal[FFT_SAMPLES];     // Parte real de los datos FFT
double vImag[FFT_SAMPLES];     // Parte imaginaria de los datos FFT
float bandValues[NUM_BANDS];   // Valores de energía para cada banda de frecuencia
float aWeightedSum = 0;        // Suma ponderada de energía (curva A)

// Task handle para procesamiento en segundo plano (no utilizado en esta versión)
TaskHandle_t audioProcessingTask;

// ==================== DECLARACIONES DE FUNCIONES ====================

void setupLEDs();              // Inicializa la tira de LEDs
void setupI2S();               // Configura el periférico I2S para captura de audio
void captureAudio();           // Captura muestras de audio desde el micrófono
void processAudio(int32_t* samples, int samplesRead);  // Procesa muestras de audio capturadas
void updateLEDDisplay(float averageDB);                // Actualiza visualización LED según nivel
float calculateAverageFromBuffer();                    // Calcula promedio desde buffer circular
void performFFT(double *vReal, double *vImag, int samples);  // Realiza análisis FFT
float applyAWeighting(float* bandValues, int numBands);      // Aplica ponderación A
float calculateRMS(int32_t* samples, int samplesRead);       // Calcula valor RMS
float freqToAWeight(float freq);                             // Calcula factor de ponderación A para frecuencia

// ==================== FUNCIONES PRINCIPALES ====================

/**
 * Configuración inicial del sistema
 */
void setup() {
  Serial.begin(115200);
  Serial.println("Inicializando sonómetro ESP32...");

  setupLEDs();
  setupI2S();

  lastUpdateTime = millis();

  // Inicializa el array de imaginarios para la FFT
  for (int i = 0; i < FFT_SAMPLES; i++) {
    vImag[i] = 0;
  }

  Serial.println("Sistema inicializado correctamente");
}

/**
 * Bucle principal del programa
 */
void loop() {
  // Captura continua de audio
  captureAudio();

  // Actualiza la visualización LED cada UPDATE_INTERVAL ms
  if (millis() - lastUpdateTime >= UPDATE_INTERVAL) {
    lastUpdateTime = millis();

    // Obtiene el promedio de nivel sonoro desde el buffer circular
    float avgDB = calculateAverageFromBuffer();

    // Actualiza visualización de LEDs con el valor promediado
    updateLEDDisplay(avgDB);
  }
}

// ==================== IMPLEMENTACIÓN DE FUNCIONES ====================

/**
 * Configura la tira de LEDs con los parámetros definidos
 */
void setupLEDs() {
  Serial.println("Configurando LEDs...");

  // Inicializar la tira de LEDs con corrección de color típica
  FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);
  FastLED.setBrightness(BRIGHTNESS);

  // Apagar todos los LEDs al inicio
  fill_solid(leds, NUM_LEDS, CRGB::Black);
  FastLED.show();

  Serial.printf("LEDs configurados: %d LEDs en pin %d\n", NUM_LEDS, LED_PIN);
}

/**
 * Configura el periférico I2S para la adquisición de audio digital
 */
void setupI2S() {
  Serial.println("Configurando interfaz I2S...");

  // Estructura de configuración I2S
  i2s_config_t i2s_config = {
    .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX),  // Modo maestro, solo recepción
    .sample_rate = SAMPLE_RATE,                           // Frecuencia de muestreo (44.1 kHz)
    .bits_per_sample = I2S_BITS_PER_SAMPLE_32BIT,         // Resolución de 32 bits
    .channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,          // Solo canal izquierdo
    .communication_format = I2S_COMM_FORMAT_I2S,          // Formato estándar I2S
    .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,             // Prioridad de interrupción
    .dma_buf_count = DMA_BUFFER_COUNT,                    // Número de buffers DMA
    .dma_buf_len = DMA_BUFFER_LENGTH,                     // Longitud de cada buffer
    .use_apll = true,                                     // Usar APLL para mayor precisión
    .tx_desc_auto_clear = false,                          // No auto-limpiar descriptores TX
    .fixed_mclk = 0                                       // No usar MCLK fijo
  };

  // Configuración de pines I2S
  i2s_pin_config_t pin_config = {
    .bck_io_num = I2S_SCK,           // Bit clock
    .ws_io_num = I2S_WS,             // Word select
    .data_out_num = I2S_PIN_NO_CHANGE, // No usamos salida de datos
    .data_in_num = I2S_SD            // Entrada de datos desde micrófono
  };

  // Instalación del driver I2S
  i2s_driver_install(I2S_PORT, &i2s_config, 0, NULL);
  i2s_set_pin(I2S_PORT, &pin_config);

  Serial.printf("I2S configurado: %d Hz, %d bits en pines WS:%d, SD:%d, SCK:%d\n",
                SAMPLE_RATE, SAMPLE_BITS, I2S_WS, I2S_SD, I2S_SCK);
}

/**
 * Captura muestras de audio desde el micrófono I2S
 */
void captureAudio() {
  static int32_t samples[DMA_BUFFER_LENGTH];  // Buffer estático para muestras de audio
  size_t bytes_read;

  // Lee muestras de audio desde I2S con espera indefinida
  i2s_read(I2S_PORT, samples, sizeof(samples), &bytes_read, portMAX_DELAY);

  // Convierte bytes leídos a número de muestras
  int samples_read = bytes_read / sizeof(int32_t);

  // Procesa las muestras capturadas
  processAudio(samples, samples_read);
}

/**
 * Procesa las muestras de audio capturadas mediante FFT y cálculos acústicos
 *
 * @param samples Puntero al array de muestras capturadas
 * @param samplesRead Número de muestras disponibles
 */
void processAudio(int32_t* samples, int samplesRead) {
  // Calcular RMS verdadero (medida de la amplitud de la señal)
  float rms = calculateRMS(samples, samplesRead);

  // Preparar datos para FFT aplicando ventana de Hanning
  for (int i = 0; i < FFT_SAMPLES && i < samplesRead; i++) {
    // Aplicar ventana de Hanning para reducir fugas espectrales
    float hann = 0.5 * (1 - cos(2 * PI * i / (FFT_SAMPLES - 1)));
    vReal[i] = (double)samples[i] * hann;
    vImag[i] = 0.0; // Parte imaginaria inicializada a 0
  }

  // Realizar la transformada rápida de Fourier (FFT)
  performFFT(vReal, vImag, FFT_SAMPLES);

  // Dividir el espectro en bandas de frecuencia (escala logarítmica)
  for (int i = 0; i < NUM_BANDS; i++) {
    // Cálculo de límites de cada banda usando escala logarítmica
    int lowerBin = 2 + pow(2, i * 10.0 / NUM_BANDS);
    int upperBin = 2 + pow(2, (i + 1) * 10.0 / NUM_BANDS);

    // Limitar los índices al rango válido de la FFT
    lowerBin = constrain(lowerBin, 2, FFT_SAMPLES / 2 - 1);
    upperBin = constrain(upperBin, 2, FFT_SAMPLES / 2 - 1);

    // Calcular el promedio de energía en esta banda
    float sum = 0;
    for (int j = lowerBin; j <= upperBin; j++) {
      sum += vReal[j];
    }
    bandValues[i] = sum / (upperBin - lowerBin + 1);
  }

  // Aplicar ponderación A (simula respuesta del oído humano)
  float dbA = applyAWeighting(bandValues, NUM_BANDS);

  // Convertir a decibelios con ponderación A (dBA)
  // Referencia estándar: 32768 es punto medio de int16 para normalización
  float dbLevel = 20 * log10(dbA / 32768.0) + 0;

  // Almacena el nivel en el buffer circular para suavizado temporal
  buffer.push(dbLevel);

  // Mostrar nivel de sonido en la consola serial
  Serial.printf("Sound level: %.1f dB(A)\n", dbLevel);
}

/**
 * Calcula el valor RMS verdadero (Root Mean Square) de la señal
 * RMS es una medida estadística de la magnitud de una cantidad variable
 *
 * @param samples Puntero al array de muestras
 * @param samplesRead Número de muestras disponibles
 * @return Valor RMS calculado
 */
float calculateRMS(int32_t* samples, int samplesRead) {
  double sum = 0;
  for (int i = 0; i < samplesRead; i++) {
    // Elevar al cuadrado cada muestra y acumular
    sum += (double)samples[i] * samples[i];
  }
  // Dividir por el número de muestras y obtener la raíz cuadrada
  return sqrt(sum / samplesRead);
}

/**
 * Realizar FFT en los datos de audio usando la biblioteca ArduinoFFT
 *
 * @param vReal Puntero al array con la parte real de los datos
 * @param vImag Puntero al array con la parte imaginaria de los datos
 * @param samples Número de muestras a procesar
 */
void performFFT(double *vReal, double *vImag, int samples) {
  // Crear objeto FFT con los arrays proporcionados
  FFT = ArduinoFFT<double>(vReal, vImag, samples, SAMPLE_RATE);

  // Aplicar ventana Hamming para mejorar precisión espectral
  FFT.windowing(FFT_WIN_TYP_HAMMING, FFT_FORWARD);

  // Calcular la FFT
  FFT.compute(FFT_FORWARD);

  // Convertir valores complejos a magnitudes
  FFT.complexToMagnitude();
}

/**
 * Aplica la ponderación A a las bandas de frecuencia para simular
 * la respuesta del oído humano a diferentes frecuencias
 *
 * @param bandValues Array con valores de energía por banda
 * @param numBands Número de bandas disponibles
 * @return Valor ponderado según curva A
 */
float applyAWeighting(float* bandValues, int numBands) {
  float weightedSum = 0;
  float totalWeight = 0;

  for (int i = 0; i < numBands; i++) {
    // Calcular la frecuencia central aproximada de cada banda (Hz)
    float freq = SAMPLE_RATE * (pow(2, i * 10.0 / numBands) + pow(2, (i + 1) * 10.0 / numBands)) / (4 * FFT_SAMPLES);

    // Obtener el factor de ponderación A para esta frecuencia
    float aWeight = freqToAWeight(freq);

    // Aplicar el peso al valor de energía de la banda
    weightedSum += bandValues[i] * aWeight;
    totalWeight += aWeight;
  }

  // Normalizar por el total de pesos para mantener escala correcta
  if (totalWeight > 0) {
    return weightedSum / totalWeight;
  }
  return weightedSum;
}

/**
 * Calcula el factor de ponderación A para una frecuencia dada
 * La curva de ponderación A simula la sensibilidad del oído humano
 * a diferentes frecuencias (menos sensible a frecuencias muy bajas o muy altas)
 *
 * @param freq Frecuencia en Hz para la que se calcula el factor
 * @return Factor de ponderación A (0-1)
 */
float freqToAWeight(float freq) {
  // Fórmula de aproximación para la curva de ponderación A
  float f2 = freq * freq;
  float f4 = f2 * f2;

  // Constantes para la fórmula estándar de ponderación A
  const float c1 = 12200 * 12200;  // Constante relacionada con la frecuencia de resonancia del oído
  const float c2 = 20.6 * 20.6;    // Constante relacionada con respuesta a bajas frecuencias
  const float c3 = 107.7 * 107.7;  // Constante de ajuste de respuesta de medias frecuencias
  const float c4 = 737.9 * 737.9;  // Constante de ajuste de respuesta de altas frecuencias

  // Calculamos la ponderación A según la fórmula estándar IEC 61672:2003
  float num = c1 * f4;
  float denom = (f2 + c2) * sqrt((f2 + c3) * (f2 + c4)) * (f2 + c1);

  float A = num / denom;

  return A;
}

/**
 * Calcula el promedio de los niveles de sonido almacenados en el buffer
 * para estabilizar la visualización y reducir fluctuaciones rápidas
 *
 * @return Nivel promedio en dB(A)
 */
float calculateAverageFromBuffer() {
  if (buffer.isEmpty()) {
    return 0.0;  // Evitar cálculos innecesarios si el buffer está vacío
  }

  float avg = 0.0;

  // Calcula el promedio de todos los valores en el buffer circular
  using index_t = decltype(buffer)::index_t;
  for (index_t i = 0; i < buffer.size(); i++) {
    avg += buffer[i];
  }
  avg /= buffer.size();

  Serial.print("Average dB(A): ");
  Serial.println(avg);

  return avg;
}

/**
 * Actualiza la visualización LED según el nivel de sonido promedio
 * Utiliza un esquema de colores que va de verde (bajo) a rojo (alto)
 *
 * @param averageDB Nivel de sonido promedio en dB(A)
 */
void updateLEDDisplay(float averageDB) {
  // Mapea el nivel de audio a la cantidad de LEDs a encender
  // DB_MIN y DB_MAX definen el rango de trabajo del sonómetro
  int max_led = map(averageDB, DB_MIN, DB_MAX, 1, NUM_LEDS/STRIPS);
  max_led = constrain(max_led, 0, NUM_LEDS/STRIPS);

  Serial.print("LEDs activos: ");
  Serial.println(max_led);

  // Limpia la pantalla LED (apaga todos los LEDs)
  FastLED.clear();

  // Configura los LEDs con un gradiente de color según la intensidad
  for(int i = 0; i < max_led; i++) {
    // Colores basados en niveles de dB(A)
    CRGB color;
    if (i < NUM_LEDS / (3 * STRIPS)) {
      // Verde para niveles bajos (0-33%)
      color = CRGB(0, 255 - i * STEP, 0);
    } else if (i < 2 * NUM_LEDS / (3 * STRIPS)) {
      // Amarillo para niveles medios (33-66%)
      color = CRGB(i * STEP, 255 - i * STEP, 0);
    } else {
      // Rojo para niveles altos (66-100%)
      color = CRGB(255, 0, 0);
    }

    // Asigna el color calculado al LED actual
    leds[i] = color;

    // Código para múltiples tiras conectadas en zigzag (deshabilitado por defecto)
    // Descomentar y ajustar para usar múltiples tiras conectadas en zigzag
    /*
    // Tira 2: orden inverso (para conexión en zigzag)
    int i2 = 2 * NUM_LEDS/STRIPS - i - 1;
    leds[i2] = color;

    // Tira 3: orden normal
    int i3 = i + 2 * NUM_LEDS/STRIPS;
    leds[i3] = color;

    // Tira 4: orden inverso
    int i4 = 4 * NUM_LEDS/STRIPS - i - 1;
    leds[i4] = color;
    */
  }

  // Actualiza físicamente los LEDs
  FastLED.show();
}

Código completo del prototipo. Autor: Vicent Villena Serrano · Herramienta de refinamiento: Claude AI · Mayo de 2025.

Referencias

  1. Marzano, R. J. y Kendall, J. S. (2007). The New Taxonomy of Educational Objectives (2.ª ed.). Corwin Press.
  2. atomic14. ESP32 Audio Input. atomic14.com/2020/09/12/esp32-audio-input

No hay comentarios:

Publicar un comentario

Nota: solo los miembros de este blog pueden publicar comentarios.