ESPHome  2024.11.0
i2s_audio_microphone.cpp
Go to the documentation of this file.
1 #include "i2s_audio_microphone.h"
2 
3 #ifdef USE_ESP32
4 
5 #include <driver/i2s.h>
6 
7 #include "esphome/core/hal.h"
8 #include "esphome/core/log.h"
9 
10 namespace esphome {
11 namespace i2s_audio {
12 
13 static const size_t BUFFER_SIZE = 512;
14 
15 static const char *const TAG = "i2s_audio.microphone";
16 
18  ESP_LOGCONFIG(TAG, "Setting up I2S Audio Microphone...");
19 #if SOC_I2S_SUPPORTS_ADC
20  if (this->adc_) {
21  if (this->parent_->get_port() != I2S_NUM_0) {
22  ESP_LOGE(TAG, "Internal ADC only works on I2S0!");
23  this->mark_failed();
24  return;
25  }
26  } else
27 #endif
28  if (this->pdm_) {
29  if (this->parent_->get_port() != I2S_NUM_0) {
30  ESP_LOGE(TAG, "PDM only works on I2S0!");
31  this->mark_failed();
32  return;
33  }
34  }
35 }
36 
38  if (this->is_failed())
39  return;
40  if (this->state_ == microphone::STATE_RUNNING)
41  return; // Already running
43 }
45  if (!this->parent_->try_lock()) {
46  return; // Waiting for another i2s to return lock
47  }
48  i2s_driver_config_t config = {
49  .mode = (i2s_mode_t) (this->i2s_mode_ | I2S_MODE_RX),
50  .sample_rate = this->sample_rate_,
51  .bits_per_sample = this->bits_per_sample_,
52  .channel_format = this->channel_,
53  .communication_format = I2S_COMM_FORMAT_STAND_I2S,
54  .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
55  .dma_buf_count = 4,
56  .dma_buf_len = 256,
57  .use_apll = this->use_apll_,
58  .tx_desc_auto_clear = false,
59  .fixed_mclk = 0,
60  .mclk_multiple = I2S_MCLK_MULTIPLE_256,
61  .bits_per_chan = this->bits_per_channel_,
62  };
63 
64  esp_err_t err;
65 
66 #if SOC_I2S_SUPPORTS_ADC
67  if (this->adc_) {
68  config.mode = (i2s_mode_t) (config.mode | I2S_MODE_ADC_BUILT_IN);
69  err = i2s_driver_install(this->parent_->get_port(), &config, 0, nullptr);
70  if (err != ESP_OK) {
71  ESP_LOGW(TAG, "Error installing I2S driver: %s", esp_err_to_name(err));
72  this->status_set_error();
73  return;
74  }
75 
76  err = i2s_set_adc_mode(ADC_UNIT_1, this->adc_channel_);
77  if (err != ESP_OK) {
78  ESP_LOGW(TAG, "Error setting ADC mode: %s", esp_err_to_name(err));
79  this->status_set_error();
80  return;
81  }
82  err = i2s_adc_enable(this->parent_->get_port());
83  if (err != ESP_OK) {
84  ESP_LOGW(TAG, "Error enabling ADC: %s", esp_err_to_name(err));
85  this->status_set_error();
86  return;
87  }
88 
89  } else
90 #endif
91  {
92  if (this->pdm_)
93  config.mode = (i2s_mode_t) (config.mode | I2S_MODE_PDM);
94 
95  err = i2s_driver_install(this->parent_->get_port(), &config, 0, nullptr);
96  if (err != ESP_OK) {
97  ESP_LOGW(TAG, "Error installing I2S driver: %s", esp_err_to_name(err));
98  this->status_set_error();
99  return;
100  }
101 
102  i2s_pin_config_t pin_config = this->parent_->get_pin_config();
103  pin_config.data_in_num = this->din_pin_;
104 
105  err = i2s_set_pin(this->parent_->get_port(), &pin_config);
106  if (err != ESP_OK) {
107  ESP_LOGW(TAG, "Error setting I2S pin: %s", esp_err_to_name(err));
108  this->status_set_error();
109  return;
110  }
111  }
113  this->high_freq_.start();
114  this->status_clear_error();
115 }
116 
118  if (this->state_ == microphone::STATE_STOPPED || this->is_failed())
119  return;
120  if (this->state_ == microphone::STATE_STARTING) {
122  return;
123  }
125 }
126 
128  esp_err_t err;
129 #if SOC_I2S_SUPPORTS_ADC
130  if (this->adc_) {
131  err = i2s_adc_disable(this->parent_->get_port());
132  if (err != ESP_OK) {
133  ESP_LOGW(TAG, "Error disabling ADC: %s", esp_err_to_name(err));
134  this->status_set_error();
135  return;
136  }
137  }
138 #endif
139  err = i2s_stop(this->parent_->get_port());
140  if (err != ESP_OK) {
141  ESP_LOGW(TAG, "Error stopping I2S microphone: %s", esp_err_to_name(err));
142  this->status_set_error();
143  return;
144  }
145  err = i2s_driver_uninstall(this->parent_->get_port());
146  if (err != ESP_OK) {
147  ESP_LOGW(TAG, "Error uninstalling I2S driver: %s", esp_err_to_name(err));
148  this->status_set_error();
149  return;
150  }
151  this->parent_->unlock();
153  this->high_freq_.stop();
154  this->status_clear_error();
155 }
156 
157 size_t I2SAudioMicrophone::read(int16_t *buf, size_t len) {
158  size_t bytes_read = 0;
159  esp_err_t err = i2s_read(this->parent_->get_port(), buf, len, &bytes_read, (100 / portTICK_PERIOD_MS));
160  if (err != ESP_OK) {
161  ESP_LOGW(TAG, "Error reading from I2S microphone: %s", esp_err_to_name(err));
162  this->status_set_warning();
163  return 0;
164  }
165  if (bytes_read == 0) {
166  this->status_set_warning();
167  return 0;
168  }
169  this->status_clear_warning();
170  // ESP-IDF I2S implementation right-extends 8-bit data to 16 bits,
171  // and 24-bit data to 32 bits.
172  switch (this->bits_per_sample_) {
173  case I2S_BITS_PER_SAMPLE_8BIT:
174  case I2S_BITS_PER_SAMPLE_16BIT:
175  return bytes_read;
176  case I2S_BITS_PER_SAMPLE_24BIT:
177  case I2S_BITS_PER_SAMPLE_32BIT: {
178  size_t samples_read = bytes_read / sizeof(int32_t);
179  for (size_t i = 0; i < samples_read; i++) {
180  int32_t temp = reinterpret_cast<int32_t *>(buf)[i] >> 14;
181  buf[i] = clamp<int16_t>(temp, INT16_MIN, INT16_MAX);
182  }
183  return samples_read * sizeof(int16_t);
184  }
185  default:
186  ESP_LOGE(TAG, "Unsupported bits per sample: %d", this->bits_per_sample_);
187  return 0;
188  }
189 }
190 
192  std::vector<int16_t> samples;
193  samples.resize(BUFFER_SIZE);
194  size_t bytes_read = this->read(samples.data(), BUFFER_SIZE / sizeof(int16_t));
195  samples.resize(bytes_read / sizeof(int16_t));
196  this->data_callbacks_.call(samples);
197 }
198 
200  switch (this->state_) {
202  break;
204  this->start_();
205  break;
207  if (this->data_callbacks_.size() > 0) {
208  this->read_();
209  }
210  break;
212  this->stop_();
213  break;
214  }
215 }
216 
217 } // namespace i2s_audio
218 } // namespace esphome
219 
220 #endif // USE_ESP32
void status_set_warning(const char *message="unspecified")
Definition: component.cpp:151
bool is_failed() const
Definition: component.cpp:143
CallbackManager< void(const std::vector< int16_t > &)> data_callbacks_
Definition: microphone.h:34
void status_set_error(const char *message="unspecified")
Definition: component.cpp:159
i2s_channel_fmt_t channel_
Definition: i2s_audio.h:25
void start()
Start running the loop continuously.
Definition: helpers.cpp:653
void status_clear_warning()
Definition: component.cpp:166
void stop()
Stop running the loop continuously.
Definition: helpers.cpp:659
size_t read(int16_t *buf, size_t len) override
void status_clear_error()
Definition: component.cpp:172
i2s_bits_per_sample_t bits_per_sample_
Definition: i2s_audio.h:27
std::string size_t len
Definition: helpers.h:293
virtual void mark_failed()
Mark this component as failed.
Definition: component.cpp:118
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
i2s_bits_per_chan_t bits_per_channel_
Definition: i2s_audio.h:28