1
0
mirror of https://github.com/esphome/esphome.git synced 2025-11-16 06:45:48 +00:00

[canbus] Optimize canbus.send memory usage - store static data in flash

This commit is contained in:
J. Nick Koston
2025-11-08 22:03:54 -06:00
parent b61027607f
commit dad5a88ecf
3 changed files with 38 additions and 12 deletions

View File

@@ -4,7 +4,7 @@ from esphome import automation
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.const import CONF_DATA, CONF_ID, CONF_TRIGGER_ID
from esphome.core import CORE
from esphome.core import CORE, ID
CODEOWNERS = ["@mvturnho", "@danielschramm"]
IS_PLATFORM_COMPONENT = True
@@ -176,5 +176,8 @@ async def canbus_action_to_code(config, action_id, template_arg, args):
else:
if isinstance(data, bytes):
data = [int(x) for x in data]
cg.add(var.set_data_static(data))
# Generate static array in flash to avoid RAM copy
arr_id = ID(f"{action_id}_data", is_declaration=True, type=cg.uint8)
arr = cg.static_const_array(arr_id, cg.ArrayInitializer(*data))
cg.add(var.set_data_static(arr, len(data)))
return var

View File

@@ -112,12 +112,16 @@ class Canbus : public Component {
template<typename... Ts> class CanbusSendAction : public Action<Ts...>, public Parented<Canbus> {
public:
void set_data_template(const std::function<std::vector<uint8_t>(Ts...)> func) {
this->data_func_ = func;
void set_data_template(std::vector<uint8_t> (*func)(Ts...)) {
// Stateless lambdas (generated by ESPHome) implicitly convert to function pointers
this->data_.func = func;
this->static_ = false;
}
void set_data_static(const std::vector<uint8_t> &data) {
this->data_static_ = data;
// Store pointer to static data in flash (no RAM copy)
void set_data_static(const uint8_t *data, size_t len) {
this->data_.static_data.ptr = data;
this->data_.static_data.len = len;
this->static_ = true;
}
@@ -133,21 +137,27 @@ template<typename... Ts> class CanbusSendAction : public Action<Ts...>, public P
auto can_id = this->can_id_.has_value() ? *this->can_id_ : this->parent_->can_id_;
auto use_extended_id =
this->use_extended_id_.has_value() ? *this->use_extended_id_ : this->parent_->use_extended_id_;
std::vector<uint8_t> data;
if (this->static_) {
this->parent_->send_data(can_id, use_extended_id, this->remote_transmission_request_, this->data_static_);
data.assign(this->data_.static_data.ptr, this->data_.static_data.ptr + this->data_.static_data.len);
} else {
auto val = this->data_func_(x...);
this->parent_->send_data(can_id, use_extended_id, this->remote_transmission_request_, val);
data = this->data_.func(x...);
}
this->parent_->send_data(can_id, use_extended_id, this->remote_transmission_request_, data);
}
protected:
optional<uint32_t> can_id_{};
optional<bool> use_extended_id_{};
bool remote_transmission_request_{false};
bool static_{false};
std::function<std::vector<uint8_t>(Ts...)> data_func_{};
std::vector<uint8_t> data_static_{};
bool static_{true}; // Default to static mode (most common case)
union Data {
std::vector<uint8_t> (*func)(Ts...); // 4 bytes on 32-bit
struct {
const uint8_t *ptr; // 4 bytes on 32-bit
size_t len; // 4 bytes on 32-bit
} static_data; // 8 bytes total on 32-bit
} data_; // Union size = 8 bytes (max of 4 and 8)
};
class CanbusTrigger : public Trigger<std::vector<uint8_t>, uint32_t, bool>, public Component {

View File

@@ -37,6 +37,15 @@ canbus:
break;
}
number:
- platform: template
name: "Test Number"
id: test_number
optimistic: true
min_value: 0
max_value: 255
step: 1
button:
- platform: template
name: Canbus Actions
@@ -44,3 +53,7 @@ button:
- canbus.send: "abc"
- canbus.send: [0, 1, 2]
- canbus.send: !lambda return {0, 1, 2};
# Test canbus.send with lambda that references a component (function pointer)
- canbus.send: !lambda |-
uint8_t val = (uint8_t)id(test_number).state;
return std::vector<uint8_t>{0xAA, val, 0xBB};