From 22f30d42a668e89b64318b1d8bf6c5cde38e2b21 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 29 Oct 2024 09:05:51 +1100 Subject: [PATCH 1/2] [lvgl] Implement qrcode (#7623) --- esphome/components/lvgl/__init__.py | 2 + esphome/components/lvgl/widgets/qrcode.py | 54 +++++++++++++++++++++++ tests/components/lvgl/lvgl-package.yaml | 12 +++++ 3 files changed, 68 insertions(+) create mode 100644 esphome/components/lvgl/widgets/qrcode.py diff --git a/esphome/components/lvgl/__init__.py b/esphome/components/lvgl/__init__.py index 215fdecdb5..4a1a26cc0b 100644 --- a/esphome/components/lvgl/__init__.py +++ b/esphome/components/lvgl/__init__.py @@ -71,6 +71,7 @@ from .widgets.meter import meter_spec from .widgets.msgbox import MSGBOX_SCHEMA, msgboxes_to_code from .widgets.obj import obj_spec from .widgets.page import add_pages, generate_page_triggers, page_spec +from .widgets.qrcode import qr_code_spec from .widgets.roller import roller_spec from .widgets.slider import slider_spec from .widgets.spinbox import spinbox_spec @@ -109,6 +110,7 @@ for w_type in ( spinbox_spec, keyboard_spec, tileview_spec, + qr_code_spec, ): WIDGET_TYPES[w_type.name] = w_type diff --git a/esphome/components/lvgl/widgets/qrcode.py b/esphome/components/lvgl/widgets/qrcode.py new file mode 100644 index 0000000000..742b538938 --- /dev/null +++ b/esphome/components/lvgl/widgets/qrcode.py @@ -0,0 +1,54 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.const import CONF_SIZE, CONF_TEXT +from esphome.cpp_generator import MockObjClass + +from ..defines import CONF_MAIN, literal +from ..lv_validation import color, color_retmapper, lv_text +from ..lvcode import LocalVariable, lv, lv_expr +from ..schemas import TEXT_SCHEMA +from ..types import WidgetType, lv_obj_t +from . import Widget + +CONF_QRCODE = "qrcode" +CONF_DARK_COLOR = "dark_color" +CONF_LIGHT_COLOR = "light_color" + +QRCODE_SCHEMA = TEXT_SCHEMA.extend( + { + cv.Optional(CONF_DARK_COLOR, default="black"): color, + cv.Optional(CONF_LIGHT_COLOR, default="white"): color, + cv.Required(CONF_SIZE): cv.int_, + } +) + + +class QrCodeType(WidgetType): + def __init__(self): + super().__init__( + CONF_QRCODE, + lv_obj_t, + (CONF_MAIN,), + QRCODE_SCHEMA, + modify_schema=TEXT_SCHEMA, + ) + + def get_uses(self): + return ("canvas", "img") + + def obj_creator(self, parent: MockObjClass, config: dict): + dark_color = color_retmapper(config[CONF_DARK_COLOR]) + light_color = color_retmapper(config[CONF_LIGHT_COLOR]) + size = config[CONF_SIZE] + return lv_expr.call("qrcode_create", parent, size, dark_color, light_color) + + async def to_code(self, w: Widget, config): + if (value := config.get(CONF_TEXT)) is not None: + value = await lv_text.process(value) + with LocalVariable( + "qr_text", cg.const_char_ptr, value, modifier="" + ) as str_obj: + lv.qrcode_update(w.obj, str_obj, literal(f"strlen({str_obj})")) + + +qr_code_spec = QrCodeType() diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index 4962a71596..9bfbb5fc95 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -458,6 +458,18 @@ lvgl: - id: page2 widgets: + - qrcode: + id: lv_qr + align: left_mid + size: 100 + light_color: whitesmoke + dark_color: steelblue + text: esphome.io + on_click: + lvgl.qrcode.update: + id: lv_qr + text: homeassistant.io + - slider: min_value: 0 max_value: 255 From 858d97ccefff68f92af1c8f722738424ff2db791 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 29 Oct 2024 09:08:29 +1100 Subject: [PATCH 2/2] [bytebuffer] Rework ByteBuffer using templates (#7638) --- CODEOWNERS | 1 + esphome/components/bytebuffer/__init__.py | 5 + esphome/components/bytebuffer/bytebuffer.h | 421 ++++++++++++++++++ esphome/core/bytebuffer.cpp | 167 ------- esphome/core/bytebuffer.h | 144 ------ tests/components/bytebuffer/common.yaml | 161 +++++++ .../components/bytebuffer/test.esp32-ard.yaml | 1 + .../bytebuffer/test.esp32-c3-ard.yaml | 1 + .../bytebuffer/test.esp32-c3-idf.yaml | 1 + .../components/bytebuffer/test.esp32-idf.yaml | 1 + .../bytebuffer/test.esp8266-ard.yaml | 1 + tests/components/bytebuffer/test.host.yaml | 1 + .../bytebuffer/test.rp2040-ard.yaml | 1 + 13 files changed, 595 insertions(+), 311 deletions(-) create mode 100644 esphome/components/bytebuffer/__init__.py create mode 100644 esphome/components/bytebuffer/bytebuffer.h delete mode 100644 esphome/core/bytebuffer.cpp delete mode 100644 esphome/core/bytebuffer.h create mode 100644 tests/components/bytebuffer/common.yaml create mode 100644 tests/components/bytebuffer/test.esp32-ard.yaml create mode 100644 tests/components/bytebuffer/test.esp32-c3-ard.yaml create mode 100644 tests/components/bytebuffer/test.esp32-c3-idf.yaml create mode 100644 tests/components/bytebuffer/test.esp32-idf.yaml create mode 100644 tests/components/bytebuffer/test.esp8266-ard.yaml create mode 100644 tests/components/bytebuffer/test.host.yaml create mode 100644 tests/components/bytebuffer/test.rp2040-ard.yaml diff --git a/CODEOWNERS b/CODEOWNERS index f96e43d5b7..5eb1f863f2 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -85,6 +85,7 @@ esphome/components/bmp581/* @kahrendt esphome/components/bp1658cj/* @Cossid esphome/components/bp5758d/* @Cossid esphome/components/button/* @esphome/core +esphome/components/bytebuffer/* @clydebarrow esphome/components/canbus/* @danielschramm @mvturnho esphome/components/cap1188/* @mreditor97 esphome/components/captive_portal/* @OttoWinter diff --git a/esphome/components/bytebuffer/__init__.py b/esphome/components/bytebuffer/__init__.py new file mode 100644 index 0000000000..3c7c695118 --- /dev/null +++ b/esphome/components/bytebuffer/__init__.py @@ -0,0 +1,5 @@ +CODEOWNERS = ["@clydebarrow"] + +# Allows bytebuffer to be configured in yaml, to allow use of the C++ api. + +CONFIG_SCHEMA = {} diff --git a/esphome/components/bytebuffer/bytebuffer.h b/esphome/components/bytebuffer/bytebuffer.h new file mode 100644 index 0000000000..030484ce32 --- /dev/null +++ b/esphome/components/bytebuffer/bytebuffer.h @@ -0,0 +1,421 @@ +#pragma once + +#include +#include +#include +#include +#include "esphome/core/helpers.h" + +namespace esphome { +namespace bytebuffer { + +enum Endian { LITTLE, BIG }; + +/** + * A class modelled on the Java ByteBuffer class. It wraps a vector of bytes and permits putting and getting + * items of various sizes, with an automatically incremented position. + * + * There are three variables maintained pointing into the buffer: + * + * capacity: the maximum amount of data that can be stored - set on construction and cannot be changed + * limit: the limit of the data currently available to get or put + * position: the current insert or extract position + * + * 0 <= position <= limit <= capacity + * + * In addition a mark can be set to the current position with mark(). A subsequent call to reset() will restore + * the position to the mark. + * + * The buffer can be marked to be little-endian (default) or big-endian. All subsequent operations will use that order. + * + * The flip() operation will reset the position to 0 and limit to the current position. This is useful for reading + * data from a buffer after it has been written. + * + * The code is defined here in the header file rather than in a .cpp file, so that it does not get compiled if not used. + * The templated functions ensure that only those typed functions actually used are compiled. The functions + * are implicitly inline-able which will aid performance. + */ +class ByteBuffer { + public: + // Default constructor (compatibility with TEMPLATABLE_VALUE) + // Creates a zero-length ByteBuffer which is little use to anybody. + ByteBuffer() : ByteBuffer(std::vector()) {} + + /** + * Create a new Bytebuffer with the given capacity + */ + ByteBuffer(size_t capacity, Endian endianness = LITTLE) + : data_(std::vector(capacity)), endianness_(endianness), limit_(capacity){}; + + // templated functions to implement putting and getting data of various types. There are two flavours of all + // functions - one that uses the position as the offset, and updates the position accordingly, and one that + // takes an explicit offset and does not update the position. + // Separate temnplates are provided for types that fit into 32 bits and those that are bigger. These delegate + // the actual put/get to common code based around those sizes. + // This reduces the code size and execution time for smaller types. A similar structure for e.g. 16 bits is unlikely + // to provide any further benefit given that all target platforms are native 32 bit. + + template + T get(typename std::enable_if::value, T>::type * = 0, + typename std::enable_if<(sizeof(T) <= sizeof(uint32_t)), T>::type * = 0) { + // integral types that fit into 32 bit + return static_cast(this->get_uint32_(sizeof(T))); + } + + template + T get(size_t offset, typename std::enable_if::value, T>::type * = 0, + typename std::enable_if<(sizeof(T) <= sizeof(uint32_t)), T>::type * = 0) { + return static_cast(this->get_uint32_(offset, sizeof(T))); + } + + template + void put(const T &value, typename std::enable_if::value, T>::type * = 0, + typename std::enable_if<(sizeof(T) <= sizeof(uint32_t)), T>::type * = 0) { + this->put_uint32_(static_cast(value), sizeof(T)); + } + + template + void put(const T &value, size_t offset, typename std::enable_if::value, T>::type * = 0, + typename std::enable_if<(sizeof(T) <= sizeof(uint32_t)), T>::type * = 0) { + this->put_uint32_(static_cast(value), offset, sizeof(T)); + } + + // integral types that do not fit into 32 bit (basically only 64 bit types) + template + T get(typename std::enable_if::value, T>::type * = 0, + typename std::enable_if<(sizeof(T) == sizeof(uint64_t)), T>::type * = 0) { + return static_cast(this->get_uint64_(sizeof(T))); + } + + template + T get(size_t offset, typename std::enable_if::value, T>::type * = 0, + typename std::enable_if<(sizeof(T) == sizeof(uint64_t)), T>::type * = 0) { + return static_cast(this->get_uint64_(offset, sizeof(T))); + } + + template + void put(const T &value, typename std::enable_if::value, T>::type * = 0, + typename std::enable_if<(sizeof(T) == sizeof(uint64_t)), T>::type * = 0) { + this->put_uint64_(value, sizeof(T)); + } + + template + void put(const T &value, size_t offset, typename std::enable_if::value, T>::type * = 0, + typename std::enable_if<(sizeof(T) == sizeof(uint64_t)), T>::type * = 0) { + this->put_uint64_(static_cast(value), offset, sizeof(T)); + } + + // floating point types. Caters for 32 and 64 bit floating point. + template + T get(typename std::enable_if::value, T>::type * = 0, + typename std::enable_if<(sizeof(T) == sizeof(uint32_t)), T>::type * = 0) { + return bit_cast(this->get_uint32_(sizeof(T))); + } + + template + T get(typename std::enable_if::value, T>::type * = 0, + typename std::enable_if<(sizeof(T) == sizeof(uint64_t)), T>::type * = 0) { + return bit_cast(this->get_uint64_(sizeof(T))); + } + + template + T get(size_t offset, typename std::enable_if::value, T>::type * = 0, + typename std::enable_if<(sizeof(T) == sizeof(uint32_t)), T>::type * = 0) { + return bit_cast(this->get_uint32_(offset, sizeof(T))); + } + + template + T get(size_t offset, typename std::enable_if::value, T>::type * = 0, + typename std::enable_if<(sizeof(T) == sizeof(uint64_t)), T>::type * = 0) { + return bit_cast(this->get_uint64_(offset, sizeof(T))); + } + template + void put(const T &value, typename std::enable_if::value, T>::type * = 0, + typename std::enable_if<(sizeof(T) <= sizeof(uint32_t)), T>::type * = 0) { + this->put_uint32_(bit_cast(value), sizeof(T)); + } + + template + void put(const T &value, typename std::enable_if::value, T>::type * = 0, + typename std::enable_if<(sizeof(T) == sizeof(uint64_t)), T>::type * = 0) { + this->put_uint64_(bit_cast(value), sizeof(T)); + } + + template + void put(const T &value, size_t offset, typename std::enable_if::value, T>::type * = 0, + typename std::enable_if<(sizeof(T) <= sizeof(uint32_t)), T>::type * = 0) { + this->put_uint32_(bit_cast(value), offset, sizeof(T)); + } + + template + void put(const T &value, size_t offset, typename std::enable_if::value, T>::type * = 0, + typename std::enable_if<(sizeof(T) == sizeof(uint64_t)), T>::type * = 0) { + this->put_uint64_(bit_cast(value), offset, sizeof(T)); + } + + template static ByteBuffer wrap(T value, Endian endianness = LITTLE) { + ByteBuffer buffer = ByteBuffer(sizeof(T), endianness); + buffer.put(value); + buffer.flip(); + return buffer; + } + + static ByteBuffer wrap(std::vector const &data, Endian endianness = LITTLE) { + ByteBuffer buffer = {data}; + buffer.endianness_ = endianness; + return buffer; + } + + static ByteBuffer wrap(const uint8_t *ptr, size_t len, Endian endianness = LITTLE) { + return wrap(std::vector(ptr, ptr + len), endianness); + } + + // convenience functions with explicit types named.. + void put_float(float value) { this->put(value); } + void put_double(double value) { this->put(value); } + + uint8_t get_uint8() { return this->data_[this->position_++]; } + // Get a 16 bit unsigned value, increment by 2 + uint16_t get_uint16() { return this->get(); } + // Get a 24 bit unsigned value, increment by 3 + uint32_t get_uint24() { return this->get_uint32_(3); }; + // Get a 32 bit unsigned value, increment by 4 + uint32_t get_uint32() { return this->get(); }; + // Get a 64 bit unsigned value, increment by 8 + uint64_t get_uint64() { return this->get(); }; + // Signed versions of the get functions + uint8_t get_int8() { return static_cast(this->get_uint8()); }; + int16_t get_int16() { return this->get(); } + int32_t get_int32() { return this->get(); } + int64_t get_int64() { return this->get(); } + // Get a float value, increment by 4 + float get_float() { return this->get(); } + // Get a double value, increment by 8 + double get_double() { return this->get(); } + + // Get a bool value, increment by 1 + bool get_bool() { return static_cast(this->get_uint8()); } + + uint32_t get_int24(size_t offset) { + auto value = this->get_uint24(offset); + uint32_t mask = (~static_cast(0)) << 23; + if ((value & mask) != 0) + value |= mask; + return value; + } + + uint32_t get_int24() { + auto value = this->get_uint24(); + uint32_t mask = (~static_cast(0)) << 23; + if ((value & mask) != 0) + value |= mask; + return value; + } + std::vector get_vector(size_t length, size_t offset) { + auto start = this->data_.begin() + offset; + return {start, start + length}; + } + + std::vector get_vector(size_t length) { + auto result = this->get_vector(length, this->position_); + this->position_ += length; + return result; + } + + // Convenience named functions + void put_uint8(uint8_t value) { this->data_[this->position_++] = value; } + void put_uint16(uint16_t value) { this->put(value); } + void put_uint24(uint32_t value) { this->put_uint32_(value, 3); } + void put_uint32(uint32_t value) { this->put(value); } + void put_uint64(uint64_t value) { this->put(value); } + // Signed versions of the put functions + void put_int8(int8_t value) { this->put_uint8(static_cast(value)); } + void put_int16(int16_t value) { this->put(value); } + void put_int24(int32_t value) { this->put_uint32_(value, 3); } + void put_int32(int32_t value) { this->put(value); } + void put_int64(int64_t value) { this->put(value); } + // Extra put functions + void put_bool(bool value) { this->put_uint8(value); } + + // versions of the above with an offset, these do not update the position + + uint64_t get_uint64(size_t offset) { return this->get(offset); } + uint32_t get_uint24(size_t offset) { return this->get_uint32_(offset, 3); }; + double get_double(size_t offset) { return get(offset); } + + // Get one byte from the buffer, increment position by 1 + uint8_t get_uint8(size_t offset) { return this->data_[offset]; } + // Get a 16 bit unsigned value, increment by 2 + uint16_t get_uint16(size_t offset) { return get(offset); } + // Get a 24 bit unsigned value, increment by 3 + uint32_t get_uint32(size_t offset) { return this->get(offset); }; + // Get a 64 bit unsigned value, increment by 8 + uint8_t get_int8(size_t offset) { return get(offset); } + int16_t get_int16(size_t offset) { return get(offset); } + int32_t get_int32(size_t offset) { return get(offset); } + int64_t get_int64(size_t offset) { return get(offset); } + // Get a float value, increment by 4 + float get_float(size_t offset) { return get(offset); } + // Get a double value, increment by 8 + + // Get a bool value, increment by 1 + bool get_bool(size_t offset) { return this->get_uint8(offset); } + + void put_uint8(uint8_t value, size_t offset) { this->data_[offset] = value; } + void put_uint16(uint16_t value, size_t offset) { this->put(value, offset); } + void put_uint24(uint32_t value, size_t offset) { this->put(value, offset); } + void put_uint32(uint32_t value, size_t offset) { this->put(value, offset); } + void put_uint64(uint64_t value, size_t offset) { this->put(value, offset); } + // Signed versions of the put functions + void put_int8(int8_t value, size_t offset) { this->put_uint8(static_cast(value), offset); } + void put_int16(int16_t value, size_t offset) { this->put(value, offset); } + void put_int24(int32_t value, size_t offset) { this->put_uint32_(value, offset, 3); } + void put_int32(int32_t value, size_t offset) { this->put(value, offset); } + void put_int64(int64_t value, size_t offset) { this->put(value, offset); } + // Extra put functions + void put_float(float value, size_t offset) { this->put(value, offset); } + void put_double(double value, size_t offset) { this->put(value, offset); } + void put_bool(bool value, size_t offset) { this->put_uint8(value, offset); } + void put(const std::vector &value, size_t offset) { + std::copy(value.begin(), value.end(), this->data_.begin() + offset); + } + void put_vector(const std::vector &value, size_t offset) { this->put(value, offset); } + void put(const std::vector &value) { + this->put_vector(value, this->position_); + this->position_ += value.size(); + } + void put_vector(const std::vector &value) { this->put(value); } + + // Getters + + inline size_t get_capacity() const { return this->data_.size(); } + inline size_t get_position() const { return this->position_; } + inline size_t get_limit() const { return this->limit_; } + inline size_t get_remaining() const { return this->get_limit() - this->get_position(); } + inline Endian get_endianness() const { return this->endianness_; } + inline void mark() { this->mark_ = this->position_; } + inline void big_endian() { this->endianness_ = BIG; } + inline void little_endian() { this->endianness_ = LITTLE; } + // retrieve a pointer to the underlying data. + std::vector get_data() { return this->data_; }; + + void get_bytes(void *dest, size_t length) { + std::copy(this->data_.begin() + this->position_, this->data_.begin() + this->position_ + length, (uint8_t *) dest); + this->position_ += length; + } + + void get_bytes(void *dest, size_t length, size_t offset) { + std::copy(this->data_.begin() + offset, this->data_.begin() + offset + length, (uint8_t *) dest); + } + + void rewind() { this->position_ = 0; } + void reset() { this->position_ = this->mark_; } + + void set_limit(size_t limit) { this->limit_ = limit; } + void set_position(size_t position) { this->position_ = position; } + void clear() { + this->limit_ = this->get_capacity(); + this->position_ = 0; + } + void flip() { + this->limit_ = this->position_; + this->position_ = 0; + } + + protected: + uint64_t get_uint64_(size_t offset, size_t length) const { + uint64_t value = 0; + if (this->endianness_ == LITTLE) { + offset += length; + while (length-- != 0) { + value <<= 8; + value |= this->data_[--offset]; + } + } else { + while (length-- != 0) { + value <<= 8; + value |= this->data_[offset++]; + } + } + return value; + } + + uint64_t get_uint64_(size_t length) { + auto result = this->get_uint64_(this->position_, length); + this->position_ += length; + return result; + } + uint32_t get_uint32_(size_t offset, size_t length) const { + uint32_t value = 0; + if (this->endianness_ == LITTLE) { + offset += length; + while (length-- != 0) { + value <<= 8; + value |= this->data_[--offset]; + } + } else { + while (length-- != 0) { + value <<= 8; + value |= this->data_[offset++]; + } + } + return value; + } + + uint32_t get_uint32_(size_t length) { + auto result = this->get_uint32_(this->position_, length); + this->position_ += length; + return result; + } + + /// Putters + + void put_uint64_(uint64_t value, size_t length) { + this->put_uint64_(value, this->position_, length); + this->position_ += length; + } + void put_uint32_(uint32_t value, size_t length) { + this->put_uint32_(value, this->position_, length); + this->position_ += length; + } + + void put_uint64_(uint64_t value, size_t offset, size_t length) { + if (this->endianness_ == LITTLE) { + while (length-- != 0) { + this->data_[offset++] = static_cast(value); + value >>= 8; + } + } else { + offset += length; + while (length-- != 0) { + this->data_[--offset] = static_cast(value); + value >>= 8; + } + } + } + + void put_uint32_(uint32_t value, size_t offset, size_t length) { + if (this->endianness_ == LITTLE) { + while (length-- != 0) { + this->data_[offset++] = static_cast(value); + value >>= 8; + } + } else { + offset += length; + while (length-- != 0) { + this->data_[--offset] = static_cast(value); + value >>= 8; + } + } + } + ByteBuffer(std::vector const &data) : data_(data), limit_(data.size()) {} + + std::vector data_; + Endian endianness_{LITTLE}; + size_t position_{0}; + size_t mark_{0}; + size_t limit_{0}; +}; + +} // namespace bytebuffer +} // namespace esphome diff --git a/esphome/core/bytebuffer.cpp b/esphome/core/bytebuffer.cpp deleted file mode 100644 index 9dd32bf87a..0000000000 --- a/esphome/core/bytebuffer.cpp +++ /dev/null @@ -1,167 +0,0 @@ -#include "bytebuffer.h" -#include -#include "esphome/core/helpers.h" - -#include -#include - -namespace esphome { - -ByteBuffer ByteBuffer::wrap(const uint8_t *ptr, size_t len, Endian endianness) { - // there is a double copy happening here, could be optimized but at cost of clarity. - std::vector data(ptr, ptr + len); - ByteBuffer buffer = {data}; - buffer.endianness_ = endianness; - return buffer; -} - -ByteBuffer ByteBuffer::wrap(std::vector const &data, Endian endianness) { - ByteBuffer buffer = {data}; - buffer.endianness_ = endianness; - return buffer; -} - -ByteBuffer ByteBuffer::wrap(uint8_t value) { - ByteBuffer buffer = ByteBuffer(1); - buffer.put_uint8(value); - buffer.flip(); - return buffer; -} - -ByteBuffer ByteBuffer::wrap(uint16_t value, Endian endianness) { - ByteBuffer buffer = ByteBuffer(2, endianness); - buffer.put_uint16(value); - buffer.flip(); - return buffer; -} - -ByteBuffer ByteBuffer::wrap(uint32_t value, Endian endianness) { - ByteBuffer buffer = ByteBuffer(4, endianness); - buffer.put_uint32(value); - buffer.flip(); - return buffer; -} - -ByteBuffer ByteBuffer::wrap(uint64_t value, Endian endianness) { - ByteBuffer buffer = ByteBuffer(8, endianness); - buffer.put_uint64(value); - buffer.flip(); - return buffer; -} - -ByteBuffer ByteBuffer::wrap(float value, Endian endianness) { - ByteBuffer buffer = ByteBuffer(sizeof(float), endianness); - buffer.put_float(value); - buffer.flip(); - return buffer; -} - -ByteBuffer ByteBuffer::wrap(double value, Endian endianness) { - ByteBuffer buffer = ByteBuffer(sizeof(double), endianness); - buffer.put_double(value); - buffer.flip(); - return buffer; -} - -void ByteBuffer::set_limit(size_t limit) { - assert(limit <= this->get_capacity()); - this->limit_ = limit; -} -void ByteBuffer::set_position(size_t position) { - assert(position <= this->get_limit()); - this->position_ = position; -} -void ByteBuffer::clear() { - this->limit_ = this->get_capacity(); - this->position_ = 0; -} -void ByteBuffer::flip() { - this->limit_ = this->position_; - this->position_ = 0; -} - -/// Getters -uint8_t ByteBuffer::get_uint8() { - assert(this->get_remaining() >= 1); - return this->data_[this->position_++]; -} -uint64_t ByteBuffer::get_uint(size_t length) { - assert(this->get_remaining() >= length); - uint64_t value = 0; - if (this->endianness_ == LITTLE) { - this->position_ += length; - auto index = this->position_; - while (length-- != 0) { - value <<= 8; - value |= this->data_[--index]; - } - } else { - while (length-- != 0) { - value <<= 8; - value |= this->data_[this->position_++]; - } - } - return value; -} - -uint32_t ByteBuffer::get_int24() { - auto value = this->get_uint24(); - uint32_t mask = (~static_cast(0)) << 23; - if ((value & mask) != 0) - value |= mask; - return value; -} -float ByteBuffer::get_float() { - assert(this->get_remaining() >= sizeof(float)); - return bit_cast(this->get_uint32()); -} -double ByteBuffer::get_double() { - assert(this->get_remaining() >= sizeof(double)); - return bit_cast(this->get_uint64()); -} - -std::vector ByteBuffer::get_vector(size_t length) { - assert(this->get_remaining() >= length); - auto start = this->data_.begin() + this->position_; - this->position_ += length; - return {start, start + length}; -} - -/// Putters -void ByteBuffer::put_uint8(uint8_t value) { - assert(this->get_remaining() >= 1); - this->data_[this->position_++] = value; -} - -void ByteBuffer::put_uint(uint64_t value, size_t length) { - assert(this->get_remaining() >= length); - if (this->endianness_ == LITTLE) { - while (length-- != 0) { - this->data_[this->position_++] = static_cast(value); - value >>= 8; - } - } else { - this->position_ += length; - auto index = this->position_; - while (length-- != 0) { - this->data_[--index] = static_cast(value); - value >>= 8; - } - } -} -void ByteBuffer::put_float(float value) { - static_assert(sizeof(float) == sizeof(uint32_t), "Float sizes other than 32 bit not supported"); - assert(this->get_remaining() >= sizeof(float)); - this->put_uint32(bit_cast(value)); -} -void ByteBuffer::put_double(double value) { - static_assert(sizeof(double) == sizeof(uint64_t), "Double sizes other than 64 bit not supported"); - assert(this->get_remaining() >= sizeof(double)); - this->put_uint64(bit_cast(value)); -} -void ByteBuffer::put_vector(const std::vector &value) { - assert(this->get_remaining() >= value.size()); - std::copy(value.begin(), value.end(), this->data_.begin() + this->position_); - this->position_ += value.size(); -} -} // namespace esphome diff --git a/esphome/core/bytebuffer.h b/esphome/core/bytebuffer.h deleted file mode 100644 index d44d01f275..0000000000 --- a/esphome/core/bytebuffer.h +++ /dev/null @@ -1,144 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -namespace esphome { - -enum Endian { LITTLE, BIG }; - -/** - * A class modelled on the Java ByteBuffer class. It wraps a vector of bytes and permits putting and getting - * items of various sizes, with an automatically incremented position. - * - * There are three variables maintained pointing into the buffer: - * - * capacity: the maximum amount of data that can be stored - set on construction and cannot be changed - * limit: the limit of the data currently available to get or put - * position: the current insert or extract position - * - * 0 <= position <= limit <= capacity - * - * In addition a mark can be set to the current position with mark(). A subsequent call to reset() will restore - * the position to the mark. - * - * The buffer can be marked to be little-endian (default) or big-endian. All subsequent operations will use that order. - * - * The flip() operation will reset the position to 0 and limit to the current position. This is useful for reading - * data from a buffer after it has been written. - * - */ -class ByteBuffer { - public: - // Default constructor (compatibility with TEMPLATABLE_VALUE) - ByteBuffer() : ByteBuffer(std::vector()) {} - /** - * Create a new Bytebuffer with the given capacity - */ - ByteBuffer(size_t capacity, Endian endianness = LITTLE) - : data_(std::vector(capacity)), endianness_(endianness), limit_(capacity){}; - /** - * Wrap an existing vector in a ByteBufffer - */ - static ByteBuffer wrap(std::vector const &data, Endian endianness = LITTLE); - /** - * Wrap an existing array in a ByteBuffer. Note that this will create a copy of the data. - */ - static ByteBuffer wrap(const uint8_t *ptr, size_t len, Endian endianness = LITTLE); - // Convenience functions to create a ByteBuffer from a value - static ByteBuffer wrap(uint8_t value); - static ByteBuffer wrap(uint16_t value, Endian endianness = LITTLE); - static ByteBuffer wrap(uint32_t value, Endian endianness = LITTLE); - static ByteBuffer wrap(uint64_t value, Endian endianness = LITTLE); - static ByteBuffer wrap(int8_t value) { return wrap(static_cast(value)); } - static ByteBuffer wrap(int16_t value, Endian endianness = LITTLE) { - return wrap(static_cast(value), endianness); - } - static ByteBuffer wrap(int32_t value, Endian endianness = LITTLE) { - return wrap(static_cast(value), endianness); - } - static ByteBuffer wrap(int64_t value, Endian endianness = LITTLE) { - return wrap(static_cast(value), endianness); - } - static ByteBuffer wrap(float value, Endian endianness = LITTLE); - static ByteBuffer wrap(double value, Endian endianness = LITTLE); - static ByteBuffer wrap(bool value) { return wrap(static_cast(value)); } - - // Get an integral value from the buffer, increment position by length - uint64_t get_uint(size_t length); - // Get one byte from the buffer, increment position by 1 - uint8_t get_uint8(); - // Get a 16 bit unsigned value, increment by 2 - uint16_t get_uint16() { return static_cast(this->get_uint(sizeof(uint16_t))); }; - // Get a 24 bit unsigned value, increment by 3 - uint32_t get_uint24() { return static_cast(this->get_uint(3)); }; - // Get a 32 bit unsigned value, increment by 4 - uint32_t get_uint32() { return static_cast(this->get_uint(sizeof(uint32_t))); }; - // Get a 64 bit unsigned value, increment by 8 - uint64_t get_uint64() { return this->get_uint(sizeof(uint64_t)); }; - // Signed versions of the get functions - uint8_t get_int8() { return static_cast(this->get_uint8()); }; - int16_t get_int16() { return static_cast(this->get_uint(sizeof(int16_t))); } - uint32_t get_int24(); - int32_t get_int32() { return static_cast(this->get_uint(sizeof(int32_t))); } - int64_t get_int64() { return static_cast(this->get_uint(sizeof(int64_t))); } - // Get a float value, increment by 4 - float get_float(); - // Get a double value, increment by 8 - double get_double(); - // Get a bool value, increment by 1 - bool get_bool() { return this->get_uint8(); } - // Get vector of bytes, increment by length - std::vector get_vector(size_t length); - - // Put values into the buffer, increment the position accordingly - // put any integral value, length represents the number of bytes - void put_uint(uint64_t value, size_t length); - void put_uint8(uint8_t value); - void put_uint16(uint16_t value) { this->put_uint(value, sizeof(uint16_t)); } - void put_uint24(uint32_t value) { this->put_uint(value, 3); } - void put_uint32(uint32_t value) { this->put_uint(value, sizeof(uint32_t)); } - void put_uint64(uint64_t value) { this->put_uint(value, sizeof(uint64_t)); } - // Signed versions of the put functions - void put_int8(int8_t value) { this->put_uint8(static_cast(value)); } - void put_int16(int32_t value) { this->put_uint(static_cast(value), sizeof(uint16_t)); } - void put_int24(int32_t value) { this->put_uint(static_cast(value), 3); } - void put_int32(int32_t value) { this->put_uint(static_cast(value), sizeof(uint32_t)); } - void put_int64(int64_t value) { this->put_uint(static_cast(value), sizeof(uint64_t)); } - // Extra put functions - void put_float(float value); - void put_double(double value); - void put_bool(bool value) { this->put_uint8(value); } - void put_vector(const std::vector &value); - - inline size_t get_capacity() const { return this->data_.size(); } - inline size_t get_position() const { return this->position_; } - inline size_t get_limit() const { return this->limit_; } - inline size_t get_remaining() const { return this->get_limit() - this->get_position(); } - inline Endian get_endianness() const { return this->endianness_; } - inline void mark() { this->mark_ = this->position_; } - inline void big_endian() { this->endianness_ = BIG; } - inline void little_endian() { this->endianness_ = LITTLE; } - void set_limit(size_t limit); - void set_position(size_t position); - // set position to 0, limit to capacity. - void clear(); - // set limit to current position, postition to zero. Used when swapping from write to read operations. - void flip(); - // retrieve a pointer to the underlying data. - std::vector get_data() { return this->data_; }; - void rewind() { this->position_ = 0; } - void reset() { this->position_ = this->mark_; } - - protected: - ByteBuffer(std::vector const &data) : data_(data), limit_(data.size()) {} - std::vector data_; - Endian endianness_{LITTLE}; - size_t position_{0}; - size_t mark_{0}; - size_t limit_{0}; -}; - -} // namespace esphome diff --git a/tests/components/bytebuffer/common.yaml b/tests/components/bytebuffer/common.yaml new file mode 100644 index 0000000000..177f487e1e --- /dev/null +++ b/tests/components/bytebuffer/common.yaml @@ -0,0 +1,161 @@ +bytebuffer: + +esphome: + on_boot: + - lambda: |- + using namespace bytebuffer; + auto buf = ByteBuffer(16); + assert(buf.get_endianness() == LITTLE); + assert(buf.get_remaining() == 16); + buf.set_limit(10); + assert(buf.get_capacity() == 16); + buf.put_uint8(1); + assert(buf.get_remaining() == 9); + buf.put_uint16(0xABCD); + auto da = buf.get_data(); + assert(buf.get_uint8(0) == 1); + auto x = buf.get_uint16(1); + assert(buf.get_uint16(1) == 0xABCD); + assert(buf.get_remaining() == 7); + buf.put_uint32(0x12345678UL); + assert(buf.get_uint32(3) == 0x12345678UL); + assert(buf.get_remaining() == 3); + assert(buf.get_data()[1] == 0xCD); + assert(buf.get_data()[2] == 0xAB); + assert(buf.get_data()[3] == 0x78); + assert(buf.get_data()[4] == 0x56); + assert(buf.get_data()[5] == 0x34); + assert(buf.get_data()[6] == 0x12); + buf.flip(); + assert(buf.get_capacity() == 16); + assert(buf.get_uint32(3) == 0x12345678UL); + assert(buf.get_uint8(0) == 1); + assert(buf.get_uint16(1) == 0xABCD); + buf.put_uint16(0x1234, 1); + assert(buf.get_uint16(1) == 0x1234); + assert(buf.get_remaining() == 7); + assert(buf.get_uint8() == 1); + assert(buf.get_uint16() == 0x1234); + assert(buf.get_uint32() == 0x12345678ul); + assert(buf.get_remaining() == 0); + assert(buf.get_remaining() == 0); + buf.rewind(); + buf.big_endian(); + assert(buf.get_remaining() == 7); + assert(buf.get_uint8() == 1); + assert(buf.get_uint16() == 0x3412); + buf.mark(); + assert(buf.get_uint32() == 0x78563412ul); + assert(buf.get_remaining() == 0); + buf.reset(); + assert(buf.get_remaining() == 4); + assert(buf.get_uint32() == 0x78563412ul); + auto buf1 = ByteBuffer::wrap(buf.get_data().data(), buf.get_limit()); + buf.clear(); + assert(buf.get_position() == 0); + assert(buf.get_capacity() == 16); + assert(buf.get_limit() == 16); + assert(buf1.get_remaining() == 7); + assert(buf1.get_capacity() == 7); + buf1.set_position(3); + assert(buf1.get_uint32() == 0x12345678ul); + buf1.clear(); + assert(buf1.get_limit() == 7); + assert(buf1.get_capacity() == 7); + assert(buf1.get_position() == 0); + float f = 1.2345; + buf1.put_float(f); + buf1.flip(); + assert(buf1.get_remaining() == 4); + assert(buf1.get_float() == f); + buf1.clear(); + buf1.put_uint16(-32760); + buf1.put_uint24(-302760); + buf1.flip(); + assert(buf1.get_int16() == -32760); + assert(buf1.get_int24() == -302760); + uint8_t arr[4] = {0x10, 0x20, 0x30, 0x40}; + buf1 = ByteBuffer::wrap(arr, 4); + assert(buf1.get_capacity() == 4); + assert(buf1.get_limit() == 4); + assert(buf1.get_position() == 0); + assert(buf1.get_uint32() == 0x40302010UL); + assert(buf1.get_position() == 4); + assert(buf1.get_remaining() == 0); + std::vector vec{}; + vec.push_back(0x10); + vec.push_back(0x20); + vec.push_back(0x30); + vec.push_back(0x40); + buf1 = ByteBuffer::wrap(vec); + assert(buf1.get_capacity() == 4); + assert(buf1.get_limit() == 4); + assert(buf1.get_position() == 0); + buf1.mark(); + buf1.reset(); + assert(buf1.get_uint32() == 0x40302010UL); + buf = ByteBuffer::wrap(true); + assert(buf.get_bool() == true); + buf = ByteBuffer::wrap((uint8_t)0xFE); + assert(buf.get_uint8() == 0xFE); + buf = ByteBuffer::wrap((uint16_t)0xA5A6, BIG); + assert(buf.get_remaining() == 2); + assert(buf.get_position() == 0); + assert(buf.get_capacity() == 2); + assert(buf.get_endianness() == BIG); + assert(buf.get_data()[0] == 0xA5); + assert(buf.get_uint16() == 0xA5A6); + buf.flip(); + buf.little_endian(); + assert(buf.get_uint16() == 0xA6A5); + buf = ByteBuffer::wrap(f, BIG); + assert(buf.get_float() == f); + double d = 1.2345678E7; + buf = ByteBuffer::wrap(d, BIG); + assert(buf.get_double() == d); + buf = ByteBuffer::wrap({1, 2, 3, 4}, BIG); + assert(buf.get_endianness() == BIG); + assert(buf.get_remaining() == 4); + assert(buf.get_data()[2] == 3); + buf.little_endian(); + assert(buf.get_data()[2] == 3); + assert(buf.get_uint16() == 0x0201); + buf.big_endian(); + assert(buf.get_uint16() == 0x0304); + buf.rewind(); + vec = buf.get_vector(3); + assert(buf.get_remaining() == 1); + assert(vec[0] == 1); + assert(vec.size() == 3); + buf = ByteBuffer(10); + buf.put_vector(vec); + assert(buf.get_remaining() == 7); + buf.flip(); + assert(buf.get_remaining() == 3); + assert(buf.get_uint24() == 0x030201); + buf = ByteBuffer(64); + buf.put_uint8(1, 1); + buf.put_uint16(16, 2); + buf.put_uint32(1232, 4); + buf.put_uint64(123432ul, 8); + buf.put_float(1.2f, 16); + buf.put_int24(0x678, 20); + + assert(buf.get_uint8(1) == 1); + assert(buf.get(1) == 1); + assert(buf.get_uint16(2) == 16); + assert(buf.get(2) == 16); + assert(buf.get_uint32(4) == 1232); + assert(buf.get(4) == 1232); + assert(buf.get_uint64(8) == 123432ul); + assert(buf.get(8) == 123432ul); + assert(buf.get_float(16) == 1.2f); + assert(buf.get(16) == 1.2f); + assert(buf.get_int24(20) == 0x678); + buf.clear(); + buf.put(1.234, 10); + double dx = buf.get(10); + assert(dx == 1.234); + buf.put((uint16_t)1, 10); + assert(buf.get_uint16(10) == 1); + ESP_LOGD("bytebuffer", "******************** All tests succeeded"); diff --git a/tests/components/bytebuffer/test.esp32-ard.yaml b/tests/components/bytebuffer/test.esp32-ard.yaml new file mode 100644 index 0000000000..380ca87628 --- /dev/null +++ b/tests/components/bytebuffer/test.esp32-ard.yaml @@ -0,0 +1 @@ +!include common.yaml diff --git a/tests/components/bytebuffer/test.esp32-c3-ard.yaml b/tests/components/bytebuffer/test.esp32-c3-ard.yaml new file mode 100644 index 0000000000..380ca87628 --- /dev/null +++ b/tests/components/bytebuffer/test.esp32-c3-ard.yaml @@ -0,0 +1 @@ +!include common.yaml diff --git a/tests/components/bytebuffer/test.esp32-c3-idf.yaml b/tests/components/bytebuffer/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..380ca87628 --- /dev/null +++ b/tests/components/bytebuffer/test.esp32-c3-idf.yaml @@ -0,0 +1 @@ +!include common.yaml diff --git a/tests/components/bytebuffer/test.esp32-idf.yaml b/tests/components/bytebuffer/test.esp32-idf.yaml new file mode 100644 index 0000000000..380ca87628 --- /dev/null +++ b/tests/components/bytebuffer/test.esp32-idf.yaml @@ -0,0 +1 @@ +!include common.yaml diff --git a/tests/components/bytebuffer/test.esp8266-ard.yaml b/tests/components/bytebuffer/test.esp8266-ard.yaml new file mode 100644 index 0000000000..380ca87628 --- /dev/null +++ b/tests/components/bytebuffer/test.esp8266-ard.yaml @@ -0,0 +1 @@ +!include common.yaml diff --git a/tests/components/bytebuffer/test.host.yaml b/tests/components/bytebuffer/test.host.yaml new file mode 100644 index 0000000000..380ca87628 --- /dev/null +++ b/tests/components/bytebuffer/test.host.yaml @@ -0,0 +1 @@ +!include common.yaml diff --git a/tests/components/bytebuffer/test.rp2040-ard.yaml b/tests/components/bytebuffer/test.rp2040-ard.yaml new file mode 100644 index 0000000000..380ca87628 --- /dev/null +++ b/tests/components/bytebuffer/test.rp2040-ard.yaml @@ -0,0 +1 @@ +!include common.yaml