mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-29 22:24:26 +00:00 
			
		
		
		
	Merge branch 'integration' into memory_api
This commit is contained in:
		| @@ -89,6 +89,7 @@ esphome/components/bp5758d/* @Cossid | ||||
| esphome/components/button/* @esphome/core | ||||
| esphome/components/bytebuffer/* @clydebarrow | ||||
| esphome/components/camera/* @DT-art1 @bdraco | ||||
| esphome/components/camera_encoder/* @DT-art1 | ||||
| esphome/components/canbus/* @danielschramm @mvturnho | ||||
| esphome/components/cap1188/* @mreditor97 | ||||
| esphome/components/captive_portal/* @esphome/core | ||||
|   | ||||
							
								
								
									
										18
									
								
								esphome/components/camera/buffer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								esphome/components/camera/buffer.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <cinttypes> | ||||
| #include <cstddef> | ||||
|  | ||||
| namespace esphome::camera { | ||||
|  | ||||
| /// Interface for a generic buffer that stores image data. | ||||
| class Buffer { | ||||
|  public: | ||||
|   /// Returns a pointer to the buffer's data. | ||||
|   virtual uint8_t *get_data_buffer() = 0; | ||||
|   /// Returns the length of the buffer in bytes. | ||||
|   virtual size_t get_data_length() = 0; | ||||
|   virtual ~Buffer() = default; | ||||
| }; | ||||
|  | ||||
| }  // namespace esphome::camera | ||||
							
								
								
									
										20
									
								
								esphome/components/camera/buffer_impl.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								esphome/components/camera/buffer_impl.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | ||||
| #include "buffer_impl.h" | ||||
|  | ||||
| namespace esphome::camera { | ||||
|  | ||||
| BufferImpl::BufferImpl(size_t size) { | ||||
|   this->data_ = this->allocator_.allocate(size); | ||||
|   this->size_ = size; | ||||
| } | ||||
|  | ||||
| BufferImpl::BufferImpl(CameraImageSpec *spec) { | ||||
|   this->data_ = this->allocator_.allocate(spec->bytes_per_image()); | ||||
|   this->size_ = spec->bytes_per_image(); | ||||
| } | ||||
|  | ||||
| BufferImpl::~BufferImpl() { | ||||
|   if (this->data_ != nullptr) | ||||
|     this->allocator_.deallocate(this->data_, this->size_); | ||||
| } | ||||
|  | ||||
| }  // namespace esphome::camera | ||||
							
								
								
									
										26
									
								
								esphome/components/camera/buffer_impl.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								esphome/components/camera/buffer_impl.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "buffer.h" | ||||
| #include "camera.h" | ||||
|  | ||||
| namespace esphome::camera { | ||||
|  | ||||
| /// Default implementation of Buffer Interface. | ||||
| /// Uses a RAMAllocator for memory reservation. | ||||
| class BufferImpl : public Buffer { | ||||
|  public: | ||||
|   explicit BufferImpl(size_t size); | ||||
|   explicit BufferImpl(CameraImageSpec *spec); | ||||
|   // -------- Buffer -------- | ||||
|   uint8_t *get_data_buffer() override { return data_; } | ||||
|   size_t get_data_length() override { return size_; } | ||||
|   // ------------------------ | ||||
|   ~BufferImpl() override; | ||||
|  | ||||
|  protected: | ||||
|   RAMAllocator<uint8_t> allocator_; | ||||
|   size_t size_{}; | ||||
|   uint8_t *data_{}; | ||||
| }; | ||||
|  | ||||
| }  // namespace esphome::camera | ||||
| @@ -15,6 +15,26 @@ namespace camera { | ||||
|  */ | ||||
| enum CameraRequester : uint8_t { IDLE, API_REQUESTER, WEB_REQUESTER }; | ||||
|  | ||||
| /// Enumeration of different pixel formats. | ||||
| enum PixelFormat : uint8_t { | ||||
|   PIXEL_FORMAT_GRAYSCALE = 0,  ///< 8-bit grayscale. | ||||
|   PIXEL_FORMAT_RGB565,         ///< 16-bit RGB (5-6-5). | ||||
|   PIXEL_FORMAT_BGR888,         ///< RGB pixel data in 8-bit format, stored as B, G, R (1 byte each). | ||||
| }; | ||||
|  | ||||
| /// Returns string name for a given PixelFormat. | ||||
| inline const char *to_string(PixelFormat format) { | ||||
|   switch (format) { | ||||
|     case PIXEL_FORMAT_GRAYSCALE: | ||||
|       return "PIXEL_FORMAT_GRAYSCALE"; | ||||
|     case PIXEL_FORMAT_RGB565: | ||||
|       return "PIXEL_FORMAT_RGB565"; | ||||
|     case PIXEL_FORMAT_BGR888: | ||||
|       return "PIXEL_FORMAT_BGR888"; | ||||
|   } | ||||
|   return "PIXEL_FORMAT_UNKNOWN"; | ||||
| } | ||||
|  | ||||
| /** Abstract camera image base class. | ||||
|  *  Encapsulates the JPEG encoded data and it is shared among | ||||
|  *  all connected clients. | ||||
| @@ -43,6 +63,29 @@ class CameraImageReader { | ||||
|   virtual ~CameraImageReader() {} | ||||
| }; | ||||
|  | ||||
| /// Specification of a caputured camera image. | ||||
| /// This struct defines the format and size details for images captured | ||||
| /// or processed by a camera component. | ||||
| struct CameraImageSpec { | ||||
|   uint16_t width; | ||||
|   uint16_t height; | ||||
|   PixelFormat format; | ||||
|   size_t bytes_per_pixel() { | ||||
|     switch (format) { | ||||
|       case PIXEL_FORMAT_GRAYSCALE: | ||||
|         return 1; | ||||
|       case PIXEL_FORMAT_RGB565: | ||||
|         return 2; | ||||
|       case PIXEL_FORMAT_BGR888: | ||||
|         return 3; | ||||
|     } | ||||
|  | ||||
|     return 1; | ||||
|   } | ||||
|   size_t bytes_per_row() { return bytes_per_pixel() * width; } | ||||
|   size_t bytes_per_image() { return bytes_per_pixel() * width * height; } | ||||
| }; | ||||
|  | ||||
| /** Abstract camera base class. Collaborates with API. | ||||
|  *  1) API server starts and installs callback (add_image_callback) | ||||
|  *     which is called by the camera when a new image is available. | ||||
|   | ||||
							
								
								
									
										69
									
								
								esphome/components/camera/encoder.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								esphome/components/camera/encoder.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,69 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "buffer.h" | ||||
| #include "camera.h" | ||||
|  | ||||
| namespace esphome::camera { | ||||
|  | ||||
| /// Result codes from the encoder used to control camera pipeline flow. | ||||
| enum EncoderError : uint8_t { | ||||
|   ENCODER_ERROR_SUCCESS = 0,   ///< Encoding succeeded, continue pipeline normally. | ||||
|   ENCODER_ERROR_SKIP_FRAME,    ///< Skip current frame, try again on next frame. | ||||
|   ENCODER_ERROR_RETRY_FRAME,   ///< Retry current frame, after buffer growth or for incremental encoding. | ||||
|   ENCODER_ERROR_CONFIGURATION  ///< Fatal config error, shut down pipeline. | ||||
| }; | ||||
|  | ||||
| /// Converts EncoderError to string. | ||||
| inline const char *to_string(EncoderError error) { | ||||
|   switch (error) { | ||||
|     case ENCODER_ERROR_SUCCESS: | ||||
|       return "ENCODER_ERROR_SUCCESS"; | ||||
|     case ENCODER_ERROR_SKIP_FRAME: | ||||
|       return "ENCODER_ERROR_SKIP_FRAME"; | ||||
|     case ENCODER_ERROR_RETRY_FRAME: | ||||
|       return "ENCODER_ERROR_RETRY_FRAME"; | ||||
|     case ENCODER_ERROR_CONFIGURATION: | ||||
|       return "ENCODER_ERROR_CONFIGURATION"; | ||||
|   } | ||||
|   return "ENCODER_ERROR_INVALID"; | ||||
| } | ||||
|  | ||||
| /// Interface for an encoder buffer supporting resizing and variable-length data. | ||||
| class EncoderBuffer { | ||||
|  public: | ||||
|   ///  Sets logical buffer size, reallocates if needed. | ||||
|   ///  @param size Required size in bytes. | ||||
|   ///  @return true on success, false on allocation failure. | ||||
|   virtual bool set_buffer_size(size_t size) = 0; | ||||
|  | ||||
|   /// Returns a pointer to the buffer data. | ||||
|   virtual uint8_t *get_data() const = 0; | ||||
|  | ||||
|   /// Returns number of bytes currently used. | ||||
|   virtual size_t get_size() const = 0; | ||||
|  | ||||
|   ///  Returns total allocated buffer size. | ||||
|   virtual size_t get_max_size() const = 0; | ||||
|  | ||||
|   virtual ~EncoderBuffer() = default; | ||||
| }; | ||||
|  | ||||
| /// Interface for image encoders used in a camera pipeline. | ||||
| class Encoder { | ||||
|  public: | ||||
|   /// Encodes pixel data from a previous camera pipeline stage. | ||||
|   /// @param spec Specification of the input pixel data. | ||||
|   /// @param pixels Image pixels in RGB or grayscale format, as specified in @p spec. | ||||
|   /// @return EncoderError Indicating the result of the encoding operation. | ||||
|   virtual EncoderError encode_pixels(CameraImageSpec *spec, Buffer *pixels) = 0; | ||||
|  | ||||
|   /// Returns the encoder's output buffer. | ||||
|   /// @return Pointer to an EncoderBuffer containing encoded data. | ||||
|   virtual EncoderBuffer *get_output_buffer() = 0; | ||||
|  | ||||
|   ///  Prints the encoder's configuration to the log. | ||||
|   virtual void dump_config() = 0; | ||||
|   virtual ~Encoder() = default; | ||||
| }; | ||||
|  | ||||
| }  // namespace esphome::camera | ||||
							
								
								
									
										62
									
								
								esphome/components/camera_encoder/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								esphome/components/camera_encoder/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,62 @@ | ||||
| import esphome.codegen as cg | ||||
| from esphome.components.esp32 import add_idf_component | ||||
| import esphome.config_validation as cv | ||||
| from esphome.const import CONF_BUFFER_SIZE, CONF_ID, CONF_TYPE | ||||
| from esphome.core import CORE | ||||
| from esphome.types import ConfigType | ||||
|  | ||||
| CODEOWNERS = ["@DT-art1"] | ||||
|  | ||||
| AUTO_LOAD = ["camera"] | ||||
|  | ||||
| CONF_BUFFER_EXPAND_SIZE = "buffer_expand_size" | ||||
| CONF_ENCODER_BUFFER_ID = "encoder_buffer_id" | ||||
| CONF_QUALITY = "quality" | ||||
|  | ||||
| ESP32_CAMERA_ENCODER = "esp32_camera" | ||||
|  | ||||
| camera_ns = cg.esphome_ns.namespace("camera") | ||||
| camera_encoder_ns = cg.esphome_ns.namespace("camera_encoder") | ||||
|  | ||||
| Encoder = camera_ns.class_("Encoder") | ||||
| EncoderBufferImpl = camera_encoder_ns.class_("EncoderBufferImpl") | ||||
|  | ||||
| ESP32CameraJPEGEncoder = camera_encoder_ns.class_("ESP32CameraJPEGEncoder", Encoder) | ||||
|  | ||||
| MAX_JPEG_BUFFER_SIZE_2MB = 2 * 1024 * 1024 | ||||
|  | ||||
| ESP32_CAMERA_ENCODER_SCHEMA = cv.Schema( | ||||
|     { | ||||
|         cv.GenerateID(): cv.declare_id(ESP32CameraJPEGEncoder), | ||||
|         cv.Optional(CONF_QUALITY, default=80): cv.int_range(1, 100), | ||||
|         cv.Optional(CONF_BUFFER_SIZE, default=4096): cv.int_range( | ||||
|             1024, MAX_JPEG_BUFFER_SIZE_2MB | ||||
|         ), | ||||
|         cv.Optional(CONF_BUFFER_EXPAND_SIZE, default=1024): cv.int_range( | ||||
|             0, MAX_JPEG_BUFFER_SIZE_2MB | ||||
|         ), | ||||
|         cv.GenerateID(CONF_ENCODER_BUFFER_ID): cv.declare_id(EncoderBufferImpl), | ||||
|     } | ||||
| ) | ||||
|  | ||||
| CONFIG_SCHEMA = cv.typed_schema( | ||||
|     { | ||||
|         ESP32_CAMERA_ENCODER: ESP32_CAMERA_ENCODER_SCHEMA, | ||||
|     }, | ||||
|     default_type=ESP32_CAMERA_ENCODER, | ||||
| ) | ||||
|  | ||||
|  | ||||
| async def to_code(config: ConfigType) -> None: | ||||
|     buffer = cg.new_Pvariable(config[CONF_ENCODER_BUFFER_ID]) | ||||
|     cg.add(buffer.set_buffer_size(config[CONF_BUFFER_SIZE])) | ||||
|     if config[CONF_TYPE] == ESP32_CAMERA_ENCODER: | ||||
|         if CORE.using_esp_idf: | ||||
|             add_idf_component(name="espressif/esp32-camera", ref="2.1.0") | ||||
|         cg.add_build_flag("-DUSE_ESP32_CAMERA_JPEG_ENCODER") | ||||
|         var = cg.new_Pvariable( | ||||
|             config[CONF_ID], | ||||
|             config[CONF_QUALITY], | ||||
|             buffer, | ||||
|         ) | ||||
|         cg.add(var.set_buffer_expand_size(config[CONF_BUFFER_EXPAND_SIZE])) | ||||
							
								
								
									
										23
									
								
								esphome/components/camera_encoder/encoder_buffer_impl.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								esphome/components/camera_encoder/encoder_buffer_impl.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| #include "encoder_buffer_impl.h" | ||||
|  | ||||
| namespace esphome::camera_encoder { | ||||
|  | ||||
| bool EncoderBufferImpl::set_buffer_size(size_t size) { | ||||
|   if (size > this->capacity_) { | ||||
|     uint8_t *p = this->allocator_.reallocate(this->data_, size); | ||||
|     if (p == nullptr) | ||||
|       return false; | ||||
|  | ||||
|     this->data_ = p; | ||||
|     this->capacity_ = size; | ||||
|   } | ||||
|   this->size_ = size; | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| EncoderBufferImpl::~EncoderBufferImpl() { | ||||
|   if (this->data_ != nullptr) | ||||
|     this->allocator_.deallocate(this->data_, this->capacity_); | ||||
| } | ||||
|  | ||||
| }  // namespace esphome::camera_encoder | ||||
							
								
								
									
										25
									
								
								esphome/components/camera_encoder/encoder_buffer_impl.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								esphome/components/camera_encoder/encoder_buffer_impl.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/components/camera/encoder.h" | ||||
| #include "esphome/core/helpers.h" | ||||
|  | ||||
| namespace esphome::camera_encoder { | ||||
|  | ||||
| class EncoderBufferImpl : public camera::EncoderBuffer { | ||||
|  public: | ||||
|   // --- EncoderBuffer  --- | ||||
|   bool set_buffer_size(size_t size) override; | ||||
|   uint8_t *get_data() const override { return this->data_; } | ||||
|   size_t get_size() const override { return this->size_; } | ||||
|   size_t get_max_size() const override { return this->capacity_; } | ||||
|   // ---------------------- | ||||
|   ~EncoderBufferImpl() override; | ||||
|  | ||||
|  protected: | ||||
|   RAMAllocator<uint8_t> allocator_; | ||||
|   size_t capacity_{}; | ||||
|   size_t size_{}; | ||||
|   uint8_t *data_{}; | ||||
| }; | ||||
|  | ||||
| }  // namespace esphome::camera_encoder | ||||
| @@ -0,0 +1,82 @@ | ||||
| #ifdef USE_ESP32_CAMERA_JPEG_ENCODER | ||||
|  | ||||
| #include "esp32_camera_jpeg_encoder.h" | ||||
|  | ||||
| namespace esphome::camera_encoder { | ||||
|  | ||||
| static const char *const TAG = "camera_encoder"; | ||||
|  | ||||
| ESP32CameraJPEGEncoder::ESP32CameraJPEGEncoder(uint8_t quality, camera::EncoderBuffer *output) { | ||||
|   this->quality_ = quality; | ||||
|   this->output_ = output; | ||||
| } | ||||
|  | ||||
| camera::EncoderError ESP32CameraJPEGEncoder::encode_pixels(camera::CameraImageSpec *spec, camera::Buffer *pixels) { | ||||
|   this->bytes_written_ = 0; | ||||
|   this->out_of_output_memory_ = false; | ||||
|   bool success = fmt2jpg_cb(pixels->get_data_buffer(), pixels->get_data_length(), spec->width, spec->height, | ||||
|                             to_internal_(spec->format), this->quality_, callback_, this); | ||||
|  | ||||
|   if (!success) | ||||
|     return camera::ENCODER_ERROR_CONFIGURATION; | ||||
|  | ||||
|   if (this->out_of_output_memory_) { | ||||
|     if (this->buffer_expand_size_ <= 0) | ||||
|       return camera::ENCODER_ERROR_SKIP_FRAME; | ||||
|  | ||||
|     size_t current_size = this->output_->get_max_size(); | ||||
|     size_t new_size = this->output_->get_max_size() + this->buffer_expand_size_; | ||||
|     if (!this->output_->set_buffer_size(new_size)) { | ||||
|       ESP_LOGE(TAG, "Failed to expand output buffer."); | ||||
|       this->buffer_expand_size_ = 0; | ||||
|       return camera::ENCODER_ERROR_SKIP_FRAME; | ||||
|     } | ||||
|  | ||||
|     ESP_LOGD(TAG, "Output buffer expanded (%u -> %u).", current_size, this->output_->get_max_size()); | ||||
|     return camera::ENCODER_ERROR_RETRY_FRAME; | ||||
|   } | ||||
|  | ||||
|   this->output_->set_buffer_size(this->bytes_written_); | ||||
|   return camera::ENCODER_ERROR_SUCCESS; | ||||
| } | ||||
|  | ||||
| void ESP32CameraJPEGEncoder::dump_config() { | ||||
|   ESP_LOGCONFIG(TAG, | ||||
|                 "ESP32 Camera JPEG Encoder:\n" | ||||
|                 "  Size: %zu\n" | ||||
|                 "  Quality: %d\n" | ||||
|                 "  Expand: %d\n", | ||||
|                 this->output_->get_max_size(), this->quality_, this->buffer_expand_size_); | ||||
| } | ||||
|  | ||||
| size_t ESP32CameraJPEGEncoder::callback_(void *arg, size_t index, const void *data, size_t len) { | ||||
|   ESP32CameraJPEGEncoder *that = reinterpret_cast<ESP32CameraJPEGEncoder *>(arg); | ||||
|   uint8_t *buffer = that->output_->get_data(); | ||||
|   size_t buffer_length = that->output_->get_max_size(); | ||||
|   if (index + len > buffer_length) { | ||||
|     that->out_of_output_memory_ = true; | ||||
|     return 0; | ||||
|   } | ||||
|  | ||||
|   std::memcpy(&buffer[index], data, len); | ||||
|   that->bytes_written_ += len; | ||||
|   return len; | ||||
| } | ||||
|  | ||||
| pixformat_t ESP32CameraJPEGEncoder::to_internal_(camera::PixelFormat format) { | ||||
|   switch (format) { | ||||
|     case camera::PIXEL_FORMAT_GRAYSCALE: | ||||
|       return PIXFORMAT_GRAYSCALE; | ||||
|     case camera::PIXEL_FORMAT_RGB565: | ||||
|       return PIXFORMAT_RGB565; | ||||
|     // Internal representation for RGB is in byte order: B, G, R | ||||
|     case camera::PIXEL_FORMAT_BGR888: | ||||
|       return PIXFORMAT_RGB888; | ||||
|   } | ||||
|  | ||||
|   return PIXFORMAT_GRAYSCALE; | ||||
| } | ||||
|  | ||||
| }  // namespace esphome::camera_encoder | ||||
|  | ||||
| #endif | ||||
| @@ -0,0 +1,39 @@ | ||||
| #pragma once | ||||
|  | ||||
| #ifdef USE_ESP32_CAMERA_JPEG_ENCODER | ||||
|  | ||||
| #include <esp_camera.h> | ||||
|  | ||||
| #include "esphome/components/camera/encoder.h" | ||||
|  | ||||
| namespace esphome::camera_encoder { | ||||
|  | ||||
| /// Encoder that uses the software-based JPEG implementation from Espressif's esp32-camera component. | ||||
| class ESP32CameraJPEGEncoder : public camera::Encoder { | ||||
|  public: | ||||
|   /// Constructs a ESP32CameraJPEGEncoder instance. | ||||
|   /// @param quality Sets the quality of the encoded image (1-100). | ||||
|   /// @param output Pointer to preallocated output buffer. | ||||
|   ESP32CameraJPEGEncoder(uint8_t quality, camera::EncoderBuffer *output); | ||||
|   /// Sets the number of bytes to expand the output buffer on underflow during encoding. | ||||
|   /// @param buffer_expand_size Number of bytes to expand the buffer. | ||||
|   void set_buffer_expand_size(size_t buffer_expand_size) { this->buffer_expand_size_ = buffer_expand_size; } | ||||
|   // -------- Encoder -------- | ||||
|   camera::EncoderError encode_pixels(camera::CameraImageSpec *spec, camera::Buffer *pixels) override; | ||||
|   camera::EncoderBuffer *get_output_buffer() override { return output_; } | ||||
|   void dump_config() override; | ||||
|   // ------------------------- | ||||
|  protected: | ||||
|   static size_t callback_(void *arg, size_t index, const void *data, size_t len); | ||||
|   pixformat_t to_internal_(camera::PixelFormat format); | ||||
|  | ||||
|   camera::EncoderBuffer *output_{}; | ||||
|   size_t buffer_expand_size_{}; | ||||
|   size_t bytes_written_{}; | ||||
|   uint8_t quality_{}; | ||||
|   bool out_of_output_memory_{}; | ||||
| }; | ||||
|  | ||||
| }  // namespace esphome::camera_encoder | ||||
|  | ||||
| #endif | ||||
| @@ -176,7 +176,7 @@ async def display_page_show_to_code(config, action_id, template_arg, args): | ||||
|     DisplayPageShowNextAction, | ||||
|     maybe_simple_id( | ||||
|         { | ||||
|             cv.Required(CONF_ID): cv.templatable(cv.use_id(Display)), | ||||
|             cv.GenerateID(CONF_ID): cv.templatable(cv.use_id(Display)), | ||||
|         } | ||||
|     ), | ||||
| ) | ||||
| @@ -190,7 +190,7 @@ async def display_page_show_next_to_code(config, action_id, template_arg, args): | ||||
|     DisplayPageShowPrevAction, | ||||
|     maybe_simple_id( | ||||
|         { | ||||
|             cv.Required(CONF_ID): cv.templatable(cv.use_id(Display)), | ||||
|             cv.GenerateID(CONF_ID): cv.templatable(cv.use_id(Display)), | ||||
|         } | ||||
|     ), | ||||
| ) | ||||
|   | ||||
| @@ -122,7 +122,7 @@ uint8_t Mcp4461Component::get_status_register_() { | ||||
|   uint8_t addr = static_cast<uint8_t>(Mcp4461Addresses::MCP4461_STATUS); | ||||
|   uint8_t reg = addr | static_cast<uint8_t>(Mcp4461Commands::READ); | ||||
|   uint16_t buf; | ||||
|   if (!this->read_byte_16(reg, &buf)) { | ||||
|   if (!this->read_16_(reg, &buf)) { | ||||
|     this->error_code_ = MCP4461_STATUS_REGISTER_ERROR; | ||||
|     this->mark_failed(); | ||||
|     return 0; | ||||
| @@ -148,6 +148,20 @@ void Mcp4461Component::read_status_register_to_log() { | ||||
|            ((status_register_value >> 3) & 0x01), ((status_register_value >> 2) & 0x01), | ||||
|            ((status_register_value >> 1) & 0x01), ((status_register_value >> 0) & 0x01)); | ||||
| } | ||||
| bool Mcp4461Component::read_16_(uint8_t address, uint16_t *buf) { | ||||
|   // read 16 bits and convert from big endian to host, | ||||
|   // Do this as two separate operations to ensure a stop condition between the write and read | ||||
|   i2c::ErrorCode err = this->write(&address, 1); | ||||
|   if (err != i2c::ERROR_OK) { | ||||
|     return false; | ||||
|   } | ||||
|   err = this->read(reinterpret_cast<uint8_t *>(buf), 2); | ||||
|   if (err != i2c::ERROR_OK) { | ||||
|     return false; | ||||
|   } | ||||
|   *buf = convert_big_endian(*buf); | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| uint8_t Mcp4461Component::get_wiper_address_(uint8_t wiper) { | ||||
|   uint8_t addr; | ||||
| @@ -205,7 +219,7 @@ uint16_t Mcp4461Component::read_wiper_level_(uint8_t wiper_idx) { | ||||
|     } | ||||
|   } | ||||
|   uint16_t buf = 0; | ||||
|   if (!(this->read_byte_16(reg, &buf))) { | ||||
|   if (!(this->read_16_(reg, &buf))) { | ||||
|     this->error_code_ = MCP4461_STATUS_I2C_ERROR; | ||||
|     this->status_set_warning(); | ||||
|     ESP_LOGW(TAG, "Error fetching %swiper %u value", (wiper_idx > 3) ? "nonvolatile " : "", wiper_idx); | ||||
| @@ -392,7 +406,7 @@ uint8_t Mcp4461Component::get_terminal_register_(Mcp4461TerminalIdx terminal_con | ||||
|                                                               : static_cast<uint8_t>(Mcp4461Addresses::MCP4461_TCON1); | ||||
|   reg |= static_cast<uint8_t>(Mcp4461Commands::READ); | ||||
|   uint16_t buf; | ||||
|   if (this->read_byte_16(reg, &buf)) { | ||||
|   if (this->read_16_(reg, &buf)) { | ||||
|     return static_cast<uint8_t>(buf & 0x00ff); | ||||
|   } else { | ||||
|     this->error_code_ = MCP4461_STATUS_I2C_ERROR; | ||||
| @@ -517,7 +531,7 @@ uint16_t Mcp4461Component::get_eeprom_value(Mcp4461EepromLocation location) { | ||||
|   if (!this->is_eeprom_ready_for_writing_(true)) { | ||||
|     return 0; | ||||
|   } | ||||
|   if (!this->read_byte_16(reg, &buf)) { | ||||
|   if (!this->read_16_(reg, &buf)) { | ||||
|     this->error_code_ = MCP4461_STATUS_I2C_ERROR; | ||||
|     this->status_set_warning(); | ||||
|     ESP_LOGW(TAG, "Error fetching EEPROM location value"); | ||||
|   | ||||
| @@ -96,6 +96,7 @@ class Mcp4461Component : public Component, public i2c::I2CDevice { | ||||
|  | ||||
|  protected: | ||||
|   friend class Mcp4461Wiper; | ||||
|   bool read_16_(uint8_t address, uint16_t *buf); | ||||
|   void update_write_protection_status_(); | ||||
|   uint8_t get_wiper_address_(uint8_t wiper); | ||||
|   uint16_t read_wiper_level_(uint8_t wiper); | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
| # Various configuration constants for MIPI displays | ||||
| # Various utility functions for MIPI DBI configuration | ||||
|  | ||||
| from typing import Any | ||||
| from typing import Any, Self | ||||
|  | ||||
| from esphome.components.const import CONF_COLOR_DEPTH | ||||
| from esphome.components.display import CONF_SHOW_TEST_CARD, display_ns | ||||
| @@ -222,7 +222,7 @@ def delay(ms): | ||||
|  | ||||
|  | ||||
| class DriverChip: | ||||
|     models = {} | ||||
|     models: dict[str, Self] = {} | ||||
|  | ||||
|     def __init__( | ||||
|         self, | ||||
|   | ||||
| @@ -16,7 +16,6 @@ DriverChip( | ||||
|     lane_bit_rate="750Mbps", | ||||
|     swap_xy=cv.UNDEFINED, | ||||
|     color_order="RGB", | ||||
|     reset_pin=27, | ||||
|     initsequence=[ | ||||
|         (0x30, 0x00), (0xF7, 0x49, 0x61, 0x02, 0x00), (0x30, 0x01), (0x04, 0x0C), (0x05, 0x00), (0x06, 0x00), | ||||
|         (0x0B, 0x11), (0x17, 0x00), (0x20, 0x04), (0x1F, 0x05), (0x23, 0x00), (0x25, 0x19), (0x28, 0x18), (0x29, 0x04), (0x2A, 0x01), | ||||
|   | ||||
| @@ -2,10 +2,13 @@ from __future__ import annotations | ||||
|  | ||||
| from pathlib import Path | ||||
|  | ||||
| from esphome import pins | ||||
| import esphome.codegen as cg | ||||
| from esphome.components.zephyr import ( | ||||
|     copy_files as zephyr_copy_files, | ||||
|     zephyr_add_pm_static, | ||||
|     zephyr_add_prj_conf, | ||||
|     zephyr_data, | ||||
|     zephyr_set_core_data, | ||||
|     zephyr_to_code, | ||||
| ) | ||||
| @@ -18,6 +21,8 @@ import esphome.config_validation as cv | ||||
| from esphome.const import ( | ||||
|     CONF_BOARD, | ||||
|     CONF_FRAMEWORK, | ||||
|     CONF_ID, | ||||
|     CONF_RESET_PIN, | ||||
|     KEY_CORE, | ||||
|     KEY_FRAMEWORK_VERSION, | ||||
|     KEY_TARGET_FRAMEWORK, | ||||
| @@ -90,18 +95,43 @@ def _detect_bootloader(config: ConfigType) -> ConfigType: | ||||
|     return config | ||||
|  | ||||
|  | ||||
| nrf52_ns = cg.esphome_ns.namespace("nrf52") | ||||
| DeviceFirmwareUpdate = nrf52_ns.class_("DeviceFirmwareUpdate", cg.Component) | ||||
|  | ||||
| CONF_DFU = "dfu" | ||||
|  | ||||
| CONFIG_SCHEMA = cv.All( | ||||
|     _detect_bootloader, | ||||
|     set_core_data, | ||||
|     cv.Schema( | ||||
|         { | ||||
|             cv.Required(CONF_BOARD): cv.string_strict, | ||||
|             cv.Optional(KEY_BOOTLOADER): cv.one_of(*BOOTLOADERS, lower=True), | ||||
|             cv.Optional(CONF_DFU): cv.Schema( | ||||
|                 { | ||||
|                     cv.GenerateID(): cv.declare_id(DeviceFirmwareUpdate), | ||||
|                     cv.Required(CONF_RESET_PIN): pins.gpio_output_pin_schema, | ||||
|                 } | ||||
|             ), | ||||
|         } | ||||
|     ), | ||||
|     _detect_bootloader, | ||||
|     set_core_data, | ||||
| ) | ||||
|  | ||||
|  | ||||
| def _validate_mcumgr(config): | ||||
|     bootloader = zephyr_data()[KEY_BOOTLOADER] | ||||
|     if bootloader == BOOTLOADER_MCUBOOT: | ||||
|         raise cv.Invalid(f"'{bootloader}' bootloader does not support DFU") | ||||
|  | ||||
|  | ||||
| def _final_validate(config): | ||||
|     if CONF_DFU in config: | ||||
|         _validate_mcumgr(config) | ||||
|  | ||||
|  | ||||
| FINAL_VALIDATE_SCHEMA = _final_validate | ||||
|  | ||||
|  | ||||
| @coroutine_with_priority(1000) | ||||
| async def to_code(config: ConfigType) -> None: | ||||
|     """Convert the configuration to code.""" | ||||
| @@ -136,6 +166,19 @@ async def to_code(config: ConfigType) -> None: | ||||
|  | ||||
|     zephyr_to_code(config) | ||||
|  | ||||
|     if dfu_config := config.get(CONF_DFU): | ||||
|         CORE.add_job(_dfu_to_code, dfu_config) | ||||
|  | ||||
|  | ||||
| @coroutine_with_priority(90) | ||||
| async def _dfu_to_code(dfu_config): | ||||
|     cg.add_define("USE_NRF52_DFU") | ||||
|     var = cg.new_Pvariable(dfu_config[CONF_ID]) | ||||
|     pin = await cg.gpio_pin_expression(dfu_config[CONF_RESET_PIN]) | ||||
|     cg.add(var.set_reset_pin(pin)) | ||||
|     zephyr_add_prj_conf("CDC_ACM_DTE_RATE_CALLBACK_SUPPORT", True) | ||||
|     await cg.register_component(var, dfu_config) | ||||
|  | ||||
|  | ||||
| def copy_files() -> None: | ||||
|     """Copy files to the build directory.""" | ||||
|   | ||||
| @@ -2,6 +2,7 @@ BOOTLOADER_ADAFRUIT = "adafruit" | ||||
| BOOTLOADER_ADAFRUIT_NRF52_SD132 = "adafruit_nrf52_sd132" | ||||
| BOOTLOADER_ADAFRUIT_NRF52_SD140_V6 = "adafruit_nrf52_sd140_v6" | ||||
| BOOTLOADER_ADAFRUIT_NRF52_SD140_V7 = "adafruit_nrf52_sd140_v7" | ||||
|  | ||||
| EXTRA_ADC = [ | ||||
|     "VDD", | ||||
|     "VDDHDIV5", | ||||
|   | ||||
							
								
								
									
										51
									
								
								esphome/components/nrf52/dfu.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								esphome/components/nrf52/dfu.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | ||||
| #include "dfu.h" | ||||
|  | ||||
| #ifdef USE_NRF52_DFU | ||||
|  | ||||
| #include <zephyr/device.h> | ||||
| #include <zephyr/drivers/uart.h> | ||||
| #include <zephyr/drivers/uart/cdc_acm.h> | ||||
| #include "esphome/core/log.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace nrf52 { | ||||
|  | ||||
| static const char *const TAG = "dfu"; | ||||
|  | ||||
| volatile bool goto_dfu = false;  // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) | ||||
|  | ||||
| static const uint32_t DFU_DBL_RESET_MAGIC = 0x5A1AD5;  // SALADS | ||||
|  | ||||
| #define DEVICE_AND_COMMA(node_id) DEVICE_DT_GET(node_id), | ||||
|  | ||||
| static void cdc_dte_rate_callback(const struct device * /*unused*/, uint32_t rate) { | ||||
|   if (rate == 1200) { | ||||
|     goto_dfu = true; | ||||
|   } | ||||
| } | ||||
| void DeviceFirmwareUpdate::setup() { | ||||
|   this->reset_pin_->setup(); | ||||
|   const struct device *cdc_dev[] = {DT_FOREACH_STATUS_OKAY(zephyr_cdc_acm_uart, DEVICE_AND_COMMA)}; | ||||
|   for (auto &idx : cdc_dev) { | ||||
|     cdc_acm_dte_rate_callback_set(idx, cdc_dte_rate_callback); | ||||
|   } | ||||
| } | ||||
|  | ||||
| void DeviceFirmwareUpdate::loop() { | ||||
|   if (goto_dfu) { | ||||
|     goto_dfu = false; | ||||
|     volatile uint32_t *dbl_reset_mem = (volatile uint32_t *) 0x20007F7C; | ||||
|     (*dbl_reset_mem) = DFU_DBL_RESET_MAGIC; | ||||
|     this->reset_pin_->digital_write(true); | ||||
|   } | ||||
| } | ||||
|  | ||||
| void DeviceFirmwareUpdate::dump_config() { | ||||
|   ESP_LOGCONFIG(TAG, "DFU:"); | ||||
|   LOG_PIN("  RESET Pin: ", this->reset_pin_); | ||||
| } | ||||
|  | ||||
| }  // namespace nrf52 | ||||
| }  // namespace esphome | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										24
									
								
								esphome/components/nrf52/dfu.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								esphome/components/nrf52/dfu.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/core/defines.h" | ||||
