diff --git a/esphome/components/i2c/i2c.cpp b/esphome/components/i2c/i2c.cpp index 48e1cf8aca..31c21f398c 100644 --- a/esphome/components/i2c/i2c.cpp +++ b/esphome/components/i2c/i2c.cpp @@ -39,18 +39,22 @@ ErrorCode I2CDevice::read_register16(uint16_t a_register, uint8_t *data, size_t } ErrorCode I2CDevice::write_register(uint8_t a_register, const uint8_t *data, size_t len) const { - std::vector v{}; - v.push_back(a_register); - v.insert(v.end(), data, data + len); - return bus_->write_readv(this->address_, v.data(), v.size(), nullptr, 0); + SmallBufferWithHeapFallback<17> buffer_alloc; // Most I2C writes are <= 16 bytes + uint8_t *buffer = buffer_alloc.get(len + 1); + + buffer[0] = a_register; + std::copy(data, data + len, buffer + 1); + return this->bus_->write_readv(this->address_, buffer, len + 1, nullptr, 0); } ErrorCode I2CDevice::write_register16(uint16_t a_register, const uint8_t *data, size_t len) const { - std::vector v(len + 2); - v[0] = a_register >> 8; - v[1] = a_register; - std::copy(data, data + len, v.begin() + 2); - return bus_->write_readv(this->address_, v.data(), v.size(), nullptr, 0); + SmallBufferWithHeapFallback<18> buffer_alloc; // Most I2C writes are <= 16 bytes + 2 for register + uint8_t *buffer = buffer_alloc.get(len + 2); + + buffer[0] = a_register >> 8; + buffer[1] = a_register; + std::copy(data, data + len, buffer + 2); + return this->bus_->write_readv(this->address_, buffer, len + 2, nullptr, 0); } bool I2CDevice::read_bytes_16(uint8_t a_register, uint16_t *data, uint8_t len) { diff --git a/esphome/components/i2c/i2c_bus.h b/esphome/components/i2c/i2c_bus.h index df4df628e8..1acbe506a3 100644 --- a/esphome/components/i2c/i2c_bus.h +++ b/esphome/components/i2c/i2c_bus.h @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -10,6 +11,22 @@ namespace esphome { namespace i2c { +/// @brief Helper class for efficient buffer allocation - uses stack for small sizes, heap for large +template class SmallBufferWithHeapFallback { + public: + uint8_t *get(size_t size) { + if (size <= STACK_SIZE) { + return this->stack_buffer_; + } + this->heap_buffer_ = std::unique_ptr(new uint8_t[size]); + return this->heap_buffer_.get(); + } + + private: + uint8_t stack_buffer_[STACK_SIZE]; + std::unique_ptr heap_buffer_; +}; + /// @brief Error codes returned by I2CBus and I2CDevice methods enum ErrorCode { NO_ERROR = 0, ///< No error found during execution of method @@ -74,14 +91,17 @@ class I2CBus { for (size_t i = 0; i != count; i++) { total_len += read_buffers[i].len; } - std::vector buffer(total_len); - auto err = this->write_readv(address, nullptr, 0, buffer.data(), total_len); + + SmallBufferWithHeapFallback<128> buffer_alloc; // Most I2C reads are small + uint8_t *buffer = buffer_alloc.get(total_len); + + auto err = this->write_readv(address, nullptr, 0, buffer, total_len); if (err != ERROR_OK) return err; size_t pos = 0; for (size_t i = 0; i != count; i++) { if (read_buffers[i].len != 0) { - std::memcpy(read_buffers[i].data, buffer.data() + pos, read_buffers[i].len); + std::memcpy(read_buffers[i].data, buffer + pos, read_buffers[i].len); pos += read_buffers[i].len; } } @@ -91,11 +111,21 @@ class I2CBus { ESPDEPRECATED("This method is deprecated and will be removed in ESPHome 2026.3.0. Use write_readv() instead.", "2025.9.0") ErrorCode writev(uint8_t address, const WriteBuffer *write_buffers, size_t count, bool stop = true) { - std::vector buffer{}; + size_t total_len = 0; for (size_t i = 0; i != count; i++) { - buffer.insert(buffer.end(), write_buffers[i].data, write_buffers[i].data + write_buffers[i].len); + total_len += write_buffers[i].len; } - return this->write_readv(address, buffer.data(), buffer.size(), nullptr, 0); + + SmallBufferWithHeapFallback<128> buffer_alloc; // Most I2C writes are small + uint8_t *buffer = buffer_alloc.get(total_len); + + size_t pos = 0; + for (size_t i = 0; i != count; i++) { + std::memcpy(buffer + pos, write_buffers[i].data, write_buffers[i].len); + pos += write_buffers[i].len; + } + + return this->write_readv(address, buffer, total_len, nullptr, 0); } protected: