¿Cuánto ruido hay de verdad en una sala de clases, en el taller o en la pieza de al lado? El oído se acostumbra y engaña, pero un micrófono digital no. El INMP441 es un micrófono MEMS del tamaño de una uña que entrega el audio ya convertido a números por el bus I2S, y el ESP32 lo lee sin un solo componente extra.

En esta guía armas el circuito paso a paso y lo haces trabajar en cuatro proyectos: medir la intensidad del sonido (RMS), ver la forma de onda en el Serial Plotter y en Python, encender y apagar un LED con dos aplausos y, como extra propio de esta versión, un medidor de ruido en decibeles que avisa por MQTT cuando una sala pasa del umbral. Al final vas a entender qué viaja por cada cable del I2S, por qué el código desplaza cada muestra 8 bits y cómo calibrar un umbral sin adivinar.

Qué es el INMP441 y por qué conviene más que un micrófono analógico

El INMP441 es un micrófono omnidireccional MEMS (sistema microelectromecánico): una membrana de silicio microscópica que vibra con el sonido, un preamplificador y un conversor análogo digital, todo dentro de la misma cápsula. Lo que sale por sus pines no es un voltaje que sube y baja, sino muestras de audio de 24 bits en formato I2S.

Módulo micrófono INMP441 visto por arriba, con sus pines rotulados

Esa diferencia tiene consecuencias prácticas:

  • Omnidireccional: capta sonido desde cualquier dirección, así que no tienes que apuntarlo con precisión.
  • Salida digital directa: el ruido eléctrico de la protoboard o de la fuente no se mete en la señal, como pasa con los módulos analógicos tipo KY-038 o MAX4466 conectados al ADC.
  • Buena relación señal/ruido y bajo consumo, ideal para proyectos IoT que funcionan todo el día.
  • Sin calibrar el ADC: el ADC del ESP32 es poco lineal en los extremos; con el INMP441 esa etapa simplemente no existe.

Los pines vienen sueltos y hay que soldarlos. Al montarlo en la protoboard, deja el agujerito del micrófono mirando hacia ti: ese orificio es la entrada de sonido y no conviene taparlo.

Parte trasera del módulo INMP441 con los pines soldados

Cómo viaja el audio por I2S

I2S (Inter IC Sound) es un protocolo serie síncrono pensado solo para audio digital entre dos chips. Usa tres señales:

  • SCK o BCLK (reloj de bit): marca el ritmo; en cada pulso viaja un bit.
  • WS o LRCLK (selección de palabra): indica si la palabra que va pasando es del canal izquierdo o del derecho.
  • SD (datos): el audio propiamente tal, bit a bit.

El INMP441 necesita 64 pulsos de reloj por cada ciclo de WS: 32 para el canal izquierdo y 32 para el derecho. Con la configuración de este tutorial (16.000 muestras por segundo y palabras de 32 bits), el ESP32 genera un reloj de 16.000 × 64 = 1,024 MHz. El micrófono solo "habla" en el canal que eligió con su pin L/R y deja el otro en silencio.

Ese detalle explica una línea clave del código que viene más adelante: el INMP441 entrega 24 bits útiles, pero el hueco es de 32. Los 24 bits van alineados a la izquierda (los más significativos primero) y los 8 de abajo quedan en cero. Por eso cada muestra se desplaza con >> 8: así el número queda en su escala real de 24 bits, entre, 8.388.608 y 8.388.607.

El ESP32 trae periféricos I2S propios que pueden funcionar como entrada (leer un micrófono) o como salida (mandar audio a un amplificador). Si quieres el detalle a nivel de registros, la documentación de Espressif sobre I2S está enlazada en la sección de recursos.

Hardware: pines y conexiones

El módulo tiene seis pines:

Pin Función Dónde va
L/R Elige el canal I2S: a GND es izquierdo, a 3V3 es derecho GND (el código lee el canal izquierdo)
WS Word Select (LRCLK) Un GPIO del ESP32
SCK Reloj serie (BCLK) Un GPIO del ESP32
SD Datos de audio que salen del micrófono Un GPIO del ESP32 (entrada)
GND Tierra GND del ESP32
VDD Alimentación 3V3 del ESP32 (nunca 5V)

