Merge remote-tracking branch 'upstream/esp8266-enable-scanf-float' into integration

This commit is contained in:
J. Nick Koston
2026-03-28 14:12:22 -10:00
2 changed files with 54 additions and 11 deletions
+51 -11
View File
@@ -1,5 +1,6 @@
import logging
from pathlib import Path
import re
import esphome.codegen as cg
import esphome.config_validation as cv
@@ -17,8 +18,9 @@ from esphome.const import (
PLATFORM_ESP8266,
ThreadModel,
)
from esphome.core import CORE, CoroPriority, coroutine_with_priority
from esphome.core import CORE, CoroPriority, Lambda, coroutine_with_priority
from esphome.helpers import copy_file_if_changed
from esphome.types import ConfigType
from .boards import BOARDS, ESP8266_LD_SCRIPTS
from .const import (
@@ -40,12 +42,42 @@ from .const import (
)
from .gpio import PinInitialState, add_pin_initial_states_array
CONF_ENABLE_SCANF_FLOAT = "enable_scanf_float"
# Matches scanf/sscanf calls with float format specifiers:
# %f, %.2f, %6.2f - basic float formats
# %lf, %Lf - double/long double
# %e, %E, %g, %G - scientific/general notation
# %*f - assignment suppression
# %8lf - width + length modifier
# Uses [\s\S]*? to match across newlines in multi-line lambdas.
_SCANF_FLOAT_RE = re.compile(r"scanf\s*\([\s\S]*?%[*\d.]*[hlL]*[feEgGaA]")
CODEOWNERS = ["@esphome/core"]
_LOGGER = logging.getLogger(__name__)
AUTO_LOAD = ["preferences"]
IS_TARGET_PLATFORM = True
def _lambdas_use_scanf_float(config: ConfigType) -> bool:
"""Check if any lambda in the config uses scanf with a float format specifier.
Comments are stripped before matching to avoid false positives from
commented-out code. The cost of a false positive is only ~8KB flash.
"""
stack: list = [config]
while stack:
obj = stack.pop()
if isinstance(obj, Lambda):
src = obj.comment_remover(obj.value)
if _SCANF_FLOAT_RE.search(src):
return True
elif isinstance(obj, dict):
stack.extend(obj.values())
elif isinstance(obj, list):
stack.extend(obj)
return False
def set_core_data(config):
CORE.data[KEY_ESP8266] = {}
CORE.data[KEY_CORE][KEY_TARGET_PLATFORM] = PLATFORM_ESP8266
@@ -181,6 +213,7 @@ CONFIG_SCHEMA = cv.All(
cv.Optional(CONF_ENABLE_SERIAL): cv.boolean,
cv.Optional(CONF_ENABLE_SERIAL1): cv.boolean,
cv.Optional(CONF_ENABLE_FULL_PRINTF, default=False): cv.boolean,
cv.Optional(CONF_ENABLE_SCANF_FLOAT, default=False): cv.boolean,
}
),
set_core_data,
@@ -201,16 +234,23 @@ async def to_code(config):
cg.add_define("ESPHOME_VARIANT", "ESP8266")
cg.add_define(ThreadModel.SINGLE)
cg.add_platformio_option(
"extra_scripts",
[
"pre:testing_mode.py",
"pre:exclude_updater.py",
"pre:exclude_waveform.py",
"pre:remove_float_scanf.py",
"post:post_build.py",
],
)
enable_scanf_float = config[CONF_ENABLE_SCANF_FLOAT]
if not enable_scanf_float and _lambdas_use_scanf_float(CORE.config):
enable_scanf_float = True
_LOGGER.warning(
"Lambda uses scanf with a float format specifier; "
"enabling scanf float support (~8KB flash)"
)
extra_scripts = [
"pre:testing_mode.py",
"pre:exclude_updater.py",
"pre:exclude_waveform.py",
]
if not enable_scanf_float:
extra_scripts.append("pre:remove_float_scanf.py")
extra_scripts.append("post:post_build.py")
cg.add_platformio_option("extra_scripts", extra_scripts)
conf = config[CONF_FRAMEWORK]
cg.add_platformio_option("framework", "arduino")
@@ -14,3 +14,6 @@ esphome:
assert(x == 95);
x = clamp_at_most(x, 40);
assert(x == 40);
- lambda: |-
float value = 0.0f;
sscanf("3.14", "%f", &value);