Neste tutorial, vais aprender a gravar sinais de áudio com a placa Seeed Studio XIAO-ESP32-S3-Sense. Se ainda não usaste a XIAO-ESP32-S3 Sense, dá uma vista de olhos ao Getting started with XIAO-ESP32-S3-Sense tutorial primeiro.
Peças Necessárias
Obviamente, vais precisar de uma placa XIAO ESP32 S3 Sense da Seeed Studio para experimentar os exemplos de código. Nota que a placa pode aquecer e talvez queiras anexar um pequeno Heatsink (ver a peça listada abaixo).
Depois, vais precisar de um cartão SD para armazenar o áudio gravado. Listei um cartão de 32 GB, mas um menor (8GB) também serve perfeitamente para experimentares. E, finalmente, se o teu computador não tiver leitor de cartão SD incorporado, vais precisar de um também.

Seeed Studio XIAO ESP32 S3 Sense

Cabo USB C

Pequeno Dissipador 9×9 mm

Cartão SD 32GB

Leitor de Cartão 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.
Microfone da XIAO-ESP32-S3-Sense
A XIAO-ESP32-S3-Sense vem com um microfone digital incorporado MEMS Microphone do tipo MSM261D3526H1CPM que está localizado na Sense Hat. Vê a imagem abaixo:

Podes comunicar com o microfone através de duas linhas de sinal (PDM_CLK, PDM_DATA) para o I2S protocol que estão ligadas a IO42 e IO41 conforme mostrado no esquema abaixo:

Nota que na parte de trás da PCB da Sense Hat existem dois pads “Jumper” rotulados J1 e J2 (setas vermelhas). Vê a imagem abaixo:

Se cortares o fio fino entre estes pads (ao longo das linhas brancas) desativas o microfone, mas os GPIOs D11 e D12 na Sense Hat ficam disponíveis; caso contrário, são usados pelo microfone. Para mais detalhes vê o Pin Multiplexing Information.
Ler Sinal do Microfone da XIAO-ESP32-S3-Sense
Como primeiro exemplo de código, vamos mostrar o sinal de áudio detetado pelo microfone no Monitor Serial e no 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 e Objetos
O código começa por incluir a biblioteca ESP_I2S. Depois definimos constantes para os pinos aos quais a interface I2S do microfone está ligada, e a taxa de amostragem:
#include "ESP_I2S.h" const int8_t I2S_CLK = 42; const int8_t I2S_DIN = 41; const uint32_t SAMPLERATE = 16000;
De seguida, criamos os objetos I2S que nos permitem transferir dados de e para o microfone via I2S protocol:
I2SClass I2S;
Função Setup
Na função setup inicializamos a interface Serial, definimos os pinos para a interface I2C e iniciamos a comunicação 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)
;
}
}
O parâmetro I2S_MODE_PDM_RX configura o periférico I2S para operar em modo de receção PDM (Pulse-Density Modulation). Microfones PDM emitem um fluxo de bits de alta frequência onde a densidade de ‘1’s corresponde à amplitude do sinal. Neste modo, o hardware do ESP32 trata da decodificação deste fluxo denso em amostras PCM de áudio utilizáveis.
O parâmetro SAMPLERATE define o número de amostras de áudio capturadas por segundo, normalmente medido em Hertz (Hz). Neste código, está definido para 16000, o que significa que o microfone será amostrado 16.000 vezes por segundo. Esta taxa de amostragem é um bom equilíbrio entre captar detalhes suficientes para a fala humana e manter baixos os requisitos de processamento e armazenamento. Taxas mais baixas reduzem a carga de memória e CPU, o que é benéfico para sistemas alimentados por bateria, mas reduzem a resolução.
O parâmetro I2S_DATA_BIT_WIDTH_16BIT define a profundidade de bits de cada amostra PCM de áudio para 16 bits. A profundidade de bits refere-se à precisão com que cada amostra de áudio representa a amplitude da onda sonora original. Uma profundidade de 16 bits oferece 65.536 níveis possíveis de amplitude por amostra, proporcionando uma boa gama dinâmica para captar variações subtis de volume e tom.
O parâmetro I2S_SLOT_MODE_MONO configura a interface I2S para operar em modo mono, significando que apenas um canal de áudio é usado para captura de dados. Isto é adequado quando se trabalha com um único microfone PDM, pois a operação estéreo (dois canais) seria desnecessária e desperdiçaria recursos.
Função Loop
Na função loop lemos uma única amostra de áudio via I2S.read() do microfone. Se a amostra for maior que 1 (para filtrar silêncio/ruído de fundo) imprimimos no monitor serial:
void loop() {
int sample = I2S.read();
if (sample > 1) {
Serial.println(sample);
}
}
Se executares o código, abrires o Serial Plotter e assobiares a uma frequência fixa, deverás ver uma bonita onda senoidal no Serial Plotter:

Se variares a frequência assobiando um pouco mais baixo ou mais alto, verás que a frequência da onda senoidal exibida muda em conformidade.
Gravar Áudio com XIAO-ESP32-S3-Sense
Neste exemplo vou mostrar como gravar 5 segundos de áudio e escrever os dados como um ficheiro de áudio em formato WAV no cartão SD.
Queremos começar a gravação ao pressionar um botão e depois gravar durante os próximos 5 segundos. A ligação seguinte mostra como conectar um botão ao pino D7 da placa:

