mirror of
https://github.com/esphome/esphome.git
synced 2025-07-29 14:46:40 +00:00
commit
16a0f9db97
2
Doxyfile
2
Doxyfile
@ -48,7 +48,7 @@ PROJECT_NAME = ESPHome
|
|||||||
# could be handy for archiving the generated documentation or if some version
|
# could be handy for archiving the generated documentation or if some version
|
||||||
# control system is used.
|
# control system is used.
|
||||||
|
|
||||||
PROJECT_NUMBER = 2025.6.0b2
|
PROJECT_NUMBER = 2025.6.0b3
|
||||||
|
|
||||||
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
||||||
# for a project that appears at the top of each page and should give viewer a
|
# for a project that appears at the top of each page and should give viewer a
|
||||||
|
@ -18,7 +18,7 @@ void I2SAudioComponent::setup() {
|
|||||||
|
|
||||||
static i2s_port_t next_port_num = I2S_NUM_0;
|
static i2s_port_t next_port_num = I2S_NUM_0;
|
||||||
if (next_port_num >= I2S_NUM_MAX) {
|
if (next_port_num >= I2S_NUM_MAX) {
|
||||||
ESP_LOGE(TAG, "Too many I2S Audio components");
|
ESP_LOGE(TAG, "Too many components");
|
||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ void I2SAudioMicrophone::setup() {
|
|||||||
#if SOC_I2S_SUPPORTS_ADC
|
#if SOC_I2S_SUPPORTS_ADC
|
||||||
if (this->adc_) {
|
if (this->adc_) {
|
||||||
if (this->parent_->get_port() != I2S_NUM_0) {
|
if (this->parent_->get_port() != I2S_NUM_0) {
|
||||||
ESP_LOGE(TAG, "Internal ADC only works on I2S0!");
|
ESP_LOGE(TAG, "Internal ADC only works on I2S0");
|
||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -55,7 +55,7 @@ void I2SAudioMicrophone::setup() {
|
|||||||
{
|
{
|
||||||
if (this->pdm_) {
|
if (this->pdm_) {
|
||||||
if (this->parent_->get_port() != I2S_NUM_0) {
|
if (this->parent_->get_port() != I2S_NUM_0) {
|
||||||
ESP_LOGE(TAG, "PDM only works on I2S0!");
|
ESP_LOGE(TAG, "PDM only works on I2S0");
|
||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -64,14 +64,14 @@ void I2SAudioMicrophone::setup() {
|
|||||||
|
|
||||||
this->active_listeners_semaphore_ = xSemaphoreCreateCounting(MAX_LISTENERS, MAX_LISTENERS);
|
this->active_listeners_semaphore_ = xSemaphoreCreateCounting(MAX_LISTENERS, MAX_LISTENERS);
|
||||||
if (this->active_listeners_semaphore_ == nullptr) {
|
if (this->active_listeners_semaphore_ == nullptr) {
|
||||||
ESP_LOGE(TAG, "Failed to create semaphore");
|
ESP_LOGE(TAG, "Creating semaphore failed");
|
||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->event_group_ = xEventGroupCreate();
|
this->event_group_ = xEventGroupCreate();
|
||||||
if (this->event_group_ == nullptr) {
|
if (this->event_group_ == nullptr) {
|
||||||
ESP_LOGE(TAG, "Failed to create event group");
|
ESP_LOGE(TAG, "Creating event group failed");
|
||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -79,6 +79,15 @@ void I2SAudioMicrophone::setup() {
|
|||||||
this->configure_stream_settings_();
|
this->configure_stream_settings_();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void I2SAudioMicrophone::dump_config() {
|
||||||
|
ESP_LOGCONFIG(TAG,
|
||||||
|
"Microphone:\n"
|
||||||
|
" Pin: %d\n"
|
||||||
|
" PDM: %s\n"
|
||||||
|
" DC offset correction: %s",
|
||||||
|
static_cast<int8_t>(this->din_pin_), YESNO(this->pdm_), YESNO(this->correct_dc_offset_));
|
||||||
|
}
|
||||||
|
|
||||||
void I2SAudioMicrophone::configure_stream_settings_() {
|
void I2SAudioMicrophone::configure_stream_settings_() {
|
||||||
uint8_t channel_count = 1;
|
uint8_t channel_count = 1;
|
||||||
#ifdef USE_I2S_LEGACY
|
#ifdef USE_I2S_LEGACY
|
||||||
@ -127,6 +136,7 @@ bool I2SAudioMicrophone::start_driver_() {
|
|||||||
if (!this->parent_->try_lock()) {
|
if (!this->parent_->try_lock()) {
|
||||||
return false; // Waiting for another i2s to return lock
|
return false; // Waiting for another i2s to return lock
|
||||||
}
|
}
|
||||||
|
this->locked_driver_ = true;
|
||||||
esp_err_t err;
|
esp_err_t err;
|
||||||
|
|
||||||
#ifdef USE_I2S_LEGACY
|
#ifdef USE_I2S_LEGACY
|
||||||
@ -151,7 +161,7 @@ bool I2SAudioMicrophone::start_driver_() {
|
|||||||
config.mode = (i2s_mode_t) (config.mode | I2S_MODE_ADC_BUILT_IN);
|
config.mode = (i2s_mode_t) (config.mode | I2S_MODE_ADC_BUILT_IN);
|
||||||
err = i2s_driver_install(this->parent_->get_port(), &config, 0, nullptr);
|
err = i2s_driver_install(this->parent_->get_port(), &config, 0, nullptr);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
ESP_LOGE(TAG, "Error installing I2S driver: %s", esp_err_to_name(err));
|
ESP_LOGE(TAG, "Error installing driver: %s", esp_err_to_name(err));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,7 +184,7 @@ bool I2SAudioMicrophone::start_driver_() {
|
|||||||
|
|
||||||
err = i2s_driver_install(this->parent_->get_port(), &config, 0, nullptr);
|
err = i2s_driver_install(this->parent_->get_port(), &config, 0, nullptr);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
ESP_LOGE(TAG, "Error installing I2S driver: %s", esp_err_to_name(err));
|
ESP_LOGE(TAG, "Error installing driver: %s", esp_err_to_name(err));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,7 +193,7 @@ bool I2SAudioMicrophone::start_driver_() {
|
|||||||
|
|
||||||
err = i2s_set_pin(this->parent_->get_port(), &pin_config);
|
err = i2s_set_pin(this->parent_->get_port(), &pin_config);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
ESP_LOGE(TAG, "Error setting I2S pin: %s", esp_err_to_name(err));
|
ESP_LOGE(TAG, "Error setting pin: %s", esp_err_to_name(err));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -198,7 +208,7 @@ bool I2SAudioMicrophone::start_driver_() {
|
|||||||
/* Allocate a new RX channel and get the handle of this channel */
|
/* Allocate a new RX channel and get the handle of this channel */
|
||||||
err = i2s_new_channel(&chan_cfg, NULL, &this->rx_handle_);
|
err = i2s_new_channel(&chan_cfg, NULL, &this->rx_handle_);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
ESP_LOGE(TAG, "Error creating new I2S channel: %s", esp_err_to_name(err));
|
ESP_LOGE(TAG, "Error creating channel: %s", esp_err_to_name(err));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -270,14 +280,14 @@ bool I2SAudioMicrophone::start_driver_() {
|
|||||||
err = i2s_channel_init_std_mode(this->rx_handle_, &std_cfg);
|
err = i2s_channel_init_std_mode(this->rx_handle_, &std_cfg);
|
||||||
}
|
}
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
ESP_LOGE(TAG, "Error initializing I2S channel: %s", esp_err_to_name(err));
|
ESP_LOGE(TAG, "Error initializing channel: %s", esp_err_to_name(err));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Before reading data, start the RX channel first */
|
/* Before reading data, start the RX channel first */
|
||||||
i2s_channel_enable(this->rx_handle_);
|
i2s_channel_enable(this->rx_handle_);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
ESP_LOGE(TAG, "Error enabling I2S Microphone: %s", esp_err_to_name(err));
|
ESP_LOGE(TAG, "Enabling failed: %s", esp_err_to_name(err));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -304,34 +314,37 @@ void I2SAudioMicrophone::stop_driver_() {
|
|||||||
if (this->adc_) {
|
if (this->adc_) {
|
||||||
err = i2s_adc_disable(this->parent_->get_port());
|
err = i2s_adc_disable(this->parent_->get_port());
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
ESP_LOGW(TAG, "Error disabling ADC - it may not have started: %s", esp_err_to_name(err));
|
ESP_LOGW(TAG, "Error disabling ADC: %s", esp_err_to_name(err));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
err = i2s_stop(this->parent_->get_port());
|
err = i2s_stop(this->parent_->get_port());
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
ESP_LOGW(TAG, "Error stopping I2S microphone - it may not have started: %s", esp_err_to_name(err));
|
ESP_LOGW(TAG, "Error stopping: %s", esp_err_to_name(err));
|
||||||
}
|
}
|
||||||
err = i2s_driver_uninstall(this->parent_->get_port());
|
err = i2s_driver_uninstall(this->parent_->get_port());
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
ESP_LOGW(TAG, "Error uninstalling I2S driver - it may not have started: %s", esp_err_to_name(err));
|
ESP_LOGW(TAG, "Error uninstalling driver: %s", esp_err_to_name(err));
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
if (this->rx_handle_ != nullptr) {
|
if (this->rx_handle_ != nullptr) {
|
||||||
/* Have to stop the channel before deleting it */
|
/* Have to stop the channel before deleting it */
|
||||||
err = i2s_channel_disable(this->rx_handle_);
|
err = i2s_channel_disable(this->rx_handle_);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
ESP_LOGW(TAG, "Error stopping I2S microphone - it may not have started: %s", esp_err_to_name(err));
|
ESP_LOGW(TAG, "Error stopping: %s", esp_err_to_name(err));
|
||||||
}
|
}
|
||||||
/* If the handle is not needed any more, delete it to release the channel resources */
|
/* If the handle is not needed any more, delete it to release the channel resources */
|
||||||
err = i2s_del_channel(this->rx_handle_);
|
err = i2s_del_channel(this->rx_handle_);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
ESP_LOGW(TAG, "Error deleting I2S channel - it may not have started: %s", esp_err_to_name(err));
|
ESP_LOGW(TAG, "Error deleting channel: %s", esp_err_to_name(err));
|
||||||
}
|
}
|
||||||
this->rx_handle_ = nullptr;
|
this->rx_handle_ = nullptr;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
if (this->locked_driver_) {
|
||||||
this->parent_->unlock();
|
this->parent_->unlock();
|
||||||
|
this->locked_driver_ = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void I2SAudioMicrophone::mic_task(void *params) {
|
void I2SAudioMicrophone::mic_task(void *params) {
|
||||||
@ -403,7 +416,7 @@ size_t I2SAudioMicrophone::read_(uint8_t *buf, size_t len, TickType_t ticks_to_w
|
|||||||
// Ignore ESP_ERR_TIMEOUT if ticks_to_wait = 0, as it will read the data on the next call
|
// Ignore ESP_ERR_TIMEOUT if ticks_to_wait = 0, as it will read the data on the next call
|
||||||
if (!this->status_has_warning()) {
|
if (!this->status_has_warning()) {
|
||||||
// Avoid spamming the logs with the error message if its repeated
|
// Avoid spamming the logs with the error message if its repeated
|
||||||
ESP_LOGW(TAG, "Error reading from I2S microphone: %s", esp_err_to_name(err));
|
ESP_LOGW(TAG, "Read error: %s", esp_err_to_name(err));
|
||||||
}
|
}
|
||||||
this->status_set_warning();
|
this->status_set_warning();
|
||||||
return 0;
|
return 0;
|
||||||
@ -431,19 +444,19 @@ void I2SAudioMicrophone::loop() {
|
|||||||
uint32_t event_group_bits = xEventGroupGetBits(this->event_group_);
|
uint32_t event_group_bits = xEventGroupGetBits(this->event_group_);
|
||||||
|
|
||||||
if (event_group_bits & MicrophoneEventGroupBits::TASK_STARTING) {
|
if (event_group_bits & MicrophoneEventGroupBits::TASK_STARTING) {
|
||||||
ESP_LOGD(TAG, "Task started, attempting to allocate buffer");
|
ESP_LOGV(TAG, "Task started, attempting to allocate buffer");
|
||||||
xEventGroupClearBits(this->event_group_, MicrophoneEventGroupBits::TASK_STARTING);
|
xEventGroupClearBits(this->event_group_, MicrophoneEventGroupBits::TASK_STARTING);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event_group_bits & MicrophoneEventGroupBits::TASK_RUNNING) {
|
if (event_group_bits & MicrophoneEventGroupBits::TASK_RUNNING) {
|
||||||
ESP_LOGD(TAG, "Task is running and reading data");
|
ESP_LOGV(TAG, "Task is running and reading data");
|
||||||
|
|
||||||
xEventGroupClearBits(this->event_group_, MicrophoneEventGroupBits::TASK_RUNNING);
|
xEventGroupClearBits(this->event_group_, MicrophoneEventGroupBits::TASK_RUNNING);
|
||||||
this->state_ = microphone::STATE_RUNNING;
|
this->state_ = microphone::STATE_RUNNING;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((event_group_bits & MicrophoneEventGroupBits::TASK_STOPPED)) {
|
if ((event_group_bits & MicrophoneEventGroupBits::TASK_STOPPED)) {
|
||||||
ESP_LOGD(TAG, "Task finished, freeing resources and uninstalling I2S driver");
|
ESP_LOGV(TAG, "Task finished, freeing resources and uninstalling driver");
|
||||||
|
|
||||||
vTaskDelete(this->task_handle_);
|
vTaskDelete(this->task_handle_);
|
||||||
this->task_handle_ = nullptr;
|
this->task_handle_ = nullptr;
|
||||||
@ -473,7 +486,8 @@ void I2SAudioMicrophone::loop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!this->start_driver_()) {
|
if (!this->start_driver_()) {
|
||||||
this->status_momentary_error("I2S driver failed to start, unloading it and attempting again in 1 second", 1000);
|
ESP_LOGE(TAG, "Driver failed to start; retrying in 1 second");
|
||||||
|
this->status_momentary_error("driver_fail", 1000);
|
||||||
this->stop_driver_(); // Stop/frees whatever possibly started
|
this->stop_driver_(); // Stop/frees whatever possibly started
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -483,7 +497,8 @@ void I2SAudioMicrophone::loop() {
|
|||||||
&this->task_handle_);
|
&this->task_handle_);
|
||||||
|
|
||||||
if (this->task_handle_ == nullptr) {
|
if (this->task_handle_ == nullptr) {
|
||||||
this->status_momentary_error("Task failed to start, attempting again in 1 second", 1000);
|
ESP_LOGE(TAG, "Task failed to start, retrying in 1 second");
|
||||||
|
this->status_momentary_error("task_fail", 1000);
|
||||||
this->stop_driver_(); // Stops the driver to return the lock; will be reloaded in next attempt
|
this->stop_driver_(); // Stops the driver to return the lock; will be reloaded in next attempt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ namespace i2s_audio {
|
|||||||
class I2SAudioMicrophone : public I2SAudioIn, public microphone::Microphone, public Component {
|
class I2SAudioMicrophone : public I2SAudioIn, public microphone::Microphone, public Component {
|
||||||
public:
|
public:
|
||||||
void setup() override;
|
void setup() override;
|
||||||
|
void dump_config() override;
|
||||||
void start() override;
|
void start() override;
|
||||||
void stop() override;
|
void stop() override;
|
||||||
|
|
||||||
@ -80,6 +81,7 @@ class I2SAudioMicrophone : public I2SAudioIn, public microphone::Microphone, pub
|
|||||||
bool pdm_{false};
|
bool pdm_{false};
|
||||||
|
|
||||||
bool correct_dc_offset_;
|
bool correct_dc_offset_;
|
||||||
|
bool locked_driver_{false};
|
||||||
int32_t dc_offset_{0};
|
int32_t dc_offset_{0};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -110,29 +110,48 @@ void I2SAudioSpeaker::setup() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void I2SAudioSpeaker::dump_config() {
|
||||||
|
ESP_LOGCONFIG(TAG,
|
||||||
|
"Speaker:\n"
|
||||||
|
" Pin: %d\n"
|
||||||
|
" Buffer duration: %" PRIu32,
|
||||||
|
static_cast<int8_t>(this->dout_pin_), this->buffer_duration_ms_);
|
||||||
|
if (this->timeout_.has_value()) {
|
||||||
|
ESP_LOGCONFIG(TAG, " Timeout: %" PRIu32 " ms", this->timeout_.value());
|
||||||
|
}
|
||||||
|
#ifdef USE_I2S_LEGACY
|
||||||
|
#if SOC_I2S_SUPPORTS_DAC
|
||||||
|
ESP_LOGCONFIG(TAG, " Internal DAC mode: %d", static_cast<int8_t>(this->internal_dac_mode_));
|
||||||
|
#endif
|
||||||
|
ESP_LOGCONFIG(TAG, " Communication format: %d", static_cast<int8_t>(this->i2s_comm_fmt_));
|
||||||
|
#else
|
||||||
|
ESP_LOGCONFIG(TAG, " Communication format: %s", this->i2s_comm_fmt_.c_str());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void I2SAudioSpeaker::loop() {
|
void I2SAudioSpeaker::loop() {
|
||||||
uint32_t event_group_bits = xEventGroupGetBits(this->event_group_);
|
uint32_t event_group_bits = xEventGroupGetBits(this->event_group_);
|
||||||
|
|
||||||
if (event_group_bits & SpeakerEventGroupBits::STATE_STARTING) {
|
if (event_group_bits & SpeakerEventGroupBits::STATE_STARTING) {
|
||||||
ESP_LOGD(TAG, "Starting Speaker");
|
ESP_LOGD(TAG, "Starting");
|
||||||
this->state_ = speaker::STATE_STARTING;
|
this->state_ = speaker::STATE_STARTING;
|
||||||
xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::STATE_STARTING);
|
xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::STATE_STARTING);
|
||||||
}
|
}
|
||||||
if (event_group_bits & SpeakerEventGroupBits::STATE_RUNNING) {
|
if (event_group_bits & SpeakerEventGroupBits::STATE_RUNNING) {
|
||||||
ESP_LOGD(TAG, "Started Speaker");
|
ESP_LOGD(TAG, "Started");
|
||||||
this->state_ = speaker::STATE_RUNNING;
|
this->state_ = speaker::STATE_RUNNING;
|
||||||
xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::STATE_RUNNING);
|
xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::STATE_RUNNING);
|
||||||
this->status_clear_warning();
|
this->status_clear_warning();
|
||||||
this->status_clear_error();
|
this->status_clear_error();
|
||||||
}
|
}
|
||||||
if (event_group_bits & SpeakerEventGroupBits::STATE_STOPPING) {
|
if (event_group_bits & SpeakerEventGroupBits::STATE_STOPPING) {
|
||||||
ESP_LOGD(TAG, "Stopping Speaker");
|
ESP_LOGD(TAG, "Stopping");
|
||||||
this->state_ = speaker::STATE_STOPPING;
|
this->state_ = speaker::STATE_STOPPING;
|
||||||
xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::STATE_STOPPING);
|
xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::STATE_STOPPING);
|
||||||
}
|
}
|
||||||
if (event_group_bits & SpeakerEventGroupBits::STATE_STOPPED) {
|
if (event_group_bits & SpeakerEventGroupBits::STATE_STOPPED) {
|
||||||
if (!this->task_created_) {
|
if (!this->task_created_) {
|
||||||
ESP_LOGD(TAG, "Stopped Speaker");
|
ESP_LOGD(TAG, "Stopped");
|
||||||
this->state_ = speaker::STATE_STOPPED;
|
this->state_ = speaker::STATE_STOPPED;
|
||||||
xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::ALL_BITS);
|
xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::ALL_BITS);
|
||||||
this->speaker_task_handle_ = nullptr;
|
this->speaker_task_handle_ = nullptr;
|
||||||
@ -140,20 +159,19 @@ void I2SAudioSpeaker::loop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (event_group_bits & SpeakerEventGroupBits::ERR_TASK_FAILED_TO_START) {
|
if (event_group_bits & SpeakerEventGroupBits::ERR_TASK_FAILED_TO_START) {
|
||||||
this->status_set_error("Failed to start speaker task");
|
this->status_set_error("Failed to start task");
|
||||||
xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::ERR_TASK_FAILED_TO_START);
|
xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::ERR_TASK_FAILED_TO_START);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event_group_bits & SpeakerEventGroupBits::ALL_ERR_ESP_BITS) {
|
if (event_group_bits & SpeakerEventGroupBits::ALL_ERR_ESP_BITS) {
|
||||||
uint32_t error_bits = event_group_bits & SpeakerEventGroupBits::ALL_ERR_ESP_BITS;
|
uint32_t error_bits = event_group_bits & SpeakerEventGroupBits::ALL_ERR_ESP_BITS;
|
||||||
ESP_LOGW(TAG, "Error writing to I2S: %s", esp_err_to_name(err_bit_to_esp_err(error_bits)));
|
ESP_LOGW(TAG, "Writing failed: %s", esp_err_to_name(err_bit_to_esp_err(error_bits)));
|
||||||
this->status_set_warning();
|
this->status_set_warning();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event_group_bits & SpeakerEventGroupBits::ERR_ESP_NOT_SUPPORTED) {
|
if (event_group_bits & SpeakerEventGroupBits::ERR_ESP_NOT_SUPPORTED) {
|
||||||
this->status_set_error("Failed to adjust I2S bus to match the incoming audio");
|
this->status_set_error("Failed to adjust bus to match incoming audio");
|
||||||
ESP_LOGE(TAG,
|
ESP_LOGE(TAG, "Incompatible audio format: sample rate = %" PRIu32 ", channels = %u, bits per sample = %u",
|
||||||
"Incompatible audio format: sample rate = %" PRIu32 ", channels = %" PRIu8 ", bits per sample = %" PRIu8,
|
|
||||||
this->audio_stream_info_.get_sample_rate(), this->audio_stream_info_.get_channels(),
|
this->audio_stream_info_.get_sample_rate(), this->audio_stream_info_.get_channels(),
|
||||||
this->audio_stream_info_.get_bits_per_sample());
|
this->audio_stream_info_.get_bits_per_sample());
|
||||||
}
|
}
|
||||||
@ -202,7 +220,7 @@ void I2SAudioSpeaker::set_mute_state(bool mute_state) {
|
|||||||
|
|
||||||
size_t I2SAudioSpeaker::play(const uint8_t *data, size_t length, TickType_t ticks_to_wait) {
|
size_t I2SAudioSpeaker::play(const uint8_t *data, size_t length, TickType_t ticks_to_wait) {
|
||||||
if (this->is_failed()) {
|
if (this->is_failed()) {
|
||||||
ESP_LOGE(TAG, "Cannot play audio, speaker failed to setup");
|
ESP_LOGE(TAG, "Setup failed; cannot play audio");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (this->state_ != speaker::STATE_RUNNING && this->state_ != speaker::STATE_STARTING) {
|
if (this->state_ != speaker::STATE_RUNNING && this->state_ != speaker::STATE_STARTING) {
|
||||||
|
@ -24,6 +24,7 @@ class I2SAudioSpeaker : public I2SAudioOut, public speaker::Speaker, public Comp
|
|||||||
float get_setup_priority() const override { return esphome::setup_priority::PROCESSOR; }
|
float get_setup_priority() const override { return esphome::setup_priority::PROCESSOR; }
|
||||||
|
|
||||||
void setup() override;
|
void setup() override;
|
||||||
|
void dump_config() override;
|
||||||
void loop() override;
|
void loop() override;
|
||||||
|
|
||||||
void set_buffer_duration(uint32_t buffer_duration_ms) { this->buffer_duration_ms_ = buffer_duration_ms; }
|
void set_buffer_duration(uint32_t buffer_duration_ms) { this->buffer_duration_ms_ = buffer_duration_ms; }
|
||||||
|
@ -17,7 +17,7 @@ namespace light {
|
|||||||
|
|
||||||
class LightOutput;
|
class LightOutput;
|
||||||
|
|
||||||
enum LightRestoreMode {
|
enum LightRestoreMode : uint8_t {
|
||||||
LIGHT_RESTORE_DEFAULT_OFF,
|
LIGHT_RESTORE_DEFAULT_OFF,
|
||||||
LIGHT_RESTORE_DEFAULT_ON,
|
LIGHT_RESTORE_DEFAULT_ON,
|
||||||
LIGHT_ALWAYS_OFF,
|
LIGHT_ALWAYS_OFF,
|
||||||
@ -212,12 +212,18 @@ class LightState : public EntityBase, public Component {
|
|||||||
|
|
||||||
/// Store the output to allow effects to have more access.
|
/// Store the output to allow effects to have more access.
|
||||||
LightOutput *output_;
|
LightOutput *output_;
|
||||||
/// Value for storing the index of the currently active effect. 0 if no effect is active
|
|
||||||
uint32_t active_effect_index_{};
|
|
||||||
/// The currently active transformer for this light (transition/flash).
|
/// The currently active transformer for this light (transition/flash).
|
||||||
std::unique_ptr<LightTransformer> transformer_{nullptr};
|
std::unique_ptr<LightTransformer> transformer_{nullptr};
|
||||||
/// Whether the light value should be written in the next cycle.
|
/// List of effects for this light.
|
||||||
bool next_write_{true};
|
std::vector<LightEffect *> effects_;
|
||||||
|
/// Value for storing the index of the currently active effect. 0 if no effect is active
|
||||||
|
uint32_t active_effect_index_{};
|
||||||
|
/// Default transition length for all transitions in ms.
|
||||||
|
uint32_t default_transition_length_{};
|
||||||
|
/// Transition length to use for flash transitions.
|
||||||
|
uint32_t flash_transition_length_{};
|
||||||
|
/// Gamma correction factor for the light.
|
||||||
|
float gamma_correct_{};
|
||||||
|
|
||||||
/// Object used to store the persisted values of the light.
|
/// Object used to store the persisted values of the light.
|
||||||
ESPPreferenceObject rtc_;
|
ESPPreferenceObject rtc_;
|
||||||
@ -236,19 +242,13 @@ class LightState : public EntityBase, public Component {
|
|||||||
*/
|
*/
|
||||||
CallbackManager<void()> target_state_reached_callback_{};
|
CallbackManager<void()> target_state_reached_callback_{};
|
||||||
|
|
||||||
/// Default transition length for all transitions in ms.
|
|
||||||
uint32_t default_transition_length_{};
|
|
||||||
/// Transition length to use for flash transitions.
|
|
||||||
uint32_t flash_transition_length_{};
|
|
||||||
/// Gamma correction factor for the light.
|
|
||||||
float gamma_correct_{};
|
|
||||||
/// Restore mode of the light.
|
|
||||||
LightRestoreMode restore_mode_;
|
|
||||||
/// Initial state of the light.
|
/// Initial state of the light.
|
||||||
optional<LightStateRTCState> initial_state_{};
|
optional<LightStateRTCState> initial_state_{};
|
||||||
/// List of effects for this light.
|
|
||||||
std::vector<LightEffect *> effects_;
|
|
||||||
|
|
||||||
|
/// Restore mode of the light.
|
||||||
|
LightRestoreMode restore_mode_;
|
||||||
|
/// Whether the light value should be written in the next cycle.
|
||||||
|
bool next_write_{true};
|
||||||
// for effects, true if a transformer (transition) is active.
|
// for effects, true if a transformer (transition) is active.
|
||||||
bool is_transformer_active_ = false;
|
bool is_transformer_active_ = false;
|
||||||
};
|
};
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace spi {
|
namespace spi {
|
||||||
|
|
||||||
#ifdef USE_ARDUINO
|
#ifdef USE_ARDUINO
|
||||||
|
|
||||||
static const char *const TAG = "spi-esp-arduino";
|
static const char *const TAG = "spi-esp-arduino";
|
||||||
@ -38,17 +37,31 @@ class SPIDelegateHw : public SPIDelegate {
|
|||||||
|
|
||||||
void write16(uint16_t data) override { this->channel_->transfer16(data); }
|
void write16(uint16_t data) override { this->channel_->transfer16(data); }
|
||||||
|
|
||||||
#ifdef USE_RP2040
|
|
||||||
void write_array(const uint8_t *ptr, size_t length) override {
|
void write_array(const uint8_t *ptr, size_t length) override {
|
||||||
// avoid overwriting the supplied buffer
|
if (length == 1) {
|
||||||
uint8_t *rxbuf = new uint8_t[length]; // NOLINT(cppcoreguidelines-owning-memory)
|
this->channel_->transfer(*ptr);
|
||||||
memcpy(rxbuf, ptr, length);
|
return;
|
||||||
this->channel_->transfer((void *) rxbuf, length);
|
}
|
||||||
delete[] rxbuf; // NOLINT(cppcoreguidelines-owning-memory)
|
#ifdef USE_RP2040
|
||||||
|
// avoid overwriting the supplied buffer. Use vector for automatic deallocation
|
||||||
|
auto rxbuf = std::vector<uint8_t>(length);
|
||||||
|
memcpy(rxbuf.data(), ptr, length);
|
||||||
|
this->channel_->transfer((void *) rxbuf.data(), length);
|
||||||
|
#elif defined(USE_ESP8266)
|
||||||
|
// ESP8266 SPI library requires the pointer to be word aligned, but the data may not be
|
||||||
|
// so we need to copy the data to a temporary buffer
|
||||||
|
if (reinterpret_cast<uintptr_t>(ptr) & 0x3) {
|
||||||
|
ESP_LOGVV(TAG, "SPI write buffer not word aligned, copying to temporary buffer");
|
||||||
|
auto txbuf = std::vector<uint8_t>(length);
|
||||||
|
memcpy(txbuf.data(), ptr, length);
|
||||||
|
this->channel_->writeBytes(txbuf.data(), length);
|
||||||
|
} else {
|
||||||
|
this->channel_->writeBytes(ptr, length);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
void write_array(const uint8_t *ptr, size_t length) override { this->channel_->writeBytes(ptr, length); }
|
this->channel_->writeBytes(ptr, length);
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void read_array(uint8_t *ptr, size_t length) override { this->channel_->transfer(ptr, length); }
|
void read_array(uint8_t *ptr, size_t length) override { this->channel_->transfer(ptr, length); }
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ const int RESTORE_MODE_PERSISTENT_MASK = 0x02;
|
|||||||
const int RESTORE_MODE_INVERTED_MASK = 0x04;
|
const int RESTORE_MODE_INVERTED_MASK = 0x04;
|
||||||
const int RESTORE_MODE_DISABLED_MASK = 0x08;
|
const int RESTORE_MODE_DISABLED_MASK = 0x08;
|
||||||
|
|
||||||
enum SwitchRestoreMode {
|
enum SwitchRestoreMode : uint8_t {
|
||||||
SWITCH_ALWAYS_OFF = !RESTORE_MODE_ON_MASK,
|
SWITCH_ALWAYS_OFF = !RESTORE_MODE_ON_MASK,
|
||||||
SWITCH_ALWAYS_ON = RESTORE_MODE_ON_MASK,
|
SWITCH_ALWAYS_ON = RESTORE_MODE_ON_MASK,
|
||||||
SWITCH_RESTORE_DEFAULT_OFF = RESTORE_MODE_PERSISTENT_MASK,
|
SWITCH_RESTORE_DEFAULT_OFF = RESTORE_MODE_PERSISTENT_MASK,
|
||||||
@ -49,12 +49,12 @@ class Switch : public EntityBase, public EntityBase_DeviceClass {
|
|||||||
*/
|
*/
|
||||||
void publish_state(bool state);
|
void publish_state(bool state);
|
||||||
|
|
||||||
/// The current reported state of the binary sensor.
|
|
||||||
bool state;
|
|
||||||
|
|
||||||
/// Indicates whether or not state is to be retrieved from flash and how
|
/// Indicates whether or not state is to be retrieved from flash and how
|
||||||
SwitchRestoreMode restore_mode{SWITCH_RESTORE_DEFAULT_OFF};
|
SwitchRestoreMode restore_mode{SWITCH_RESTORE_DEFAULT_OFF};
|
||||||
|
|
||||||
|
/// The current reported state of the binary sensor.
|
||||||
|
bool state;
|
||||||
|
|
||||||
/** Turn this switch on. This is called by the front-end.
|
/** Turn this switch on. This is called by the front-end.
|
||||||
*
|
*
|
||||||
* For implementing switches, please override write_state.
|
* For implementing switches, please override write_state.
|
||||||
@ -123,10 +123,16 @@ class Switch : public EntityBase, public EntityBase_DeviceClass {
|
|||||||
*/
|
*/
|
||||||
virtual void write_state(bool state) = 0;
|
virtual void write_state(bool state) = 0;
|
||||||
|
|
||||||
CallbackManager<void(bool)> state_callback_{};
|
// Pointer first (4 bytes)
|
||||||
bool inverted_{false};
|
|
||||||
Deduplicator<bool> publish_dedup_;
|
|
||||||
ESPPreferenceObject rtc_;
|
ESPPreferenceObject rtc_;
|
||||||
|
|
||||||
|
// CallbackManager (12 bytes on 32-bit - contains vector)
|
||||||
|
CallbackManager<void(bool)> state_callback_{};
|
||||||
|
|
||||||
|
// Small types grouped together
|
||||||
|
Deduplicator<bool> publish_dedup_; // 2 bytes (bool has_value_ + bool last_value_)
|
||||||
|
bool inverted_{false}; // 1 byte
|
||||||
|
// Total: 3 bytes, 1 byte padding
|
||||||
};
|
};
|
||||||
|
|
||||||
#define LOG_SWITCH(prefix, type, obj) log_switch((TAG), (prefix), LOG_STR_LITERAL(type), (obj))
|
#define LOG_SWITCH(prefix, type, obj) log_switch((TAG), (prefix), LOG_STR_LITERAL(type), (obj))
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
"""Constants used by esphome."""
|
"""Constants used by esphome."""
|
||||||
|
|
||||||
__version__ = "2025.6.0b2"
|
__version__ = "2025.6.0b3"
|
||||||
|
|
||||||
ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_"
|
ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_"
|
||||||
VALID_SUBSTITUTIONS_CHARACTERS = (
|
VALID_SUBSTITUTIONS_CHARACTERS = (
|
||||||
|
Loading…
x
Reference in New Issue
Block a user