Merge branch 'devirtualize-ota-backend' into integration

This commit is contained in:
J. Nick Koston
2026-03-04 09:51:43 -10:00
22 changed files with 96 additions and 280 deletions
+12 -2
View File
@@ -2,7 +2,7 @@
#include "esphome/core/defines.h"
#ifdef USE_OTA
#include "esphome/components/ota/ota_backend.h"
#include "esphome/components/ota/ota_backend_factory.h"
#include "esphome/components/socket/socket.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
@@ -86,7 +86,17 @@ class ESPHomeOTAComponent final : public ota::OTAComponent {
socket::ListenSocket *server_{nullptr};
std::unique_ptr<socket::Socket> client_;
std::unique_ptr<ota::OTABackend> backend_;
#ifdef USE_ESP8266
std::unique_ptr<ota::ESP8266OTABackend> backend_;
#elif defined(USE_ESP32)
std::unique_ptr<ota::IDFOTABackend> backend_;
#elif defined(USE_RP2040)
std::unique_ptr<ota::ArduinoRP2040OTABackend> backend_;
#elif defined(USE_LIBRETINY)
std::unique_ptr<ota::ArduinoLibreTinyOTABackend> backend_;
#elif defined(USE_HOST)
std::unique_ptr<ota::HostOTABackend> backend_;
#endif
uint32_t client_connect_time_{0};
uint16_t port_;
@@ -8,10 +8,6 @@
#include "esphome/components/md5/md5.h"
#include "esphome/components/watchdog/watchdog.h"
#include "esphome/components/ota/ota_backend.h"
#include "esphome/components/ota/ota_backend_esp8266.h"
#include "esphome/components/ota/ota_backend_arduino_rp2040.h"
#include "esphome/components/ota/ota_backend_esp_idf.h"
namespace esphome {
namespace http_request {
@@ -69,7 +65,7 @@ void OtaHttpRequestComponent::flash() {
}
}
void OtaHttpRequestComponent::cleanup_(std::unique_ptr<ota::OTABackend> backend,
void OtaHttpRequestComponent::cleanup_(decltype(ota::make_ota_backend()) backend,
const std::shared_ptr<HttpContainer> &container) {
if (this->update_started_) {
ESP_LOGV(TAG, "Aborting OTA backend");
@@ -1,6 +1,6 @@
#pragma once
#include "esphome/components/ota/ota_backend.h"
#include "esphome/components/ota/ota_backend_factory.h"
#include "esphome/core/component.h"
#include "esphome/core/defines.h"
#include "esphome/core/helpers.h"
@@ -39,7 +39,7 @@ class OtaHttpRequestComponent final : public ota::OTAComponent, public Parented<
void flash();
protected:
void cleanup_(std::unique_ptr<ota::OTABackend> backend, const std::shared_ptr<HttpContainer> &container);
void cleanup_(decltype(ota::make_ota_backend()) backend, const std::shared_ptr<HttpContainer> &container);
uint8_t do_ota_();
std::string get_url_with_auth_(const std::string &url);
bool http_get_md5_();
@@ -438,24 +438,14 @@ void MixerSpeaker::loop() {
// Handle pending start request
if (event_group_bits & MIXER_TASK_COMMAND_START) {
// Only start the task if it's fully stopped and cleaned up
if (!this->status_has_error() && (this->task_handle_ == nullptr) && (this->task_stack_buffer_ == nullptr)) {
esp_err_t err = this->start_task_();
switch (err) {
case ESP_OK:
xEventGroupClearBits(this->event_group_, MIXER_TASK_COMMAND_START);
break;
case ESP_ERR_NO_MEM:
ESP_LOGE(TAG, "Failed to start; retrying in 1 second");
this->status_momentary_error("memory-failure", 1000);
return;
case ESP_ERR_INVALID_STATE:
ESP_LOGE(TAG, "Failed to start; retrying in 1 second");
this->status_momentary_error("task-failure", 1000);
return;
default:
ESP_LOGE(TAG, "Failed to start; retrying in 1 second");
this->status_momentary_error("failure", 1000);
return;
if (!this->status_has_error() && !this->task_.is_created()) {
if (this->task_.create(audio_mixer_task, "mixer", TASK_STACK_SIZE, (void *) this, MIXER_TASK_PRIORITY,
this->task_stack_in_psram_)) {
xEventGroupClearBits(this->event_group_, MIXER_TASK_COMMAND_START);
} else {
ESP_LOGE(TAG, "Failed to start; retrying in 1 second");
this->status_momentary_error("failure", 1000);
return;
}
}
}
@@ -478,13 +468,12 @@ void MixerSpeaker::loop() {
xEventGroupClearBits(this->event_group_, MIXER_TASK_STATE_STOPPING);
}
if (event_group_bits & MIXER_TASK_STATE_STOPPED) {
if (this->delete_task_() == ESP_OK) {
ESP_LOGD(TAG, "Stopped");
xEventGroupClearBits(this->event_group_, MIXER_TASK_ALL_BITS);
}
this->task_.deallocate();
ESP_LOGD(TAG, "Stopped");
xEventGroupClearBits(this->event_group_, MIXER_TASK_ALL_BITS);
}
if (this->task_handle_ != nullptr) {
if (this->task_.is_created()) {
// If the mixer task is running, check if all source speakers are stopped
bool all_stopped = true;
@@ -497,7 +486,7 @@ void MixerSpeaker::loop() {
// Send stop command signal to the mixer task since no source speakers are active
xEventGroupSetBits(this->event_group_, MIXER_TASK_COMMAND_STOP);
}
} else if (this->task_stack_buffer_ == nullptr) {
} else {
// Task is fully stopped and cleaned up, check if we can disable loop
event_group_bits = xEventGroupGetBits(this->event_group_);
if (event_group_bits == 0) {
@@ -538,60 +527,6 @@ esp_err_t MixerSpeaker::start(audio::AudioStreamInfo &stream_info) {
return ESP_OK;
}
esp_err_t MixerSpeaker::start_task_() {
if (this->task_stack_buffer_ == nullptr) {
if (this->task_stack_in_psram_) {
RAMAllocator<StackType_t> stack_allocator(RAMAllocator<StackType_t>::ALLOC_EXTERNAL);
this->task_stack_buffer_ = stack_allocator.allocate(TASK_STACK_SIZE);
} else {
RAMAllocator<StackType_t> stack_allocator(RAMAllocator<StackType_t>::ALLOC_INTERNAL);
this->task_stack_buffer_ = stack_allocator.allocate(TASK_STACK_SIZE);
}
}
if (this->task_stack_buffer_ == nullptr) {
return ESP_ERR_NO_MEM;
}
if (this->task_handle_ == nullptr) {
this->task_handle_ = xTaskCreateStatic(audio_mixer_task, "mixer", TASK_STACK_SIZE, (void *) this,
MIXER_TASK_PRIORITY, this->task_stack_buffer_, &this->task_stack_);
}
if (this->task_handle_ == nullptr) {
return ESP_ERR_INVALID_STATE;
}
return ESP_OK;
}
esp_err_t MixerSpeaker::delete_task_() {
if (this->task_handle_ != nullptr) {
// Delete the task
vTaskDelete(this->task_handle_);
this->task_handle_ = nullptr;
}
if ((this->task_handle_ == nullptr) && (this->task_stack_buffer_ != nullptr)) {
// Deallocate the task stack buffer
if (this->task_stack_in_psram_) {
RAMAllocator<StackType_t> stack_allocator(RAMAllocator<StackType_t>::ALLOC_EXTERNAL);
stack_allocator.deallocate(this->task_stack_buffer_, TASK_STACK_SIZE);
} else {
RAMAllocator<StackType_t> stack_allocator(RAMAllocator<StackType_t>::ALLOC_INTERNAL);
stack_allocator.deallocate(this->task_stack_buffer_, TASK_STACK_SIZE);
}
this->task_stack_buffer_ = nullptr;
}
if ((this->task_handle_ != nullptr) || (this->task_stack_buffer_ != nullptr)) {
return ESP_ERR_INVALID_STATE;
}
return ESP_OK;
}
void MixerSpeaker::copy_frames(const int16_t *input_buffer, audio::AudioStreamInfo input_stream_info,
int16_t *output_buffer, audio::AudioStreamInfo output_stream_info,
uint32_t frames_to_transfer) {
@@ -8,8 +8,8 @@
#include "esphome/core/component.h"
#include "esphome/core/helpers.h"
#include "esphome/core/static_task.h"
#include <freertos/FreeRTOS.h>
#include <freertos/event_groups.h>
#include <atomic>
@@ -143,8 +143,6 @@ class MixerSpeaker : public Component {
/// @param stream_info The calling source speaker's audio stream information
/// @return ESP_ERR_NOT_SUPPORTED if the incoming stream is incompatible due to unsupported bits per sample
/// ESP_ERR_INVALID_ARG if the incoming stream is incompatible to be mixed with the other input audio stream
/// ESP_ERR_NO_MEM if there isn't enough memory for the task's stack
/// ESP_ERR_INVALID_STATE if the task fails to start
/// ESP_OK if the incoming stream is compatible and the mixer task starts
esp_err_t start(audio::AudioStreamInfo &stream_info);
@@ -188,16 +186,6 @@ class MixerSpeaker : public Component {
static void audio_mixer_task(void *params);
/// @brief Starts the mixer task after allocating memory for the task stack.
/// @return ESP_ERR_NO_MEM if there isn't enough memory for the task's stack
/// ESP_ERR_INVALID_STATE if the task didn't start
/// ESP_OK if successful
esp_err_t start_task_();
/// @brief If the task is stopped, it sets the task handle to the nullptr and deallocates its stack
/// @return ESP_OK if the task was stopped, ESP_ERR_INVALID_STATE otherwise.
esp_err_t delete_task_();
EventGroupHandle_t event_group_{nullptr};
FixedVector<SourceSpeaker *> source_speakers_;
@@ -207,9 +195,7 @@ class MixerSpeaker : public Component {
bool queue_mode_;
bool task_stack_in_psram_{false};
TaskHandle_t task_handle_{nullptr};
StaticTask_t task_stack_;
StackType_t *task_stack_buffer_{nullptr};
StaticTask task_;
optional<audio::AudioStreamInfo> audio_stream_info_;
-2
View File
@@ -130,7 +130,5 @@ OTAGlobalCallback *get_global_ota_callback();
// - notify_state_deferred_() when in separate task (e.g., web_server OTA)
// This ensures proper listener execution in all contexts.
#endif
std::unique_ptr<ota::OTABackend> make_ota_backend();
} // namespace ota
} // namespace esphome
@@ -12,7 +12,7 @@ namespace ota {
static const char *const TAG = "ota.arduino_libretiny";
std::unique_ptr<ota::OTABackend> make_ota_backend() { return make_unique<ota::ArduinoLibreTinyOTABackend>(); }
std::unique_ptr<ArduinoLibreTinyOTABackend> make_ota_backend() { return make_unique<ArduinoLibreTinyOTABackend>(); }
OTAResponseTypes ArduinoLibreTinyOTABackend::begin(size_t image_size) {
// Handle UPDATE_SIZE_UNKNOWN (0) which is used by web server OTA
@@ -20,6 +20,8 @@ class ArduinoLibreTinyOTABackend final : public OTABackend {
bool md5_set_{false};
};
std::unique_ptr<ArduinoLibreTinyOTABackend> make_ota_backend();
} // namespace ota
} // namespace esphome
@@ -14,7 +14,7 @@ namespace ota {
static const char *const TAG = "ota.arduino_rp2040";
std::unique_ptr<ota::OTABackend> make_ota_backend() { return make_unique<ota::ArduinoRP2040OTABackend>(); }
std::unique_ptr<ArduinoRP2040OTABackend> make_ota_backend() { return make_unique<ArduinoRP2040OTABackend>(); }
OTAResponseTypes ArduinoRP2040OTABackend::begin(size_t image_size) {
// OTA size of 0 is not currently handled, but
@@ -22,6 +22,8 @@ class ArduinoRP2040OTABackend final : public OTABackend {
bool md5_set_{false};
};
std::unique_ptr<ArduinoRP2040OTABackend> make_ota_backend();
} // namespace ota
} // namespace esphome
@@ -48,7 +48,7 @@ namespace esphome::ota {
static const char *const TAG = "ota.esp8266";
std::unique_ptr<ota::OTABackend> make_ota_backend() { return make_unique<ota::ESP8266OTABackend>(); }
std::unique_ptr<ESP8266OTABackend> make_ota_backend() { return make_unique<ESP8266OTABackend>(); }
OTAResponseTypes ESP8266OTABackend::begin(size_t image_size) {
// Handle UPDATE_SIZE_UNKNOWN (0) by calculating available space
@@ -54,5 +54,7 @@ class ESP8266OTABackend final : public OTABackend {
bool md5_set_{false};
};
std::unique_ptr<ESP8266OTABackend> make_ota_backend();
} // namespace esphome::ota
#endif // USE_ESP8266
@@ -11,7 +11,7 @@
namespace esphome {
namespace ota {
std::unique_ptr<ota::OTABackend> make_ota_backend() { return make_unique<ota::IDFOTABackend>(); }
std::unique_ptr<IDFOTABackend> make_ota_backend() { return make_unique<IDFOTABackend>(); }
OTAResponseTypes IDFOTABackend::begin(size_t image_size) {
#ifdef USE_OTA_ROLLBACK
@@ -27,6 +27,8 @@ class IDFOTABackend final : public OTABackend {
bool md5_set_{false};
};
std::unique_ptr<IDFOTABackend> make_ota_backend();
} // namespace ota
} // namespace esphome
#endif // USE_ESP32
@@ -0,0 +1,15 @@
#pragma once
#include "ota_backend.h"
#ifdef USE_ESP8266
#include "ota_backend_esp8266.h"
#elif defined(USE_ESP32)
#include "ota_backend_esp_idf.h"
#elif defined(USE_RP2040)
#include "ota_backend_arduino_rp2040.h"
#elif defined(USE_LIBRETINY)
#include "ota_backend_arduino_libretiny.h"
#elif defined(USE_HOST)
#include "ota_backend_host.h"
#endif
+1 -1
View File
@@ -8,7 +8,7 @@ namespace esphome::ota {
// Stub implementation - OTA is not supported on host platform.
// All methods return error codes to allow compilation of configs with OTA triggers.
std::unique_ptr<ota::OTABackend> make_ota_backend() { return make_unique<ota::HostOTABackend>(); }
std::unique_ptr<HostOTABackend> make_ota_backend() { return make_unique<HostOTABackend>(); }
OTAResponseTypes HostOTABackend::begin(size_t image_size) { return OTA_RESPONSE_ERROR_UPDATE_PREPARE; }
@@ -17,5 +17,7 @@ class HostOTABackend final : public OTABackend {
bool supports_compression() override { return false; }
};
std::unique_ptr<HostOTABackend> make_ota_backend();
} // namespace esphome::ota
#endif
@@ -147,7 +147,7 @@ void ResamplerSpeaker::loop() {
xEventGroupClearBits(this->event_group_, ResamplingEventGroupBits::STATE_STOPPING);
}
if (event_group_bits & ResamplingEventGroupBits::STATE_STOPPED) {
this->delete_task_();
this->task_.deallocate();
ESP_LOGD(TAG, "Stopped");
xEventGroupClearBits(this->event_group_, ResamplingEventGroupBits::ALL_BITS);
}
@@ -190,7 +190,7 @@ void ResamplerSpeaker::loop() {
this->output_speaker_->stop();
}
if (this->output_speaker_->is_stopped() && (this->task_handle_ == nullptr)) {
if (this->output_speaker_->is_stopped() && !this->task_.is_created()) {
// Only transition to stopped state once the output speaker and resampler task are fully stopped
this->waiting_for_output_ = false;
this->state_ = speaker::STATE_STOPPED;
@@ -209,9 +209,6 @@ void ResamplerSpeaker::loop() {
void ResamplerSpeaker::set_start_error_(esp_err_t err) {
switch (err) {
case ESP_ERR_INVALID_STATE:
this->status_set_error(LOG_STR("Task failed to start"));
break;
case ESP_ERR_NO_MEM:
this->status_set_error(LOG_STR("Not enough memory"));
break;
@@ -267,36 +264,12 @@ esp_err_t ResamplerSpeaker::start_() {
if (this->requires_resampling_()) {
// Start the resampler task to handle converting sample rates
return this->start_task_();
}
return ESP_OK;
}
esp_err_t ResamplerSpeaker::start_task_() {
if (this->task_stack_buffer_ == nullptr) {
if (this->task_stack_in_psram_) {
RAMAllocator<StackType_t> stack_allocator(RAMAllocator<StackType_t>::ALLOC_EXTERNAL);
this->task_stack_buffer_ = stack_allocator.allocate(TASK_STACK_SIZE);
} else {
RAMAllocator<StackType_t> stack_allocator(RAMAllocator<StackType_t>::ALLOC_INTERNAL);
this->task_stack_buffer_ = stack_allocator.allocate(TASK_STACK_SIZE);
if (!this->task_.create(resample_task, "resampler", TASK_STACK_SIZE, (void *) this, RESAMPLER_TASK_PRIORITY,
this->task_stack_in_psram_)) {
return ESP_ERR_NO_MEM;
}
}
if (this->task_stack_buffer_ == nullptr) {
return ESP_ERR_NO_MEM;
}
if (this->task_handle_ == nullptr) {
this->task_handle_ = xTaskCreateStatic(resample_task, "resampler", TASK_STACK_SIZE, (void *) this,
RESAMPLER_TASK_PRIORITY, this->task_stack_buffer_, &this->task_stack_);
}
if (this->task_handle_ == nullptr) {
return ESP_ERR_INVALID_STATE;
}
return ESP_OK;
}
@@ -305,33 +278,12 @@ void ResamplerSpeaker::stop() { this->send_command_(ResamplingEventGroupBits::CO
void ResamplerSpeaker::enter_stopping_state_() {
this->state_ = speaker::STATE_STOPPING;
this->state_start_ms_ = App.get_loop_component_start_time();
if (this->task_handle_ != nullptr) {
if (this->task_.is_created()) {
xEventGroupSetBits(this->event_group_, ResamplingEventGroupBits::TASK_COMMAND_STOP);
}
this->output_speaker_->stop();
}
void ResamplerSpeaker::delete_task_() {
if (this->task_handle_ != nullptr) {
// Delete the suspended task
vTaskDelete(this->task_handle_);
this->task_handle_ = nullptr;
}
if (this->task_stack_buffer_ != nullptr) {
// Deallocate the task stack buffer
if (this->task_stack_in_psram_) {
RAMAllocator<StackType_t> stack_allocator(RAMAllocator<StackType_t>::ALLOC_EXTERNAL);
stack_allocator.deallocate(this->task_stack_buffer_, TASK_STACK_SIZE);
} else {
RAMAllocator<StackType_t> stack_allocator(RAMAllocator<StackType_t>::ALLOC_INTERNAL);
stack_allocator.deallocate(this->task_stack_buffer_, TASK_STACK_SIZE);
}
this->task_stack_buffer_ = nullptr;
}
}
void ResamplerSpeaker::finish() { this->send_command_(ResamplingEventGroupBits::COMMAND_FINISH); }
bool ResamplerSpeaker::has_buffered_data() const {
@@ -7,8 +7,8 @@
#include "esphome/components/speaker/speaker.h"
#include "esphome/core/component.h"
#include "esphome/core/static_task.h"
#include <freertos/FreeRTOS.h>
#include <freertos/event_groups.h>
namespace esphome {
@@ -57,15 +57,9 @@ class ResamplerSpeaker : public Component, public speaker::Speaker {
protected:
/// @brief Starts the output speaker after setting the resampled stream info. If resampling is required, it starts the
/// task.
/// @return ESP_OK if resampling is required
/// return value of start_task_() if resampling is required
esp_err_t start_();
/// @brief Starts the resampler task after allocating the task stack
/// @return ESP_OK if successful,
/// ESP_ERR_NO_MEM if the task stack couldn't be allocated
/// ESP_ERR_INVALID_STATE if the task wasn't created
esp_err_t start_task_();
/// ESP_ERR_NO_MEM if the resampler task couldn't be created
esp_err_t start_();
/// @brief Transitions to STATE_STOPPING, records the stopping timestamp, sends the task stop command if the task is
/// running, and stops the output speaker.
@@ -74,9 +68,6 @@ class ResamplerSpeaker : public Component, public speaker::Speaker {
/// @brief Sets the appropriate status error based on the start failure reason.
void set_start_error_(esp_err_t err);
/// @brief Deletes the resampler task if suspended, deallocates the task stack, and resets the related pointers.
void delete_task_();
/// @brief Sends a command via event group bits, enables the loop, and optionally wakes the main loop.
void send_command_(uint32_t command_bit, bool wake_loop = false);
@@ -92,9 +83,7 @@ class ResamplerSpeaker : public Component, public speaker::Speaker {
bool task_stack_in_psram_{false};
bool waiting_for_output_{false};
TaskHandle_t task_handle_{nullptr};
StaticTask_t task_stack_;
StackType_t *task_stack_buffer_{nullptr};
StaticTask task_;
audio::AudioStreamInfo target_stream_info_;
@@ -87,20 +87,20 @@ void AudioPipeline::set_pause_state(bool pause_state) {
}
void AudioPipeline::suspend_tasks() {
if (this->read_task_handle_ != nullptr) {
vTaskSuspend(this->read_task_handle_);
if (this->read_task_.is_created()) {
vTaskSuspend(this->read_task_.get_handle());
}
if (this->decode_task_handle_ != nullptr) {
vTaskSuspend(this->decode_task_handle_);
if (this->decode_task_.is_created()) {
vTaskSuspend(this->decode_task_.get_handle());
}
}
void AudioPipeline::resume_tasks() {
if (this->read_task_handle_ != nullptr) {
vTaskResume(this->read_task_handle_);
if (this->read_task_.is_created()) {
vTaskResume(this->read_task_.get_handle());
}
if (this->decode_task_handle_ != nullptr) {
vTaskResume(this->decode_task_handle_);
if (this->decode_task_.is_created()) {
vTaskResume(this->decode_task_.get_handle());
}
}
@@ -159,7 +159,7 @@ AudioPipelineState AudioPipeline::process_state() {
// Init command pending
if (!(event_bits & EventGroupBits::PIPELINE_COMMAND_STOP)) {
// Only start if there is no pending stop command
if ((this->read_task_handle_ == nullptr) || (this->decode_task_handle_ == nullptr)) {
if (!this->read_task_.is_created() || !this->decode_task_.is_created()) {
// At least one task isn't running
this->start_tasks_();
}
@@ -202,8 +202,9 @@ AudioPipelineState AudioPipeline::process_state() {
if (!this->is_playing_) {
// The tasks have been stopped for two ``process_state`` calls in a row, so delete the tasks
if ((this->read_task_handle_ != nullptr) || (this->decode_task_handle_ != nullptr)) {
this->delete_tasks_();
if (this->read_task_.is_created() || this->decode_task_.is_created()) {
this->read_task_.deallocate();
this->decode_task_.deallocate();
if (this->hard_stop_) {
// Stop command was sent, so immediately end the playback
this->speaker_->stop();
@@ -234,7 +235,7 @@ AudioPipelineState AudioPipeline::process_state() {
}
}
if ((this->read_task_handle_ == nullptr) && (this->decode_task_handle_ == nullptr)) {
if (!this->read_task_.is_created() && !this->decode_task_.is_created()) {
// No tasks are running, so the pipeline is stopped.
xEventGroupClearBits(this->event_group_, EventGroupBits::PIPELINE_COMMAND_STOP);
return AudioPipelineState::STOPPED;
@@ -262,94 +263,25 @@ esp_err_t AudioPipeline::allocate_communications_() {
}
esp_err_t AudioPipeline::start_tasks_() {
if (this->read_task_handle_ == nullptr) {
if (this->read_task_stack_buffer_ == nullptr) {
// Reader task uses the AudioReader class which uses esp_http_client. This crashes on IDF 5.4 if the task stack is
// in PSRAM. As a workaround, always allocate the read task in internal memory.
RAMAllocator<StackType_t> stack_allocator(RAMAllocator<StackType_t>::ALLOC_INTERNAL);
this->read_task_stack_buffer_ = stack_allocator.allocate(READ_TASK_STACK_SIZE);
}
if (this->read_task_stack_buffer_ == nullptr) {
if (!this->read_task_.is_created()) {
// Reader task uses the AudioReader class which uses esp_http_client. This crashes on IDF 5.4 if the task stack is
// in PSRAM. As a workaround, always allocate the read task in internal memory.
if (!this->read_task_.create(read_task, (this->base_name_ + "_read").c_str(), READ_TASK_STACK_SIZE, (void *) this,
this->priority_, false)) {
return ESP_ERR_NO_MEM;
}
if (this->read_task_handle_ == nullptr) {
this->read_task_handle_ =
xTaskCreateStatic(read_task, (this->base_name_ + "_read").c_str(), READ_TASK_STACK_SIZE, (void *) this,
this->priority_, this->read_task_stack_buffer_, &this->read_task_stack_);
}
if (this->read_task_handle_ == nullptr) {
return ESP_ERR_INVALID_STATE;
}
}
if (this->decode_task_handle_ == nullptr) {
if (this->decode_task_stack_buffer_ == nullptr) {
if (this->task_stack_in_psram_) {
RAMAllocator<StackType_t> stack_allocator(RAMAllocator<StackType_t>::ALLOC_EXTERNAL);
this->decode_task_stack_buffer_ = stack_allocator.allocate(DECODE_TASK_STACK_SIZE);
} else {
RAMAllocator<StackType_t> stack_allocator(RAMAllocator<StackType_t>::ALLOC_INTERNAL);
this->decode_task_stack_buffer_ = stack_allocator.allocate(DECODE_TASK_STACK_SIZE);
}
}
if (this->decode_task_stack_buffer_ == nullptr) {
if (!this->decode_task_.is_created()) {
if (!this->decode_task_.create(decode_task, (this->base_name_ + "_decode").c_str(), DECODE_TASK_STACK_SIZE,
(void *) this, this->priority_, this->task_stack_in_psram_)) {
return ESP_ERR_NO_MEM;
}
if (this->decode_task_handle_ == nullptr) {
this->decode_task_handle_ =
xTaskCreateStatic(decode_task, (this->base_name_ + "_decode").c_str(), DECODE_TASK_STACK_SIZE, (void *) this,
this->priority_, this->decode_task_stack_buffer_, &this->decode_task_stack_);
}
if (this->decode_task_handle_ == nullptr) {
return ESP_ERR_INVALID_STATE;
}
}
return ESP_OK;
}
void AudioPipeline::delete_tasks_() {
if (this->read_task_handle_ != nullptr) {
vTaskDelete(this->read_task_handle_);
if (this->read_task_stack_buffer_ != nullptr) {
if (this->task_stack_in_psram_) {
RAMAllocator<StackType_t> stack_allocator(RAMAllocator<StackType_t>::ALLOC_EXTERNAL);
stack_allocator.deallocate(this->read_task_stack_buffer_, READ_TASK_STACK_SIZE);
} else {
RAMAllocator<StackType_t> stack_allocator(RAMAllocator<StackType_t>::ALLOC_INTERNAL);
stack_allocator.deallocate(this->read_task_stack_buffer_, READ_TASK_STACK_SIZE);
}
this->read_task_stack_buffer_ = nullptr;
this->read_task_handle_ = nullptr;
}
}
if (this->decode_task_handle_ != nullptr) {
vTaskDelete(this->decode_task_handle_);
if (this->decode_task_stack_buffer_ != nullptr) {
if (this->task_stack_in_psram_) {
RAMAllocator<StackType_t> stack_allocator(RAMAllocator<StackType_t>::ALLOC_EXTERNAL);
stack_allocator.deallocate(this->decode_task_stack_buffer_, DECODE_TASK_STACK_SIZE);
} else {
RAMAllocator<StackType_t> stack_allocator(RAMAllocator<StackType_t>::ALLOC_INTERNAL);
stack_allocator.deallocate(this->decode_task_stack_buffer_, DECODE_TASK_STACK_SIZE);
}
this->decode_task_stack_buffer_ = nullptr;
this->decode_task_handle_ = nullptr;
}
}
}
void AudioPipeline::read_task(void *params) {
AudioPipeline *this_pipeline = (AudioPipeline *) params;
@@ -8,10 +8,10 @@
#include "esphome/components/speaker/speaker.h"
#include "esphome/core/ring_buffer.h"
#include "esphome/core/static_task.h"
#include "esp_err.h"
#include <freertos/FreeRTOS.h>
#include <freertos/event_groups.h>
#include <freertos/queue.h>
@@ -104,9 +104,6 @@ class AudioPipeline {
/// @return ESP_OK if successful or an appropriate error if not
esp_err_t start_tasks_();
/// @brief Resets the task related pointers and deallocates their stacks.
void delete_tasks_();
std::string base_name_;
UBaseType_t priority_;
@@ -143,15 +140,11 @@ class AudioPipeline {
// Handles reading the media file from flash or a url
static void read_task(void *params);
TaskHandle_t read_task_handle_{nullptr};
StaticTask_t read_task_stack_;
StackType_t *read_task_stack_buffer_{nullptr};
StaticTask read_task_;
// Decodes the media file into PCM audio
static void decode_task(void *params);
TaskHandle_t decode_task_handle_{nullptr};
StaticTask_t decode_task_stack_;
StackType_t *decode_task_stack_buffer_{nullptr};
StaticTask decode_task_;
};
} // namespace speaker
@@ -1,7 +1,7 @@
#include "ota_web_server.h"
#ifdef USE_WEBSERVER_OTA
#include "esphome/components/ota/ota_backend.h"
#include "esphome/components/ota/ota_backend_factory.h"
#include "esphome/core/application.h"
#include "esphome/core/log.h"
@@ -71,7 +71,7 @@ class OTARequestHandler : public AsyncWebHandler {
bool ota_success_{false};
private:
std::unique_ptr<ota::OTABackend> ota_backend_{nullptr};
decltype(ota::make_ota_backend()) ota_backend_{nullptr};
};
void OTARequestHandler::report_ota_progress_(AsyncWebServerRequest *request) {