| #ifdef USE_NRF52_DFU | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/core/gpio.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace nrf52 { | ||||
| class DeviceFirmwareUpdate : public Component { | ||||
|  public: | ||||
|   void setup() override; | ||||
|   void loop() override; | ||||
|   void set_reset_pin(GPIOPin *reset) { this->reset_pin_ = reset; } | ||||
|   void dump_config() override; | ||||
|  | ||||
|  protected: | ||||
|   GPIOPin *reset_pin_; | ||||
| }; | ||||
|  | ||||
| }  // namespace nrf52 | ||||
| }  // namespace esphome | ||||
|  | ||||
| #endif | ||||
| @@ -14,8 +14,13 @@ namespace sntp { | ||||
|  | ||||
| static const char *const TAG = "sntp"; | ||||
|  | ||||
| #if defined(USE_ESP32) | ||||
| SNTPComponent *SNTPComponent::instance = nullptr;  // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) | ||||
| #endif | ||||
|  | ||||
| void SNTPComponent::setup() { | ||||
| #if defined(USE_ESP32) | ||||
|   SNTPComponent::instance = this; | ||||
|   if (esp_sntp_enabled()) { | ||||
|     esp_sntp_stop(); | ||||
|   } | ||||
| @@ -25,6 +30,11 @@ void SNTPComponent::setup() { | ||||
|     esp_sntp_setservername(i++, server.c_str()); | ||||
|   } | ||||
|   esp_sntp_set_sync_interval(this->get_update_interval()); | ||||
|   esp_sntp_set_time_sync_notification_cb([](struct timeval *tv) { | ||||
|     if (SNTPComponent::instance != nullptr) { | ||||
|       SNTPComponent::instance->defer([]() { SNTPComponent::instance->time_synced(); }); | ||||
|     } | ||||
|   }); | ||||
|   esp_sntp_init(); | ||||
| #else | ||||
|   sntp_stop(); | ||||
| @@ -34,6 +44,14 @@ void SNTPComponent::setup() { | ||||
|   for (auto &server : this->servers_) { | ||||
|     sntp_setservername(i++, server.c_str()); | ||||
|   } | ||||
|  | ||||
| #if defined(USE_ESP8266) | ||||
|   settimeofday_cb([this](bool from_sntp) { | ||||
|     if (from_sntp) | ||||
|       this->time_synced(); | ||||
|   }); | ||||
| #endif | ||||
|  | ||||
|   sntp_init(); | ||||
| #endif | ||||
| } | ||||
| @@ -46,7 +64,8 @@ void SNTPComponent::dump_config() { | ||||
| } | ||||
| void SNTPComponent::update() { | ||||
| #if !defined(USE_ESP32) | ||||
|   // force resync | ||||
|   // Some platforms currently cannot set the sync interval at runtime so we need | ||||
|   // to do the re-sync by hand for now. | ||||
|   if (sntp_enabled()) { | ||||
|     sntp_stop(); | ||||
|     this->has_time_ = false; | ||||
| @@ -55,23 +74,31 @@ void SNTPComponent::update() { | ||||
| #endif | ||||
| } | ||||
| void SNTPComponent::loop() { | ||||
| // The loop is used to infer whether we have valid time on platforms where we | ||||
| // cannot tell whether SNTP has succeeded. | ||||
| // One limitation of this approach is that we cannot tell if it was the SNTP | ||||
| // component that set the time. | ||||
| // ESP-IDF and ESP8266 use callbacks from the SNTP task to trigger the | ||||
| // `on_time_sync` trigger on successful sync events. | ||||
| #if defined(USE_ESP32) || defined(USE_ESP8266) | ||||
|   this->disable_loop(); | ||||
| #endif | ||||
|  | ||||
|   if (this->has_time_) | ||||
|     return; | ||||
|  | ||||
|   this->time_synced(); | ||||
| } | ||||
|  | ||||
| void SNTPComponent::time_synced() { | ||||
|   auto time = this->now(); | ||||
|   if (!time.is_valid()) | ||||
|   this->has_time_ = time.is_valid(); | ||||
|   if (!this->has_time_) | ||||
|     return; | ||||
|  | ||||
|   ESP_LOGD(TAG, "Synchronized time: %04d-%02d-%02d %02d:%02d:%02d", time.year, time.month, time.day_of_month, time.hour, | ||||
|            time.minute, time.second); | ||||
|   this->time_sync_callback_.call(); | ||||
|   this->has_time_ = true; | ||||
|  | ||||
| #ifdef USE_ESP_IDF | ||||
|   // On ESP-IDF, time sync is permanent and update() doesn't force resync | ||||
|   // Time is now synchronized, no need to check anymore | ||||
|   this->disable_loop(); | ||||
| #endif | ||||
| } | ||||
|  | ||||
| }  // namespace sntp | ||||
|   | ||||
| @@ -26,9 +26,16 @@ class SNTPComponent : public time::RealTimeClock { | ||||
|   void update() override; | ||||
|   void loop() override; | ||||
|  | ||||
|   void time_synced(); | ||||
|  | ||||
|  protected: | ||||
|   std::vector<std::string> servers_; | ||||
|   bool has_time_{false}; | ||||
|  | ||||
| #if defined(USE_ESP32) | ||||
|  private: | ||||
|   static SNTPComponent *instance; | ||||
| #endif | ||||
| }; | ||||
|  | ||||
| }  // namespace sntp | ||||
|   | ||||
| @@ -654,12 +654,14 @@ const char *get_disconnect_reason_str(uint8_t reason) { | ||||
|       return "Association comeback time too long"; | ||||
|     case WIFI_REASON_SA_QUERY_TIMEOUT: | ||||
|       return "SA query timeout"; | ||||
| #if (ESP_IDF_VERSION_MAJOR >= 5) && (ESP_IDF_VERSION_MINOR >= 2) | ||||
|     case WIFI_REASON_NO_AP_FOUND_W_COMPATIBLE_SECURITY: | ||||
|       return "No AP found with compatible security"; | ||||
|     case WIFI_REASON_NO_AP_FOUND_IN_AUTHMODE_THRESHOLD: | ||||
|       return "No AP found in auth mode threshold"; | ||||
|     case WIFI_REASON_NO_AP_FOUND_IN_RSSI_THRESHOLD: | ||||
|       return "No AP found in RSSI threshold"; | ||||
| #endif | ||||
|     case WIFI_REASON_UNSPECIFIED: | ||||
|     default: | ||||
|       return "Unspecified"; | ||||
|   | ||||
| @@ -240,6 +240,10 @@ | ||||
| #define USE_SOCKET_SELECT_SUPPORT | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_NRF52 | ||||
| #define USE_NRF52_DFU | ||||
| #endif | ||||
|  | ||||
| // Disabled feature flags | ||||
| // #define USE_BSEC   // Requires a library with proprietary license | ||||
| // #define USE_BSEC2  // Requires a library with proprietary license | ||||
|   | ||||
| @@ -45,10 +45,15 @@ void EntityBase::set_icon(const char *icon) { | ||||
| #endif | ||||
| } | ||||
|  | ||||
| // Check if the object_id is dynamic (changes with MAC suffix) | ||||
| bool EntityBase::is_object_id_dynamic_() const { | ||||
|   return !this->flags_.has_own_name && App.is_name_add_mac_suffix_enabled(); | ||||
| } | ||||
|  | ||||
| // Entity Object ID | ||||
| std::string EntityBase::get_object_id() const { | ||||
|   // Check if `App.get_friendly_name()` is constant or dynamic. | ||||
|   if (!this->flags_.has_own_name && App.is_name_add_mac_suffix_enabled()) { | ||||
|   if (this->is_object_id_dynamic_()) { | ||||
|     // `App.get_friendly_name()` is dynamic. | ||||
|     return str_sanitize(str_snake_case(App.get_friendly_name())); | ||||
|   } | ||||
| @@ -58,7 +63,7 @@ std::string EntityBase::get_object_id() const { | ||||
| StringRef EntityBase::get_object_id_ref_for_api_() const { | ||||
|   static constexpr auto EMPTY_STRING = StringRef::from_lit(""); | ||||
|   // Return empty for dynamic case (MAC suffix) | ||||
|   if (!this->flags_.has_own_name && App.is_name_add_mac_suffix_enabled()) { | ||||
|   if (this->is_object_id_dynamic_()) { | ||||
|     return EMPTY_STRING; | ||||
|   } | ||||
|   // For static case, return the string or empty if null | ||||
| @@ -70,7 +75,10 @@ void EntityBase::set_object_id(const char *object_id) { | ||||
| } | ||||
|  | ||||
| // Calculate Object ID Hash from Entity Name | ||||
| void EntityBase::calc_object_id_() { this->object_id_hash_ = fnv1_hash(this->get_object_id()); } | ||||
| void EntityBase::calc_object_id_() { | ||||
|   this->object_id_hash_ = | ||||
|       fnv1_hash(this->is_object_id_dynamic_() ? this->get_object_id().c_str() : this->object_id_c_str_); | ||||
| } | ||||
|  | ||||
| uint32_t EntityBase::get_object_id_hash() { return this->object_id_hash_; } | ||||
|  | ||||
|   | ||||
| @@ -126,6 +126,9 @@ class EntityBase { | ||||
|   virtual uint32_t hash_base() { return 0L; } | ||||
|   void calc_object_id_(); | ||||
|  | ||||
|   /// Check if the object_id is dynamic (changes with MAC suffix) | ||||
|   bool is_object_id_dynamic_() const; | ||||
|  | ||||
|   StringRef name_; | ||||
|   const char *object_id_c_str_{nullptr}; | ||||
| #ifdef USE_ENTITY_ICON | ||||
|   | ||||
| @@ -142,11 +142,13 @@ uint16_t crc16be(const uint8_t *data, uint16_t len, uint16_t crc, uint16_t poly, | ||||
|   return refout ? (crc ^ 0xffff) : crc; | ||||
| } | ||||
|  | ||||
| uint32_t fnv1_hash(const std::string &str) { | ||||
| uint32_t fnv1_hash(const char *str) { | ||||
|   uint32_t hash = 2166136261UL; | ||||
|   for (char c : str) { | ||||
|     hash *= 16777619UL; | ||||
|     hash ^= c; | ||||
|   if (str) { | ||||
|     while (*str) { | ||||
|       hash *= 16777619UL; | ||||
|       hash ^= *str++; | ||||
|     } | ||||
|   } | ||||
|   return hash; | ||||
| } | ||||
|   | ||||
| @@ -155,7 +155,8 @@ uint16_t crc16be(const uint8_t *data, uint16_t len, uint16_t crc = 0, uint16_t p | ||||
|                  bool refout = false); | ||||
|  | ||||
| /// Calculate a FNV-1 hash of \p str. | ||||
| uint32_t fnv1_hash(const std::string &str); | ||||
| uint32_t fnv1_hash(const char *str); | ||||
| inline uint32_t fnv1_hash(const std::string &str) { return fnv1_hash(str.c_str()); } | ||||
|  | ||||
| /// Return a random 32-bit unsigned integer. | ||||
| uint32_t random_uint32(); | ||||
|   | ||||
							
								
								
									
										5
									
								
								tests/components/camera_encoder/common.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								tests/components/camera_encoder/common.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| camera_encoder: | ||||
|   id: jpeg_encoder | ||||
|   quality: 80 | ||||
|   buffer_size: 4096 | ||||
|   buffer_expand_size: 1024 | ||||
							
								
								
									
										1
									
								
								tests/components/camera_encoder/test.esp32-ard.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tests/components/camera_encoder/test.esp32-ard.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| <<: !include common.yaml | ||||
							
								
								
									
										1
									
								
								tests/components/camera_encoder/test.esp32-idf.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tests/components/camera_encoder/test.esp32-idf.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| <<: !include common.yaml | ||||
							
								
								
									
										7
									
								
								tests/components/nrf52/test.nrf52-adafruit.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								tests/components/nrf52/test.nrf52-adafruit.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| nrf52: | ||||
|   dfu: | ||||
|     reset_pin: | ||||
|       number: 14 | ||||
|       inverted: true | ||||
|       mode: | ||||
|         output: true | ||||
		Reference in New Issue
	
	Block a user