El ESP32 no tiene pines I2S fijos: la matriz de GPIO permite llevar cada señal a casi cualquier pin seguro. El tutorial original usa una ESP32-S3 DevKitC con esta distribución:

INMP441 ESP32-S3
L/R GND
WS GPIO 40
SCK GPIO 42
SD GPIO 41
GND GND
VDD 3V3

Circuito armado con el ESP32 y el micrófono INMP441 conectados por I2S

Diagrama de conexión entre la ESP32-S3 y el módulo INMP441

Si tienes un ESP32 clásico (DevKit V1 de 30 o 38 pines), los GPIO 40 a 42 no existen. Una combinación que funciona sin pelear con los pines de arranque es SCK en GPIO 26, WS en GPIO 25 y SD en GPIO 33. Solo cambias los tres #define del inicio de cada sketch. Evita los GPIO 0, 2, 12 y 15, que el chip lee al arrancar y pueden impedir que el programa cargue.

Software: lo que necesitas antes de programar

Necesitas Arduino IDE 2 con el soporte de placas ESP32 instalado (desde el gestor de placas, buscando "esp32" de Espressif). No hace falta ninguna librería externa: los tres ejemplos usan driver/i2s.h, el driver I2S de ESP IDF que ya viene dentro del core de Arduino para ESP32.

Un dato para evitar sustos: driver/i2s.h es el driver "heredado" de ESP IDF. En el core 3.x de Arduino para ESP32 sigue compilando, pero puede mostrar un aviso de que está obsoleto. No es un error y el programa funciona igual. Si más adelante quieres migrar, el reemplazo es driver/i2s_std.h o la librería ESP_I2S.h del propio core.

Los tres sketches comparten la misma configuración, que conviene entender una vez:

  • Frecuencia de muestreo de 16.000 Hz: es el estándar para voz. Por el teorema de Nyquist captura frecuencias hasta 8 kHz, de sobra para voz, aplausos y ruido ambiente.
  • Buffer de 512 muestras: a 16 kHz son 32 milisegundos de audio por lectura. Esa es la "ventana" sobre la que se calcula cada valor.
  • 32 bits por muestra y solo canal izquierdo: coincide con el hueco de 32 bits del I2S y con el pin L/R a GND. Si cambias L/R a 3V3, tienes que cambiar I2S_CHANNEL_FMT_ONLY_LEFT por I2S_CHANNEL_FMT_ONLY_RIGHT.
  • 8 buffers DMA: el periférico copia el audio a memoria por su cuenta mientras el procesador hace otra cosa, así no se pierden muestras.

Proyecto 1: medir la intensidad del sonido (RMS)

Una muestra aislada de audio no dice mucho, porque la onda cruza por cero miles de veces por segundo. Lo que representa el volumen es el valor RMS (raíz cuadrática media) de un bloque de muestras:

  1. Elevas al cuadrado cada muestra (así las negativas también suman).
  2. Sacas el promedio de todos esos cuadrados.
  3. Calculas la raíz cuadrada del resultado.

Mientras más fuerte el sonido, más alto el RMS. Copia este código en Arduino IDE:

C++
/*
  Rui Santos & Sara Santos - Random Nerd Tutorials
  Complete project details at https://RandomNerdTutorials.com/esp32-inmp441-i2s-microphone-arduino/
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*/
#include <driver/i2s.h>

// Pin configuration
#define I2S_SCK   42   // BCLK
#define I2S_WS    40   // LRCLK / WS
#define I2S_SD    41   // DOUT from mic

#define I2S_PORT  I2S_NUM_0
#define SAMPLE_RATE 16000
#define BUFFER_LEN  512

int32_t sBuffer[BUFFER_LEN];

