Audio I2S con ESP32-P4: codec ES8311, tono, PCM e microfono
L’audio digitale sui microcontrollori passa quasi sempre dal bus I2S: un’interfaccia seriale sincrona pensata solo per trasportare campioni audio, senza compressione e senza pacchetti. In questo tutorial usi l’ESP32-P4-Function-EV-Board, che ha a bordo il codec ES8311 e un amplificatore per altoparlante, per suonare un tono sinusoidale generato in tempo reale, riprodurre un breve campione PCM incorporato nel firmware, registrare dal microfono e riascoltare la registrazione. Il codec viene configurato con il componente ufficiale espressif/esp_codec_dev. Alla fine trovi anche una variante minima per ESP32-C6 con un modulo amplificatore MAX98357A e un microfono I2S INMP441, senza codec.
Cosa serve
| Componente | Esempio | Note |
|---|---|---|
| Scheda ESP32-P4 | ESP32-P4-Function-EV-Board | Codec ES8311 e amplificatore NS4150B già a bordo |
| Altoparlante | 4-8 Ω, 2-3 W | Sul connettore altoparlante della scheda |
| Microfono | Quello collegato all’ingresso del codec sulla scheda | Verifica sullo schematico della tua revisione |
| Variante C6: scheda | ESP32-C6-DevKitC-1 | |
| Variante C6: amplificatore | Modulo MAX98357A | DAC I2S + classe D da 3 W in un solo chip |
| Variante C6: microfono | Modulo INMP441 | Microfono MEMS con uscita I2S a 24 bit |
| Software | ESP-IDF 5.4 + espressif/esp_codec_dev 1.3 |
Il componente viene scaricato dal Component Manager alla prima build |
Come funziona
I segnali I2S
- BCLK (bit clock, detto anche SCK o SCLK): un impulso per ogni bit trasmesso.
- WS (word select, detto anche LRCLK o LRC): basso per il canale sinistro, alto per il destro. La sua frequenza è la frequenza di campionamento.
- DOUT e DIN: i dati in uscita dal microcontrollore (verso il DAC) e in ingresso (dall’ADC o dal microfono). Nel formato Philips standard il bit più significativo arriva un colpo di BCLK dopo il cambio di WS.
- MCLK (master clock): un clock più veloce, tipicamente 256 volte la frequenza di campionamento, che serve ai convertitori sigma-delta dei codec come l’ES8311. Il MAX98357A e l’INMP441 ne fanno a meno perché lo ricavano da BCLK.
Con 16 kHz, 16 bit e 2 canali BCLK vale 16.000 × 16 × 2 = 512 kHz e MCLK 16.000 × 256 = 4,096 MHz. Il driver I2S di ESP-IDF calcola questi valori da solo a partire da I2S_STD_CLK_DEFAULT_CONFIG(frequenza).
Frequenze di campionamento e bit
La frequenza di campionamento deve essere almeno il doppio della frequenza più alta da riprodurre: 8 kHz basta per la voce al telefono, 16 kHz per il riconoscimento vocale, 44,1 o 48 kHz per la musica. La profondità in bit fissa la dinamica: circa 6 dB per bit, cioè 96 dB con 16 bit e 144 dB teorici con 24 bit (i convertitori reali si fermano molto prima). In memoria un campione a 16 bit occupa un int16_t; i campioni a 24 bit viaggiano in slot da 32 bit allineati a sinistra, per questo nella variante C6 il valore del microfono viene spostato di 8 bit.
Codec e esp_codec_dev
L’ES8311 è un codec mono a basso consumo: un DAC per l’uscita, un ADC per il microfono, registri di configurazione accessibili via I2C. Il flusso audio passa via I2S, la configurazione (volume, guadagno del microfono, formato, divisori di clock) via I2C. Scrivere a mano la sequenza di registri è possibile ma lungo; esp_codec_dev la incapsula dietro un’API uniforme: crei un’interfaccia di controllo I2C, un’interfaccia dati I2S e un driver per il tuo codec, e poi usi le stesse funzioni open, write, read, set_out_vol e set_in_gain per qualsiasi codec supportato. Il componente gestisce anche il pin di abilitazione dell’amplificatore.
Collegamenti
ESP32-P4-Function-EV-Board: tutto è già cablato. I pin sono quelli del BSP ufficiale della scheda e dell’esempio peripherals/i2s/i2s_codec/i2s_es8311 di ESP-IDF 5.4.
| Segnale | GPIO ESP32-P4 | Note |
|---|---|---|
| I2C SDA | GPIO7 | Controllo dell’ES8311 (indirizzo 0x18, scritto 0x30 nel formato a 8 bit) |
| I2C SCL | GPIO8 | |
| I2S MCLK | GPIO13 | 256 × fs |
| I2S BCLK | GPIO12 | |
| I2S WS | GPIO10 | |
| I2S DOUT | GPIO9 | Verso il DAC del codec |
| I2S DIN | GPIO11 | Dall’ADC del codec |
| Abilitazione amplificatore | GPIO53 | Alto = amplificatore acceso |
ESP32-C6-DevKitC-1 + MAX98357A + INMP441:
| Segnale | GPIO ESP32-C6 | MAX98357A | INMP441 |
|---|---|---|---|
| BCLK | GPIO20 | BCLK | SCK |
| WS | GPIO21 | LRC | WS |
| DOUT | GPIO22 | DIN | – |
| DIN | GPIO23 | – | SD |
| Canale | – | SD non collegato (somma L+R) | L/R a GND (canale sinistro) |
| Alimentazione | – | VIN 5 V (più potenza) o 3,3 V | VDD 3,3 V |
Sul MAX98357A il pin GAIN lasciato libero dà 9 dB di guadagno; il comportamento del pin SD dipende dal modulo (sui moduli più diffusi, lasciato libero, l’uscita è la media dei due canali). Verifica la documentazione del tuo modulo.
La periferica dell’ESP32-P4
Il P4 ha tre controller I2S. Qui usi I2S_NUM_0 in full duplex: un solo i2s_new_channel() crea sia il canale TX sia quello RX, che condividono BCLK, WS e MCLK, come richiede il codec. Il P4 è master (genera i clock) e l’ES8311 è slave. Il driver è quello nuovo di ESP-IDF 5.x, driver/i2s_std.h, in modalità standard Philips.
Per il controllo uso il nuovo driver I2C master (driver/i2c_master.h). esp_codec_dev 1.3 lo supporta direttamente: basta passargli il bus_handle nella configurazione. Una volta aperto il dispositivo con esp_codec_dev_open(), il componente riconfigura slot e clock dei canali I2S secondo la frequenza e i bit richiesti e programma i registri del codec di conseguenza.
L’ES8311 ha un solo ADC. Quando leggi in stereo, il canale sinistro contiene il microfono; il destro, con la configurazione di default del driver, contiene un riferimento del DAC utile per la cancellazione dell’eco. Il codice usa quindi solo i campioni di sinistra.
Il codice
Progetto completo in progetti/esp-c6p4/15-audio-i2s, compilato con ESP-IDF 5.4 per esp32p4. Il file main/idf_component.yml dichiara la dipendenza:
## Componenti gestiti (IDF Component Manager)
dependencies:
idf: ">=5.3"
espressif/esp_codec_dev: "~1.3"
Il file main/CMakeLists.txt incorpora il campione PCM nel firmware con EMBED_FILES:
idf_component_register(SRCS "main.c"
INCLUDE_DIRS "."
PRIV_REQUIRES esp_driver_i2s esp_driver_i2c esp_driver_gpio
EMBED_FILES "campione.pcm")
Il campione campione.pcm sono i primi 4 secondi (256.000 byte) del file canon.pcm dell’esempio ufficiale i2s_es8311: PCM grezzo, 16 bit little-endian, stereo, 16 kHz. Puoi crearne uno tuo con ffmpeg:
ffmpeg -i suono.wav -t 4 -f s16le -ar 16000 -ac 2 -acodec pcm_s16le campione.pcm
Ecco il main/main.c:
/*
* Audio I2S su ESP32-P4-Function-EV-Board: codec ES8311 + amplificatore NS4150B - ESP-IDF 5.4
* Tono sinusoidale, campione PCM incorporato, registrazione dal microfono e riascolto.
* Componente gestito: espressif/esp_codec_dev
* Serie ESP32-C6 / ESP32-P4 di www.webappportal.com
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "driver/i2c_master.h"
#include "driver/i2s_std.h"
#include "esp_codec_dev.h"
#include "esp_codec_dev_defaults.h"
static const char *TAG = "audio_p4";
/* Pin della ESP32-P4-Function-EV-Board (BSP ufficiale ed esempio i2s_es8311) */
#define PIN_I2C_SDA GPIO_NUM_7
#define PIN_I2C_SCL GPIO_NUM_8
#define PIN_I2S_MCLK GPIO_NUM_13
#define PIN_I2S_BCLK GPIO_NUM_12
#define PIN_I2S_WS GPIO_NUM_10
#define PIN_I2S_DOUT GPIO_NUM_9 /* P4 -> ES8311 (DAC) */
#define PIN_I2S_DIN GPIO_NUM_11 /* ES8311 (ADC) -> P4 */
#define PIN_PA_EN GPIO_NUM_53 /* abilitazione amplificatore NS4150B */
#define SAMPLE_RATE 16000
#define CHANNELS 2
#define BITS 16
#define REC_SECONDS 3
/* Campione PCM 16 bit stereo 16 kHz incorporato nel firmware (EMBED_FILES) */
extern const uint8_t pcm_start[] asm("_binary_campione_pcm_start");
extern const uint8_t pcm_end[] asm("_binary_campione_pcm_end");
static esp_codec_dev_handle_t s_codec;
static void audio_init(void)
{
/* 1) bus I2C per i registri del codec (nuovo driver i2c_master) */
i2c_master_bus_handle_t i2c_bus;
i2c_master_bus_config_t i2c_cfg = {
.i2c_port = I2C_NUM_0,
.sda_io_num = PIN_I2C_SDA,
.scl_io_num = PIN_I2C_SCL,
.clk_source = I2C_CLK_SRC_DEFAULT,
.glitch_ignore_cnt = 7,
.flags.enable_internal_pullup = true,
};
ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_cfg, &i2c_bus));
/* 2) canale I2S full duplex: il P4 e' master e genera MCLK, BCLK e WS */
i2s_chan_handle_t tx, rx;
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
chan_cfg.auto_clear = true;
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx, &rx));
i2s_std_config_t std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE), /* MCLK = 256 x fs */
.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT,
I2S_SLOT_MODE_STEREO),
.gpio_cfg = {
.mclk = PIN_I2S_MCLK,
.bclk = PIN_I2S_BCLK,
.ws = PIN_I2S_WS,
.dout = PIN_I2S_DOUT,
.din = PIN_I2S_DIN,
},
};
ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx, &std_cfg));
ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx, &std_cfg));
/* 3) interfacce di esp_codec_dev: controllo (I2C), dati (I2S), GPIO */
audio_codec_i2c_cfg_t ctrl_cfg = {
.port = I2C_NUM_0,
.addr = ES8311_CODEC_DEFAULT_ADDR,
.bus_handle = i2c_bus,
};
const audio_codec_ctrl_if_t *ctrl_if = audio_codec_new_i2c_ctrl(&ctrl_cfg);
audio_codec_i2s_cfg_t data_cfg = {
.port = I2S_NUM_0,
.rx_handle = rx,
.tx_handle = tx,
};
const audio_codec_data_if_t *data_if = audio_codec_new_i2s_data(&data_cfg);
const audio_codec_gpio_if_t *gpio_if = audio_codec_new_gpio();
assert(ctrl_if && data_if && gpio_if);
/* 4) driver del codec ES8311: DAC + ADC, amplificatore su GPIO53 */
es8311_codec_cfg_t es_cfg = {
.ctrl_if = ctrl_if,
.gpio_if = gpio_if,
.codec_mode = ESP_CODEC_DEV_WORK_MODE_BOTH,
.pa_pin = PIN_PA_EN,
.pa_reverted = false,
.master_mode = false, /* il codec e' slave I2S */
.use_mclk = true,
.hw_gain = { .pa_voltage = 5.0f, .codec_dac_voltage = 3.3f },
};
const audio_codec_if_t *codec_if = es8311_codec_new(&es_cfg);
assert(codec_if);
esp_codec_dev_cfg_t dev_cfg = {
.dev_type = ESP_CODEC_DEV_TYPE_IN_OUT,
.codec_if = codec_if,
.data_if = data_if,
};
s_codec = esp_codec_dev_new(&dev_cfg);
assert(s_codec);
esp_codec_dev_sample_info_t fs = {
.sample_rate = SAMPLE_RATE,
.channel = CHANNELS,
.bits_per_sample = BITS,
};
ESP_ERROR_CHECK(esp_codec_dev_open(s_codec, &fs));
ESP_ERROR_CHECK(esp_codec_dev_set_out_vol(s_codec, 60)); /* 0..100 */
ESP_ERROR_CHECK(esp_codec_dev_set_in_gain(s_codec, 30.0f)); /* dB */
}
static void play_sine(float freq_hz, float seconds)
{
static int16_t buf[256 * CHANNELS];
const float step = 2.0f * (float)M_PI * freq_hz / SAMPLE_RATE;
float phase = 0.0f;
int blocks = (int)(seconds * SAMPLE_RATE / 256);
for (int b = 0; b < blocks; b++) {
for (int i = 0; i < 256; i++) {
int16_t v = (int16_t)(sinf(phase) * 0.3f * 32767.0f);
buf[2 * i] = v;
buf[2 * i + 1] = v;
phase += step;
if (phase > 2.0f * (float)M_PI) {
phase -= 2.0f * (float)M_PI;
}
}
esp_codec_dev_write(s_codec, buf, sizeof(buf));
}
}
static void play_pcm(const uint8_t *data, size_t len)
{
const size_t chunk = 4096;
while (len > 0) {
size_t n = len > chunk ? chunk : len;
esp_codec_dev_write(s_codec, (void *)data, (int)n);
data += n;
len -= n;
}
}
/* Registra REC_SECONDS secondi, stampa il livello ogni 100 ms e restituisce il buffer */
static int16_t *record(size_t *out_bytes)
{
const size_t bytes = SAMPLE_RATE * CHANNELS * (BITS / 8) * REC_SECONDS;
int16_t *rec = malloc(bytes);
if (rec == NULL) {
ESP_LOGE(TAG, "Memoria insufficiente per la registrazione");
return NULL;
}
const size_t block = SAMPLE_RATE / 10 * CHANNELS * (BITS / 8); /* 100 ms */
for (size_t off = 0; off < bytes; off += block) {
int16_t *p = (int16_t *)((uint8_t *)rec + off);
esp_codec_dev_read(s_codec, p, (int)block);
/* l'ES8311 ha un solo ADC: il microfono e' nel canale sinistro */
double sum = 0.0;
int n = block / (CHANNELS * sizeof(int16_t));
for (int i = 0; i < n; i++) {
sum += (double)p[2 * i] * p[2 * i];
}
double dbfs = 20.0 * log10(sqrt(sum / n) / 32768.0 + 1e-9);
ESP_LOGI(TAG, "mic %6.1f dBFS", dbfs);
}
/* copia il canale del microfono anche a destra per il riascolto */
for (size_t i = 0; i < bytes / sizeof(int16_t); i += 2) {
rec[i + 1] = rec[i];
}
*out_bytes = bytes;
return rec;
}
void app_main(void)
{
audio_init();
ESP_LOGI(TAG, "Tono 440 Hz per 2 s");
play_sine(440.0f, 2.0f);
ESP_LOGI(TAG, "Campione PCM: %u byte", (unsigned)(pcm_end - pcm_start));
play_pcm(pcm_start, pcm_end - pcm_start);
ESP_LOGI(TAG, "Parla nel microfono per %d s...", REC_SECONDS);
size_t rec_bytes = 0;
int16_t *rec = record(&rec_bytes);
if (rec != NULL) {
ESP_LOGI(TAG, "Riascolto della registrazione");
play_pcm((const uint8_t *)rec, rec_bytes);
free(rec);
}
esp_codec_dev_close(s_codec);
ESP_LOGI(TAG, "Fine");
}
Come funziona il codice
audio_init()segue quattro passi: bus I2C, canali I2S, interfacce diesp_codec_dev, driver ES8311. Poi apre il dispositivo a 16 kHz, 16 bit, stereo, imposta il volume di uscita (0-100) e il guadagno del microfono in dB.hw_gaindescrive la catena analogica: DAC a 3,3 V e amplificatore a 5 V. Serve al componente per convertire correttamente il volume in dB; non cambia nulla nel suono se lasci i valori tipici.play_sine()genera blocchi di 256 campioni stereo consinf()e li scrive conesp_codec_dev_write(), che si blocca finché c’è posto nei buffer DMA. L’ampiezza di 0,3 lascia margine per non saturare l’amplificatore.play_pcm()scrive a blocchi di 4 kB un buffer già in formato PCM: il campione incorporato o la registrazione.record()legge blocchi da 100 ms, calcola il valore efficace (RMS) del canale sinistro e lo stampa in dBFS (0 dBFS = fondo scala). Alla fine copia il canale sinistro anche nel destro, così il riascolto esce da entrambi i canali.auto_clear = truenel canale I2S fa uscire silenzio quando non scrivi nulla, invece di ripetere l’ultimo buffer.
Prova e risultati attesi
idf.py set-target esp32p4
idf.py -p COMx flash monitor
Senti due secondi di La a 440 Hz, poi il frammento del Canone, poi il programma ti chiede di parlare e dopo tre secondi ti fa riascoltare la tua voce. Sul log (i livelli dipendono dall’ambiente):
I (1102) audio_p4: Tono 440 Hz per 2 s
I (3131) audio_p4: Campione PCM: 256000 byte
I (7140) audio_p4: Parla nel microfono per 3 s...
I (7243) audio_p4: mic -62.4 dBFS
I (7343) audio_p4: mic -35.8 dBFS
I (7443) audio_p4: mic -28.1 dBFS
...
I (10150) audio_p4: Riascolto della registrazione
I (13160) audio_p4: Fine
Il componente stampa anche alcune righe informative con tag I2S_IF e ES8311 durante l’apertura: sono normali.
La variante per ESP32-C6
Il C6 ha un solo controller I2S e nessun codec a bordo, ma per molti progetti basta un amplificatore con DAC integrato e un microfono digitale. Il MAX98357A riceve I2S e pilota direttamente l’altoparlante; l’INMP441 converte il suono in campioni a 24 bit e li trasmette in I2S. Nessuno dei due ha registri da configurare, quindi niente I2C e niente esp_codec_dev.
Il progetto è in progetti/esp-c6p4/15-audio-i2s/variante-c6, compilato con ESP-IDF 5.4 per esp32c6. Usa slot da 32 bit a 44,1 kHz (BCLK = 2,82 MHz, dentro l’intervallo accettato dall’INMP441), suona un tono a 1 kHz per 2 secondi e poi stampa ogni 100 ms il livello del microfono con una piccola barra: un VU meter testuale, antipasto di quello su matrice LED.
/*
* Audio I2S con ESP32-C6: amplificatore MAX98357A + microfono INMP441 - ESP-IDF 5.4
* Tono sinusoidale sull'altoparlante, poi livello del microfono in dBFS sul log.
* Serie ESP32-C6 / ESP32-P4 di www.webappportal.com
*/
#include <stdio.h>
#include <string.h>
#include <math.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "driver/i2s_std.h"
static const char *TAG = "audio_c6";
#define PIN_I2S_BCLK GPIO_NUM_20 /* MAX98357A BCLK + INMP441 SCK */
#define PIN_I2S_WS GPIO_NUM_21 /* MAX98357A LRC + INMP441 WS */
#define PIN_I2S_DOUT GPIO_NUM_22 /* verso MAX98357A DIN */
#define PIN_I2S_DIN GPIO_NUM_23 /* da INMP441 SD */
#define SAMPLE_RATE 44100
#define TONE_HZ 1000.0f
#define TONE_AMPL 0.25f /* -12 dBFS: volume moderato */
#define FRAMES 256 /* campioni per blocco */
static i2s_chan_handle_t s_tx, s_rx;
static void i2s_init(void)
{
/* un solo controller I2S in full duplex: TX e RX condividono BCLK e WS */
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
chan_cfg.auto_clear = true; /* se non scrivi nulla esce silenzio */
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &s_tx, &s_rx));
i2s_std_config_t std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_32BIT,
I2S_SLOT_MODE_STEREO),
.gpio_cfg = {
.mclk = I2S_GPIO_UNUSED, /* MAX98357A e INMP441 non usano MCLK */
.bclk = PIN_I2S_BCLK,
.ws = PIN_I2S_WS,
.dout = PIN_I2S_DOUT,
.din = PIN_I2S_DIN,
},
};
ESP_ERROR_CHECK(i2s_channel_init_std_mode(s_tx, &std_cfg));
/* INMP441 con L/R a GND: trasmette solo nello slot sinistro */
std_cfg.slot_cfg.slot_mode = I2S_SLOT_MODE_MONO;
std_cfg.slot_cfg.slot_mask = I2S_STD_SLOT_LEFT;
ESP_ERROR_CHECK(i2s_channel_init_std_mode(s_rx, &std_cfg));
ESP_ERROR_CHECK(i2s_channel_enable(s_tx));
ESP_ERROR_CHECK(i2s_channel_enable(s_rx));
}
static void play_tone(float seconds)
{
static int32_t buf[FRAMES * 2]; /* stereo: L, R, L, R... */
float phase = 0.0f;
const float step = 2.0f * (float)M_PI * TONE_HZ / SAMPLE_RATE;
int blocks = (int)(seconds * SAMPLE_RATE / FRAMES);
size_t written;
for (int b = 0; b < blocks; b++) {
for (int i = 0; i < FRAMES; i++) {
int32_t v = (int32_t)(sinf(phase) * TONE_AMPL * 2147483647.0f);
buf[2 * i] = v;
buf[2 * i + 1] = v;
phase += step;
if (phase > 2.0f * (float)M_PI) {
phase -= 2.0f * (float)M_PI;
}
}
ESP_ERROR_CHECK(i2s_channel_write(s_tx, buf, sizeof(buf), &written, portMAX_DELAY));
}
}
static void mic_level_task(void *arg)
{
static int32_t buf[SAMPLE_RATE / 10]; /* 100 ms di audio */
size_t bytes;
while (1) {
ESP_ERROR_CHECK(i2s_channel_read(s_rx, buf, sizeof(buf), &bytes, portMAX_DELAY));
int n = bytes / sizeof(int32_t);
double sum = 0.0;
for (int i = 0; i < n; i++) {
int32_t s = buf[i] >> 8; /* INMP441: 24 bit allineati a sinistra */
sum += (double)s * s;
}
double rms = sqrt(sum / n);
double dbfs = 20.0 * log10(rms / 8388608.0 + 1e-9);
int bars = (int)((dbfs + 90.0) / 3.0); /* -90..0 dBFS -> 0..30 caratteri */
if (bars < 0) {
bars = 0;
}
if (bars > 30) {
bars = 30;
}
char vu[31];
memset(vu, '#', bars);
vu[bars] = '\0';
ESP_LOGI(TAG, "mic %6.1f dBFS |%-30s|", dbfs, vu);
}
}
void app_main(void)
{
i2s_init();
ESP_LOGI(TAG, "Tono %.0f Hz per 2 s", TONE_HZ);
play_tone(2.0f);
ESP_LOGI(TAG, "Ascolto il microfono");
xTaskCreate(mic_level_task, "mic", 4096, NULL, 5, NULL);
}
Nota il canale RX configurato MONO con slot_mask = I2S_STD_SLOT_LEFT: con il pin L/R a GND l’INMP441 trasmette nello slot sinistro e lascia il destro in alta impedenza.
Problemi comuni
esp_codec_devnon si scarica: la prima build ha bisogno di internet per il Component Manager. Dietro un proxy imposta le variabiliHTTPS_PROXYprima diidf.py build.- Nessun suono sul P4: controlla che l’altoparlante sia sul connettore giusto e che GPIO53 non sia usato da altro codice. Prova ad alzare il volume a 80.
- Il codec non risponde su I2C: se nello stesso progetto usi anche il driver I2C legacy (
driver/i2c.h), i due driver non possono convivere sullo stesso bus. Usa soloi2c_master. - Suono distorto o accelerato: la frequenza del file PCM non corrisponde a quella di
esp_codec_dev_open(), oppure il file è mono e lo stai suonando come stereo. - Livello del microfono sempre intorno a -90 dBFS: sul P4 alza il guadagno con
esp_codec_dev_set_in_gain(); sul C6 controlla L/R dell’INMP441 e la maschera dello slot. - Fruscio o ronzio sul MAX98357A: alimentalo a 5 V con un condensatore da 100 µF vicino al modulo e tieni corti i fili di BCLK.
Va oltre
- Nel prossimo progetto della serie l’amplificatore in classe D riceve musica via Wi-Fi e mostra il livello sulla matrice LED 16×32 dell’articolo 10.
- Salva le registrazioni su microSD come file WAV: ti serve solo un’intestazione di 44 byte davanti ai dati PCM (vedi l’articolo 7 sulla SD con FAT32).
- Usa l’audio per un avviso vocale nei progetti di antifurto o domotica.
- Altri tutorial sulla famiglia ESP32 nella sezione ESP32.
