Copy rc_switch table entries out of flash with progmem_memcpy

Codegen now calls rc_switch_protocol(index) instead of copying RC_SWITCH_PROTOCOLS[index] by value, so the ESP8266 copy path never byte loads from the PROGMEM table. Document the keyed slot_counter in AGENTS.md.
This commit is contained in:
J. Nick Koston
2026-09-11 10:22:57 -05:00
parent a2159aee86
commit bf5e60ed41
4 changed files with 19 additions and 4 deletions
+3
View File
@@ -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);
+2 -2
View File
@@ -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)
@@ -1,12 +1,19 @@
#include "rc_switch_protocol.h"
#include <iterator>
#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_);
@@ -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);