void setup() {
  Serial.begin(115200);
  delay(1000);

  Serial.println("INMP441 Microphone");

  // I2S configuration
  i2s_config_t i2s_config = {
    .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,   // L/R = GND
    .communication_format = I2S_COMM_FORMAT_STAND_I2S,
    .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
    .dma_buf_count = 8,
    .dma_buf_len = BUFFER_LEN,
    .use_apll = false
  };

  // Pin configuration
  i2s_pin_config_t pin_config = {
    .bck_io_num = I2S_SCK,
    .ws_io_num = I2S_WS,
    .data_out_num = I2S_PIN_NO_CHANGE,
    .data_in_num = I2S_SD
  };

  // Start I2S
  i2s_driver_install(I2S_PORT, &i2s_config, 0, NULL);
  i2s_set_pin(I2S_PORT, &pin_config);
  i2s_zero_dma_buffer(I2S_PORT);

  Serial.println("I2S started.");
  Serial.println("Reading microphone...");
}

void loop() {
  size_t bytes_read = 0;

  // Read microphone data
  i2s_read(I2S_PORT, sBuffer, sizeof(sBuffer), &bytes_read, portMAX_DELAY);

  int samples_read = bytes_read / sizeof(int32_t);
  double sum = 0;

  // Print the samples
  for (int i = 0; i < samples_read; i++) {

    // INMP441 sends 24-bit audio left-aligned in a 32-bit I2S word
    int32_t sample = sBuffer[i] >> 8;
    //Serial.println(sample);
    sum += (double)sample * sample;
  }
    // Calculate RMS
    double rms = sqrt(sum / samples_read);

    // Print RMS
    //Serial.print("RMS: ");
    Serial.println(rms);
}

Cómo funciona el código

  • #include <driver/i2s.h> trae el driver I2S. Los tres #define de pines son los únicos que cambias si usas otra placa.
  • I2S_PORT elige el periférico I2S número 0, SAMPLE_RATE fija 16.000 muestras por segundo y BUFFER_LEN define cuántas muestras entran en cada cálculo. El arreglo sBuffer es de int32_t porque cada muestra ocupa un hueco de 32 bits.
  • En setup() se arma la estructura i2s_config_t (modo maestro y recepción, frecuencia, bits por muestra, canal, formato estándar I2S, buffers DMA) y la estructura i2s_pin_config_t con los pines. data_out_num va en I2S_PIN_NO_CHANGE porque solo recibes audio. Luego i2s_driver_install(), i2s_set_pin() e i2s_zero_dma_buffer() dejan el periférico andando.
  • En loop(), i2s_read() llena el buffer y devuelve en bytes_read cuántos bytes llegaron. Dividir por sizeof(int32_t) da el número real de muestras.
  • El for desplaza cada muestra >> 8 (los 24 bits útiles, como se explicó arriba), la eleva al cuadrado y la acumula en sum como double para que no se desborde: una muestra de 24 bits al cuadrado ya no cabe en 32 bits.
  • Al final, sqrt(sum / samples_read) es el RMS, y se imprime solo el número para que el Serial Plotter lo grafique limpio.

Prueba

Sube el código seleccionando tu placa y el puerto COM correcto. Después abre el Serial Plotter, que está al lado del ícono del Monitor Serie:

Botón para abrir el Serial Plotter en Arduino IDE

Aplaude, habla o chasquea los dedos y mira cómo sube y baja la curva. En la captura, el primer peak es un aplauso y el segundo, un chasquido:

Valor RMS del INMP441 graficado en el Serial Plotter, con peaks por un aplauso y un chasquido

Anota qué valores ves con la pieza en silencio y cuáles con un aplauso: esa es la materia prima para elegir un umbral en cualquier proyecto que reaccione al sonido.

Proyecto 2: ver la forma de onda del sonido

Si en vez del RMS imprimes cada muestra, lo que aparece en el Serial Plotter es la onda misma: la amplitud del sonido en cada instante.

