mirror of
https://github.com/esphome/esphome.git
synced 2026-09-15 17:18:40 +00:00
Merge branch 'devirtualize-ota-backend' into integration
This commit is contained in:
@@ -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_;
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user