Control del micrófono de reTerminal D1001

Introducción
Este wiki muestra cómo controlar el micrófono I2S en reTerminal D1001, y cómo construir un bucle completo de grabación y reproducción:
- Grabar desde el micrófono durante 10 segundos (ruta de captura ES7210).
- Reproducir los datos PCM grabados durante 10 segundos (ruta del altavoz ES8311).
- Repetir continuamente para validar tanto las canalizaciones de audio RX como TX.
La ruta de audio en este ejemplo incluye tres partes clave:
- ESP32-P4: Captura datos PCM del micrófono y envía datos PCM de reproducción.
- ES7210 (I2C address:
0x40): Convierte las señales analógicas del micrófono en datos I2S digitales. - ES8311 + control de PA (PCA9535): Convierte PCM en salida de altavoz y habilita el amplificador de potencia para la reproducción.
Arquitectura de micrófono + altavoz
Diagrama de bloques (ES7210 + ES8311)

Tabla de asignación de pines
| Grupo de señales | Nombre de señal | Pin ESP32-P4 | Dispositivo conectado | Descripción |
|---|---|---|---|---|
| Control I2C compartido | I2C_SDA | GPIO20 | ES7210 / PCA9535 | Línea de datos I2C compartida para la configuración de ES7210 y el control de PA |
| Control I2C compartido | I2C_SCL | GPIO21 | ES7210 / PCA9535 | Línea de reloj I2C compartida |
| I2S de micrófono (RX) | ADC_I2S_MCLK | GPIO29 | ES7210 | Reloj maestro para ES7210 |
| I2S de micrófono (RX) | ADC_I2S_SCLK | GPIO28 | ES7210 | Reloj de bits para la ruta de captura ES7210 |
| I2S de micrófono (RX) | ADC_I2S_LRCK | GPIO27 | ES7210 | Selección de palabra (LRCK) |
| I2S de micrófono (RX) | ADC_I2S_SDOUT | GPIO26 | ES7210 | Salida de datos PCM desde ES7210 hacia ESP32-P4 |
| Control de PA | EN_PA (EXP_GPO11) | PCA9535 P13 | Amplificador de potencia | Mantener en HIGH al validar la reproducción del bucle micrófono + altavoz |
Esta tabla se centra en la ruta del micrófono. En este ejemplo, ES8311 también comparte el mismo bus I2C para la configuración del códec de reproducción.
Flujo de software
Repositorio de ejemplo en GitHub
Descarga el repositorio oficial de reTerminal D1001 desde GitHub:
Estructura del directorio del proyecto
El siguiente diagrama muestra la estructura del directorio del proyecto utilizada en este ejemplo.