C++
/*
  Rui Santos & Sara Santos - Random Nerd Tutorials
  Complete project details at https://RandomNerdTutorials.com/esp32-inmp441-i2s-microphone-arduino/
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*/
#include <driver/i2s.h>

// Pin configuration
#define I2S_SCK   42   // BCLK
#define I2S_WS    40   // LRCLK / WS
#define I2S_SD    41   // DOUT from mic

#define I2S_PORT  I2S_NUM_0
#define SAMPLE_RATE 16000
#define BUFFER_LEN  512

int32_t sBuffer[BUFFER_LEN];

void setup() {
  Serial.begin(115200);
  delay(1000);

  Serial.println("INMP441 Microphone");

  // I2S configuration
  i2s_config_t i2s_config = {
    .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,   // L/R = GND
    .communication_format = I2S_COMM_FORMAT_STAND_I2S,
    .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
    .dma_buf_count = 8,
    .dma_buf_len = BUFFER_LEN,
    .use_apll = false
  };

  // Pin configuration
  i2s_pin_config_t pin_config = {
    .bck_io_num = I2S_SCK,
    .ws_io_num = I2S_WS,
    .data_out_num = I2S_PIN_NO_CHANGE,
    .data_in_num = I2S_SD
  };

  // Start I2S
  i2s_driver_install(I2S_PORT, &i2s_config, 0, NULL);
  i2s_set_pin(I2S_PORT, &pin_config);
  i2s_zero_dma_buffer(I2S_PORT);

  Serial.println("I2S started.");
  Serial.println("Reading microphone...");
}

void loop() {
  size_t bytes_read = 0;

  // Read microphone data
  i2s_read(I2S_PORT, sBuffer, sizeof(sBuffer), &bytes_read, portMAX_DELAY);

  int samples_read = bytes_read / sizeof(int32_t);

  // Print the samples
  for (int i = 0; i < samples_read; i++) {

    // INMP441 sends 24-bit audio left-aligned in a 32-bit I2S word
    int32_t sample = sBuffer[i] >> 8;

    Serial.println(sample);
  }
}

El código es idéntico al anterior, salvo que no calcula el RMS: recorre el buffer y manda cada muestra (ya desplazada >> 8) por el puerto serie. Cada valor es la amplitud de la señal en un momento puntual.

Súbelo y abre el Serial Plotter. Prueba con vocales largas (aaaa, eeee, iiii, oooo, uuuu): cada una combina frecuencias distintas y la forma de la onda cambia.

Onda de sonido del INMP441 en el Serial Plotter de Arduino IDE

El Serial Plotter se queda corto para apreciar la forma: dibuja pocos puntos y ajusta la escala solo. Para ver bien la diferencia entre vocales conviene graficar con Python y matplotlib:

Formas de onda de las vocales a, e, i, o, u captadas con el INMP441

Extra: graficar la onda con matplotlib

  1. Instala Python y, durante la instalación, marca la opción "Add Python to PATH".
  2. En la terminal (o el símbolo del sistema en Windows), instala las librerías:
Bash
pip install pyserial matplotlib
  1. Deja el sketch de la forma de onda corriendo en el ESP32, anota el puerto serie (por ejemplo, COM5) y cierra Arduino IDE: si queda abierto, el puerto sigue ocupado y Python no puede leerlo.
  2. Descarga el script INMP441-microphone.py del repositorio de los autores (enlace en Recursos), cambia el puerto por el tuyo y guárdalo, por ejemplo, en el Escritorio.
  3. Ejecútalo desde la terminal:
Bash
cd Desktop
python INMP441-microphone.py

Se abre una ventana de matplotlib con la onda, donde puedes ajustar la escala y exportar una captura:

Onda de audio del INMP441 graficada en matplotlib

Proyecto 3: encender y apagar un LED con dos aplausos

Con lo anterior ya tienes todo para un clásico: dos aplausos seguidos cambian el estado de un LED. En lugar del LED puede ir un módulo relé para una lámpara o una cortina motorizada; el código es el mismo.

Demostración del control por aplausos con el ESP32 y el INMP441