Abaixo está o código completo para o projeto. Dá uma vista de olhos rápida e depois vamos analisar os detalhes:
#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);
}
}
Bibliotecas
O código começa por incluir duas bibliotecas importantes:
#include "ESP_I2S.h" #include "FS.h" #include "SD.h"
A biblioteca ESP_I2S.h fornece uma abstração para lidar com entrada de áudio via interface I2S, que o microfone PDM usa. As bibliotecas FS.h e SD.h permitem o acesso ao sistema de ficheiros do cartão SD, possibilitando guardar ficheiros de áudio.
Constantes
De seguida, são definidas várias constantes:
const uint32_t SAMPLERATE = 16000; const int LEN = 5; // seconds const byte btnPin = D7; const byte ledPin = BUILTIN_LED;
A SAMPLERATE está definida para 16.000 amostras por segundo, uma taxa padrão para gravação de fala inteligível.LEN especifica a duração da gravação em segundos.btnPin está atribuída ao pino de entrada do botão (D7), e ledPin é o LED onboard usado para feedback visual durante a gravação.
Objetos
De seguida, criamos o objeto I2SClass, que é usado para configurar e controlar a interface de áudio I2S.
I2SClass i2s;
Função recordAudio
Na função recordAudio() tratamos da gravação de áudio e do salvamento do ficheiro:
void recordAudio() {
static int cnt = 0;
static char filename[64];
uint8_t *wav_buffer;
size_t wav_size;
Um contador estático cnt é usado para criar nomes de ficheiros únicos para cada gravação. Um ponteiro para buffer wav_buffer e uma variável wav_size são declarados para armazenar os dados de áudio gravados e o seu tamanho.
Serial.print("RECORDING ... ");
wav_buffer = i2s.recordWAV(LEN, &wav_size);
A função começa por imprimir uma mensagem no monitor serial. A função recordWAV() é então chamada no objeto i2s, que grava áudio durante a duração de LEN segundos e retorna um ponteiro para os dados WAV e o seu tamanho.
sprintf(filename, "/audio%d.wav", cnt++); File file = SD.open(filename, FILE_WRITE); file.write(wav_buffer, wav_size); file.close(); free(wav_buffer);
É gerado um nome de ficheiro usando o contador, como /audio0.wav, /audio1.wav, etc. O cartão SD é acedido usando SD.open, e os dados WAV são escritos no ficheiro. A memória usada por wav_buffer é depois libertada para evitar fugas de memória.
Serial.printf("COMPLETE => %s\n", filename);
}
Após salvar, uma mensagem de conclusão é impressa no monitor serial com o nome do ficheiro gravado.
Função Setup
Na função setup(), inicializamos primeiro a comunicação Serial a 115200 baud para permitir mensagens de depuração.
void setup() {
Serial.begin(115200);
De seguida, configuramos o botão como entrada com resistor pull-up interno, significando que lê HIGH por defeito e LOW quando pressionado. O pino do LED é configurado como saída.
pinMode(btnPin, INPUT_PULLUP); pinMode(ledPin, OUTPUT);
Depois definimos os GPIO 42 e 41 para a entrada do microfone PDM, correspondendo à ligação padrão do microfone onboard da XIAO ESP32-S3 Sense.
i2s.setPinsPdmRx(42, 41);
A interface I2S é inicializada em modo de receção PDM, com a taxa de amostragem especificada, largura de 16 bits e canal mono. Se a inicialização falhar, é mostrada uma mensagem de erro.
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 o cartão SD usando o GPIO 21 como pino CS (Chip Select). Se a montagem falhar, imprimimos uma mensagem de erro.
if (!SD.begin(21)) {
Serial.println("Failed to mount SD Card!");
}
}
Função Loop
Por fim, a função loop() verifica se o botão foi pressionado e inicia a gravação:
void loop() {
if (!digitalRead(btnPin)) {
delay(500);
digitalWrite(ledPin, LOW);
recordAudio();
digitalWrite(ledPin, HIGH);
}
}
O botão é lido; se estiver LOW (pressionado), o programa espera 500 milissegundos para debouncing, depois liga o LED, chama recordAudio(), e desliga o LED quando termina. Isto fornece um sinal visual de que a gravação está a decorrer.
Conclusões
Neste tutorial aprendeste a gravar áudio usando a tua XIAO-ESP32-S3 Sense. Agora estás pronto, por exemplo, para construir aplicações controladas por voz. Vê o nosso Voice control with XIAO-ESP32-S3-Sense and Edge Impulse tutorial.
Se precisares de mais informações sobre a XIAO-ESP32-S3 Sense, dá uma vista de olhos ao Getting started with XIAO-ESP32-S3-Sense tutorial. E para streaming de vídeo vê o nosso Stream Video with with XIAO-ESP32-S3-Sense tutorial.
Por fim, não te esqueças de consultar o Getting Started Wiki by Seeed Studio para a XIAO-ESP32-S3-Sense, que também fornece muita informação sobre a placa e muitos exemplos de código.
Se tiveres alguma dúvida, não hesites em deixar nos comentários.
Boas experiências a criar ; )

