From ea01cc598bc5cbd1f2d242783120a54f76a7106b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 3 Sep 2025 21:56:47 -0500 Subject: [PATCH 1/2] [esp8266] Store component source strings in PROGMEM to save RAM --- esphome/core/component.cpp | 15 +++++++++++++++ esphome/cpp_helpers.py | 8 ++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/esphome/core/component.cpp b/esphome/core/component.cpp index 40cda17ca3..ca8d31e922 100644 --- a/esphome/core/component.cpp +++ b/esphome/core/component.cpp @@ -12,6 +12,9 @@ #ifdef USE_RUNTIME_STATS #include "esphome/components/runtime_stats/runtime_stats.h" #endif +#ifdef USE_ESP8266 +#include +#endif namespace esphome { @@ -185,7 +188,19 @@ void Component::call() { const char *Component::get_component_source() const { if (this->component_source_ == nullptr) return ""; +#ifdef USE_ESP8266 + // On ESP8266, component_source_ is stored in PROGMEM + // We need a static buffer to hold the string when read from flash + // Since this is only used for logging, a single shared buffer is fine + static char buffer[64]; // Component names are typically short + + // Copy from PROGMEM to buffer + strncpy_P(buffer, this->component_source_, sizeof(buffer) - 1); + buffer[sizeof(buffer) - 1] = '\0'; // Ensure null termination + return buffer; +#else return this->component_source_; +#endif } bool Component::should_warn_of_blocking(uint32_t blocking_time) { if (blocking_time > this->warn_if_blocking_over_) { diff --git a/esphome/cpp_helpers.py b/esphome/cpp_helpers.py index b61b215bdc..5164923b2d 100644 --- a/esphome/cpp_helpers.py +++ b/esphome/cpp_helpers.py @@ -9,7 +9,7 @@ from esphome.const import ( ) from esphome.core import CORE, ID, coroutine from esphome.coroutine import FakeAwaitable -from esphome.cpp_generator import add, get_variable +from esphome.cpp_generator import RawExpression, add, get_variable from esphome.cpp_types import App from esphome.types import ConfigFragmentType, ConfigType from esphome.util import Registry, RegistryEntry @@ -76,7 +76,11 @@ async def register_component(var, config): "Error while finding name of component, please report this", exc_info=e ) if name is not None: - add(var.set_component_source(name)) + # On ESP8266, store component source strings in PROGMEM to save RAM + if CORE.is_esp8266: + add(var.set_component_source(RawExpression(f'PSTR("{name}")'))) + else: + add(var.set_component_source(name)) add(App.register_component(var)) return var From da9a7c41d157e2f7886262989be676d6af70e71c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 3 Sep 2025 22:05:21 -0500 Subject: [PATCH 2/2] preen --- esphome/core/macros.h | 11 +++++++++++ esphome/cpp_helpers.py | 7 ++----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/esphome/core/macros.h b/esphome/core/macros.h index 8b2383321b..0f0d556616 100644 --- a/esphome/core/macros.h +++ b/esphome/core/macros.h @@ -6,3 +6,14 @@ #ifdef USE_ARDUINO #include #endif + +// Portable PROGMEM string macro +// On ESP8266, PSTR() stores strings in flash memory +// On other platforms, it's a no-op +#ifndef ESPHOME_PSTR +#ifdef USE_ESP8266 +#define ESPHOME_PSTR(x) PSTR(x) +#else +#define ESPHOME_PSTR(x) (x) +#endif +#endif diff --git a/esphome/cpp_helpers.py b/esphome/cpp_helpers.py index 5164923b2d..26a7084010 100644 --- a/esphome/cpp_helpers.py +++ b/esphome/cpp_helpers.py @@ -76,11 +76,8 @@ async def register_component(var, config): "Error while finding name of component, please report this", exc_info=e ) if name is not None: - # On ESP8266, store component source strings in PROGMEM to save RAM - if CORE.is_esp8266: - add(var.set_component_source(RawExpression(f'PSTR("{name}")'))) - else: - add(var.set_component_source(name)) + # Use ESPHOME_PSTR macro which stores strings in PROGMEM on ESP8266, no-op on other platforms + add(var.set_component_source(RawExpression(f'ESPHOME_PSTR("{name}")'))) add(App.register_component(var)) return var