La idea: un aplauso produce un peak de amplitud que sobresale claramente del ruido ambiente. Si aparecen dos peaks sobre un umbral dentro de un tiempo corto, el programa lo toma como doble aplauso e invierte el LED.

Dos peaks de amplitud sobre el ruido ambiente al detectar un doble aplauso

Ojo: el programa no reconoce aplausos como tales, sino dos peaks consecutivos sobre el umbral. Un golpe en la mesa o un chasquido fuerte también cuentan.

Conexión del LED

Además del micrófono, conecta un LED con una resistencia de 220 Ω (o un valor parecido) en serie al GPIO 38 de la ESP32-S3. En un ESP32 clásico puedes usar, por ejemplo, el GPIO 2 o el 4; solo cambia LED_PIN.

Materiales de este proyecto:

  • Placa ESP32-S3 DevKitC (o un ESP32 DevKit con los pines ajustados)
  • Módulo micrófono INMP441
  • 1 resistencia de 220 Ω
  • 1 LED de 5 mm
  • Protoboard
  • Cables jumper

Diagrama de conexión del ESP32 con el INMP441 y un LED para el control por aplausos

C++
/*
  Rui Santos & Sara Santos - Random Nerd Tutorials
  Complete project details at https://RandomNerdTutorials.com/esp32-inmp441-i2s-microphone-arduino/
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*/
#include <driver/i2s.h>

// Pin configuration
#define I2S_SCK   42   // BCLK
#define I2S_WS    40   // LRCLK / WS
#define I2S_SD    41   // DOUT from mic

#define I2S_PORT  I2S_NUM_0
#define SAMPLE_RATE 16000
#define BUFFER_LEN  512

int32_t sBuffer[BUFFER_LEN];

// LED
#define LED_PIN 38

// Clap detection
#define CLAP_THRESHOLD 1000000

// Maximum time allowed between the first and second clap
const unsigned long CLAP_GAP = 700;

// Prevent the same clap from being detected multiple times
const unsigned long CLAP_COOLDOWN = 250;

// State variables
bool ledState = false;

int clapCount = 0;

unsigned long firstClapTime = 0;
unsigned long lastClapTime = 0;

void setup() {
  Serial.begin(115200);
  delay(1000);

  Serial.println("INMP441 Microphone");

  // LED setup
  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, LOW);

  // I2S configuration
  i2s_config_t i2s_config = {
    .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,   // L/R = GND
    .communication_format = I2S_COMM_FORMAT_STAND_I2S,
    .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
    .dma_buf_count = 8,
    .dma_buf_len = BUFFER_LEN,
    .use_apll = false
  };

  // Pin configuration
  i2s_pin_config_t pin_config = {
    .bck_io_num = I2S_SCK,
    .ws_io_num = I2S_WS,
    .data_out_num = I2S_PIN_NO_CHANGE,
    .data_in_num = I2S_SD
  };

  // Start I2S
  i2s_driver_install(I2S_PORT, &i2s_config, 0, NULL);
  i2s_set_pin(I2S_PORT, &pin_config);
  i2s_zero_dma_buffer(I2S_PORT);

  Serial.println("I2S started.");
  Serial.println("Double clap to control LED");
}


