Add more configuration for microphones - i2s/pdm/adc (#4775)

This commit is contained in:
Jesse Hills
2023-05-10 16:37:21 +12:00
committed by GitHub
parent b19c7d462b
commit 39a650ee54
6 changed files with 245 additions and 129 deletions
@@ -2,8 +2,9 @@ import esphome.config_validation as cv
import esphome.codegen as cg
from esphome import pins
from esphome.const import CONF_ID
from esphome.components import microphone
from esphome.const import CONF_ID, CONF_NUMBER
from esphome.components import microphone, esp32
from esphome.components.adc import ESP32_VARIANT_ADC1_PIN_TO_CHANNEL, validate_adc_pin
from .. import (
i2s_audio_ns,
@@ -16,18 +17,59 @@ from .. import (
CODEOWNERS = ["@jesserockz"]
DEPENDENCIES = ["i2s_audio"]
CONF_ADC_PIN = "adc_pin"
CONF_ADC_TYPE = "adc_type"
CONF_PDM = "pdm"
I2SAudioMicrophone = i2s_audio_ns.class_(
"I2SAudioMicrophone", I2SAudioIn, microphone.Microphone, cg.Component
)
CONFIG_SCHEMA = microphone.MICROPHONE_SCHEMA.extend(
INTERNAL_ADC_VARIANTS = [esp32.const.VARIANT_ESP32]
PDM_VARIANTS = [esp32.const.VARIANT_ESP32, esp32.const.VARIANT_ESP32S3]
def validate_esp32_variant(config):
variant = esp32.get_esp32_variant()
if config[CONF_ADC_TYPE] == "external":
if config[CONF_PDM]:
if variant not in PDM_VARIANTS:
raise cv.Invalid(f"{variant} does not support PDM")
return config
if config[CONF_ADC_TYPE] == "internal":
if variant not in INTERNAL_ADC_VARIANTS:
raise cv.Invalid(f"{variant} does not have an internal ADC")
return config
raise NotImplementedError
BASE_SCHEMA = microphone.MICROPHONE_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(I2SAudioMicrophone),
cv.GenerateID(CONF_I2S_AUDIO_ID): cv.use_id(I2SAudioComponent),
cv.Required(CONF_I2S_DIN_PIN): pins.internal_gpio_input_pin_number,
}
).extend(cv.COMPONENT_SCHEMA)
CONFIG_SCHEMA = cv.All(
cv.typed_schema(
{
"internal": BASE_SCHEMA.extend(
{
cv.Required(CONF_ADC_PIN): validate_adc_pin,
}
),
"external": BASE_SCHEMA.extend(
{
cv.Required(CONF_I2S_DIN_PIN): pins.internal_gpio_input_pin_number,
cv.Required(CONF_PDM): cv.boolean,
}
),
},
key=CONF_ADC_TYPE,
),
validate_esp32_variant,
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
@@ -35,6 +77,13 @@ async def to_code(config):
await cg.register_parented(var, config[CONF_I2S_AUDIO_ID])
cg.add(var.set_din_pin(config[CONF_I2S_DIN_PIN]))
if config[CONF_ADC_TYPE] == "internal":
variant = esp32.get_esp32_variant()
pin_num = config[CONF_ADC_PIN][CONF_NUMBER]
channel = ESP32_VARIANT_ADC1_PIN_TO_CHANNEL[variant][pin_num]
cg.add(var.set_adc_channel(channel))
else:
cg.add(var.set_din_pin(config[CONF_I2S_DIN_PIN]))
cg.add(var.set_pdm(config[CONF_PDM]))
await microphone.register_microphone(var, config)
@@ -17,15 +17,36 @@ static const char *const TAG = "i2s_audio.microphone";
void I2SAudioMicrophone::setup() {
ESP_LOGCONFIG(TAG, "Setting up I2S Audio Microphone...");
this->buffer_.resize(BUFFER_SIZE);
#if SOC_I2S_SUPPORTS_ADC
if (this->adc_) {
if (this->parent_->get_port() != I2S_NUM_0) {
ESP_LOGE(TAG, "Internal ADC only works on I2S0!");
this->mark_failed();
return;
}
} else
#endif
if (this->pdm_) {
if (this->parent_->get_port() != I2S_NUM_0) {
ESP_LOGE(TAG, "PDM only works on I2S0!");
this->mark_failed();
return;
}
}
}
void I2SAudioMicrophone::start() { this->state_ = microphone::STATE_STARTING; }
void I2SAudioMicrophone::start() {
if (this->is_failed())
return;
this->state_ = microphone::STATE_STARTING;
}
void I2SAudioMicrophone::start_() {
if (!this->parent_->try_lock()) {
return; // Waiting for another i2s to return lock
}
i2s_driver_config_t config = {
.mode = (i2s_mode_t) (I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_PDM),
.mode = (i2s_mode_t) (I2S_MODE_MASTER | I2S_MODE_RX),
.sample_rate = 16000,
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
.channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT,
@@ -40,18 +61,33 @@ void I2SAudioMicrophone::start_() {
.bits_per_chan = I2S_BITS_PER_CHAN_DEFAULT,
};
i2s_driver_install(this->parent_->get_port(), &config, 0, nullptr);
#if SOC_I2S_SUPPORTS_ADC
if (this->adc_) {
config.mode = (i2s_mode_t) (config.mode | I2S_MODE_ADC_BUILT_IN);
i2s_driver_install(this->parent_->get_port(), &config, 0, nullptr);
i2s_pin_config_t pin_config = this->parent_->get_pin_config();
pin_config.data_in_num = this->din_pin_;
i2s_set_adc_mode(ADC_UNIT_1, this->adc_channel_);
i2s_adc_enable(this->parent_->get_port());
} else {
#endif
if (this->pdm_)
config.mode = (i2s_mode_t) (config.mode | I2S_MODE_PDM);
i2s_set_pin(this->parent_->get_port(), &pin_config);
i2s_driver_install(this->parent_->get_port(), &config, 0, nullptr);
i2s_pin_config_t pin_config = this->parent_->get_pin_config();
pin_config.data_in_num = this->din_pin_;
i2s_set_pin(this->parent_->get_port(), &pin_config);
#if SOC_I2S_SUPPORTS_ADC
}
#endif
this->state_ = microphone::STATE_RUNNING;
this->high_freq_.start();
}
void I2SAudioMicrophone::stop() {
if (this->state_ == microphone::STATE_STOPPED)
if (this->state_ == microphone::STATE_STOPPED || this->is_failed())
return;
this->state_ = microphone::STATE_STOPPING;
}
@@ -18,14 +18,27 @@ class I2SAudioMicrophone : public I2SAudioIn, public microphone::Microphone, pub
void loop() override;
void set_din_pin(uint8_t pin) { this->din_pin_ = pin; }
void set_din_pin(int8_t pin) { this->din_pin_ = pin; }
void set_pdm(bool pdm) { this->pdm_ = pdm; }
#if SOC_I2S_SUPPORTS_ADC
void set_adc_channel(adc1_channel_t channel) {
this->adc_channel_ = channel;
this->adc_ = true;
}
#endif
protected:
void start_();
void stop_();
void read_();
uint8_t din_pin_{0};
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
bool pdm_{false};
std::vector<uint8_t> buffer_;
HighFrequencyLoopRequester high_freq_;