8#include <driver/i2s_std.h>
9#include <driver/i2s_pdm.h>
20static const UBaseType_t MAX_LISTENERS = 16;
22static const uint32_t READ_DURATION_MS = 16;
24static const size_t TASK_STACK_SIZE = 4096;
25static const ssize_t TASK_PRIORITY = 23;
28static const int32_t DC_OFFSET_MOVING_AVERAGE_COEFFICIENT_DENOMINATOR = 1000;
30static const char *
const TAG =
"i2s_audio.microphone";
43 ESP_LOGCONFIG(TAG,
"Running setup");
45#if SOC_I2S_SUPPORTS_ADC
47 if (this->
parent_->get_port() != I2S_NUM_0) {
48 ESP_LOGE(TAG,
"Internal ADC only works on I2S0");
57 if (this->
parent_->get_port() != I2S_NUM_0) {
58 ESP_LOGE(TAG,
"PDM only works on I2S0");
67 ESP_LOGE(TAG,
"Creating semaphore failed");
74 ESP_LOGE(TAG,
"Creating event group failed");
87 " DC offset correction: %s",
92 uint8_t channel_count = 1;
96 if (this->
channel_ == I2S_CHANNEL_FMT_RIGHT_LEFT) {
100 uint8_t bits_per_sample = 16;
105 if (this->
slot_mode_ == I2S_SLOT_MODE_STEREO) {
110#ifdef USE_ESP32_VARIANT_ESP32
114 if (bits_per_sample < 16) {
115 bits_per_sample = 16;
116 }
else if ((bits_per_sample > 16) && (bits_per_sample <= 32)) {
117 bits_per_sample = 32;
122 bits_per_sample = 16;
135bool I2SAudioMicrophone::start_driver_() {
136 if (!this->
parent_->try_lock()) {
143 i2s_driver_config_t config = {
144 .mode = (i2s_mode_t) (this->
i2s_mode_ | I2S_MODE_RX),
148 .communication_format = I2S_COMM_FORMAT_STAND_I2S,
149 .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
153 .tx_desc_auto_clear =
false,
159#if SOC_I2S_SUPPORTS_ADC
161 config.mode = (i2s_mode_t) (config.mode | I2S_MODE_ADC_BUILT_IN);
162 err = i2s_driver_install(this->
parent_->get_port(), &config, 0,
nullptr);
164 ESP_LOGE(TAG,
"Error installing driver: %s", esp_err_to_name(err));
170 ESP_LOGE(TAG,
"Error setting ADC mode: %s", esp_err_to_name(err));
174 err = i2s_adc_enable(this->
parent_->get_port());
176 ESP_LOGE(TAG,
"Error enabling ADC: %s", esp_err_to_name(err));
183 config.mode = (i2s_mode_t) (config.mode | I2S_MODE_PDM);
185 err = i2s_driver_install(this->
parent_->get_port(), &config, 0,
nullptr);
187 ESP_LOGE(TAG,
"Error installing driver: %s", esp_err_to_name(err));
191 i2s_pin_config_t pin_config = this->
parent_->get_pin_config();
192 pin_config.data_in_num = this->
din_pin_;
194 err = i2s_set_pin(this->
parent_->get_port(), &pin_config);
196 ESP_LOGE(TAG,
"Error setting pin: %s", esp_err_to_name(err));
201 i2s_chan_config_t chan_cfg = {
202 .id = this->
parent_->get_port(),
205 .dma_frame_num = 256,
209 err = i2s_new_channel(&chan_cfg, NULL, &this->
rx_handle_);
211 ESP_LOGE(TAG,
"Error creating channel: %s", esp_err_to_name(err));
215 i2s_clock_src_t clk_src = I2S_CLK_SRC_DEFAULT;
216#ifdef I2S_CLK_SRC_APLL
218 clk_src = I2S_CLK_SRC_APLL;
221 i2s_std_gpio_config_t pin_config = this->
parent_->get_pin_config();
222#if SOC_I2S_SUPPORTS_PDM_RX
224 i2s_pdm_rx_clk_config_t clk_cfg = {
228 .dn_sample_mode = I2S_PDM_DSR_8S,
231 i2s_pdm_rx_slot_config_t slot_cfg = I2S_PDM_RX_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, this->
slot_mode_);
233 case I2S_STD_SLOT_LEFT:
234 slot_cfg.slot_mask = I2S_PDM_SLOT_LEFT;
236 case I2S_STD_SLOT_RIGHT:
237 slot_cfg.slot_mask = I2S_PDM_SLOT_RIGHT;
239 case I2S_STD_SLOT_BOTH:
240 slot_cfg.slot_mask = I2S_PDM_SLOT_BOTH;
245 i2s_pdm_rx_config_t pdm_rx_cfg = {
247 .slot_cfg = slot_cfg,
250 .clk = pin_config.ws,
254 .clk_inv = pin_config.invert_flags.ws_inv,
258 err = i2s_channel_init_pdm_rx_mode(this->
rx_handle_, &pdm_rx_cfg);
262 i2s_std_clk_config_t clk_cfg = {
267 i2s_std_slot_config_t std_slot_cfg =
274 i2s_std_config_t std_cfg = {
276 .slot_cfg = std_slot_cfg,
277 .gpio_cfg = pin_config,
280 err = i2s_channel_init_std_mode(this->
rx_handle_, &std_cfg);
283 ESP_LOGE(TAG,
"Error initializing channel: %s", esp_err_to_name(err));
290 ESP_LOGE(TAG,
"Enabling failed: %s", esp_err_to_name(err));
307void I2SAudioMicrophone::stop_driver_() {
313#if SOC_I2S_SUPPORTS_ADC
315 err = i2s_adc_disable(this->
parent_->get_port());
317 ESP_LOGW(TAG,
"Error disabling ADC: %s", esp_err_to_name(err));
321 err = i2s_stop(this->
parent_->get_port());
323 ESP_LOGW(TAG,
"Error stopping: %s", esp_err_to_name(err));
325 err = i2s_driver_uninstall(this->
parent_->get_port());
327 ESP_LOGW(TAG,
"Error uninstalling driver: %s", esp_err_to_name(err));
334 ESP_LOGW(TAG,
"Error stopping: %s", esp_err_to_name(err));
339 ESP_LOGW(TAG,
"Error deleting channel: %s", esp_err_to_name(err));
357 std::vector<uint8_t> samples;
358 samples.reserve(bytes_to_read);
364 samples.resize(bytes_to_read);
365 size_t bytes_read = this_microphone->read_(samples.data(), bytes_to_read, 2 * pdMS_TO_TICKS(READ_DURATION_MS));
366 samples.resize(bytes_read);
368 this_microphone->fix_dc_offset_(samples);
372 vTaskDelay(pdMS_TO_TICKS(READ_DURATION_MS));
380 vTaskDelay(pdMS_TO_TICKS(10));
384void I2SAudioMicrophone::fix_dc_offset_(std::vector<uint8_t> &data) {
388 if (total_samples == 0) {
392 int64_t offset_accumulator = 0;
393 for (uint32_t sample_index = 0; sample_index < total_samples; ++sample_index) {
394 const uint32_t byte_index = sample_index * bytes_per_sample;
396 offset_accumulator += sample;
401 const int32_t new_offset = offset_accumulator / total_samples;
402 this->
dc_offset_ = new_offset / DC_OFFSET_MOVING_AVERAGE_COEFFICIENT_DENOMINATOR +
403 (DC_OFFSET_MOVING_AVERAGE_COEFFICIENT_DENOMINATOR - 1) * this->
dc_offset_ /
404 DC_OFFSET_MOVING_AVERAGE_COEFFICIENT_DENOMINATOR;
407size_t I2SAudioMicrophone::read_(uint8_t *buf,
size_t len, TickType_t ticks_to_wait) {
408 size_t bytes_read = 0;
410 esp_err_t err = i2s_read(this->
parent_->get_port(), buf,
len, &bytes_read, ticks_to_wait);
413 esp_err_t err = i2s_channel_read(this->
rx_handle_, buf,
len, &bytes_read, pdTICKS_TO_MS(ticks_to_wait));
415 if ((err != ESP_OK) && ((err != ESP_ERR_TIMEOUT) || (ticks_to_wait != 0))) {
419 ESP_LOGW(TAG,
"Read error: %s", esp_err_to_name(err));
424 if ((bytes_read == 0) && (ticks_to_wait > 0)) {
429#if defined(USE_ESP32_VARIANT_ESP32) and not defined(USE_I2S_LEGACY)
432 size_t samples_read = bytes_read /
sizeof(int16_t);
433 for (
int i = 0; i < samples_read; i += 2) {
434 int16_t tmp = buf[i];
444 uint32_t event_group_bits = xEventGroupGetBits(this->
event_group_);
447 ESP_LOGV(TAG,
"Task started, attempting to allocate buffer");
448 xEventGroupClearBits(this->
event_group_, MicrophoneEventGroupBits::TASK_STARTING);
452 ESP_LOGV(TAG,
"Task is running and reading data");
454 xEventGroupClearBits(this->
event_group_, MicrophoneEventGroupBits::TASK_RUNNING);
459 ESP_LOGV(TAG,
"Task finished, freeing resources and uninstalling driver");
463 this->stop_driver_();
488 if (!this->start_driver_()) {
489 ESP_LOGE(TAG,
"Driver failed to start; retrying in 1 second");
491 this->stop_driver_();
500 ESP_LOGE(TAG,
"Task failed to start, retrying in 1 second");
502 this->stop_driver_();
virtual void mark_failed()
Mark this component as failed.
void status_clear_error()
void status_momentary_error(const std::string &name, uint32_t length=5000)
bool status_has_warning() const
bool status_has_error() const
void status_set_warning(const char *message="unspecified")
void status_clear_warning()
I2SAudioComponent * parent_
size_t ms_to_bytes(uint32_t ms) const
Converts duration to bytes.
size_t samples_to_bytes(uint32_t samples) const
Converts samples to bytes.
uint32_t bytes_to_samples(size_t bytes) const
Convert bytes to samples.
i2s_std_slot_mask_t std_slot_mask_
i2s_slot_bit_width_t slot_bit_width_
i2s_bits_per_chan_t bits_per_channel_
i2s_channel_fmt_t channel_
i2s_slot_mode_t slot_mode_
i2s_mclk_multiple_t mclk_multiple_
i2s_bits_per_sample_t bits_per_sample_
SemaphoreHandle_t active_listeners_semaphore_
TaskHandle_t task_handle_
EventGroupHandle_t event_group_
void configure_stream_settings_()
Starts the I2S driver.
void dump_config() override
static void mic_task(void *params)
i2s_chan_handle_t rx_handle_
adc1_channel_t adc_channel_
audio::AudioStreamInfo audio_stream_info_
CallbackManager< void(const std::vector< uint8_t > &)> data_callbacks_
int32_t unpack_audio_sample_to_q31(const uint8_t *data, size_t bytes_per_sample)
Unpacks a quantized audio sample into a Q31 fixed-point number.
void pack_q31_as_audio_sample(int32_t sample, uint8_t *data, size_t bytes_per_sample)
Packs a Q31 fixed-point number as an audio sample with the specified number of bytes per sample.
Providing packet encoding functions for exchanging data with a remote host.