void loop() {
  size_t bytes_read = 0;

  // Read microphone data
  i2s_read(I2S_PORT, sBuffer, sizeof(sBuffer), &bytes_read, portMAX_DELAY);

  int samples_read = bytes_read / sizeof(int32_t);

  // Find the loudest sample
  int32_t peak = 0;

  // Print the samples
  for (int i = 0; i < samples_read; i++) {
    int32_t sample = sBuffer[i] >> 8;
    int32_t magnitude = abs(sample);

    if (magnitude > peak) {
      peak = magnitude;
    }
  }

  // Print peak
  Serial.println(peak);

  // Detect a clap
  unsigned long now = millis();

  if (peak > CLAP_THRESHOLD && now - lastClapTime > CLAP_COOLDOWN) {
    lastClapTime = now;

    // First clap
    if (clapCount == 0) {
      clapCount = 1;
      firstClapTime = now;
      Serial.println("First clap detected");
    }

    // Second clap
    else if (now - firstClapTime <= CLAP_GAP) {
      clapCount = 0;

      // Double clap - invert LED state
      ledState = !ledState;
      digitalWrite(LED_PIN, ledState);
      if (ledState) {
        Serial.println("DOUBLE CLAP - LED ON");
      }
      else {
        Serial.println("DOUBLE CLAP - LED OFF");
      }
    }

    // Too much time passed
    else {

      // Treat this clap as the beginning of a new pair
      clapCount = 1;
      firstClapTime = now;
      Serial.println("New first clap");
    }
  }

  // Reset if second clap doesn't arrive in time
  if (clapCount == 1 && now - firstClapTime > CLAP_GAP) {
    clapCount = 0;
  }
}

Cómo funciona la detección

La lectura I2S es igual a la de los proyectos anteriores. Lo nuevo son tres parámetros y una pequeña máquina de estados:

  • CLAP_THRESHOLD (1.000.000): la amplitud desde la cual un peak cuenta como aplauso. Tiene que quedar sobre el ruido ambiente de tu lugar, así que casi siempre toca ajustarlo.
  • CLAP_GAP (700 ms): el tiempo máximo entre el primer y el segundo aplauso.
  • CLAP_COOLDOWN (250 ms): durante ese lapso tras un peak, los siguientes se ignoran. Un aplauso real dura varios bloques de 32 ms, y sin esta pausa un solo aplauso se contaría dos o tres veces.

En cada vuelta del loop(), el programa busca la muestra más fuerte del bloque (peak, usando abs() para que las negativas también cuenten). Si supera el umbral y ya pasó el cooldown:

  • Si clapCount está en 0, es el primer aplauso: guarda la hora en firstClapTime.
  • Si ya había uno y no han pasado más de 700 ms, es el segundo: invierte ledState, escribe el pin y vuelve clapCount a 0.
  • Si pasó demasiado tiempo, ese aplauso se toma como el primero de un par nuevo.

Al final, si hay un primer aplauso esperando y se venció el plazo, clapCount vuelve a 0.

Prueba y calibración

Sube el código, aplaude dos veces y el LED se enciende; otras dos, y se apaga.

  • Si el LED parpadea solo, el umbral está bajo para tu ruido ambiente: sube CLAP_THRESHOLD.
  • Si no reacciona a los aplausos, está alto: bájalo.

