From 4d9c514a1a7046fce53ea1d6f13d2d36f9c136c2 Mon Sep 17 00:00:00 2001 From: Egor Vorontsov Date: Mon, 3 Aug 2026 16:22:03 +0300 Subject: [PATCH] [i2s_audio] Implemented I2S PDM Microphone DSR selection (#17751) Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- esphome/components/i2s_audio/__init__.py | 1 + .../i2s_audio/microphone/__init__.py | 12 +++++++++ .../microphone/i2s_audio_microphone.cpp | 2 +- .../microphone/i2s_audio_microphone.h | 7 ++++++ tests/components/microphone/common-pdm.yaml | 25 +++++++++++++++++++ tests/components/microphone/common.yaml | 10 -------- .../components/microphone/test.esp32-idf.yaml | 8 +++--- .../microphone/test.esp32-s2-idf.yaml | 8 ++++++ .../common/i2s_audio/esp32-s2-idf.yaml | 14 +++++++++++ 9 files changed, 72 insertions(+), 15 deletions(-) create mode 100644 tests/components/microphone/common-pdm.yaml create mode 100644 tests/components/microphone/test.esp32-s2-idf.yaml create mode 100644 tests/test_build_components/common/i2s_audio/esp32-s2-idf.yaml diff --git a/esphome/components/i2s_audio/__init__.py b/esphome/components/i2s_audio/__init__.py index 8e432695a14..4809bf5a929 100644 --- a/esphome/components/i2s_audio/__init__.py +++ b/esphome/components/i2s_audio/__init__.py @@ -29,6 +29,7 @@ DEPENDENCIES = ["esp32"] MULTI_CONF = True CONF_PDM = "pdm" +CONF_PDM_DSR = "pdm_dsr" CONF_ADC_TYPE = "adc_type" CONF_I2S_DOUT_PIN = "i2s_dout_pin" diff --git a/esphome/components/i2s_audio/microphone/__init__.py b/esphome/components/i2s_audio/microphone/__init__.py index 1392d1d4ec1..9c6228087c9 100644 --- a/esphome/components/i2s_audio/microphone/__init__.py +++ b/esphome/components/i2s_audio/microphone/__init__.py @@ -17,6 +17,7 @@ from .. import ( CONF_LEFT, CONF_MONO, CONF_PDM, + CONF_PDM_DSR, CONF_RIGHT, I2SAudioIn, i2s_audio_component_schema, @@ -38,6 +39,12 @@ I2SAudioMicrophone = i2s_audio_ns.class_( INTERNAL_ADC_VARIANTS = [esp32.VARIANT_ESP32] PDM_VARIANTS = [esp32.VARIANT_ESP32, esp32.VARIANT_ESP32S3, esp32.VARIANT_ESP32P4] +i2s_pdm_dsr_t = cg.global_ns.enum("i2s_pdm_dsr_t") +I2S_PDM_DSR = { + 8: i2s_pdm_dsr_t.I2S_PDM_DSR_8S, + 16: i2s_pdm_dsr_t.I2S_PDM_DSR_16S, +} + def _validate_esp32_variant(config): variant = esp32.get_esp32_variant() @@ -111,6 +118,9 @@ CONFIG_SCHEMA = cv.All( { cv.Required(CONF_I2S_DIN_PIN): pins.internal_gpio_input_pin_number, cv.Optional(CONF_PDM, default=False): cv.boolean, + cv.Optional(CONF_PDM_DSR, default=8): cv.enum( + I2S_PDM_DSR, int=True + ), } ), }, @@ -142,5 +152,7 @@ async def to_code(config): cg.add(var.set_din_pin(config[CONF_I2S_DIN_PIN])) cg.add(var.set_pdm(config[CONF_PDM])) + if esp32.get_esp32_variant() in PDM_VARIANTS: + cg.add(var.set_pdm_dsr(config[CONF_PDM_DSR])) cg.add(var.set_correct_dc_offset(config[CONF_CORRECT_DC_OFFSET])) diff --git a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp index 7b074b2e8f6..c577ed092ed 100644 --- a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +++ b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp @@ -128,7 +128,7 @@ bool I2SAudioMicrophone::start_driver_() { .sample_rate_hz = this->sample_rate_, .clk_src = clk_src, .mclk_multiple = this->mclk_multiple_, - .dn_sample_mode = I2S_PDM_DSR_8S, + .dn_sample_mode = this->pdm_dsr_, }; i2s_pdm_rx_slot_config_t slot_cfg = I2S_PDM_RX_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, this->slot_mode_); diff --git a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.h b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.h index 2c6528d8bf4..37895ac4e79 100644 --- a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.h +++ b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.h @@ -29,6 +29,10 @@ class I2SAudioMicrophone final : public I2SAudioIn, public microphone::Microphon void set_pdm(bool pdm) { this->pdm_ = pdm; } +#if SOC_I2S_SUPPORTS_PDM_RX + void set_pdm_dsr(i2s_pdm_dsr_t pdm_dsr) { this->pdm_dsr_ = pdm_dsr; } +#endif + protected: /// @brief Starts the I2S driver. Updates the ``audio_stream_info_`` member variable with the current setttings. /// @return True if succesful, false otherwise @@ -57,6 +61,9 @@ class I2SAudioMicrophone final : public I2SAudioIn, public microphone::Microphon gpio_num_t din_pin_{I2S_GPIO_UNUSED}; i2s_chan_handle_t rx_handle_; bool pdm_{false}; +#if SOC_I2S_SUPPORTS_PDM_RX + i2s_pdm_dsr_t pdm_dsr_{I2S_PDM_DSR_8S}; +#endif bool correct_dc_offset_; bool locked_driver_{false}; diff --git a/tests/components/microphone/common-pdm.yaml b/tests/components/microphone/common-pdm.yaml new file mode 100644 index 00000000000..093dcb24f85 --- /dev/null +++ b/tests/components/microphone/common-pdm.yaml @@ -0,0 +1,25 @@ +microphone: + - platform: i2s_audio + id: mic_id_external + i2s_din_pin: ${i2s_din_pin1} + adc_type: external + pdm: false + mclk_multiple: 384 + correct_dc_offset: true + on_data: + - if: + condition: + - microphone.is_muted: + id: mic_id_external + then: + - microphone.unmute: + id: mic_id_external + else: + - microphone.mute: + id: mic_id_external + - platform: i2s_audio + id: mic_id_pdm + i2s_din_pin: ${i2s_din_pin2} + adc_type: external + pdm: true + pdm_dsr: 16 diff --git a/tests/components/microphone/common.yaml b/tests/components/microphone/common.yaml index 39ab06da616..281bda1ce09 100644 --- a/tests/components/microphone/common.yaml +++ b/tests/components/microphone/common.yaml @@ -1,8 +1,3 @@ -i2s_audio: - i2s_bclk_pin: ${i2s_bclk_pin} - i2s_lrclk_pin: ${i2s_lrclk_pin} - i2s_mclk_pin: ${i2s_mclk_pin} - microphone: - platform: i2s_audio id: mic_id_external @@ -22,8 +17,3 @@ microphone: else: - microphone.mute: id: mic_id_external - - platform: i2s_audio - id: mic_id_pdm - i2s_din_pin: ${i2s_din_pin2} - adc_type: external - pdm: true diff --git a/tests/components/microphone/test.esp32-idf.yaml b/tests/components/microphone/test.esp32-idf.yaml index 2f39263a435..65d7081bcd2 100644 --- a/tests/components/microphone/test.esp32-idf.yaml +++ b/tests/components/microphone/test.esp32-idf.yaml @@ -1,8 +1,8 @@ substitutions: - i2s_bclk_pin: GPIO15 - i2s_lrclk_pin: GPIO4 - i2s_mclk_pin: GPIO5 i2s_din_pin1: GPIO33 i2s_din_pin2: GPIO34 -<<: !include common.yaml +packages: + i2s_audio: !include ../../test_build_components/common/i2s_audio/esp32-idf.yaml + +<<: !include common-pdm.yaml diff --git a/tests/components/microphone/test.esp32-s2-idf.yaml b/tests/components/microphone/test.esp32-s2-idf.yaml new file mode 100644 index 00000000000..47b9283720a --- /dev/null +++ b/tests/components/microphone/test.esp32-s2-idf.yaml @@ -0,0 +1,8 @@ +substitutions: + i2s_din_pin1: GPIO33 + i2s_din_pin2: GPIO34 + +packages: + i2s_audio: !include ../../test_build_components/common/i2s_audio/esp32-s2-idf.yaml + +<<: !include common.yaml diff --git a/tests/test_build_components/common/i2s_audio/esp32-s2-idf.yaml b/tests/test_build_components/common/i2s_audio/esp32-s2-idf.yaml new file mode 100644 index 00000000000..386389ed774 --- /dev/null +++ b/tests/test_build_components/common/i2s_audio/esp32-s2-idf.yaml @@ -0,0 +1,14 @@ +# Common I2S audio bus configuration for ESP32-S2 IDF tests +# Provides a shared i2s_audio bus that speaker/microphone components can use +# Each consumer must give its speaker/microphone a unique data pin + +substitutions: + i2s_bclk_pin: GPIO5 + i2s_lrclk_pin: GPIO4 + i2s_mclk_pin: GPIO15 + +i2s_audio: + - id: i2s_audio_bus + i2s_bclk_pin: ${i2s_bclk_pin} + i2s_lrclk_pin: ${i2s_lrclk_pin} + i2s_mclk_pin: ${i2s_mclk_pin}