mirror of
https://github.com/esphome/esphome.git
synced 2025-09-08 14:22:21 +01:00
[i2c] Optimize memory usage with stack allocation for small buffers (#10565)
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
This commit is contained in:
@@ -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 {
|
ErrorCode I2CDevice::write_register(uint8_t a_register, const uint8_t *data, size_t len) const {
|
||||||
std::vector<uint8_t> v{};
|
SmallBufferWithHeapFallback<17> buffer_alloc; // Most I2C writes are <= 16 bytes
|
||||||
v.push_back(a_register);
|
uint8_t *buffer = buffer_alloc.get(len + 1);
|
||||||
v.insert(v.end(), data, data + len);
|
|
||||||
return bus_->write_readv(this->address_, v.data(), v.size(), nullptr, 0);
|
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 {
|
ErrorCode I2CDevice::write_register16(uint16_t a_register, const uint8_t *data, size_t len) const {
|
||||||
std::vector<uint8_t> v(len + 2);
|
SmallBufferWithHeapFallback<18> buffer_alloc; // Most I2C writes are <= 16 bytes + 2 for register
|
||||||
v[0] = a_register >> 8;
|
uint8_t *buffer = buffer_alloc.get(len + 2);
|
||||||
v[1] = a_register;
|
|
||||||
std::copy(data, data + len, v.begin() + 2);
|
buffer[0] = a_register >> 8;
|
||||||
return bus_->write_readv(this->address_, v.data(), v.size(), nullptr, 0);
|
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) {
|
bool I2CDevice::read_bytes_16(uint8_t a_register, uint16_t *data, uint8_t len) {
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <memory>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -10,6 +11,22 @@
|
|||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace i2c {
|
namespace i2c {
|
||||||
|
|
||||||
|
/// @brief Helper class for efficient buffer allocation - uses stack for small sizes, heap for large
|
||||||
|
template<size_t STACK_SIZE> class SmallBufferWithHeapFallback {
|
||||||
|
public:
|
||||||
|
uint8_t *get(size_t size) {
|
||||||
|
if (size <= STACK_SIZE) {
|
||||||
|
return this->stack_buffer_;
|
||||||
|
}
|
||||||
|
this->heap_buffer_ = std::unique_ptr<uint8_t[]>(new uint8_t[size]);
|
||||||
|
return this->heap_buffer_.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint8_t stack_buffer_[STACK_SIZE];
|
||||||
|
std::unique_ptr<uint8_t[]> heap_buffer_;
|
||||||
|
};
|
||||||
|
|
||||||
/// @brief Error codes returned by I2CBus and I2CDevice methods
|
/// @brief Error codes returned by I2CBus and I2CDevice methods
|
||||||
enum ErrorCode {
|
enum ErrorCode {
|
||||||
NO_ERROR = 0, ///< No error found during execution of method
|
NO_ERROR = 0, ///< No error found during execution of method
|
||||||
@@ -74,14 +91,17 @@ class I2CBus {
|
|||||||
for (size_t i = 0; i != count; i++) {
|
for (size_t i = 0; i != count; i++) {
|
||||||
total_len += read_buffers[i].len;
|
total_len += read_buffers[i].len;
|
||||||
}
|
}
|
||||||
std::vector<uint8_t> 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)
|
if (err != ERROR_OK)
|
||||||
return err;
|
return err;
|
||||||
size_t pos = 0;
|
size_t pos = 0;
|
||||||
for (size_t i = 0; i != count; i++) {
|
for (size_t i = 0; i != count; i++) {
|
||||||
if (read_buffers[i].len != 0) {
|
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;
|
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.",
|
ESPDEPRECATED("This method is deprecated and will be removed in ESPHome 2026.3.0. Use write_readv() instead.",
|
||||||
"2025.9.0")
|
"2025.9.0")
|
||||||
ErrorCode writev(uint8_t address, const WriteBuffer *write_buffers, size_t count, bool stop = true) {
|
ErrorCode writev(uint8_t address, const WriteBuffer *write_buffers, size_t count, bool stop = true) {
|
||||||
std::vector<uint8_t> buffer{};
|
size_t total_len = 0;
|
||||||
for (size_t i = 0; i != count; i++) {
|
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:
|
protected:
|
||||||
|
Reference in New Issue
Block a user