Para elegirlo con datos, el sketch imprime el peak de cada bloque con Serial.println(peak); (si en tu versión esa línea está comentada, quítale las //). Abre el Serial Plotter, mira los valores en silencio y con un aplauso, y deja el umbral entre ambos, un poco por debajo del peak típico del aplauso.

Extra de esta versión: medidor de ruido en decibeles con alerta MQTT

El RMS del proyecto 1 es un número sin unidad. Para que diga algo a un profesor o a quien administra una oficina, conviene pasarlo a decibeles. El datasheet del INMP441 da el dato necesario: su sensibilidad es de, 26 dBFS para un tono de 94 dB SPL. Es decir, un sonido de 94 dB produce una señal 26 dB por debajo del máximo digital. De ahí sale una conversión directa:

  • dBFS (decibeles respecto de la escala completa) = 20 × log10(RMS / 8.388.607)
  • dB SPL aproximado = dBFS + 120

Este sketch promedia un segundo de audio, publica el nivel en un tema MQTT y manda una alerta cuando se pasa del umbral, con histéresis para que no oscile cuando el ruido anda justo en el límite. Usa la librería PubSubClient de Nick O'Leary (instálala desde el gestor de librerías) y los mismos pines de la ESP32-S3:

C++
#include <WiFi.h>
#include <PubSubClient.h>
#include <driver/i2s.h>
#include <math.h>

// Red y broker MQTT (cambia estos datos)
const char* WIFI_SSID = "TU_RED";
const char* WIFI_PASS = "TU_CLAVE";
const char* MQTT_HOST = "192.168.1.50";
const int   MQTT_PORT = 1883;
const char* TOPIC_NIVEL  = "sala/ruido/db";
const char* TOPIC_ALERTA = "sala/ruido/alerta";

// Pines (ESP32-S3; en un ESP32 clasico usa 26, 25 y 33)
#define I2S_SCK   42
#define I2S_WS    40
#define I2S_SD    41
#define LED_PIN   38

#define I2S_PORT    I2S_NUM_0
#define SAMPLE_RATE 16000
#define BUFFER_LEN  512

// INMP441: -26 dBFS a 94 dB SPL, entonces dB SPL = dBFS + 120
const float OFFSET_DB     = 120.0;
const float UMBRAL_DB     = 70.0;
const float HISTERESIS_DB = 5.0;
const unsigned long PERIODO_MS = 1000;

int32_t sBuffer[BUFFER_LEN];
WiFiClient red;
PubSubClient mqtt(red);

double sumaCuadrados = 0;
uint32_t nMuestras = 0;
unsigned long ultimoEnvio = 0;
bool enAlerta = false;

void conectarMqtt() {
  while (!mqtt.connected()) {
    if (mqtt.connect("esp32-ruido-sala")) break;
    delay(2000);
  }
}

void setup() {
  Serial.begin(115200);
  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, LOW);

  WiFi.begin(WIFI_SSID, WIFI_PASS);
  while (WiFi.status() != WL_CONNECTED) delay(500);
  mqtt.setServer(MQTT_HOST, MQTT_PORT);

  i2s_config_t i2s_config = {
    .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 = 8,
    .dma_buf_len = BUFFER_LEN,
    .use_apll = false
  };

  i2s_pin_config_t pin_config = {
    .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_PORT, &i2s_config, 0, NULL);
  i2s_set_pin(I2S_PORT, &pin_config);
  i2s_zero_dma_buffer(I2S_PORT);
}

void loop() {
  if (!mqtt.connected()) conectarMqtt();
  mqtt.loop();

  size_t bytes_read = 0;
  i2s_read(I2S_PORT, sBuffer, sizeof(sBuffer), &bytes_read, portMAX_DELAY);
  int samples_read = bytes_read / sizeof(int32_t);
  if (samples_read == 0) return;

  // Promedio del bloque, para restar un posible offset DC
  double media = 0;
  for (int i = 0; i < samples_read; i++) media += (sBuffer[i] >> 8);
  media /= samples_read;

  for (int i = 0; i < samples_read; i++) {
    double x = (sBuffer[i] >> 8) - media;
    sumaCuadrados += x * x;
  }
  nMuestras += samples_read;

  unsigned long ahora = millis();
  if (ahora - ultimoEnvio >= PERIODO_MS) {
    double rms = sqrt(sumaCuadrados / nMuestras);
    float db = 20.0 * log10(rms / 8388607.0 + 1e-12) + OFFSET_DB;

    char msg[16];
    snprintf(msg, sizeof(msg), "%.1f", db);
    mqtt.publish(TOPIC_NIVEL, msg);
    Serial.println(msg);

    if (!enAlerta && db > UMBRAL_DB) {
      enAlerta = true;
      digitalWrite(LED_PIN, HIGH);
      mqtt.publish(TOPIC_ALERTA, "ON", true);
    } else if (enAlerta && db < UMBRAL_DB - HISTERESIS_DB) {
      enAlerta = false;
      digitalWrite(LED_PIN, LOW);
      mqtt.publish(TOPIC_ALERTA, "OFF", true);
    }

    sumaCuadrados = 0;
    nMuestras = 0;
    ultimoEnvio = ahora;
  }
}

