mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 07:03:55 +00:00 
			
		
		
		
	[i2s_audio] Move microphone reads into a task (#8651)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
		| @@ -39,6 +39,7 @@ CONF_SECONDARY = "secondary" | |||||||
|  |  | ||||||
| CONF_USE_APLL = "use_apll" | CONF_USE_APLL = "use_apll" | ||||||
| CONF_BITS_PER_CHANNEL = "bits_per_channel" | CONF_BITS_PER_CHANNEL = "bits_per_channel" | ||||||
|  | CONF_MCLK_MULTIPLE = "mclk_multiple" | ||||||
| CONF_MONO = "mono" | CONF_MONO = "mono" | ||||||
| CONF_LEFT = "left" | CONF_LEFT = "left" | ||||||
| CONF_RIGHT = "right" | CONF_RIGHT = "right" | ||||||
| @@ -122,8 +123,25 @@ I2S_SLOT_BIT_WIDTH = { | |||||||
|     32: i2s_slot_bit_width_t.I2S_SLOT_BIT_WIDTH_32BIT, |     32: i2s_slot_bit_width_t.I2S_SLOT_BIT_WIDTH_32BIT, | ||||||
| } | } | ||||||
|  |  | ||||||
|  | i2s_mclk_multiple_t = cg.global_ns.enum("i2s_mclk_multiple_t") | ||||||
|  | I2S_MCLK_MULTIPLE = { | ||||||
|  |     128: i2s_mclk_multiple_t.I2S_MCLK_MULTIPLE_128, | ||||||
|  |     256: i2s_mclk_multiple_t.I2S_MCLK_MULTIPLE_256, | ||||||
|  |     384: i2s_mclk_multiple_t.I2S_MCLK_MULTIPLE_384, | ||||||
|  |     512: i2s_mclk_multiple_t.I2S_MCLK_MULTIPLE_512, | ||||||
|  | } | ||||||
|  |  | ||||||
| _validate_bits = cv.float_with_unit("bits", "bit") | _validate_bits = cv.float_with_unit("bits", "bit") | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def validate_mclk_divisible_by_3(config): | ||||||
|  |     if config[CONF_BITS_PER_SAMPLE] == 24 and config[CONF_MCLK_MULTIPLE] % 3 != 0: | ||||||
|  |         raise cv.Invalid( | ||||||
|  |             f"{CONF_MCLK_MULTIPLE} must be divisible by 3 when bits per sample is 24" | ||||||
|  |         ) | ||||||
|  |     return config | ||||||
|  |  | ||||||
|  |  | ||||||
| _use_legacy_driver = None | _use_legacy_driver = None | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -155,6 +173,7 @@ def i2s_audio_component_schema( | |||||||
|                 cv.Any(cv.float_with_unit("bits", "bit"), "default"), |                 cv.Any(cv.float_with_unit("bits", "bit"), "default"), | ||||||
|                 cv.one_of(*I2S_BITS_PER_CHANNEL), |                 cv.one_of(*I2S_BITS_PER_CHANNEL), | ||||||
|             ), |             ), | ||||||
|  |             cv.Optional(CONF_MCLK_MULTIPLE, default=256): cv.one_of(*I2S_MCLK_MULTIPLE), | ||||||
|         } |         } | ||||||
|     ) |     ) | ||||||
|  |  | ||||||
| @@ -182,11 +201,10 @@ async def register_i2s_audio_component(var, config): | |||||||
|             slot_mask = CONF_BOTH |             slot_mask = CONF_BOTH | ||||||
|         cg.add(var.set_slot_mode(I2S_SLOT_MODE[slot_mode])) |         cg.add(var.set_slot_mode(I2S_SLOT_MODE[slot_mode])) | ||||||
|         cg.add(var.set_std_slot_mask(I2S_STD_SLOT_MASK[slot_mask])) |         cg.add(var.set_std_slot_mask(I2S_STD_SLOT_MASK[slot_mask])) | ||||||
|         cg.add( |         cg.add(var.set_slot_bit_width(I2S_SLOT_BIT_WIDTH[config[CONF_BITS_PER_SAMPLE]])) | ||||||
|             var.set_slot_bit_width(I2S_SLOT_BIT_WIDTH[config[CONF_BITS_PER_CHANNEL]]) |  | ||||||
|         ) |  | ||||||
|     cg.add(var.set_sample_rate(config[CONF_SAMPLE_RATE])) |     cg.add(var.set_sample_rate(config[CONF_SAMPLE_RATE])) | ||||||
|     cg.add(var.set_use_apll(config[CONF_USE_APLL])) |     cg.add(var.set_use_apll(config[CONF_USE_APLL])) | ||||||
|  |     cg.add(var.set_mclk_multiple(I2S_MCLK_MULTIPLE[config[CONF_MCLK_MULTIPLE]])) | ||||||
|  |  | ||||||
|  |  | ||||||
| def validate_use_legacy(value): | def validate_use_legacy(value): | ||||||
|   | |||||||
| @@ -31,6 +31,7 @@ class I2SAudioBase : public Parented<I2SAudioComponent> { | |||||||
| #endif | #endif | ||||||
|   void set_sample_rate(uint32_t sample_rate) { this->sample_rate_ = sample_rate; } |   void set_sample_rate(uint32_t sample_rate) { this->sample_rate_ = sample_rate; } | ||||||
|   void set_use_apll(uint32_t use_apll) { this->use_apll_ = use_apll; } |   void set_use_apll(uint32_t use_apll) { this->use_apll_ = use_apll; } | ||||||
|  |   void set_mclk_multiple(i2s_mclk_multiple_t mclk_multiple) { this->mclk_multiple_ = mclk_multiple; } | ||||||
|  |  | ||||||
|  protected: |  protected: | ||||||
| #ifdef USE_I2S_LEGACY | #ifdef USE_I2S_LEGACY | ||||||
| @@ -46,6 +47,7 @@ class I2SAudioBase : public Parented<I2SAudioComponent> { | |||||||
| #endif | #endif | ||||||
|   uint32_t sample_rate_; |   uint32_t sample_rate_; | ||||||
|   bool use_apll_; |   bool use_apll_; | ||||||
|  |   i2s_mclk_multiple_t mclk_multiple_; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| class I2SAudioIn : public I2SAudioBase {}; | class I2SAudioIn : public I2SAudioBase {}; | ||||||
|   | |||||||
| @@ -22,6 +22,7 @@ from .. import ( | |||||||
|     i2s_audio_ns, |     i2s_audio_ns, | ||||||
|     register_i2s_audio_component, |     register_i2s_audio_component, | ||||||
|     use_legacy, |     use_legacy, | ||||||
|  |     validate_mclk_divisible_by_3, | ||||||
| ) | ) | ||||||
|  |  | ||||||
| CODEOWNERS = ["@jesserockz"] | CODEOWNERS = ["@jesserockz"] | ||||||
| @@ -112,6 +113,7 @@ CONFIG_SCHEMA = cv.All( | |||||||
|     _validate_channel, |     _validate_channel, | ||||||
|     _set_num_channels_from_config, |     _set_num_channels_from_config, | ||||||
|     _set_stream_limits, |     _set_stream_limits, | ||||||
|  |     validate_mclk_divisible_by_3, | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -15,10 +15,25 @@ | |||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace i2s_audio { | namespace i2s_audio { | ||||||
|  |  | ||||||
| static const size_t BUFFER_SIZE = 512; | static const UBaseType_t MAX_LISTENERS = 16; | ||||||
|  |  | ||||||
|  | static const uint32_t READ_DURATION_MS = 16; | ||||||
|  |  | ||||||
|  | static const size_t TASK_STACK_SIZE = 4096; | ||||||
|  | static const ssize_t TASK_PRIORITY = 23; | ||||||
|  |  | ||||||
| static const char *const TAG = "i2s_audio.microphone"; | static const char *const TAG = "i2s_audio.microphone"; | ||||||
|  |  | ||||||
|  | enum MicrophoneEventGroupBits : uint32_t { | ||||||
|  |   COMMAND_STOP = (1 << 0),  // stops the microphone task | ||||||
|  |   TASK_STARTING = (1 << 10), | ||||||
|  |   TASK_RUNNING = (1 << 11), | ||||||
|  |   TASK_STOPPING = (1 << 12), | ||||||
|  |   TASK_STOPPED = (1 << 13), | ||||||
|  |  | ||||||
|  |   ALL_BITS = 0x00FFFFFF,  // All valid FreeRTOS event group bits | ||||||
|  | }; | ||||||
|  |  | ||||||
| void I2SAudioMicrophone::setup() { | void I2SAudioMicrophone::setup() { | ||||||
|   ESP_LOGCONFIG(TAG, "Setting up I2S Audio Microphone..."); |   ESP_LOGCONFIG(TAG, "Setting up I2S Audio Microphone..."); | ||||||
| #ifdef USE_I2S_LEGACY | #ifdef USE_I2S_LEGACY | ||||||
| @@ -41,18 +56,32 @@ void I2SAudioMicrophone::setup() { | |||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   this->active_listeners_semaphore_ = xSemaphoreCreateCounting(MAX_LISTENERS, MAX_LISTENERS); | ||||||
|  |   if (this->active_listeners_semaphore_ == nullptr) { | ||||||
|  |     ESP_LOGE(TAG, "Failed to create semaphore"); | ||||||
|  |     this->mark_failed(); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   this->event_group_ = xEventGroupCreate(); | ||||||
|  |   if (this->event_group_ == nullptr) { | ||||||
|  |     ESP_LOGE(TAG, "Failed to create event group"); | ||||||
|  |     this->mark_failed(); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| void I2SAudioMicrophone::start() { | void I2SAudioMicrophone::start() { | ||||||
|   if (this->is_failed()) |   if (this->is_failed()) | ||||||
|     return; |     return; | ||||||
|   if (this->state_ == microphone::STATE_RUNNING) |  | ||||||
|     return;  // Already running |   xSemaphoreTake(this->active_listeners_semaphore_, 0); | ||||||
|   this->state_ = microphone::STATE_STARTING; |  | ||||||
| } | } | ||||||
| void I2SAudioMicrophone::start_() { |  | ||||||
|  | bool I2SAudioMicrophone::start_driver_() { | ||||||
|   if (!this->parent_->try_lock()) { |   if (!this->parent_->try_lock()) { | ||||||
|     return;  // Waiting for another i2s to return lock |     return false;  // Waiting for another i2s to return lock | ||||||
|   } |   } | ||||||
|   esp_err_t err; |   esp_err_t err; | ||||||
|  |  | ||||||
| @@ -94,11 +123,11 @@ void I2SAudioMicrophone::start_() { | |||||||
|       .communication_format = I2S_COMM_FORMAT_STAND_I2S, |       .communication_format = I2S_COMM_FORMAT_STAND_I2S, | ||||||
|       .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, |       .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, | ||||||
|       .dma_buf_count = 4, |       .dma_buf_count = 4, | ||||||
|       .dma_buf_len = 256, |       .dma_buf_len = 240,  // Must be divisible by 3 to support 24 bits per sample on old driver and newer variants | ||||||
|       .use_apll = this->use_apll_, |       .use_apll = this->use_apll_, | ||||||
|       .tx_desc_auto_clear = false, |       .tx_desc_auto_clear = false, | ||||||
|       .fixed_mclk = 0, |       .fixed_mclk = 0, | ||||||
|       .mclk_multiple = I2S_MCLK_MULTIPLE_256, |       .mclk_multiple = this->mclk_multiple_, | ||||||
|       .bits_per_chan = this->bits_per_channel_, |       .bits_per_chan = this->bits_per_channel_, | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
| @@ -109,20 +138,20 @@ void I2SAudioMicrophone::start_() { | |||||||
|     if (err != ESP_OK) { |     if (err != ESP_OK) { | ||||||
|       ESP_LOGW(TAG, "Error installing I2S driver: %s", esp_err_to_name(err)); |       ESP_LOGW(TAG, "Error installing I2S driver: %s", esp_err_to_name(err)); | ||||||
|       this->status_set_error(); |       this->status_set_error(); | ||||||
|       return; |       return false; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     err = i2s_set_adc_mode(ADC_UNIT_1, this->adc_channel_); |     err = i2s_set_adc_mode(ADC_UNIT_1, this->adc_channel_); | ||||||
|     if (err != ESP_OK) { |     if (err != ESP_OK) { | ||||||
|       ESP_LOGW(TAG, "Error setting ADC mode: %s", esp_err_to_name(err)); |       ESP_LOGW(TAG, "Error setting ADC mode: %s", esp_err_to_name(err)); | ||||||
|       this->status_set_error(); |       this->status_set_error(); | ||||||
|       return; |       return false; | ||||||
|     } |     } | ||||||
|     err = i2s_adc_enable(this->parent_->get_port()); |     err = i2s_adc_enable(this->parent_->get_port()); | ||||||
|     if (err != ESP_OK) { |     if (err != ESP_OK) { | ||||||
|       ESP_LOGW(TAG, "Error enabling ADC: %s", esp_err_to_name(err)); |       ESP_LOGW(TAG, "Error enabling ADC: %s", esp_err_to_name(err)); | ||||||
|       this->status_set_error(); |       this->status_set_error(); | ||||||
|       return; |       return false; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   } else |   } else | ||||||
| @@ -135,7 +164,7 @@ void I2SAudioMicrophone::start_() { | |||||||
|     if (err != ESP_OK) { |     if (err != ESP_OK) { | ||||||
|       ESP_LOGW(TAG, "Error installing I2S driver: %s", esp_err_to_name(err)); |       ESP_LOGW(TAG, "Error installing I2S driver: %s", esp_err_to_name(err)); | ||||||
|       this->status_set_error(); |       this->status_set_error(); | ||||||
|       return; |       return false; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     i2s_pin_config_t pin_config = this->parent_->get_pin_config(); |     i2s_pin_config_t pin_config = this->parent_->get_pin_config(); | ||||||
| @@ -145,7 +174,7 @@ void I2SAudioMicrophone::start_() { | |||||||
|     if (err != ESP_OK) { |     if (err != ESP_OK) { | ||||||
|       ESP_LOGW(TAG, "Error setting I2S pin: %s", esp_err_to_name(err)); |       ESP_LOGW(TAG, "Error setting I2S pin: %s", esp_err_to_name(err)); | ||||||
|       this->status_set_error(); |       this->status_set_error(); | ||||||
|       return; |       return false; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| #else | #else | ||||||
| @@ -161,7 +190,7 @@ void I2SAudioMicrophone::start_() { | |||||||
|   if (err != ESP_OK) { |   if (err != ESP_OK) { | ||||||
|     ESP_LOGW(TAG, "Error creating new I2S channel: %s", esp_err_to_name(err)); |     ESP_LOGW(TAG, "Error creating new I2S channel: %s", esp_err_to_name(err)); | ||||||
|     this->status_set_error(); |     this->status_set_error(); | ||||||
|     return; |     return false; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   i2s_clock_src_t clk_src = I2S_CLK_SRC_DEFAULT; |   i2s_clock_src_t clk_src = I2S_CLK_SRC_DEFAULT; | ||||||
| @@ -178,7 +207,7 @@ void I2SAudioMicrophone::start_() { | |||||||
|     i2s_pdm_rx_clk_config_t clk_cfg = { |     i2s_pdm_rx_clk_config_t clk_cfg = { | ||||||
|         .sample_rate_hz = this->sample_rate_, |         .sample_rate_hz = this->sample_rate_, | ||||||
|         .clk_src = clk_src, |         .clk_src = clk_src, | ||||||
|         .mclk_multiple = I2S_MCLK_MULTIPLE_256, |         .mclk_multiple = this->mclk_multiple_, | ||||||
|         .dn_sample_mode = I2S_PDM_DSR_8S, |         .dn_sample_mode = I2S_PDM_DSR_8S, | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
| @@ -216,7 +245,7 @@ void I2SAudioMicrophone::start_() { | |||||||
|     i2s_std_clk_config_t clk_cfg = { |     i2s_std_clk_config_t clk_cfg = { | ||||||
|         .sample_rate_hz = this->sample_rate_, |         .sample_rate_hz = this->sample_rate_, | ||||||
|         .clk_src = clk_src, |         .clk_src = clk_src, | ||||||
|         .mclk_multiple = I2S_MCLK_MULTIPLE_256, |         .mclk_multiple = this->mclk_multiple_, | ||||||
|     }; |     }; | ||||||
|     i2s_std_slot_config_t std_slot_cfg = |     i2s_std_slot_config_t std_slot_cfg = | ||||||
|         I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG((i2s_data_bit_width_t) this->slot_bit_width_, this->slot_mode_); |         I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG((i2s_data_bit_width_t) this->slot_bit_width_, this->slot_mode_); | ||||||
| @@ -236,7 +265,7 @@ void I2SAudioMicrophone::start_() { | |||||||
|   if (err != ESP_OK) { |   if (err != ESP_OK) { | ||||||
|     ESP_LOGW(TAG, "Error initializing I2S channel: %s", esp_err_to_name(err)); |     ESP_LOGW(TAG, "Error initializing I2S channel: %s", esp_err_to_name(err)); | ||||||
|     this->status_set_error(); |     this->status_set_error(); | ||||||
|     return; |     return false; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   /* Before reading data, start the RX channel first */ |   /* Before reading data, start the RX channel first */ | ||||||
| @@ -244,28 +273,25 @@ void I2SAudioMicrophone::start_() { | |||||||
|   if (err != ESP_OK) { |   if (err != ESP_OK) { | ||||||
|     ESP_LOGW(TAG, "Error enabling I2S Microphone: %s", esp_err_to_name(err)); |     ESP_LOGW(TAG, "Error enabling I2S Microphone: %s", esp_err_to_name(err)); | ||||||
|     this->status_set_error(); |     this->status_set_error(); | ||||||
|     return; |     return false; | ||||||
|   } |   } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|   this->audio_stream_info_ = audio::AudioStreamInfo(bits_per_sample, channel_count, this->sample_rate_); |   this->audio_stream_info_ = audio::AudioStreamInfo(bits_per_sample, channel_count, this->sample_rate_); | ||||||
|  |  | ||||||
|   this->state_ = microphone::STATE_RUNNING; |  | ||||||
|   this->high_freq_.start(); |  | ||||||
|   this->status_clear_error(); |   this->status_clear_error(); | ||||||
|  |  | ||||||
|  |   return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| void I2SAudioMicrophone::stop() { | void I2SAudioMicrophone::stop() { | ||||||
|   if (this->state_ == microphone::STATE_STOPPED || this->is_failed()) |   if (this->state_ == microphone::STATE_STOPPED || this->is_failed()) | ||||||
|     return; |     return; | ||||||
|   if (this->state_ == microphone::STATE_STARTING) { |  | ||||||
|     this->state_ = microphone::STATE_STOPPED; |   xSemaphoreGive(this->active_listeners_semaphore_); | ||||||
|     return; |  | ||||||
|   } |  | ||||||
|   this->state_ = microphone::STATE_STOPPING; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void I2SAudioMicrophone::stop_() { | void I2SAudioMicrophone::stop_driver_() { | ||||||
|   esp_err_t err; |   esp_err_t err; | ||||||
| #ifdef USE_I2S_LEGACY | #ifdef USE_I2S_LEGACY | ||||||
| #if SOC_I2S_SUPPORTS_ADC | #if SOC_I2S_SUPPORTS_ADC | ||||||
| @@ -307,11 +333,51 @@ void I2SAudioMicrophone::stop_() { | |||||||
|   } |   } | ||||||
| #endif | #endif | ||||||
|   this->parent_->unlock(); |   this->parent_->unlock(); | ||||||
|   this->state_ = microphone::STATE_STOPPED; |  | ||||||
|   this->high_freq_.stop(); |  | ||||||
|   this->status_clear_error(); |   this->status_clear_error(); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void I2SAudioMicrophone::mic_task(void *params) { | ||||||
|  |   I2SAudioMicrophone *this_microphone = (I2SAudioMicrophone *) params; | ||||||
|  |  | ||||||
|  |   xEventGroupSetBits(this_microphone->event_group_, MicrophoneEventGroupBits::TASK_STARTING); | ||||||
|  |  | ||||||
|  |   uint8_t start_counter = 0; | ||||||
|  |   bool started = this_microphone->start_driver_(); | ||||||
|  |   while (!started && start_counter < 10) { | ||||||
|  |     // Attempt to load the driver again in 100 ms. Doesn't slow down main loop since its in a task. | ||||||
|  |     vTaskDelay(pdMS_TO_TICKS(100)); | ||||||
|  |     ++start_counter; | ||||||
|  |     started = this_microphone->start_driver_(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (started) { | ||||||
|  |     xEventGroupSetBits(this_microphone->event_group_, MicrophoneEventGroupBits::TASK_RUNNING); | ||||||
|  |     const size_t bytes_to_read = this_microphone->audio_stream_info_.ms_to_bytes(READ_DURATION_MS); | ||||||
|  |     std::vector<uint8_t> samples; | ||||||
|  |     samples.reserve(bytes_to_read); | ||||||
|  |  | ||||||
|  |     while (!(xEventGroupGetBits(this_microphone->event_group_) & COMMAND_STOP)) { | ||||||
|  |       if (this_microphone->data_callbacks_.size() > 0) { | ||||||
|  |         samples.resize(bytes_to_read); | ||||||
|  |         size_t bytes_read = this_microphone->read_(samples.data(), bytes_to_read, 2 * pdMS_TO_TICKS(READ_DURATION_MS)); | ||||||
|  |         samples.resize(bytes_read); | ||||||
|  |         this_microphone->data_callbacks_.call(samples); | ||||||
|  |       } else { | ||||||
|  |         delay(READ_DURATION_MS); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   xEventGroupSetBits(this_microphone->event_group_, MicrophoneEventGroupBits::TASK_STOPPING); | ||||||
|  |   this_microphone->stop_driver_(); | ||||||
|  |  | ||||||
|  |   xEventGroupSetBits(this_microphone->event_group_, MicrophoneEventGroupBits::TASK_STOPPED); | ||||||
|  |   while (true) { | ||||||
|  |     // Continuously delay until the loop method delete the task | ||||||
|  |     delay(10); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
| size_t I2SAudioMicrophone::read_(uint8_t *buf, size_t len, TickType_t ticks_to_wait) { | size_t I2SAudioMicrophone::read_(uint8_t *buf, size_t len, TickType_t ticks_to_wait) { | ||||||
|   size_t bytes_read = 0; |   size_t bytes_read = 0; | ||||||
| #ifdef USE_I2S_LEGACY | #ifdef USE_I2S_LEGACY | ||||||
| @@ -345,29 +411,60 @@ size_t I2SAudioMicrophone::read_(uint8_t *buf, size_t len, TickType_t ticks_to_w | |||||||
|   return bytes_read; |   return bytes_read; | ||||||
| } | } | ||||||
|  |  | ||||||
| void I2SAudioMicrophone::read_() { |  | ||||||
|   std::vector<uint8_t> samples; |  | ||||||
|   const size_t bytes_to_read = this->audio_stream_info_.ms_to_bytes(32); |  | ||||||
|   samples.resize(bytes_to_read); |  | ||||||
|   size_t bytes_read = this->read_(samples.data(), bytes_to_read, 0); |  | ||||||
|   samples.resize(bytes_read); |  | ||||||
|   this->data_callbacks_.call(samples); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void I2SAudioMicrophone::loop() { | void I2SAudioMicrophone::loop() { | ||||||
|  |   uint32_t event_group_bits = xEventGroupGetBits(this->event_group_); | ||||||
|  |  | ||||||
|  |   if (event_group_bits & MicrophoneEventGroupBits::TASK_STARTING) { | ||||||
|  |     ESP_LOGD(TAG, "Task has started, attempting to setup I2S audio driver"); | ||||||
|  |     xEventGroupClearBits(this->event_group_, MicrophoneEventGroupBits::TASK_STARTING); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (event_group_bits & MicrophoneEventGroupBits::TASK_RUNNING) { | ||||||
|  |     ESP_LOGD(TAG, "Task is running and reading data"); | ||||||
|  |  | ||||||
|  |     xEventGroupClearBits(this->event_group_, MicrophoneEventGroupBits::TASK_RUNNING); | ||||||
|  |     this->state_ = microphone::STATE_RUNNING; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (event_group_bits & MicrophoneEventGroupBits::TASK_STOPPING) { | ||||||
|  |     ESP_LOGD(TAG, "Task is stopping, attempting to unload the I2S audio driver"); | ||||||
|  |     xEventGroupClearBits(this->event_group_, MicrophoneEventGroupBits::TASK_STOPPING); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if ((event_group_bits & MicrophoneEventGroupBits::TASK_STOPPED)) { | ||||||
|  |     ESP_LOGD(TAG, "Task is finished, freeing resources"); | ||||||
|  |     vTaskDelete(this->task_handle_); | ||||||
|  |     this->task_handle_ = nullptr; | ||||||
|  |     xEventGroupClearBits(this->event_group_, ALL_BITS); | ||||||
|  |     this->state_ = microphone::STATE_STOPPED; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if ((uxSemaphoreGetCount(this->active_listeners_semaphore_) < MAX_LISTENERS) && | ||||||
|  |       (this->state_ == microphone::STATE_STOPPED)) { | ||||||
|  |     this->state_ = microphone::STATE_STARTING; | ||||||
|  |   } | ||||||
|  |   if ((uxSemaphoreGetCount(this->active_listeners_semaphore_) == MAX_LISTENERS) && | ||||||
|  |       (this->state_ == microphone::STATE_RUNNING)) { | ||||||
|  |     this->state_ = microphone::STATE_STOPPING; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   switch (this->state_) { |   switch (this->state_) { | ||||||
|     case microphone::STATE_STOPPED: |  | ||||||
|       break; |  | ||||||
|     case microphone::STATE_STARTING: |     case microphone::STATE_STARTING: | ||||||
|       this->start_(); |       if ((this->task_handle_ == nullptr) && !this->status_has_error()) { | ||||||
|       break; |         xTaskCreate(I2SAudioMicrophone::mic_task, "mic_task", TASK_STACK_SIZE, (void *) this, TASK_PRIORITY, | ||||||
|     case microphone::STATE_RUNNING: |                     &this->task_handle_); | ||||||
|       if (this->data_callbacks_.size() > 0) { |  | ||||||
|         this->read_(); |         if (this->task_handle_ == nullptr) { | ||||||
|  |           this->status_momentary_error("Task failed to start, attempting again in 1 second", 1000); | ||||||
|  |         } | ||||||
|       } |       } | ||||||
|       break; |       break; | ||||||
|  |     case microphone::STATE_RUNNING: | ||||||
|  |       break; | ||||||
|     case microphone::STATE_STOPPING: |     case microphone::STATE_STOPPING: | ||||||
|       this->stop_(); |       xEventGroupSetBits(this->event_group_, MicrophoneEventGroupBits::COMMAND_STOP); | ||||||
|  |       break; | ||||||
|  |     case microphone::STATE_STOPPED: | ||||||
|       break; |       break; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -7,6 +7,9 @@ | |||||||
| #include "esphome/components/microphone/microphone.h" | #include "esphome/components/microphone/microphone.h" | ||||||
| #include "esphome/core/component.h" | #include "esphome/core/component.h" | ||||||
|  |  | ||||||
|  | #include <freertos/event_groups.h> | ||||||
|  | #include <freertos/semphr.h> | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace i2s_audio { | namespace i2s_audio { | ||||||
|  |  | ||||||
| @@ -35,11 +38,18 @@ class I2SAudioMicrophone : public I2SAudioIn, public microphone::Microphone, pub | |||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  protected: |  protected: | ||||||
|   void start_(); |   bool start_driver_(); | ||||||
|   void stop_(); |   void stop_driver_(); | ||||||
|   void read_(); |  | ||||||
|   size_t read_(uint8_t *buf, size_t len, TickType_t ticks_to_wait); |   size_t read_(uint8_t *buf, size_t len, TickType_t ticks_to_wait); | ||||||
|  |  | ||||||
|  |   static void mic_task(void *params); | ||||||
|  |  | ||||||
|  |   SemaphoreHandle_t active_listeners_semaphore_{nullptr}; | ||||||
|  |   EventGroupHandle_t event_group_{nullptr}; | ||||||
|  |  | ||||||
|  |   TaskHandle_t task_handle_{nullptr}; | ||||||
|  |  | ||||||
| #ifdef USE_I2S_LEGACY | #ifdef USE_I2S_LEGACY | ||||||
|   int8_t din_pin_{I2S_PIN_NO_CHANGE}; |   int8_t din_pin_{I2S_PIN_NO_CHANGE}; | ||||||
| #if SOC_I2S_SUPPORTS_ADC | #if SOC_I2S_SUPPORTS_ADC | ||||||
| @@ -51,8 +61,6 @@ class I2SAudioMicrophone : public I2SAudioIn, public microphone::Microphone, pub | |||||||
|   i2s_chan_handle_t rx_handle_; |   i2s_chan_handle_t rx_handle_; | ||||||
| #endif | #endif | ||||||
|   bool pdm_{false}; |   bool pdm_{false}; | ||||||
|  |  | ||||||
|   HighFrequencyLoopRequester high_freq_; |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| }  // namespace i2s_audio | }  // namespace i2s_audio | ||||||
|   | |||||||
| @@ -27,6 +27,7 @@ from .. import ( | |||||||
|     i2s_audio_ns, |     i2s_audio_ns, | ||||||
|     register_i2s_audio_component, |     register_i2s_audio_component, | ||||||
|     use_legacy, |     use_legacy, | ||||||
|  |     validate_mclk_divisible_by_3, | ||||||
| ) | ) | ||||||
|  |  | ||||||
| AUTO_LOAD = ["audio"] | AUTO_LOAD = ["audio"] | ||||||
| @@ -155,6 +156,7 @@ CONFIG_SCHEMA = cv.All( | |||||||
|     _validate_esp32_variant, |     _validate_esp32_variant, | ||||||
|     _set_num_channels_from_config, |     _set_num_channels_from_config, | ||||||
|     _set_stream_limits, |     _set_stream_limits, | ||||||
|  |     validate_mclk_divisible_by_3, | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -545,7 +545,7 @@ esp_err_t I2SAudioSpeaker::start_i2s_driver_(audio::AudioStreamInfo &audio_strea | |||||||
|     .use_apll = this->use_apll_, |     .use_apll = this->use_apll_, | ||||||
|     .tx_desc_auto_clear = true, |     .tx_desc_auto_clear = true, | ||||||
|     .fixed_mclk = I2S_PIN_NO_CHANGE, |     .fixed_mclk = I2S_PIN_NO_CHANGE, | ||||||
|     .mclk_multiple = I2S_MCLK_MULTIPLE_256, |     .mclk_multiple = this->mclk_multiple_, | ||||||
|     .bits_per_chan = this->bits_per_channel_, |     .bits_per_chan = this->bits_per_channel_, | ||||||
| #if SOC_I2S_SUPPORTS_TDM | #if SOC_I2S_SUPPORTS_TDM | ||||||
|     .chan_mask = (i2s_channel_t) (I2S_TDM_ACTIVE_CH0 | I2S_TDM_ACTIVE_CH1), |     .chan_mask = (i2s_channel_t) (I2S_TDM_ACTIVE_CH0 | I2S_TDM_ACTIVE_CH1), | ||||||
| @@ -614,7 +614,7 @@ esp_err_t I2SAudioSpeaker::start_i2s_driver_(audio::AudioStreamInfo &audio_strea | |||||||
|   i2s_std_clk_config_t clk_cfg = { |   i2s_std_clk_config_t clk_cfg = { | ||||||
|       .sample_rate_hz = audio_stream_info.get_sample_rate(), |       .sample_rate_hz = audio_stream_info.get_sample_rate(), | ||||||
|       .clk_src = clk_src, |       .clk_src = clk_src, | ||||||
|       .mclk_multiple = I2S_MCLK_MULTIPLE_256, |       .mclk_multiple = this->mclk_multiple_, | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
|   i2s_slot_mode_t slot_mode = this->slot_mode_; |   i2s_slot_mode_t slot_mode = this->slot_mode_; | ||||||
|   | |||||||
| @@ -9,3 +9,4 @@ microphone: | |||||||
|     i2s_din_pin: ${i2s_din_pin} |     i2s_din_pin: ${i2s_din_pin} | ||||||
|     adc_type: external |     adc_type: external | ||||||
|     pdm: false |     pdm: false | ||||||
|  |     mclk_multiple: 384 | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user