diff --git a/AGENTS.md b/AGENTS.md index 98bdd58ec5..8db3cd3d62 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -629,6 +629,9 @@ file does, and it is the authority when they disagree. The most useful starting _request_listener_slot() cg.add(hub.register_listener(var)) ``` + When several instances each own a list declared at the same size (one per hub of a + `MULTI_CONF` component), pass the owning object as the key, `_request_listener_slot(str(hub))`; + the define is then the largest count any one key requested instead of the total. ```cpp #ifdef MY_COMPONENT_LISTENER_COUNT void register_listener(MyComponentListener *listener); diff --git a/esphome/components/remote_base/__init__.py b/esphome/components/remote_base/__init__.py index fbb1f684da..ff1d85b4bc 100644 --- a/esphome/components/remote_base/__init__.py +++ b/esphome/components/remote_base/__init__.py @@ -1535,7 +1535,7 @@ def validate_rc_switch_raw_code(value): def build_rc_switch_protocol(config): if isinstance(config, int): - return rc_switch_protocols[config] + return rc_switch_protocol(config) pl = config[CONF_PULSE_LENGTH] return RCSwitchBase( config[CONF_SYNC][0] * pl, @@ -1622,7 +1622,7 @@ RC_SWITCH_TRANSMITTER = cv.Schema( } ) -rc_switch_protocols = ns.RC_SWITCH_PROTOCOLS +rc_switch_protocol = ns.rc_switch_protocol RCSwitchData = ns.struct("RCSwitchData") RCSwitchBase = ns.class_("RCSwitchBase") RCSwitchTrigger = ns.class_("RCSwitchTrigger", RemoteReceiverTrigger) diff --git a/esphome/components/remote_base/rc_switch_protocol.cpp b/esphome/components/remote_base/rc_switch_protocol.cpp index cd64fd74f3..ef02ec3e3c 100644 --- a/esphome/components/remote_base/rc_switch_protocol.cpp +++ b/esphome/components/remote_base/rc_switch_protocol.cpp @@ -1,12 +1,19 @@ #include "rc_switch_protocol.h" #include +#include "esphome/core/hal.h" #include "esphome/core/log.h" namespace esphome::remote_base { static const char *const TAG = "remote.rc_switch"; +RCSwitchBase rc_switch_protocol(uint8_t index) { + RCSwitchBase protocol; + progmem_memcpy(&protocol, &RC_SWITCH_PROTOCOLS[index], sizeof(protocol)); + return protocol; +} + void RCSwitchBase::one(RemoteTransmitData *dst) const { if (!this->inverted_) { dst->mark(this->one_high_); diff --git a/esphome/components/remote_base/rc_switch_protocol.h b/esphome/components/remote_base/rc_switch_protocol.h index 5885df180d..007d18d0b0 100644 --- a/esphome/components/remote_base/rc_switch_protocol.h +++ b/esphome/components/remote_base/rc_switch_protocol.h @@ -68,8 +68,9 @@ class RCSwitchBase { uint32_t inverted_{}; // bool widened so every field is a word: the table is read from flash }; -// Constant-initialized and kept in flash on every platform; ESP8266 reads it in place, which only -// works while every field is a whole word +// Constant-initialized and kept in flash on every platform. The decoder reads entries in place +// through a pointer, which ESP8266 only allows while every field is a whole word; copies out of +// the table go through rc_switch_protocol() static_assert(sizeof(RCSwitchBase) == 7 * sizeof(uint32_t), "RCSwitchBase must stay word-only for flash reads"); inline constexpr RCSwitchBase RC_SWITCH_PROTOCOLS[] PROGMEM = { {0, 0, 0, 0, 0, 0, false}, @@ -83,6 +84,10 @@ inline constexpr RCSwitchBase RC_SWITCH_PROTOCOLS[] PROGMEM = { {250, 2500, 250, 1250, 250, 250, false}, }; +/// RAM copy of RC_SWITCH_PROTOCOLS[index] for the transmit actions and the dumper, made with +/// progmem_memcpy so no byte load ever touches the flash table on ESP8266 +RCSwitchBase rc_switch_protocol(uint8_t index); + uint64_t decode_binary_string(const std::string &data); uint64_t decode_binary_string_mask(const std::string &data);