Tres cosas para interpretar bien los números:

  • Es una medición sin ponderación (dB Z), no dB(A). Los sonómetros oficiales aplican la curva A, que resta peso a los graves porque el oído los percibe menos. Para comparar una sala consigo misma a lo largo del día sirve de sobra; para un informe formal de ruido no reemplaza a un sonómetro certificado.
  • La conversión es aproximada. Cada cápsula tiene una tolerancia de sensibilidad, así que conviene contrastarla una vez con un sonómetro o con una app de celular calibrada y ajustar OFFSET_DB hasta que coincidan.
  • El umbral depende del lugar. Una clase expositiva normal suele quedar varios decibeles por debajo de 70 y un recreo dentro de la sala, bastante por encima. Deja el medidor un par de días publicando y elige el umbral mirando los datos, igual que con el aplauso.

Con el tema sala/ruido/db puedes graficar el historial en Node RED o Home Assistant. El tema de alerta se publica con la bandera retained, así que un tablero que se conecte después igual sabe si la sala está en alerta.

Errores comunes y cómo resolverlos

  • Todo sale en 0: casi siempre es el canal. Si L/R está a 3V3 y el código lee ONLY_LEFT (o al revés), el ESP32 escucha el canal vacío. También pasa si L/R quedó al aire: conéctalo siempre a GND o a 3V3.
  • Valores fijos en, 1 o en un número enorme que no cambia: revisa SD y SCK; es típico tenerlos intercambiados o con una soldadura fría.
  • Ruido constante muy alto: comprueba que VDD vaya a 3V3 y que los cables del I2S sean cortos. Con cables largos de protoboard, un reloj de 1 MHz ya empieza a sufrir.
  • El programa no carga o la placa se reinicia en bucle: usaste un pin de arranque (en el ESP32 clásico, 0, 2, 12 o 15) para una señal I2S. Muévela a otro GPIO.
  • Un aplauso cuenta doble: sube CLAP_COOLDOWN a 300 o 350 ms.

Variantes y mejoras

  • Contador de aplausos por patrones: en vez de dos aplausos, reconoce tres para una segunda salida o un aplauso largo para apagar todo, guardando las horas de cada peak en un arreglo pequeño y comparando los intervalos.
  • Detector de frecuencia con FFT: con la librería arduinoFFT puedes sacar la frecuencia dominante de cada bloque y, por ejemplo, hacer un afinador de guitarra o encender algo solo con un silbido y no con cualquier ruido.
  • Grabar audio en una microSD: guarda las muestras de 16 bits en un archivo WAV mediante un módulo microSD por SPI y tendrás una grabadora de voz. Con dos INMP441 (uno con L/R a GND y otro a 3V3, compartiendo SCK y WS) grabas en estéreo.
  • Salida de audio por I2S: junto con un amplificador MAX98357A, también por I2S, el ESP32 puede reproducir lo que escucha o armar un intercomunicador.

Personalización para Chile

En Chile consigues todo lo necesario en MechatronicStore:

  • Módulo micrófono INMP441 I2S: el sensor central de los cuatro proyectos.
  • Placa ESP32-S3 DevKitC o ESP32 DevKit V1: cualquiera de las dos sirve; con la S3 usas los pines del tutorial tal cual y con la DevKit V1 cambias los tres #define a 26, 25 y 33.
  • LED de 5 mm y resistencia de 220 Ω: la salida del proyecto de aplausos.
  • Módulo relé de 1 canal a 5V: para reemplazar el LED por una lámpara u otro equipo de 220 V. Si trabajas con la red eléctrica, hazlo con el circuito desenergizado.
  • Protoboard de 830 puntos y cables jumper macho macho: para armar todo sin soldar, salvo los pines del micrófono.
  • Cable USB de datos (USB-C para la S3, micro USB para la mayoría de las DevKit V1): uno solo de carga no deja que la placa aparezca en Arduino IDE.

Si en el tutorial original usan la ESP32-S3 DevKitC de otro fabricante, cualquier ESP32-S3 de la tienda cumple la misma función: el I2S es parte del chip, no de la placa.

Recursos

Versión chilena con componentes en stock local en MechatronicStore.