Navega al directorio de ejemplo de grabación y reproducción para ES7210 + ES8311 en el repositorio (por ejemplo, driver_examples/02_I2SCodec_es7210).
Secuencia de ejecución de desarrollo
Paso 1. Inicializar el control de PA mediante PCA9535
La ruta del altavoz requiere que el pin de habilitación de PA se active a través de PCA9535 (P13 = HIGH).
static esp_err_t pca9535_write_reg(uint8_t reg, uint8_t data)
{
uint8_t write_buf[2] = {reg, data};
return i2c_master_write_to_device(1, PCA9535_I2C_ADDR, write_buf, sizeof(write_buf), 1000 / portTICK_PERIOD_MS);
}
static void pca9535_init(void)
{
int i2c_master_port = 1; // Use I2C_NUM_1
i2c_config_t conf = {
.mode = I2C_MODE_MASTER,
.sda_io_num = MISC_I2C_SDA,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_io_num = MISC_I2C_SCL,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = 100000,
};
i2c_param_config(i2c_master_port, &conf);
i2c_driver_install(i2c_master_port, conf.mode, 0, 0, 0);
pca9535_write_reg(0x06, 0x00);
pca9535_write_reg(0x07, 0x00);
pca9535_write_reg(0x02, 0x00);
pca9535_write_reg(0x03, 0x08); // Set P13 (EXP_GPO11) to HIGH
}
Paso 2. Inicializar I2S para ES8311 (TX de altavoz)
Configura un canal I2S TX para la reproducción hacia ES8311.
static esp_err_t i2s_driver_init_es8311(void)
{
i2s_chan_config_t tx_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM, I2S_ROLE_MASTER);
tx_chan_cfg.auto_clear = true;
ESP_ERROR_CHECK(i2s_new_channel(&tx_chan_cfg, &tx_handle_es8311, NULL));
i2s_std_config_t std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(EXAMPLE_SAMPLE_RATE),
.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO),
.gpio_cfg = {
.mclk = I2S_MCK_IO,
.bclk = I2S_BCK_IO,
.ws = I2S_WS_IO,
.dout = I2S_DO_IO,
.din = I2S_GPIO_UNUSED,
},
};
std_cfg.clk_cfg.mclk_multiple = EXAMPLE_MCLK_MULTIPLE;
ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_handle_es8311, &std_cfg));
ESP_ERROR_CHECK(i2s_channel_enable(tx_handle_es8311));
ESP_LOGI(TAG, "ES8311 I2S initialized");
return ESP_OK;
}
Paso 3. Inicializar el códec ES8311
ES8311 se configura para el formato de reproducción del altavoz y el volumen.
static esp_err_t es8311_codec_init(void)
{
const i2c_config_t es_i2c_cfg = {
.sda_io_num = I2C_SDA_IO,
.scl_io_num = I2C_SCL_IO,
.mode = I2C_MODE_MASTER,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = 100000,
};
i2c_param_config(I2C_NUM, &es_i2c_cfg);
i2c_driver_install(I2C_NUM, I2C_MODE_MASTER, 0, 0, 0);
es8311_handle_t es_handle = es8311_create(I2C_NUM, ES8311_ADDRRES_0);
const es8311_clock_config_t es_clk = {
.mclk_inverted = false,
.sclk_inverted = false,
.mclk_from_mclk_pin = true,
.mclk_frequency = EXAMPLE_MCLK_FREQ_HZ,
.sample_frequency = EXAMPLE_SAMPLE_RATE
};
ESP_ERROR_CHECK(es8311_init(es_handle, &es_clk, ES8311_RESOLUTION_16, ES8311_RESOLUTION_16));
ESP_ERROR_CHECK(es8311_sample_frequency_config(es_handle, EXAMPLE_SAMPLE_RATE * EXAMPLE_MCLK_MULTIPLE, EXAMPLE_SAMPLE_RATE));
ESP_ERROR_CHECK(es8311_voice_volume_set(es_handle, EXAMPLE_VOICE_VOLUME, NULL));
ESP_ERROR_CHECK(es8311_microphone_config(es_handle, false));
ESP_LOGI(TAG, "ES8311 codec configured");
return ESP_OK;
}
Paso 4. Inicializar I2S + ES7210 (RX de micrófono)
Configura la captura del micrófono desde ES7210 a través de I2S RX.
static esp_err_t i2s_driver_init_es7210(void)
{
i2s_chan_config_t rx_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(ES7210_I2S_NUM, I2S_ROLE_MASTER);
ESP_ERROR_CHECK(i2s_new_channel(&rx_chan_cfg, NULL, &rx_handle_es7210));
i2s_std_config_t std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(EXAMPLE_SAMPLE_RATE),
.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO),
.gpio_cfg = {
.mclk = ES7210_I2S_MCK_IO,
.bclk = ES7210_I2S_BCK_IO,
.ws = ES7210_I2S_WS_IO,
.dout = I2S_GPIO_UNUSED,
.din = ES7210_I2S_DI_IO,
},
};
std_cfg.clk_cfg.mclk_multiple = EXAMPLE_MCLK_MULTIPLE;
ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_handle_es7210, &std_cfg));
ESP_ERROR_CHECK(i2s_channel_enable(rx_handle_es7210));
ESP_LOGI(TAG, "ES7210 I2S initialized in STD mode (Stereo)");
return ESP_OK;
}
static esp_err_t es7210_codec_init(void)
{
es7210_dev_handle_t es7210_handle = NULL;
es7210_i2c_config_t i2c_conf = {
.i2c_port = I2C_NUM,
.i2c_addr = ES7210_ADDRRES_00
};
ESP_ERROR_CHECK(es7210_new_codec(&i2c_conf, &es7210_handle));
es7210_codec_config_t codec_conf = {
.sample_rate_hz = EXAMPLE_SAMPLE_RATE,
.mclk_ratio = EXAMPLE_MCLK_MULTIPLE,
.i2s_format = ES7210_I2S_FMT_I2S,
.bit_width = ES7210_I2S_BITS_16B,
.mic_bias = ES7210_MIC_BIAS_2V87,
.mic_gain = ES7210_MIC_GAIN_24DB,
.flags.tdm_enable = false
};
ESP_ERROR_CHECK(es7210_config_codec(es7210_handle, &codec_conf));
ESP_ERROR_CHECK(es7210_config_volume(es7210_handle, 0));
ESP_LOGI(TAG, "ES7210 codec configured");
return ESP_OK;
}
Paso 5. Grabar 10 s y reproducir 10 s en bucle
Reserva un búfer PSRAM, graba por bloques desde ES7210 y luego escribe el PCM grabado en ES8311.
static void record_play_task(void *args)
{
int16_t *record_buf = heap_caps_malloc(RECORD_BUFFER_SIZE, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
if (!record_buf) {
vTaskDelete(NULL);
return;
}
size_t bytes_read = 0;
size_t bytes_written = 0;
while (1) {
size_t total_read = 0;
size_t chunk_size = 4096;
// Record phase (10s)
while (total_read < RECORD_BUFFER_SIZE) {
size_t to_read = RECORD_BUFFER_SIZE - total_read;
if (to_read > chunk_size) {
to_read = chunk_size;
}
if (i2s_channel_read(rx_handle_es7210, (uint8_t *)record_buf + total_read, to_read, &bytes_read, portMAX_DELAY) == ESP_OK) {
total_read += bytes_read;
} else {
break;
}
}
// Playback phase (10s)
size_t total_written = 0;
while (total_written < total_read) {
size_t to_write = total_read - total_written;
if (to_write > chunk_size) {
to_write = chunk_size;
}
if (i2s_channel_write(tx_handle_es8311, (uint8_t *)record_buf + total_written, to_write, &bytes_written, portMAX_DELAY) == ESP_OK) {
total_written += bytes_written;
} else {
break;
}
}
}
}
Paso 6. Entrada principal
Inicializa todos los módulos en orden y luego inicia la tarea de grabación-reproducción.
void app_main(void)
{
printf("\n============================================\n");
printf(" Record & Play Example (ES7210 + ES8311) \n");
printf("============================================\n\n");
pca9535_init();
if (i2s_driver_init_es8311() != ESP_OK) {
abort();
}
if (es8311_codec_init() != ESP_OK) {
abort();
}
if (i2s_driver_init_es7210() != ESP_OK) {
abort();
}
if (es7210_codec_init() != ESP_OK) {
abort();
}
xTaskCreatePinnedToCore(record_play_task, "record_play_task", 32768, NULL, 5, NULL, 0);
}
Registro serie esperado antes de la grabación
Cuando uses nuestro proyecto de ejemplo, antes de que comience la primera grabación de 10 segundos, el monitor serie debería mostrar mensajes de inicio similares a:
Record & Play Example (ES7210 + ES8311)
PCA9535 initialized, P13 set to HIGH
ES8311 I2S initialized
ES8311 codec configured
ES7210 I2S initialized in STD mode (Stereo)
ES7210 codec configured
=== Start Recording for 10 seconds ===
Si estos registros no aparecen en secuencia, primero comprueba el valor de retorno de cada paso de inicialización.
Solución de problemas
P1: No hay sonido durante la reproducción
- Comprobar: Confirma que el PCA9535 está inicializado y que P13 (
EN_PA) está en HIGH. - Comprobar: Verifica los pines I2S TX del ES8311 y la ejecución de
i2s_channel_enable(tx_handle_es8311).
P2: No se capturan datos del micrófono
- Comprobar: Verifica la asignación de pines I2S RX del ES7210 y la ejecución de
i2s_channel_enable(rx_handle_es7210). - Comprobar: Confirma la dirección I2C del ES7210 y el valor de retorno de la inicialización del códec.
P3: Audio distorsionado o ruido fuerte
- Comprobar: Mantén la frecuencia de muestreo y la relación MCLK coherentes entre ES8311, ES7210 y las configuraciones de I2S.
- Comprobar: Reduce la ganancia del micrófono (por ejemplo, por debajo de
ES7210_MIC_GAIN_24DB) si se produce recorte en campo cercano. - Comprobar: Si la salida del altavoz tiene ruido metálico continuo o voz incompleta, verifica la configuración de ranuras de canal:
slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO). - Comprobar: Asegúrate de que la alineación de los canales RX/TX sea correcta; una descoordinación mono/estéreo puede causar voz entrecortada o robótica.