En este tutorial aprenderás cómo grabar señales de audio con la placa Seeed Studio XIAO-ESP32-S3-Sense. Si no has usado antes la XIAO-ESP32-S3 Sense, echa un vistazo al Getting started with XIAO-ESP32-S3-Sense tutorial primero.
Partes necesarias
Obviamente, necesitarás una placa XIAO ESP32 S3 Sense de Seeed Studio para probar los ejemplos de código. Ten en cuenta que la placa puede calentarse y puede que quieras colocar un pequeño Heatsink (ver la pieza listada más abajo).
Luego, necesitarás una tarjeta SD para almacenar el audio grabado. He listado una tarjeta de 32 GB, pero una más pequeña (8GB) también servirá para probar. Y, finalmente, si tu ordenador no tiene lector de tarjetas SD incorporado, también necesitarás uno.

Seeed Studio XIAO ESP32 S3 Sense

Cable USB C

Pequeño disipador de calor 9×9 mm

Tarjeta SD 32GB

Lector de tarjetas SD
Makerguides is a participant in affiliate advertising programs designed to provide a means for sites to earn advertising fees by linking to Amazon, AliExpress, Elecrow, and other sites. As an Affiliate we may earn from qualifying purchases.
Micrófono de XIAO-ESP32-S3-Sense
La XIAO-ESP32-S3-Sense viene con un micrófono digital integrado MEMS Microphone del tipo MSM261D3526H1CPM que está ubicado en el Sense Hat. Mira la imagen a continuación:

Puedes comunicarte con el micrófono a través de dos líneas de señal (PDM_CLK, PDM_DATA) para el I2S protocol que están conectadas a IO42 y IO41 como se muestra en el esquema a continuación:

Ten en cuenta que en la parte trasera del PCB del Sense Hat hay dos pads «Jumper» etiquetados J1 y J2 (flechas rojas). Mira la imagen a continuación:

Si cortas el cable fino entre estos pads (a lo largo de las líneas blancas) deshabilitas el micrófono, pero los GPIOs D11 y D12 en el Sense Hat quedan disponibles; de lo contrario, están usados por el micrófono. Para más detalles, consulta el Pin Multiplexing Information.
Leer señal del micrófono desde XIAO-ESP32-S3-Sense
Como primer ejemplo de código, mostraremos la señal de audio detectada por el micrófono en el Monitor Serial y el Serial Plotter:
#include "ESP_I2S.h"
const int8_t I2S_CLK = 42;
const int8_t I2S_DIN = 41;
const uint32_t SAMPLERATE = 16000;
I2SClass I2S;
void setup() {
Serial.begin(115200);
I2S.setPinsPdmRx(I2S_CLK, I2S_DIN);
if (!I2S.begin(I2S_MODE_PDM_RX, SAMPLERATE, I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO)) {
Serial.println("Can't find microphone!");
while (1)
;
}
}
void loop() {
int sample = I2S.read();
if (sample > 1) {
Serial.println(sample);
}
}
Constantes y objetos
El código comienza incluyendo la librería ESP_I2S. Luego definimos constantes para los pines a los que está conectado el interfaz I2S del micrófono y la tasa de muestreo:
#include "ESP_I2S.h" const int8_t I2S_CLK = 42; const int8_t I2S_DIN = 41; const uint32_t SAMPLERATE = 16000;
A continuación creamos los objetos I2S que nos permiten transferir datos desde y hacia el micrófono vía I2S protocol:
I2SClass I2S;
Función setup
En la función setup inicializamos la interfaz Serial, configuramos los pines para la interfaz I2C e iniciamos la comunicación para I2S:
void setup() {
Serial.begin(115200);
I2S.setPinsPdmRx(I2S_CLK, I2S_DIN);
if (!I2S.begin(I2S_MODE_PDM_RX, SAMPLERATE, I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO)) {
Serial.println("Can't find microphone!");
while (1)
;
}
}
El parámetro I2S_MODE_PDM_RX configura el periférico I2S para operar en modo recepción PDM (Pulse-Density Modulation). Los micrófonos PDM emiten un flujo de bits de alta frecuencia donde la densidad de ‘1’s corresponde a la amplitud de la señal. En este modo, el hardware del ESP32 se encarga de decodificar este flujo denso en muestras PCM de audio utilizables.
El parámetro SAMPLERATE define el número de muestras de audio capturadas por segundo, típicamente medido en Hertz (Hz). En este código, está configurado a 16000, lo que significa que el micrófono será muestreado 16,000 veces por segundo. Esta tasa de muestreo es un buen equilibrio entre capturar suficiente detalle para el habla humana y mantener bajos los requisitos de procesamiento y almacenamiento. Tasas de muestreo más bajas reducen la carga de memoria y CPU, lo cual es beneficioso para sistemas con batería, pero disminuyen la resolución.
El parámetro I2S_DATA_BIT_WIDTH_16BIT establece la profundidad de bits de cada muestra PCM de audio a 16 bits. La profundidad de bits se refiere a cuán precisamente cada muestra de audio representa la amplitud de la onda sonora original. Una profundidad de 16 bits ofrece 65,536 niveles posibles de amplitud por muestra, lo que proporciona un buen rango dinámico para capturar variaciones sutiles en volumen y tono.
El parámetro I2S_SLOT_MODE_MONO configura la interfaz I2S para operar en modo mono, lo que significa que solo se usa un canal de audio para la captura de datos. Esto es apropiado cuando se trabaja con un solo micrófono PDM, ya que la operación estéreo (dos canales) sería innecesaria y un desperdicio.
Función loop
En la función loop leemos una sola muestra de audio vía I2S.read() desde el micrófono. Si la muestra es mayor que 1 (para filtrar silencio o ruido de fondo) la imprimimos en el monitor serial:
void loop() {
int sample = I2S.read();
if (sample > 1) {
Serial.println(sample);
}
}
Si ejecutas el código, abres el Serial Plotter y silbas a una frecuencia fija, deberías ver una bonita onda sinusoidal en el Serial Plotter:

