1
0
mirror of https://github.com/esphome/esphome.git synced 2025-03-14 06:38:17 +00:00

Add basic support for new esp-idf 5.x.x i2s driver.

This commit is contained in:
raul 2025-02-02 18:35:16 +01:00
parent 02bf33c548
commit be0f033541
7 changed files with 327 additions and 39 deletions

View File

@ -8,7 +8,15 @@ from esphome.components.esp32.const import (
VARIANT_ESP32S3,
)
import esphome.config_validation as cv
from esphome.const import CONF_BITS_PER_SAMPLE, CONF_CHANNEL, CONF_ID, CONF_SAMPLE_RATE
from esphome.const import (
CONF_BITS_PER_SAMPLE,
CONF_CHANNEL,
CONF_ID,
CONF_SAMPLE_RATE,
KEY_CORE,
KEY_FRAMEWORK_VERSION,
)
from esphome.core import CORE
from esphome.cpp_generator import MockObjClass
import esphome.final_validate as fv
@ -35,6 +43,9 @@ CONF_MONO = "mono"
CONF_LEFT = "left"
CONF_RIGHT = "right"
CONF_STEREO = "stereo"
CONF_BOTH = "both"
CONF_USE_LEGACY = "use_legacy"
i2s_audio_ns = cg.esphome_ns.namespace("i2s_audio")
I2SAudioComponent = i2s_audio_ns.class_("I2SAudioComponent", cg.Component)
@ -50,6 +61,12 @@ I2S_MODE_OPTIONS = {
CONF_SECONDARY: i2s_mode_t.I2S_MODE_SLAVE, # NOLINT
}
i2s_role_t = cg.global_ns.enum("i2s_role_t")
I2S_ROLE_OPTIONS = {
CONF_PRIMARY: i2s_role_t.I2S_ROLE_MASTER, # NOLINT
CONF_SECONDARY: i2s_role_t.I2S_ROLE_SLAVE, # NOLINT
}
# https://github.com/espressif/esp-idf/blob/master/components/soc/{variant}/include/soc/soc_caps.h
I2S_PORTS = {
VARIANT_ESP32: 2,
@ -60,10 +77,23 @@ I2S_PORTS = {
i2s_channel_fmt_t = cg.global_ns.enum("i2s_channel_fmt_t")
I2S_CHANNELS = {
CONF_MONO: i2s_channel_fmt_t.I2S_CHANNEL_FMT_ALL_LEFT,
CONF_LEFT: i2s_channel_fmt_t.I2S_CHANNEL_FMT_ONLY_LEFT,
CONF_RIGHT: i2s_channel_fmt_t.I2S_CHANNEL_FMT_ONLY_RIGHT,
CONF_STEREO: i2s_channel_fmt_t.I2S_CHANNEL_FMT_RIGHT_LEFT,
CONF_MONO: i2s_channel_fmt_t.I2S_CHANNEL_FMT_ALL_LEFT, # left data to both channels
CONF_LEFT: i2s_channel_fmt_t.I2S_CHANNEL_FMT_ONLY_LEFT, # mono data
CONF_RIGHT: i2s_channel_fmt_t.I2S_CHANNEL_FMT_ONLY_RIGHT, # mono data
CONF_STEREO: i2s_channel_fmt_t.I2S_CHANNEL_FMT_RIGHT_LEFT, # stereo data to both channels
}
i2s_slot_mode_t = cg.global_ns.enum("i2s_slot_mode_t")
I2S_SLOT_MODE = {
CONF_MONO: i2s_slot_mode_t.I2S_SLOT_MODE_MONO,
CONF_STEREO: i2s_slot_mode_t.I2S_SLOT_MODE_STEREO,
}
i2s_std_slot_mask_t = cg.global_ns.enum("i2s_std_slot_mask_t")
I2S_STD_SLOT_MASK = {
CONF_LEFT: i2s_std_slot_mask_t.I2S_STD_SLOT_LEFT,
CONF_RIGHT: i2s_std_slot_mask_t.I2S_STD_SLOT_RIGHT,
CONF_BOTH: i2s_std_slot_mask_t.I2S_STD_SLOT_BOTH,
}
i2s_bits_per_sample_t = cg.global_ns.enum("i2s_bits_per_sample_t")
@ -74,6 +104,14 @@ I2S_BITS_PER_SAMPLE = {
32: i2s_bits_per_sample_t.I2S_BITS_PER_SAMPLE_32BIT,
}
i2s_data_bit_width_t = cg.global_ns.enum("i2s_data_bit_width_t")
I2S_DATA_BIT_WIDTH = {
8: i2s_data_bit_width_t.I2S_DATA_BIT_WIDTH_8BIT,
16: i2s_data_bit_width_t.I2S_DATA_BIT_WIDTH_16BIT,
24: i2s_data_bit_width_t.I2S_DATA_BIT_WIDTH_24BIT,
32: i2s_data_bit_width_t.I2S_DATA_BIT_WIDTH_32BIT,
}
i2s_bits_per_chan_t = cg.global_ns.enum("i2s_bits_per_chan_t")
I2S_BITS_PER_CHANNEL = {
"default": i2s_bits_per_chan_t.I2S_BITS_PER_CHAN_DEFAULT,
@ -83,8 +121,19 @@ I2S_BITS_PER_CHANNEL = {
32: i2s_bits_per_chan_t.I2S_BITS_PER_CHAN_32BIT,
}
i2s_slot_bit_width_t = cg.global_ns.enum("i2s_slot_bit_width_t")
I2S_SLOT_BIT_WIDTH = {
"default": i2s_slot_bit_width_t.I2S_SLOT_BIT_WIDTH_AUTO,
8: i2s_slot_bit_width_t.I2S_SLOT_BIT_WIDTH_8BIT,
16: i2s_slot_bit_width_t.I2S_SLOT_BIT_WIDTH_16BIT,
24: i2s_slot_bit_width_t.I2S_SLOT_BIT_WIDTH_24BIT,
32: i2s_slot_bit_width_t.I2S_SLOT_BIT_WIDTH_32BIT,
}
_validate_bits = cv.float_with_unit("bits", "bit")
_use_legacy_driver = None
def i2s_audio_component_schema(
class_: MockObjClass,
@ -97,20 +146,22 @@ def i2s_audio_component_schema(
{
cv.GenerateID(): cv.declare_id(class_),
cv.GenerateID(CONF_I2S_AUDIO_ID): cv.use_id(I2SAudioComponent),
cv.Optional(CONF_CHANNEL, default=default_channel): cv.enum(I2S_CHANNELS),
cv.Optional(CONF_CHANNEL, default=default_channel): cv.one_of(
*I2S_CHANNELS
),
cv.Optional(CONF_SAMPLE_RATE, default=default_sample_rate): cv.int_range(
min=1
),
cv.Optional(CONF_BITS_PER_SAMPLE, default=default_bits_per_sample): cv.All(
_validate_bits, cv.enum(I2S_BITS_PER_SAMPLE)
_validate_bits, cv.one_of(*I2S_BITS_PER_SAMPLE)
),
cv.Optional(CONF_I2S_MODE, default=CONF_PRIMARY): cv.enum(
I2S_MODE_OPTIONS, lower=True
cv.Optional(CONF_I2S_MODE, default=CONF_PRIMARY): cv.one_of(
*I2S_MODE_OPTIONS, lower=True
),
cv.Optional(CONF_USE_APLL, default=False): cv.boolean,
cv.Optional(CONF_BITS_PER_CHANNEL, default="default"): cv.All(
cv.Any(cv.float_with_unit("bits", "bit"), "default"),
cv.enum(I2S_BITS_PER_CHANNEL),
cv.one_of(*I2S_BITS_PER_CHANNEL),
),
}
)
@ -118,22 +169,64 @@ def i2s_audio_component_schema(
async def register_i2s_audio_component(var, config):
await cg.register_parented(var, config[CONF_I2S_AUDIO_ID])
cg.add(var.set_i2s_mode(config[CONF_I2S_MODE]))
cg.add(var.set_channel(config[CONF_CHANNEL]))
if use_legacy():
print("Use legacy")
cg.add(var.set_i2s_mode(I2S_MODE_OPTIONS[config[CONF_I2S_MODE]]))
cg.add(var.set_channel(I2S_CHANNELS[config[CONF_CHANNEL]]))
cg.add(
var.set_bits_per_sample(I2S_BITS_PER_SAMPLE[config[CONF_BITS_PER_SAMPLE]])
)
cg.add(
var.set_bits_per_channel(
I2S_BITS_PER_CHANNEL[config[CONF_BITS_PER_CHANNEL]]
)
)
cg.add(var.set_use_apll(config[CONF_USE_APLL]))
else:
cg.add(var.set_i2s_role(I2S_ROLE_OPTIONS[config[CONF_I2S_MODE]]))
slot_mode = config[CONF_CHANNEL]
if slot_mode != CONF_STEREO:
slot_mode = CONF_MONO
slot_mask = config[CONF_CHANNEL]
if slot_mask not in [CONF_LEFT, CONF_RIGHT]:
slot_mask = CONF_BOTH
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_data_bit_width(I2S_DATA_BIT_WIDTH[config[CONF_BITS_PER_SAMPLE]]))
cg.add(
var.set_slot_bit_width(I2S_SLOT_BIT_WIDTH[config[CONF_BITS_PER_CHANNEL]])
)
# cg.add(var.set_use_apll(config[CONF_USE_APLL]))
cg.add_define("USE_I2S_APLL")
cg.add(var.set_sample_rate(config[CONF_SAMPLE_RATE]))
cg.add(var.set_bits_per_sample(config[CONF_BITS_PER_SAMPLE]))
cg.add(var.set_bits_per_channel(config[CONF_BITS_PER_CHANNEL]))
cg.add(var.set_use_apll(config[CONF_USE_APLL]))
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(I2SAudioComponent),
cv.Required(CONF_I2S_LRCLK_PIN): pins.internal_gpio_output_pin_number,
cv.Optional(CONF_I2S_BCLK_PIN): pins.internal_gpio_output_pin_number,
cv.Optional(CONF_I2S_MCLK_PIN): pins.internal_gpio_output_pin_number,
}
def validate_use_legacy(value):
global _use_legacy_driver # noqa: PLW0603
if CONF_USE_LEGACY in value:
if (_use_legacy_driver is not None) and (
_use_legacy_driver != value[CONF_USE_LEGACY]
):
raise cv.Invalid(
f"All i2s_audio components must set {CONF_USE_LEGACY} to the same value."
)
if (not value[CONF_USE_LEGACY]) and (CORE.using_arduino):
raise cv.Invalid("Arduino supports only the legacy i2s driver.")
_use_legacy_driver = value[CONF_USE_LEGACY]
return value
CONFIG_SCHEMA = cv.All(
cv.Schema(
{
cv.GenerateID(): cv.declare_id(I2SAudioComponent),
cv.Required(CONF_I2S_LRCLK_PIN): pins.internal_gpio_output_pin_number,
cv.Optional(CONF_I2S_BCLK_PIN): pins.internal_gpio_output_pin_number,
cv.Optional(CONF_I2S_MCLK_PIN): pins.internal_gpio_output_pin_number,
cv.Optional(CONF_USE_LEGACY): cv.boolean,
},
),
validate_use_legacy,
)
@ -148,12 +241,22 @@ def _final_validate(_):
)
def use_legacy():
framework_version = CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION]
if CORE.using_esp_idf and framework_version >= cv.Version(5, 0, 0):
if not _use_legacy_driver:
return False
return True
FINAL_VALIDATE_SCHEMA = _final_validate
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
if use_legacy():
cg.add_define("USE_I2S_LEGACY")
cg.add(var.set_lrclk_pin(config[CONF_I2S_LRCLK_PIN]))
if CONF_I2S_BCLK_PIN in config:

View File

@ -2,9 +2,14 @@
#ifdef USE_ESP32
#include <driver/i2s.h>
#include "esphome/core/component.h"
#include "esphome/core/helpers.h"
#include "esphome/core/defines.h"
#ifdef USE_I2S_LEGACY
#include <driver/i2s.h>
#else
#include <driver/i2s_std.h>
#endif
namespace esphome {
namespace i2s_audio {
@ -13,19 +18,35 @@ class I2SAudioComponent;
class I2SAudioBase : public Parented<I2SAudioComponent> {
public:
#ifdef USE_I2S_LEGACY
void set_i2s_mode(i2s_mode_t mode) { this->i2s_mode_ = mode; }
void set_channel(i2s_channel_fmt_t channel) { this->channel_ = channel; }
void set_sample_rate(uint32_t sample_rate) { this->sample_rate_ = sample_rate; }
void set_bits_per_sample(i2s_bits_per_sample_t bits_per_sample) { this->bits_per_sample_ = bits_per_sample; }
void set_bits_per_channel(i2s_bits_per_chan_t bits_per_channel) { this->bits_per_channel_ = bits_per_channel; }
#else
void set_i2s_role(i2s_role_t role) { this->i2s_role_ = role; }
void set_slot_mode(i2s_slot_mode_t slot_mode) { this->slot_mode_ = slot_mode; }
void set_std_slot_mask(i2s_std_slot_mask_t std_slot_mask) { this->std_slot_mask_ = std_slot_mask; }
void set_data_bit_width(i2s_data_bit_width_t data_bit_width) { this->data_bit_width_ = data_bit_width; }
void set_slot_bit_width(i2s_slot_bit_width_t slot_bit_width) { this->slot_bit_width_ = slot_bit_width; }
#endif
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; }
protected:
#ifdef USE_I2S_LEGACY
i2s_mode_t i2s_mode_{};
i2s_channel_fmt_t channel_;
uint32_t sample_rate_;
i2s_bits_per_sample_t bits_per_sample_;
i2s_bits_per_chan_t bits_per_channel_;
#else
i2s_role_t i2s_role_{};
i2s_slot_mode_t slot_mode_;
i2s_std_slot_mask_t std_slot_mask_;
i2s_data_bit_width_t data_bit_width_;
i2s_slot_bit_width_t slot_bit_width_;
#endif
uint32_t sample_rate_;
bool use_apll_;
};
@ -37,6 +58,7 @@ class I2SAudioComponent : public Component {
public:
void setup() override;
#ifdef USE_I2S_LEGACY
i2s_pin_config_t get_pin_config() const {
return {
.mck_io_num = this->mclk_pin_,
@ -46,6 +68,20 @@ class I2SAudioComponent : public Component {
.data_in_num = I2S_PIN_NO_CHANGE,
};
}
#else
i2s_std_gpio_config_t get_pin_config() const {
return {.mclk = (gpio_num_t) this->mclk_pin_,
.bclk = (gpio_num_t) this->bclk_pin_,
.ws = (gpio_num_t) this->lrclk_pin_,
.dout = I2S_GPIO_UNUSED, // add local ports
.din = I2S_GPIO_UNUSED,
.invert_flags = {
.mclk_inv = false,
.bclk_inv = false,
.ws_inv = false,
}};
}
#endif
void set_mclk_pin(int pin) { this->mclk_pin_ = pin; }
void set_bclk_pin(int pin) { this->bclk_pin_ = pin; }
@ -62,9 +98,13 @@ class I2SAudioComponent : public Component {
I2SAudioIn *audio_in_{nullptr};
I2SAudioOut *audio_out_{nullptr};
#ifdef USE_I2S_LEGACY
int mclk_pin_{I2S_PIN_NO_CHANGE};
int bclk_pin_{I2S_PIN_NO_CHANGE};
#else
int mclk_pin_{I2S_GPIO_UNUSED};
int bclk_pin_{I2S_GPIO_UNUSED};
#endif
int lrclk_pin_;
i2s_port_t port_{};
};

View File

@ -1,21 +1,20 @@
import esphome.codegen as cg
from esphome.components import media_player, esp32
import esphome.config_validation as cv
from esphome import pins
import esphome.codegen as cg
from esphome.components import esp32, media_player
import esphome.config_validation as cv
from esphome.const import CONF_ID, CONF_MODE
from .. import (
i2s_audio_ns,
I2SAudioComponent,
I2SAudioOut,
CONF_I2S_AUDIO_ID,
CONF_I2S_DOUT_PIN,
CONF_LEFT,
CONF_RIGHT,
CONF_MONO,
CONF_RIGHT,
CONF_STEREO,
I2SAudioComponent,
I2SAudioOut,
i2s_audio_ns,
use_legacy,
)
CODEOWNERS = ["@jesserockz"]
@ -89,6 +88,14 @@ CONFIG_SCHEMA = cv.All(
)
def _final_validate(_):
if not use_legacy():
raise cv.Invalid("I2S media player is only compatible with legacy i2s driver.")
FINAL_VALIDATE_SCHEMA = _final_validate
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)

View File

@ -12,6 +12,7 @@ from .. import (
i2s_audio_component_schema,
i2s_audio_ns,
register_i2s_audio_component,
use_legacy,
)
CODEOWNERS = ["@jesserockz"]
@ -74,6 +75,17 @@ CONFIG_SCHEMA = cv.All(
)
def _final_validate(config):
if not use_legacy():
if config[CONF_ADC_TYPE] == "internal":
raise cv.Invalid("Internal DAC is only compatible with legacy i2s driver.")
if config.get(CONF_PDM):
raise cv.Invalid("PDM mode is only compatible with legacy i2s driver.")
FINAL_VALIDATE_SCHEMA = _final_validate
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)

View File

@ -2,7 +2,11 @@
#ifdef USE_ESP32
#ifdef USE_I2S_LEGACY
#include <driver/i2s.h>
#else
#include <driver/i2s_std.h>
#endif
#include "esphome/core/hal.h"
#include "esphome/core/log.h"
@ -16,6 +20,7 @@ static const char *const TAG = "i2s_audio.microphone";
void I2SAudioMicrophone::setup() {
ESP_LOGCONFIG(TAG, "Setting up I2S Audio Microphone...");
#ifdef USE_I2S_LEGACY
#if SOC_I2S_SUPPORTS_ADC
if (this->adc_) {
if (this->parent_->get_port() != I2S_NUM_0) {
@ -24,6 +29,7 @@ void I2SAudioMicrophone::setup() {
return;
}
} else
#endif
#endif
{
if (this->pdm_) {
@ -47,6 +53,9 @@ void I2SAudioMicrophone::start_() {
if (!this->parent_->try_lock()) {
return; // Waiting for another i2s to return lock
}
esp_err_t err;
#ifdef USE_I2S_LEGACY
i2s_driver_config_t config = {
.mode = (i2s_mode_t) (this->i2s_mode_ | I2S_MODE_RX),
.sample_rate = this->sample_rate_,
@ -63,8 +72,6 @@ void I2SAudioMicrophone::start_() {
.bits_per_chan = this->bits_per_channel_,
};
esp_err_t err;
#if SOC_I2S_SUPPORTS_ADC
if (this->adc_) {
config.mode = (i2s_mode_t) (config.mode | I2S_MODE_ADC_BUILT_IN);
@ -111,6 +118,64 @@ void I2SAudioMicrophone::start_() {
return;
}
}
#else
i2s_chan_config_t chan_cfg = {
.id = this->parent_->get_port(),
.role = this->i2s_role_,
.dma_desc_num = 4,
.dma_frame_num = 256,
.auto_clear = false,
};
/* Allocate a new RX channel and get the handle of this channel */
err = i2s_new_channel(&chan_cfg, NULL, &this->rx_handle);
if (err != ESP_OK) {
ESP_LOGW(TAG, "Error creating new I2S channel: %s", esp_err_to_name(err));
this->status_set_error();
return;
}
/* Setting the configurations, the slot configuration and clock configuration can be generated by the macros
* These two helper macros are defined in 'i2s_std.h' which can only be used in STD mode.
* They can help to specify the slot and clock configurations for initialization or updating */
#ifdef USE_I2S_APLL
i2s_clock_src_t clk_src = I2S_CLK_SRC_APLL;
#else
i2s_clock_src_t clk_src = I2S_CLK_SRC_DEFAULT;
#endif
i2s_std_clk_config_t clk_cfg = {
.sample_rate_hz = this->sample_rate_,
.clk_src = clk_src,
.mclk_multiple = I2S_MCLK_MULTIPLE_256,
};
i2s_std_slot_config_t std_slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(this->data_bit_width_, this->slot_mode_);
std_slot_cfg.slot_bit_width = this->slot_bit_width_;
std_slot_cfg.slot_mask = this->std_slot_mask_;
i2s_std_gpio_config_t pin_config = this->parent_->get_pin_config();
pin_config.din = this->din_pin_;
i2s_std_config_t std_cfg = {
.clk_cfg = clk_cfg,
.slot_cfg = std_slot_cfg,
.gpio_cfg = pin_config,
};
/* Initialize the channel */
err = i2s_channel_init_std_mode(this->rx_handle, &std_cfg);
if (err != ESP_OK) {
ESP_LOGW(TAG, "Error initializing I2S channel: %s", esp_err_to_name(err));
this->status_set_error();
return;
}
/* Before reading data, start the RX channel first */
i2s_channel_enable(this->rx_handle);
if (err != ESP_OK) {
ESP_LOGW(TAG, "Error enabling I2S Microphone: %s", esp_err_to_name(err));
this->status_set_error();
return;
}
#endif
this->state_ = microphone::STATE_RUNNING;
this->high_freq_.start();
this->status_clear_error();
@ -128,6 +193,7 @@ void I2SAudioMicrophone::stop() {
void I2SAudioMicrophone::stop_() {
esp_err_t err;
#ifdef USE_I2S_LEGACY
#if SOC_I2S_SUPPORTS_ADC
if (this->adc_) {
err = i2s_adc_disable(this->parent_->get_port());
@ -150,6 +216,22 @@ void I2SAudioMicrophone::stop_() {
this->status_set_error();
return;
}
#else
/* Have to stop the channel before deleting it */
err = i2s_channel_disable(this->rx_handle);
if (err != ESP_OK) {
ESP_LOGW(TAG, "Error stopping I2S microphone: %s", esp_err_to_name(err));
this->status_set_error();
return;
}
/* If the handle is not needed any more, delete it to release the channel resources */
err = i2s_del_channel(this->rx_handle);
if (err != ESP_OK) {
ESP_LOGW(TAG, "Error deleting I2S channel: %s", esp_err_to_name(err));
this->status_set_error();
return;
}
#endif
this->parent_->unlock();
this->state_ = microphone::STATE_STOPPED;
this->high_freq_.stop();
@ -158,7 +240,11 @@ void I2SAudioMicrophone::stop_() {
size_t I2SAudioMicrophone::read(int16_t *buf, size_t len) {
size_t bytes_read = 0;
#ifdef USE_I2S_LEGACY
esp_err_t err = i2s_read(this->parent_->get_port(), buf, len, &bytes_read, (100 / portTICK_PERIOD_MS));
#else
esp_err_t err = i2s_channel_read(this->rx_handle, buf, len, &bytes_read, (100 / portTICK_PERIOD_MS));
#endif
if (err != ESP_OK) {
ESP_LOGW(TAG, "Error reading from I2S microphone: %s", esp_err_to_name(err));
this->status_set_warning();
@ -171,6 +257,7 @@ size_t I2SAudioMicrophone::read(int16_t *buf, size_t len) {
this->status_clear_warning();
// ESP-IDF I2S implementation right-extends 8-bit data to 16 bits,
// and 24-bit data to 32 bits.
#ifdef USE_I2S_LEGACY
switch (this->bits_per_sample_) {
case I2S_BITS_PER_SAMPLE_8BIT:
case I2S_BITS_PER_SAMPLE_16BIT:
@ -188,6 +275,25 @@ size_t I2SAudioMicrophone::read(int16_t *buf, size_t len) {
ESP_LOGE(TAG, "Unsupported bits per sample: %d", this->bits_per_sample_);
return 0;
}
#else
switch (this->data_bit_width_) {
case I2S_DATA_BIT_WIDTH_8BIT:
case I2S_DATA_BIT_WIDTH_16BIT:
return bytes_read;
case I2S_DATA_BIT_WIDTH_24BIT:
case I2S_DATA_BIT_WIDTH_32BIT: {
size_t samples_read = bytes_read / sizeof(int32_t);
for (size_t i = 0; i < samples_read; i++) {
int32_t temp = reinterpret_cast<int32_t *>(buf)[i] >> 14;
buf[i] = clamp<int16_t>(temp, INT16_MIN, INT16_MAX);
}
return samples_read * sizeof(int16_t);
}
default:
ESP_LOGE(TAG, "Unsupported data bit width: %d", this->data_bit_width_);
return 0;
}
#endif
}
void I2SAudioMicrophone::read_() {

View File

@ -17,17 +17,23 @@ class I2SAudioMicrophone : public I2SAudioIn, public microphone::Microphone, pub
void stop() override;
void loop() override;
#ifdef USE_I2S_LEGACY
void set_din_pin(int8_t pin) { this->din_pin_ = pin; }
#else
void set_din_pin(int8_t pin) { this->din_pin_ = (gpio_num_t) pin; }
#endif
void set_pdm(bool pdm) { this->pdm_ = pdm; }
size_t read(int16_t *buf, size_t len) override;
#ifdef USE_I2S_LEGACY
#if SOC_I2S_SUPPORTS_ADC
void set_adc_channel(adc1_channel_t channel) {
this->adc_channel_ = channel;
this->adc_ = true;
}
#endif
#endif
protected:
@ -35,10 +41,15 @@ class I2SAudioMicrophone : public I2SAudioIn, public microphone::Microphone, pub
void stop_();
void read_();
#ifdef USE_I2S_LEGACY
int8_t din_pin_{I2S_PIN_NO_CHANGE};
#if SOC_I2S_SUPPORTS_ADC
adc1_channel_t adc_channel_{ADC1_CHANNEL_MAX};
bool adc_{false};
#endif
#else
gpio_num_t din_pin_{I2S_GPIO_UNUSED};
i2s_chan_handle_t rx_handle;
#endif
bool pdm_{false};

View File

@ -26,6 +26,7 @@ from .. import (
i2s_audio_component_schema,
i2s_audio_ns,
register_i2s_audio_component,
use_legacy,
)
AUTO_LOAD = ["audio"]
@ -157,6 +158,14 @@ CONFIG_SCHEMA = cv.All(
)
def _final_validate(_):
if not use_legacy():
raise cv.Invalid("I2S speaker is only compatible with legacy i2s driver.")
FINAL_VALIDATE_SCHEMA = _final_validate
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)