mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 06:33:51 +00:00 
			
		
		
		
	| @@ -7,13 +7,16 @@ | |||||||
| #ifdef USE_ARDUINO | #ifdef USE_ARDUINO | ||||||
| #include <esp32-hal-dac.h> | #include <esp32-hal-dac.h> | ||||||
| #endif | #endif | ||||||
| #ifdef USE_ESP_IDF |  | ||||||
| #include <driver/dac.h> |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace esp32_dac { | namespace esp32_dac { | ||||||
|  |  | ||||||
|  | #ifdef USE_ESP32_VARIANT_ESP32S2 | ||||||
|  | static constexpr uint8_t DAC0_PIN = 17; | ||||||
|  | #else | ||||||
|  | static constexpr uint8_t DAC0_PIN = 25; | ||||||
|  | #endif | ||||||
|  |  | ||||||
| static const char *const TAG = "esp32_dac"; | static const char *const TAG = "esp32_dac"; | ||||||
|  |  | ||||||
| void ESP32DAC::setup() { | void ESP32DAC::setup() { | ||||||
| @@ -22,8 +25,15 @@ void ESP32DAC::setup() { | |||||||
|   this->turn_off(); |   this->turn_off(); | ||||||
|  |  | ||||||
| #ifdef USE_ESP_IDF | #ifdef USE_ESP_IDF | ||||||
|   auto channel = pin_->get_pin() == 25 ? DAC_CHANNEL_1 : DAC_CHANNEL_2; |   const dac_channel_t channel = this->pin_->get_pin() == DAC0_PIN ? DAC_CHAN_0 : DAC_CHAN_1; | ||||||
|   dac_output_enable(channel); |   const dac_oneshot_config_t oneshot_cfg{channel}; | ||||||
|  |   dac_oneshot_new_channel(&oneshot_cfg, &this->dac_handle_); | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ESP32DAC::on_safe_shutdown() { | ||||||
|  | #ifdef USE_ESP_IDF | ||||||
|  |   dac_oneshot_del_channel(this->dac_handle_); | ||||||
| #endif | #endif | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -40,8 +50,7 @@ void ESP32DAC::write_state(float state) { | |||||||
|   state = state * 255; |   state = state * 255; | ||||||
|  |  | ||||||
| #ifdef USE_ESP_IDF | #ifdef USE_ESP_IDF | ||||||
|   auto channel = pin_->get_pin() == 25 ? DAC_CHANNEL_1 : DAC_CHANNEL_2; |   dac_oneshot_output_voltage(this->dac_handle_, state); | ||||||
|   dac_output_voltage(channel, (uint8_t) state); |  | ||||||
| #endif | #endif | ||||||
| #ifdef USE_ARDUINO | #ifdef USE_ARDUINO | ||||||
|   dacWrite(this->pin_->get_pin(), state); |   dacWrite(this->pin_->get_pin(), state); | ||||||
|   | |||||||
| @@ -7,6 +7,10 @@ | |||||||
|  |  | ||||||
| #ifdef USE_ESP32 | #ifdef USE_ESP32 | ||||||
|  |  | ||||||
|  | #ifdef USE_ESP_IDF | ||||||
|  | #include <driver/dac_oneshot.h> | ||||||
|  | #endif | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace esp32_dac { | namespace esp32_dac { | ||||||
|  |  | ||||||
| @@ -16,6 +20,7 @@ class ESP32DAC : public output::FloatOutput, public Component { | |||||||
|  |  | ||||||
|   /// Initialize pin |   /// Initialize pin | ||||||
|   void setup() override; |   void setup() override; | ||||||
|  |   void on_safe_shutdown() override; | ||||||
|   void dump_config() override; |   void dump_config() override; | ||||||
|   /// HARDWARE setup_priority |   /// HARDWARE setup_priority | ||||||
|   float get_setup_priority() const override { return setup_priority::HARDWARE; } |   float get_setup_priority() const override { return setup_priority::HARDWARE; } | ||||||
| @@ -24,6 +29,9 @@ class ESP32DAC : public output::FloatOutput, public Component { | |||||||
|   void write_state(float state) override; |   void write_state(float state) override; | ||||||
|  |  | ||||||
|   InternalGPIOPin *pin_; |   InternalGPIOPin *pin_; | ||||||
|  | #ifdef USE_ESP_IDF | ||||||
|  |   dac_oneshot_handle_t dac_handle_; | ||||||
|  | #endif | ||||||
| }; | }; | ||||||
|  |  | ||||||
| }  // namespace esp32_dac | }  // namespace esp32_dac | ||||||
|   | |||||||
| @@ -1,15 +1,27 @@ | |||||||
|  | import esphome.codegen as cg | ||||||
|  | import esphome.config_validation as cv | ||||||
| from esphome import pins | from esphome import pins | ||||||
| from esphome.components import output | from esphome.components import output | ||||||
| import esphome.config_validation as cv | from esphome.components.esp32 import get_esp32_variant | ||||||
| import esphome.codegen as cg | from esphome.components.esp32.const import VARIANT_ESP32, VARIANT_ESP32S2 | ||||||
| from esphome.const import CONF_ID, CONF_NUMBER, CONF_PIN | from esphome.const import CONF_ID, CONF_NUMBER, CONF_PIN | ||||||
|  |  | ||||||
| DEPENDENCIES = ["esp32"] | DEPENDENCIES = ["esp32"] | ||||||
|  |  | ||||||
|  | DAC_PINS = { | ||||||
|  |     VARIANT_ESP32: (25, 26), | ||||||
|  |     VARIANT_ESP32S2: (17, 18), | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
| def valid_dac_pin(value): | def valid_dac_pin(value): | ||||||
|     num = value[CONF_NUMBER] |     variant = get_esp32_variant() | ||||||
|     cv.one_of(25, 26)(num) |     try: | ||||||
|  |         valid_pins = DAC_PINS[variant] | ||||||
|  |     except KeyError as ex: | ||||||
|  |         raise cv.Invalid(f"DAC is not supported on {variant}") from ex | ||||||
|  |     given_pin = value[CONF_NUMBER] | ||||||
|  |     cv.one_of(*valid_pins)(given_pin) | ||||||
|     return value |     return value | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -247,8 +247,8 @@ async def to_code(config): | |||||||
|         ) |         ) | ||||||
|     cg.add(log.pre_setup()) |     cg.add(log.pre_setup()) | ||||||
|  |  | ||||||
|     for tag, level in config[CONF_LOGS].items(): |     for tag, log_level in config[CONF_LOGS].items(): | ||||||
|         cg.add(log.set_log_level(tag, LOG_LEVELS[level])) |         cg.add(log.set_log_level(tag, LOG_LEVELS[log_level])) | ||||||
|  |  | ||||||
|     cg.add_define("USE_LOGGER") |     cg.add_define("USE_LOGGER") | ||||||
|     this_severity = LOG_LEVEL_SEVERITY.index(level) |     this_severity = LOG_LEVEL_SEVERITY.index(level) | ||||||
|   | |||||||
| @@ -102,9 +102,6 @@ void Logger::log_vprintf_(int level, const char *tag, int line, const __FlashStr | |||||||
| #endif | #endif | ||||||
|  |  | ||||||
| int HOT Logger::level_for(const char *tag) { | int HOT Logger::level_for(const char *tag) { | ||||||
|   // Uses std::vector<> for low memory footprint, though the vector |  | ||||||
|   // could be sorted to minimize lookup times. This feature isn't used that |  | ||||||
|   // much anyway so it doesn't matter too much. |  | ||||||
|   if (this->log_levels_.count(tag) != 0) |   if (this->log_levels_.count(tag) != 0) | ||||||
|     return this->log_levels_[tag]; |     return this->log_levels_[tag]; | ||||||
|   return this->current_level_; |   return this->current_level_; | ||||||
|   | |||||||
| @@ -9,10 +9,10 @@ namespace online_image { | |||||||
| static const char *const TAG = "online_image.decoder"; | static const char *const TAG = "online_image.decoder"; | ||||||
|  |  | ||||||
| bool ImageDecoder::set_size(int width, int height) { | bool ImageDecoder::set_size(int width, int height) { | ||||||
|   bool resized = this->image_->resize_(width, height); |   bool success = this->image_->resize_(width, height) > 0; | ||||||
|   this->x_scale_ = static_cast<double>(this->image_->buffer_width_) / width; |   this->x_scale_ = static_cast<double>(this->image_->buffer_width_) / width; | ||||||
|   this->y_scale_ = static_cast<double>(this->image_->buffer_height_) / height; |   this->y_scale_ = static_cast<double>(this->image_->buffer_height_) / height; | ||||||
|   return resized; |   return success; | ||||||
| } | } | ||||||
|  |  | ||||||
| void ImageDecoder::draw(int x, int y, int w, int h, const Color &color) { | void ImageDecoder::draw(int x, int y, int w, int h, const Color &color) { | ||||||
| @@ -51,8 +51,9 @@ size_t DownloadBuffer::read(size_t len) { | |||||||
| } | } | ||||||
|  |  | ||||||
| size_t DownloadBuffer::resize(size_t size) { | size_t DownloadBuffer::resize(size_t size) { | ||||||
|   if (this->size_ == size) { |   if (this->size_ >= size) { | ||||||
|     return size; |     // Avoid useless reallocations; if the buffer is big enough, don't reallocate. | ||||||
|  |     return this->size_; | ||||||
|   } |   } | ||||||
|   this->allocator_.deallocate(this->buffer_, this->size_); |   this->allocator_.deallocate(this->buffer_, this->size_); | ||||||
|   this->buffer_ = this->allocator_.allocate(size); |   this->buffer_ = this->allocator_.allocate(size); | ||||||
| @@ -61,6 +62,8 @@ size_t DownloadBuffer::resize(size_t size) { | |||||||
|     this->size_ = size; |     this->size_ = size; | ||||||
|     return size; |     return size; | ||||||
|   } else { |   } else { | ||||||
|  |     ESP_LOGE(TAG, "allocation of %zu bytes failed. Biggest block in heap: %zu Bytes", size, | ||||||
|  |              this->allocator_.get_max_free_block_size()); | ||||||
|     this->size_ = 0; |     this->size_ = 0; | ||||||
|     return 0; |     return 0; | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -58,7 +58,7 @@ int HOT JpegDecoder::decode(uint8_t *buffer, size_t size) { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   if (!this->jpeg_.openRAM(buffer, size, draw_callback)) { |   if (!this->jpeg_.openRAM(buffer, size, draw_callback)) { | ||||||
|     ESP_LOGE(TAG, "Could not open image for decoding."); |     ESP_LOGE(TAG, "Could not open image for decoding: %d", this->jpeg_.getLastError()); | ||||||
|     return DECODE_ERROR_INVALID_TYPE; |     return DECODE_ERROR_INVALID_TYPE; | ||||||
|   } |   } | ||||||
|   auto jpeg_type = this->jpeg_.getJPEGType(); |   auto jpeg_type = this->jpeg_.getJPEGType(); | ||||||
| @@ -73,7 +73,9 @@ int HOT JpegDecoder::decode(uint8_t *buffer, size_t size) { | |||||||
|  |  | ||||||
|   this->jpeg_.setUserPointer(this); |   this->jpeg_.setUserPointer(this); | ||||||
|   this->jpeg_.setPixelType(RGB8888); |   this->jpeg_.setPixelType(RGB8888); | ||||||
|   this->set_size(this->jpeg_.getWidth(), this->jpeg_.getHeight()); |   if (!this->set_size(this->jpeg_.getWidth(), this->jpeg_.getHeight())) { | ||||||
|  |     return DECODE_ERROR_OUT_OF_MEMORY; | ||||||
|  |   } | ||||||
|   if (!this->jpeg_.decode(0, 0, 0)) { |   if (!this->jpeg_.decode(0, 0, 0)) { | ||||||
|     ESP_LOGE(TAG, "Error while decoding."); |     ESP_LOGE(TAG, "Error while decoding."); | ||||||
|     this->jpeg_.close(); |     this->jpeg_.close(); | ||||||
|   | |||||||
| @@ -64,33 +64,34 @@ void OnlineImage::release() { | |||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| bool OnlineImage::resize_(int width_in, int height_in) { | size_t OnlineImage::resize_(int width_in, int height_in) { | ||||||
|   int width = this->fixed_width_; |   int width = this->fixed_width_; | ||||||
|   int height = this->fixed_height_; |   int height = this->fixed_height_; | ||||||
|   if (this->auto_resize_()) { |   if (this->is_auto_resize_()) { | ||||||
|     width = width_in; |     width = width_in; | ||||||
|     height = height_in; |     height = height_in; | ||||||
|     if (this->width_ != width && this->height_ != height) { |     if (this->width_ != width && this->height_ != height) { | ||||||
|       this->release(); |       this->release(); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   if (this->buffer_) { |  | ||||||
|     return false; |  | ||||||
|   } |  | ||||||
|   size_t new_size = this->get_buffer_size_(width, height); |   size_t new_size = this->get_buffer_size_(width, height); | ||||||
|  |   if (this->buffer_) { | ||||||
|  |     // Buffer already allocated => no need to resize | ||||||
|  |     return new_size; | ||||||
|  |   } | ||||||
|   ESP_LOGD(TAG, "Allocating new buffer of %zu bytes", new_size); |   ESP_LOGD(TAG, "Allocating new buffer of %zu bytes", new_size); | ||||||
|   this->buffer_ = this->allocator_.allocate(new_size); |   this->buffer_ = this->allocator_.allocate(new_size); | ||||||
|   if (this->buffer_ == nullptr) { |   if (this->buffer_ == nullptr) { | ||||||
|     ESP_LOGE(TAG, "allocation of %zu bytes failed. Biggest block in heap: %zu Bytes", new_size, |     ESP_LOGE(TAG, "allocation of %zu bytes failed. Biggest block in heap: %zu Bytes", new_size, | ||||||
|              this->allocator_.get_max_free_block_size()); |              this->allocator_.get_max_free_block_size()); | ||||||
|     this->end_connection_(); |     this->end_connection_(); | ||||||
|     return false; |     return 0; | ||||||
|   } |   } | ||||||
|   this->buffer_width_ = width; |   this->buffer_width_ = width; | ||||||
|   this->buffer_height_ = height; |   this->buffer_height_ = height; | ||||||
|   this->width_ = width; |   this->width_ = width; | ||||||
|   ESP_LOGV(TAG, "New size: (%d, %d)", width, height); |   ESP_LOGV(TAG, "New size: (%d, %d)", width, height); | ||||||
|   return true; |   return new_size; | ||||||
| } | } | ||||||
|  |  | ||||||
| void OnlineImage::update() { | void OnlineImage::update() { | ||||||
|   | |||||||
| @@ -99,9 +99,22 @@ class OnlineImage : public PollingComponent, | |||||||
|  |  | ||||||
|   int get_position_(int x, int y) const { return (x + y * this->buffer_width_) * this->get_bpp() / 8; } |   int get_position_(int x, int y) const { return (x + y * this->buffer_width_) * this->get_bpp() / 8; } | ||||||
|  |  | ||||||
|   ESPHOME_ALWAYS_INLINE bool auto_resize_() const { return this->fixed_width_ == 0 || this->fixed_height_ == 0; } |   ESPHOME_ALWAYS_INLINE bool is_auto_resize_() const { return this->fixed_width_ == 0 || this->fixed_height_ == 0; } | ||||||
|  |  | ||||||
|   bool resize_(int width, int height); |   /** | ||||||
|  |    * @brief Resize the image buffer to the requested dimensions. | ||||||
|  |    * | ||||||
|  |    * The buffer will be allocated if not existing. | ||||||
|  |    * If the dimensions have been fixed in the yaml config, the buffer will be created | ||||||
|  |    * with those dimensions and not resized, even on request. | ||||||
|  |    * Otherwise, the old buffer will be deallocated and a new buffer with the requested | ||||||
|  |    * allocated | ||||||
|  |    * | ||||||
|  |    * @param width | ||||||
|  |    * @param height | ||||||
|  |    * @return 0 if no memory could be allocated, the size of the new buffer otherwise. | ||||||
|  |    */ | ||||||
|  |   size_t resize_(int width, int height); | ||||||
|  |  | ||||||
|   /** |   /** | ||||||
|    * @brief Draw a pixel into the buffer. |    * @brief Draw a pixel into the buffer. | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| """Constants used by esphome.""" | """Constants used by esphome.""" | ||||||
|  |  | ||||||
| __version__ = "2025.2.0b2" | __version__ = "2025.2.0b3" | ||||||
|  |  | ||||||
| ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" | ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" | ||||||
| VALID_SUBSTITUTIONS_CHARACTERS = ( | VALID_SUBSTITUTIONS_CHARACTERS = ( | ||||||
|   | |||||||
| @@ -853,7 +853,7 @@ class InfoRequestHandler(BaseHandler): | |||||||
|         dashboard = DASHBOARD |         dashboard = DASHBOARD | ||||||
|         entry = dashboard.entries.get(yaml_path) |         entry = dashboard.entries.get(yaml_path) | ||||||
|  |  | ||||||
|         if not entry: |         if not entry or entry.storage is None: | ||||||
|             self.set_status(404) |             self.set_status(404) | ||||||
|             return |             return | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user