Si varías la frecuencia silbando un poco más bajo o más alto, verás que la frecuencia de la onda sinusoidal mostrada cambia en consecuencia.
Grabar audio con XIAO-ESP32-S3-Sense
En este ejemplo mostraré cómo grabar 5 segundos de audio y guardar los datos como un archivo de audio en formato WAV en la tarjeta SD.
Queremos iniciar la grabación presionando un botón y luego grabar durante los siguientes 5 segundos. El siguiente esquema muestra cómo conectar un botón al pin D7 de la placa:

A continuación está el código completo del proyecto. Échale un vistazo rápido primero y luego entraremos en detalles:
#include "ESP_I2S.h"
#include "FS.h"
#include "SD.h"
const uint32_t SAMPLERATE = 16000;
const int LEN = 5; // seconds
const byte btnPin = D7;
const byte ledPin = BUILTIN_LED;
I2SClass i2s;
void recordAudio() {
static int cnt = 0;
static char filename[64];
uint8_t *wav_buffer;
size_t wav_size;
Serial.print("RECORDING ... ");
wav_buffer = i2s.recordWAV(LEN, &wav_size);
sprintf(filename, "/audio%d.wav", cnt++);
File file = SD.open(filename, FILE_WRITE);
file.write(wav_buffer, wav_size);
file.close();
free(wav_buffer);
Serial.printf("COMPLETE => %s\n", filename);
}
void setup() {
Serial.begin(115200);
pinMode(btnPin, INPUT_PULLUP);
pinMode(ledPin, OUTPUT);
i2s.setPinsPdmRx(42, 41);
if (!i2s.begin(I2S_MODE_PDM_RX, SAMPLERATE,
I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO)) {
Serial.println("Can't find microphone!");
}
if (!SD.begin(21)) {
Serial.println("Failed to mount SD Card!");
}
}
void loop() {
if (!digitalRead(btnPin)) {
delay(500);
digitalWrite(ledPin, LOW);
recordAudio();
digitalWrite(ledPin, HIGH);
}
}
Librerías
El código comienza incluyendo dos librerías importantes:
#include "ESP_I2S.h" #include "FS.h" #include "SD.h"
La librería ESP_I2S.h proporciona una abstracción para manejar la entrada de audio vía la interfaz I2S, que usa el micrófono PDM. Las librerías FS.h y SD.h permiten el acceso al sistema de archivos de la tarjeta SD, facilitando guardar archivos de audio.
Constantes
Luego se definen varias constantes:
const uint32_t SAMPLERATE = 16000; const int LEN = 5; // seconds const byte btnPin = D7; const byte ledPin = BUILTIN_LED;
La constante SAMPLERATE está fijada a 16,000 muestras por segundo, una tasa estándar para grabación de voz inteligible.LEN define la duración de la grabación en segundos.btnPin está asignada al pin de entrada del botón (D7), y ledPin es el LED integrado usado para feedback visual durante la grabación.
Objetos
Luego creamos el objeto I2SClass, que se usa para configurar y controlar la interfaz de audio I2S.
I2SClass i2s;
Función recordAudio
En la función recordAudio() manejamos la grabación de audio y el guardado del archivo:
void recordAudio() {
static int cnt = 0;
static char filename[64];
uint8_t *wav_buffer;
size_t wav_size;
Se usa un contador estático cnt para crear nombres de archivo únicos para cada grabación. Se declaran un puntero a buffer wav_buffer y una variable wav_size para almacenar los datos de audio grabados y su tamaño.
Serial.print("RECORDING ... ");
wav_buffer = i2s.recordWAV(LEN, &wav_size);
La función comienza imprimiendo un mensaje en el monitor serial. Luego se llama a la función recordWAV() del objeto i2s, que graba audio durante LEN segundos y devuelve un puntero a los datos WAV y su tamaño.
sprintf(filename, "/audio%d.wav", cnt++); File file = SD.open(filename, FILE_WRITE); file.write(wav_buffer, wav_size); file.close(); free(wav_buffer);
Se genera un nombre de archivo usando el contador, como /audio0.wav, /audio1.wav, etc. Se accede a la tarjeta SD usando SD.open, y se escriben los datos WAV en el archivo. Luego se libera la memoria usada por wav_buffer para evitar fugas de memoria.
Serial.printf("COMPLETE => %s\n", filename);
}
Después de guardar, se imprime un mensaje de finalización en el monitor serial con el nombre del archivo guardado.
Función setup
En la función setup(), primero inicializamos la comunicación Serial a 115200 baudios para permitir mensajes de depuración.
void setup() {
Serial.begin(115200);
Luego configuramos el botón como entrada con resistencia pull-up interna, lo que significa que lee HIGH por defecto y LOW cuando se presiona. El pin del LED se configura como salida.
pinMode(btnPin, INPUT_PULLUP); pinMode(ledPin, OUTPUT);
Después configuramos los GPIO 42 y 41 para la entrada del micrófono PDM, coincidiendo con el cableado por defecto del micrófono integrado de la XIAO ESP32-S3 Sense.
i2s.setPinsPdmRx(42, 41);
La interfaz I2S se inicializa en modo recepción PDM, con la tasa de muestreo especificada, 16 bits de profundidad y canal mono. Si la inicialización falla, se muestra un mensaje de error.
if (!i2s.begin(I2S_MODE_PDM_RX, SAMPLERATE,
I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO)) {
Serial.println("Can't find microphone!");
}
Finalmente, inicializamos la tarjeta SD usando el GPIO 21 como pin CS (Chip Select). Si el montaje falla, imprimimos un mensaje de error.
if (!SD.begin(21)) {
Serial.println("Failed to mount SD Card!");
}
}
Función loop
Finalmente, la función loop() comprueba si se presiona el botón y comienza la grabación:
void loop() {
if (!digitalRead(btnPin)) {
delay(500);
digitalWrite(ledPin, LOW);
recordAudio();
digitalWrite(ledPin, HIGH);
}
}
Se lee el botón; si está en LOW (presionado), el programa espera 500 milisegundos para eliminar rebotes, luego enciende el LED, llama a recordAudio() y apaga el LED cuando termina. Esto proporciona una señal visual de que la grabación está en curso.
Conclusiones
En este tutorial aprendiste cómo grabar audio usando tu XIAO-ESP32-S3 Sense. Ahora estás listo, por ejemplo, para construir aplicaciones controladas por voz. Consulta nuestro Voice control with XIAO-ESP32-S3-Sense and Edge Impulse tutorial.
Si necesitas más información sobre la XIAO-ESP32-S3 Sense, echa un vistazo al Getting started with XIAO-ESP32-S3-Sense tutorial. Y para streaming de vídeo, consulta nuestro Stream Video with with XIAO-ESP32-S3-Sense tutorial.
Finalmente, no olvides revisar el Getting Started Wiki by Seeed Studio para la XIAO-ESP32-S3-Sense, que también ofrece mucha información sobre la placa y numerosos ejemplos de código.
Si tienes alguna pregunta, no dudes en dejarla en la sección de comentarios.
¡Feliz bricolaje ; )

