diff --git a/extra_scripts/esp32_pre.py b/extra_scripts/esp32_pre.py index 8e21770e9..b2c4171e7 100755 --- a/extra_scripts/esp32_pre.py +++ b/extra_scripts/esp32_pre.py @@ -71,3 +71,99 @@ def mtjson_esp32_part(target, source, env): env.AddPreAction("mtjson", mtjson_esp32_part) + + +# --------------------------------------------------------------------------- +# HybridCompile cache-poisoning workaround (pioarduino platform-espressif32) +# +# The platform decides whether the precompiled Arduino IDF libs can be reused +# by hashing only the custom_sdkconfig text + mcu +# (builder/frameworks/arduino.py::matching_custom_sdkconfig). The board JSON's +# PSRAM configuration (BOARD_HAS_PSRAM / memory_type) is not part of that +# hash, and nearly all our S3 variants share esp32s3_base's identical +# custom_sdkconfig. Building a PSRAM env (e.g. heltec-v4) followed by a +# no-PSRAM env (e.g. heltec-v3) therefore skips the framework-libs recompile +# and links CONFIG_SPIRAM=y libs into the no-PSRAM firmware, which boot-loops +# with "quad_psram: PSRAM chip is not connected". +# +# Workaround: before the platform's arduino.py SConscript reads the option, +# append a comment line derived from the board's PSRAM/memory configuration to +# this env's custom_sdkconfig. The comment is inert in kconfig but changes the +# md5, forcing the IDF-libs recompile whenever the PSRAM class changes. The +# derivation mirrors espidf.py::generate_board_specific_config(). +# +# The marker must stay lowercase: arduino.py greps custom_sdkconfig for the +# substrings "PSRAM" and "CONFIG_SPIRAM=y" (has_psram_config) and would +# otherwise treat every board as having PSRAM. +# --------------------------------------------------------------------------- + +SDKCONFIG_CACHE_KEY = "meshtastic_hybridcompile_cache_key" + + +def spiram_cache_class(env): + board = env.BoardConfig() + mcu = board.get("build.mcu", "esp32") + + # memory_type: platformio.ini override > build.arduino.memory_type > build.memory_type + memory_type = None + try: + memory_type = env.GetProjectOption("board_build.memory_type", None) + except Exception: + pass + if not memory_type: + build_section = board.get("build", {}) + memory_type = build_section.get("arduino", {}).get( + "memory_type" + ) or build_section.get("memory_type") + + extra_flags = board.get("build.extra_flags", "") + if isinstance(extra_flags, str): + has_psram = "PSRAM" in extra_flags + else: + has_psram = any("PSRAM" in str(flag) for flag in extra_flags) + if not has_psram: + if memory_type and ( + "opi" in memory_type.lower() or "psram" in memory_type.lower() + ): + has_psram = True + elif "psram_type" in board.get("build", {}): + has_psram = True + + if has_psram: + psram_type = None + try: + psram_type = env.GetProjectOption("board_build.psram_type", None) + except Exception: + pass + if not psram_type and memory_type and len(memory_type.split("_")) == 2: + psram_type = memory_type.split("_")[1] + if not psram_type: + psram_type = board.get("build.psram_type", "") or ( + "hex" if mcu == "esp32p4" else "qio" + ) + psram_type = psram_type.lower() + if psram_type == "opi" and mcu == "esp32s3": + spiram = "oct" + elif psram_type == "hex": + spiram = "hex" + else: + spiram = "quad" + else: + spiram = "none" + + return "memory_type=%s spiram=%s" % ((memory_type or "default").lower(), spiram) + + +def tag_sdkconfig_cache_key(env): + config = env.GetProjectConfig() + section = "env:" + env["PIOENV"] + if not config.has_option(section, "custom_sdkconfig"): + return + current = env.GetProjectOption("custom_sdkconfig") + if SDKCONFIG_CACHE_KEY in current: + return + marker = "# %s: %s" % (SDKCONFIG_CACHE_KEY, spiram_cache_class(env)) + config.set(section, "custom_sdkconfig", current.rstrip("\n") + "\n" + marker) + + +tag_sdkconfig_cache_key(env)