Arduino core 3.3.3
This commit is contained in:
@@ -1,8 +1,6 @@
|
||||
dependencies:
|
||||
# Required IDF version
|
||||
idf: ">=5.1"
|
||||
espressif/cmake_utilities:
|
||||
version: "0.*"
|
||||
espressif/fb_gfx:
|
||||
version: "master"
|
||||
path: components/fb_gfx
|
||||
|
||||
@@ -565,7 +565,8 @@ FRAMEWORK_LIB_DIR = path_cache.framework_lib_dir
|
||||
|
||||
SConscript("_embed_files.py", exports="env")
|
||||
|
||||
flag_any_custom_sdkconfig = exists(str(Path(FRAMEWORK_LIB_DIR) / "sdkconfig"))
|
||||
flag_any_custom_sdkconfig = (FRAMEWORK_LIB_DIR is not None and
|
||||
exists(str(Path(FRAMEWORK_LIB_DIR) / "sdkconfig")))
|
||||
|
||||
|
||||
def has_unicore_flags():
|
||||
@@ -644,6 +645,8 @@ IS_INTEGRATION_DUMP = env.IsIntegrationDump()
|
||||
def is_framework_subfolder(potential_subfolder):
|
||||
"""Check if a path is a subfolder of the framework SDK directory"""
|
||||
# carefully check before change this function
|
||||
if FRAMEWORK_SDK_DIR is None:
|
||||
return False
|
||||
if not isabs(potential_subfolder):
|
||||
return False
|
||||
if (splitdrive(FRAMEWORK_SDK_DIR)[0] !=
|
||||
|
||||
+390
-37
@@ -78,7 +78,7 @@ config = env.GetProjectConfig()
|
||||
board = env.BoardConfig()
|
||||
mcu = board.get("build.mcu", "esp32")
|
||||
flash_speed = board.get("build.f_flash", "40000000L")
|
||||
flash_frequency = str(flash_speed.replace("000000L", "m"))
|
||||
flash_frequency = str(flash_speed.replace("000000L", ""))
|
||||
flash_mode = board.get("build.flash_mode", "dio")
|
||||
idf_variant = mcu.lower()
|
||||
flag_custom_sdkonfig = False
|
||||
@@ -218,6 +218,26 @@ if config.has_option("env:"+env["PIOENV"], "custom_sdkconfig"):
|
||||
if "espidf.custom_sdkconfig" in board:
|
||||
flag_custom_sdkonfig = True
|
||||
|
||||
|
||||
# Check for board-specific configurations that require sdkconfig generation
|
||||
def has_board_specific_config():
|
||||
"""Check if board has configuration that needs to be applied to sdkconfig."""
|
||||
# Check for PSRAM support
|
||||
extra_flags = board.get("build.extra_flags", [])
|
||||
has_psram = any("-DBOARD_HAS_PSRAM" in flag for flag in extra_flags)
|
||||
|
||||
# Check for special memory types
|
||||
memory_type = None
|
||||
build_section = board.get("build", {})
|
||||
arduino_section = build_section.get("arduino", {})
|
||||
if "memory_type" in arduino_section:
|
||||
memory_type = arduino_section["memory_type"]
|
||||
elif "memory_type" in build_section:
|
||||
memory_type = build_section["memory_type"]
|
||||
has_special_memory = memory_type and ("opi" in memory_type.lower())
|
||||
|
||||
return has_psram or has_special_memory
|
||||
|
||||
def HandleArduinoIDFsettings(env):
|
||||
"""
|
||||
Handles Arduino IDF settings configuration with custom sdkconfig support.
|
||||
@@ -284,48 +304,366 @@ def HandleArduinoIDFsettings(env):
|
||||
return line.split("=")[0]
|
||||
return None
|
||||
|
||||
def generate_board_specific_config():
|
||||
"""Generate board-specific sdkconfig settings from board.json manifest."""
|
||||
board_config_flags = []
|
||||
|
||||
# Handle memory type configuration with platformio.ini override support
|
||||
# Priority: platformio.ini > board.json manifest
|
||||
memory_type = None
|
||||
|
||||
# Check for memory_type override in platformio.ini
|
||||
if hasattr(env, 'GetProjectOption'):
|
||||
try:
|
||||
memory_type = env.GetProjectOption("board_build.memory_type", None)
|
||||
except:
|
||||
pass
|
||||
|
||||
# Fallback to board.json manifest
|
||||
if not memory_type:
|
||||
build_section = board.get("build", {})
|
||||
arduino_section = build_section.get("arduino", {})
|
||||
if "memory_type" in arduino_section:
|
||||
memory_type = arduino_section["memory_type"]
|
||||
elif "memory_type" in build_section:
|
||||
memory_type = build_section["memory_type"]
|
||||
|
||||
flash_memory_type = None
|
||||
psram_memory_type = None
|
||||
if memory_type:
|
||||
parts = memory_type.split("_")
|
||||
if len(parts) == 2:
|
||||
flash_memory_type, psram_memory_type = parts
|
||||
else:
|
||||
flash_memory_type = memory_type
|
||||
|
||||
# Check for additional flash configuration indicators
|
||||
boot_mode = board.get("build", {}).get("boot", None)
|
||||
flash_mode = board.get("build", {}).get("flash_mode", None)
|
||||
|
||||
# Override flash_memory_type if boot mode indicates OPI
|
||||
if boot_mode == "opi" or flash_mode in ["dout", "opi"]:
|
||||
if not flash_memory_type or flash_memory_type.lower() != "opi":
|
||||
flash_memory_type = "opi"
|
||||
print(f"Info: Detected OPI Flash via boot_mode='{boot_mode}' or flash_mode='{flash_mode}'")
|
||||
|
||||
# Set CPU frequency with platformio.ini override support
|
||||
# Priority: platformio.ini > board.json manifest
|
||||
f_cpu = None
|
||||
if hasattr(env, 'GetProjectOption'):
|
||||
# Check for board_build.f_cpu override in platformio.ini
|
||||
try:
|
||||
f_cpu = env.GetProjectOption("board_build.f_cpu", None)
|
||||
except:
|
||||
pass
|
||||
|
||||
# Fallback to board.json manifest
|
||||
if not f_cpu:
|
||||
f_cpu = board.get("build.f_cpu", None)
|
||||
|
||||
if f_cpu:
|
||||
cpu_freq = str(f_cpu).replace("000000L", "")
|
||||
board_config_flags.append(f"CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ={cpu_freq}")
|
||||
|
||||
# MCU name mapping for config flags (uppercase MCU name)
|
||||
mcu_upper = mcu.upper().replace("-", "")
|
||||
|
||||
# Disable other CPU frequency options and enable the specific one
|
||||
common_cpu_freqs = ["80", "160", "240"]
|
||||
for freq in common_cpu_freqs:
|
||||
if freq != cpu_freq:
|
||||
board_config_flags.append(f"# CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_{freq} is not set")
|
||||
board_config_flags.append(f"# CONFIG_{mcu_upper}_DEFAULT_CPU_FREQ_{freq} is not set")
|
||||
|
||||
# Enable the specific CPU frequency (both generic and MCU-specific)
|
||||
board_config_flags.append(f"CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_{cpu_freq}=y")
|
||||
board_config_flags.append(f"CONFIG_{mcu_upper}_DEFAULT_CPU_FREQ_{cpu_freq}=y")
|
||||
|
||||
# Set flash size with platformio.ini override support
|
||||
# Priority: platformio.ini > board.json manifest
|
||||
flash_size = None
|
||||
if hasattr(env, 'GetProjectOption'):
|
||||
# Check for board_upload.flash_size override in platformio.ini
|
||||
try:
|
||||
flash_size = env.GetProjectOption("board_upload.flash_size", None)
|
||||
except:
|
||||
pass
|
||||
|
||||
# Fallback to board.json manifest
|
||||
if not flash_size:
|
||||
flash_size = board.get("upload", {}).get("flash_size", None)
|
||||
|
||||
if flash_size:
|
||||
# Configure both string and boolean flash size formats
|
||||
# Disable other flash size options first
|
||||
flash_sizes = ["4MB", "8MB", "16MB", "32MB", "64MB", "128MB"]
|
||||
for size in flash_sizes:
|
||||
if size != flash_size:
|
||||
board_config_flags.append(f"# CONFIG_ESPTOOLPY_FLASHSIZE_{size} is not set")
|
||||
|
||||
# Set the specific flash size configs
|
||||
board_config_flags.append(f"CONFIG_ESPTOOLPY_FLASHSIZE=\"{flash_size}\"")
|
||||
board_config_flags.append(f"CONFIG_ESPTOOLPY_FLASHSIZE_{flash_size}=y")
|
||||
|
||||
# Handle Flash and PSRAM frequency configuration with platformio.ini override support
|
||||
# Priority: platformio.ini > board.json manifest
|
||||
# From 80MHz onwards, Flash and PSRAM frequencies must be identical
|
||||
|
||||
# Get f_flash with override support
|
||||
f_flash = None
|
||||
if hasattr(env, 'GetProjectOption'):
|
||||
try:
|
||||
f_flash = env.GetProjectOption("board_build.f_flash", None)
|
||||
except:
|
||||
pass
|
||||
if not f_flash:
|
||||
f_flash = board.get("build.f_flash", None)
|
||||
|
||||
# Get f_boot with override support
|
||||
f_boot = None
|
||||
if hasattr(env, 'GetProjectOption'):
|
||||
try:
|
||||
f_boot = env.GetProjectOption("board_build.f_boot", None)
|
||||
except:
|
||||
pass
|
||||
if not f_boot:
|
||||
f_boot = board.get("build.f_boot", None)
|
||||
|
||||
# Get f_psram with override support (ESP32-P4 specific)
|
||||
f_psram = None
|
||||
if hasattr(env, 'GetProjectOption'):
|
||||
try:
|
||||
f_psram = env.GetProjectOption("board_build.f_psram", None)
|
||||
except:
|
||||
pass
|
||||
if not f_psram:
|
||||
f_psram = board.get("build.f_psram", None)
|
||||
|
||||
# Determine the frequencies to use
|
||||
# ESP32-P4: f_flash for Flash, f_psram for PSRAM (doesn't affect bootloader name)
|
||||
|
||||
if mcu == "esp32p4":
|
||||
# ESP32-P4: f_flash is always used for Flash frequency
|
||||
# f_psram is used for PSRAM frequency (if set), otherwise use f_flash
|
||||
# Note: f_boot is NOT used for P4 as it affects bootloader filename
|
||||
flash_compile_freq = f_flash
|
||||
psram_compile_freq = f_psram if f_psram else f_flash
|
||||
else:
|
||||
# Other chips: f_boot overrides f_flash for compile-time (both Flash and PSRAM)
|
||||
compile_freq = f_boot if f_boot else f_flash
|
||||
flash_compile_freq = compile_freq
|
||||
psram_compile_freq = compile_freq
|
||||
|
||||
if f_flash and flash_compile_freq and psram_compile_freq:
|
||||
# Validate and parse frequency values
|
||||
try:
|
||||
flash_freq_val = int(str(flash_compile_freq).replace("000000L", ""))
|
||||
psram_freq_val = int(str(psram_compile_freq).replace("000000L", ""))
|
||||
except (ValueError, AttributeError):
|
||||
print("Warning: Invalid frequency values, skipping frequency configuration")
|
||||
flash_freq_val = None
|
||||
psram_freq_val = None
|
||||
|
||||
if flash_freq_val and psram_freq_val:
|
||||
# Determine frequency strings
|
||||
flash_freq_str = f"{flash_freq_val}m"
|
||||
psram_freq_str = str(psram_freq_val)
|
||||
|
||||
# Info message
|
||||
if mcu == "esp32p4":
|
||||
print(f"Info: ESP32-P4 frequency mode: Flash={flash_freq_val}MHz, PSRAM={psram_freq_val}MHz")
|
||||
elif flash_freq_val >= 80:
|
||||
print(f"Info: Unified frequency mode (>= 80MHz): {flash_freq_val}MHz for both Flash and PSRAM")
|
||||
else:
|
||||
print(f"Info: Independent frequency mode (< 80MHz): Flash={flash_freq_str}, PSRAM={psram_freq_str}")
|
||||
|
||||
# Configure Flash frequency
|
||||
# Disable other flash frequency options first
|
||||
flash_freqs = ["20m", "26m", "40m", "80m", "120m"]
|
||||
for freq in flash_freqs:
|
||||
if freq != flash_freq_str:
|
||||
board_config_flags.append(f"# CONFIG_ESPTOOLPY_FLASHFREQ_{freq.upper()} is not set")
|
||||
# Then set the specific frequency configs
|
||||
board_config_flags.append(f"CONFIG_ESPTOOLPY_FLASHFREQ=\"{flash_freq_str}\"")
|
||||
board_config_flags.append(f"CONFIG_ESPTOOLPY_FLASHFREQ_{flash_freq_str.upper()}=y")
|
||||
|
||||
# ESP32-P4 requires additional FLASHFREQ_VAL setting
|
||||
if mcu == "esp32p4":
|
||||
board_config_flags.append(f"CONFIG_ESPTOOLPY_FLASHFREQ_VAL={flash_freq_val}")
|
||||
|
||||
# Configure PSRAM frequency
|
||||
# Disable other SPIRAM speed options first
|
||||
psram_freqs = ["20", "40", "80", "120", "200"]
|
||||
for freq in psram_freqs:
|
||||
if freq != psram_freq_str:
|
||||
board_config_flags.append(f"# CONFIG_SPIRAM_SPEED_{freq}M is not set")
|
||||
# Then set the specific SPIRAM configs
|
||||
board_config_flags.append(f"CONFIG_SPIRAM_SPEED={psram_freq_str}")
|
||||
board_config_flags.append(f"CONFIG_SPIRAM_SPEED_{psram_freq_str}M=y")
|
||||
|
||||
# Enable experimental features for Flash frequencies > 80MHz
|
||||
if flash_freq_val > 80:
|
||||
board_config_flags.append("CONFIG_IDF_EXPERIMENTAL_FEATURES=y")
|
||||
board_config_flags.append("CONFIG_SPI_FLASH_HPM_ENABLE=y")
|
||||
board_config_flags.append("CONFIG_SPI_FLASH_HPM_AUTO=y")
|
||||
|
||||
# Check for PSRAM support based on board flags
|
||||
extra_flags = board.get("build.extra_flags", "")
|
||||
# Handle both string and list formats
|
||||
if isinstance(extra_flags, str):
|
||||
has_psram = "-DBOARD_HAS_PSRAM" in extra_flags
|
||||
else:
|
||||
has_psram = any("-DBOARD_HAS_PSRAM" in flag for flag in extra_flags)
|
||||
|
||||
# Additional PSRAM detection methods
|
||||
if not has_psram:
|
||||
# Check if memory_type contains psram indicators
|
||||
if memory_type and ("opi" in memory_type.lower() or "psram" in memory_type.lower()):
|
||||
has_psram = True
|
||||
# Check build.psram_type
|
||||
elif "psram_type" in board.get("build", {}):
|
||||
has_psram = True
|
||||
# Check for SPIRAM mentions in extra_flags
|
||||
elif isinstance(extra_flags, str) and "PSRAM" in extra_flags:
|
||||
has_psram = True
|
||||
elif not isinstance(extra_flags, str) and any("PSRAM" in str(flag) for flag in extra_flags):
|
||||
has_psram = True
|
||||
|
||||
if has_psram:
|
||||
# Enable basic SPIRAM support
|
||||
board_config_flags.append("CONFIG_SPIRAM=y")
|
||||
|
||||
# Determine PSRAM type with platformio.ini override support
|
||||
# Priority: platformio.ini > memory_type > build.psram_type > default
|
||||
psram_type = None
|
||||
|
||||
# Priority 1: Check for platformio.ini override
|
||||
if hasattr(env, 'GetProjectOption'):
|
||||
try:
|
||||
psram_type = env.GetProjectOption("board_build.psram_type", None)
|
||||
if psram_type:
|
||||
psram_type = psram_type.lower()
|
||||
except:
|
||||
pass
|
||||
|
||||
# Priority 2: Check psram_memory_type from memory_type field (e.g., "qio_opi")
|
||||
if not psram_type and psram_memory_type:
|
||||
psram_type = psram_memory_type.lower()
|
||||
# Priority 3: Check build.psram_type field as fallback
|
||||
elif not psram_type and "psram_type" in board.get("build", {}):
|
||||
psram_type = board.get("build.psram_type", "qio").lower()
|
||||
# Priority 4: Default based on MCU
|
||||
elif not psram_type:
|
||||
# ESP32-P4 defaults to HEX (only type available)
|
||||
if mcu == "esp32p4":
|
||||
psram_type = "hex"
|
||||
else:
|
||||
psram_type = "qio"
|
||||
|
||||
# Configure PSRAM mode based on detected type
|
||||
if psram_type == "hex":
|
||||
# HEX PSRAM configuration (ESP32-P4 only)
|
||||
board_config_flags.append("CONFIG_SPIRAM_MODE_HEX=y")
|
||||
|
||||
elif psram_type == "opi":
|
||||
# Octal PSRAM configuration (for ESP32-S3)
|
||||
if mcu == "esp32s3":
|
||||
board_config_flags.extend([
|
||||
"CONFIG_IDF_EXPERIMENTAL_FEATURES=y",
|
||||
"# CONFIG_SPIRAM_MODE_QUAD is not set",
|
||||
"CONFIG_SPIRAM_MODE_OCT=y",
|
||||
"CONFIG_SPIRAM_TYPE_AUTO=y"
|
||||
])
|
||||
else:
|
||||
# Fallback to QUAD for other chips
|
||||
board_config_flags.extend([
|
||||
"# CONFIG_SPIRAM_MODE_OCT is not set",
|
||||
"CONFIG_SPIRAM_MODE_QUAD=y"
|
||||
])
|
||||
|
||||
elif psram_type in ["qio", "qspi"]:
|
||||
# Quad PSRAM configuration
|
||||
if mcu in ["esp32s2", "esp32s3"]:
|
||||
board_config_flags.extend([
|
||||
"# CONFIG_SPIRAM_MODE_OCT is not set",
|
||||
"CONFIG_SPIRAM_MODE_QUAD=y"
|
||||
])
|
||||
elif mcu == "esp32":
|
||||
board_config_flags.extend([
|
||||
"# CONFIG_SPIRAM_MODE_OCT is not set",
|
||||
"# CONFIG_SPIRAM_MODE_QUAD is not set"
|
||||
])
|
||||
else:
|
||||
# Explicitly disable PSRAM if not present
|
||||
board_config_flags.extend([
|
||||
"# CONFIG_SPIRAM is not set"
|
||||
])
|
||||
|
||||
# Use flash_memory_type for flash config
|
||||
if flash_memory_type and "opi" in flash_memory_type.lower():
|
||||
# OPI Flash configurations require specific settings
|
||||
# According to ESP-IDF documentation, OPI flash must use DOUT mode for bootloader
|
||||
# The bootloader starts in DOUT mode and switches to OPI at runtime
|
||||
# Reference: ESP-IDF Programming Guide - SPI Flash Configuration
|
||||
board_config_flags.extend([
|
||||
"# CONFIG_ESPTOOLPY_FLASHMODE_QIO is not set",
|
||||
"# CONFIG_ESPTOOLPY_FLASHMODE_QOUT is not set",
|
||||
"# CONFIG_ESPTOOLPY_FLASHMODE_DIO is not set",
|
||||
"CONFIG_ESPTOOLPY_FLASHMODE_DOUT=y",
|
||||
"CONFIG_ESPTOOLPY_OCT_FLASH=y",
|
||||
"# CONFIG_ESPTOOLPY_FLASH_SAMPLE_MODE_STR is not set",
|
||||
"CONFIG_ESPTOOLPY_FLASH_SAMPLE_MODE_DTR=y"
|
||||
])
|
||||
|
||||
return board_config_flags
|
||||
|
||||
def build_idf_config_flags():
|
||||
"""Build complete IDF configuration flags from all sources."""
|
||||
flags = []
|
||||
|
||||
# Add board-specific flags first
|
||||
if "espidf.custom_sdkconfig" in board:
|
||||
board_flags = board.get("espidf.custom_sdkconfig", [])
|
||||
if board_flags:
|
||||
flags.extend(board_flags)
|
||||
# FIRST: Add board-specific flags derived from board.json manifest
|
||||
board_flags = generate_board_specific_config()
|
||||
if board_flags:
|
||||
flags.extend(board_flags)
|
||||
|
||||
# Add custom sdkconfig file content
|
||||
# SECOND: Add board-specific flags from board manifest (espidf.custom_sdkconfig)
|
||||
if "espidf.custom_sdkconfig" in board:
|
||||
board_manifest_flags = board.get("espidf.custom_sdkconfig", [])
|
||||
if board_manifest_flags:
|
||||
flags.extend(board_manifest_flags)
|
||||
|
||||
# THIRD: Add custom sdkconfig file content
|
||||
custom_file_content = load_custom_sdkconfig_file()
|
||||
if custom_file_content:
|
||||
flags.append(custom_file_content)
|
||||
|
||||
# Add project-level custom sdkconfig
|
||||
# FOURTH: Add project-level custom sdkconfig (highest precedence for user overrides)
|
||||
if config.has_option("env:" + env["PIOENV"], "custom_sdkconfig"):
|
||||
custom_flags = env.GetProjectOption("custom_sdkconfig").rstrip("\n")
|
||||
if custom_flags:
|
||||
flags.append(custom_flags)
|
||||
|
||||
# FIFTH: Apply ESP32-specific compatibility fixes
|
||||
all_flags_str = "\n".join(flags) + "\n" if flags else ""
|
||||
esp32_compatibility_flags = apply_esp32_compatibility_fixes(all_flags_str)
|
||||
if esp32_compatibility_flags:
|
||||
flags.extend(esp32_compatibility_flags)
|
||||
|
||||
return "\n".join(flags) + "\n" if flags else ""
|
||||
|
||||
def add_flash_configuration(config_flags):
|
||||
"""Add flash frequency and mode configuration."""
|
||||
if flash_frequency != "80m":
|
||||
config_flags += "# CONFIG_ESPTOOLPY_FLASHFREQ_80M is not set\n"
|
||||
config_flags += f"CONFIG_ESPTOOLPY_FLASHFREQ_{flash_frequency.upper()}=y\n"
|
||||
config_flags += f"CONFIG_ESPTOOLPY_FLASHFREQ=\"{flash_frequency}\"\n"
|
||||
|
||||
if flash_mode != "qio":
|
||||
config_flags += "# CONFIG_ESPTOOLPY_FLASHMODE_QIO is not set\n"
|
||||
|
||||
flash_mode_flag = f"CONFIG_ESPTOOLPY_FLASHMODE_{flash_mode.upper()}=y\n"
|
||||
if flash_mode_flag not in config_flags:
|
||||
config_flags += flash_mode_flag
|
||||
def apply_esp32_compatibility_fixes(config_flags_str):
|
||||
"""Apply ESP32-specific compatibility fixes based on final configuration."""
|
||||
compatibility_flags = []
|
||||
|
||||
# ESP32 specific SPIRAM configuration
|
||||
if mcu == "esp32" and "CONFIG_FREERTOS_UNICORE=y" in config_flags:
|
||||
config_flags += "# CONFIG_SPIRAM is not set\n"
|
||||
# On ESP32, SPIRAM is not used with UNICORE mode
|
||||
if mcu == "esp32" and "CONFIG_FREERTOS_UNICORE=y" in config_flags_str:
|
||||
if "CONFIG_SPIRAM=y" in config_flags_str:
|
||||
compatibility_flags.append("# CONFIG_SPIRAM is not set")
|
||||
print("Info: ESP32 SPIRAM disabled since solo1 core mode is enabled")
|
||||
|
||||
return config_flags
|
||||
return compatibility_flags
|
||||
|
||||
|
||||
def write_sdkconfig_file(idf_config_flags, checksum_source):
|
||||
if "arduino" not in env.subst("$PIOFRAMEWORK"):
|
||||
@@ -346,7 +684,9 @@ def HandleArduinoIDFsettings(env):
|
||||
dst.write(f"# TASMOTA__{checksum}\n")
|
||||
|
||||
# Process each line from source sdkconfig
|
||||
for line in src:
|
||||
src_lines = src.readlines()
|
||||
|
||||
for line in src_lines:
|
||||
flag_name = extract_flag_name(line)
|
||||
|
||||
if flag_name is None:
|
||||
@@ -375,20 +715,25 @@ def HandleArduinoIDFsettings(env):
|
||||
print(f"Add: {cleaned_flag}")
|
||||
dst.write(cleaned_flag + "\n")
|
||||
|
||||
|
||||
# Main execution logic
|
||||
has_custom_config = (
|
||||
config.has_option("env:" + env["PIOENV"], "custom_sdkconfig") or
|
||||
"espidf.custom_sdkconfig" in board
|
||||
)
|
||||
|
||||
if not has_custom_config:
|
||||
has_board_config = has_board_specific_config()
|
||||
|
||||
if not has_custom_config and not has_board_config:
|
||||
return
|
||||
|
||||
print("*** Add \"custom_sdkconfig\" settings to IDF sdkconfig.defaults ***")
|
||||
if has_board_config and not has_custom_config:
|
||||
print("*** Apply board-specific settings to IDF sdkconfig.defaults ***")
|
||||
else:
|
||||
print("*** Add \"custom_sdkconfig\" settings to IDF sdkconfig.defaults ***")
|
||||
|
||||
# Build complete configuration
|
||||
idf_config_flags = build_idf_config_flags()
|
||||
idf_config_flags = add_flash_configuration(idf_config_flags)
|
||||
|
||||
# Convert to list for processing
|
||||
idf_config_list = [line for line in idf_config_flags.splitlines() if line.strip()]
|
||||
@@ -1678,8 +2023,7 @@ def install_python_deps():
|
||||
# https://github.com/platformio/platform-espressif32/issues/635
|
||||
"cryptography": "~=44.0.0",
|
||||
"pyparsing": ">=3.1.0,<4",
|
||||
"pydantic": "~=2.11.10",
|
||||
"idf-component-manager": "~=2.2",
|
||||
"idf-component-manager": "~=2.4",
|
||||
"esp-idf-kconfig": "~=2.5.0"
|
||||
}
|
||||
|
||||
@@ -2222,7 +2566,9 @@ env["BUILDERS"]["ElfToBin"].action = action
|
||||
#
|
||||
|
||||
ulp_dir = str(Path(PROJECT_DIR) / "ulp")
|
||||
if os.path.isdir(ulp_dir) and os.listdir(ulp_dir) and mcu not in ("esp32c2", "esp32c3", "esp32h2"):
|
||||
# ULP support: ESP32, ESP32-S2, ESP32-S3, ESP32-C6, ESP32-P4
|
||||
# No ULP: ESP32-C2, ESP32-C3, ESP32-C5, ESP32-H2
|
||||
if os.path.isdir(ulp_dir) and os.listdir(ulp_dir) and mcu not in ("esp32c2", "esp32c3", "esp32c5", "esp32h2"):
|
||||
env.SConscript("ulp.py", exports="env sdk_config project_config app_includes idf_variant")
|
||||
|
||||
#
|
||||
@@ -2320,12 +2666,19 @@ if "espidf" in env.subst("$PIOFRAMEWORK") and (flag_custom_component_add == True
|
||||
try:
|
||||
shutil.copy(str(Path(PROJECT_SRC_DIR) / "idf_component.yml.orig"), str(Path(PROJECT_SRC_DIR) / "idf_component.yml"))
|
||||
print("*** Original \"idf_component.yml\" restored ***")
|
||||
except (FileNotFoundError, PermissionError, OSError): # no "idf_component.yml" in source folder
|
||||
try:
|
||||
os.remove(str(Path(PROJECT_SRC_DIR) / "idf_component.yml"))
|
||||
print("*** pioarduino generated \"idf_component.yml\" removed ***")
|
||||
except (FileNotFoundError, PermissionError, OSError):
|
||||
print("*** no custom \"idf_component.yml\" found for removing ***")
|
||||
except (FileNotFoundError, PermissionError, OSError):
|
||||
# Only remove idf_component.yml if a .orig backup exists
|
||||
# This indicates the file was created/modified by pioarduino
|
||||
orig_file = Path(PROJECT_SRC_DIR) / "idf_component.yml.orig"
|
||||
yml_file = Path(PROJECT_SRC_DIR) / "idf_component.yml"
|
||||
if orig_file.exists() and yml_file.exists():
|
||||
try:
|
||||
os.remove(str(yml_file))
|
||||
print("*** pioarduino generated \"idf_component.yml\" removed ***")
|
||||
except (FileNotFoundError, PermissionError, OSError):
|
||||
print("*** Failed to remove pioarduino generated \"idf_component.yml\" ***")
|
||||
elif yml_file.exists():
|
||||
print("*** User-created \"idf_component.yml\" preserved (no .orig backup found) ***")
|
||||
if "arduino" in env.subst("$PIOFRAMEWORK"):
|
||||
# Restore original pioarduino-build.py, only used with Arduino
|
||||
from component_manager import ComponentManager
|
||||
|
||||
+149
-2
@@ -596,8 +596,8 @@ def firmware_metrics(target, source, env):
|
||||
source: SCons source
|
||||
env: SCons environment object
|
||||
"""
|
||||
if terminal_cp != "utf-8":
|
||||
print("Firmware metrics can not be shown. Set the terminal codepage to \"utf-8\"")
|
||||
if terminal_cp not in ["utf-8", "cp65001"]:
|
||||
print("Firmware metrics can not be shown. Set the terminal codepage to \"utf-8\" or \"cp65001\" on Windows.")
|
||||
return
|
||||
|
||||
map_file = str(Path(env.subst("$BUILD_DIR")) / (env.subst("$PROGNAME") + ".map"))
|
||||
@@ -650,6 +650,132 @@ def firmware_metrics(target, source, env):
|
||||
print(f'Make sure esp-idf-size is installed: uv pip install --python "{PYTHON_EXE}" esp-idf-size')
|
||||
|
||||
|
||||
def coredump_analysis(target, source, env):
|
||||
"""
|
||||
Custom target to run esp-coredump with support for command line parameters.
|
||||
Usage: pio run -t coredump -- [esp-coredump arguments]
|
||||
|
||||
Args:
|
||||
target: SCons target
|
||||
source: SCons source
|
||||
env: SCons environment object
|
||||
"""
|
||||
if terminal_cp != "utf-8":
|
||||
print("Coredump analysis can not be shown. Set the terminal codepage to \"utf-8\"")
|
||||
return
|
||||
|
||||
elf_file = str(Path(env.subst("$BUILD_DIR")) / (env.subst("$PROGNAME") + ".elf"))
|
||||
if not Path(elf_file).is_file():
|
||||
# elf file can be in project dir
|
||||
elf_file = str(Path(get_project_dir()) / (env.subst("$PROGNAME") + ".elf"))
|
||||
|
||||
if not Path(elf_file).is_file():
|
||||
print(f"Error: ELF file not found: {elf_file}")
|
||||
print("Make sure the project is built first with 'pio run'")
|
||||
return
|
||||
|
||||
try:
|
||||
cmd = [PYTHON_EXE, "-m", "esp_coredump"]
|
||||
|
||||
# Command Line Parameter, after --
|
||||
cli_args = []
|
||||
if "--" in sys.argv:
|
||||
dash_index = sys.argv.index("--")
|
||||
if dash_index + 1 < len(sys.argv):
|
||||
cli_args = sys.argv[dash_index + 1:]
|
||||
|
||||
# Add CLI arguments or use defaults
|
||||
if cli_args:
|
||||
cmd.extend(cli_args)
|
||||
# ELF file should be at the end as positional argument
|
||||
if not any(arg.endswith('.elf') for arg in cli_args):
|
||||
cmd.append(elf_file)
|
||||
else:
|
||||
# Default arguments if none provided
|
||||
# Parameters from platformio.ini
|
||||
extra_args = env.GetProjectOption("custom_esp_coredump_args", "")
|
||||
if extra_args:
|
||||
args = shlex.split(extra_args)
|
||||
cmd.extend(args)
|
||||
# Ensure ELF is last positional if not present
|
||||
if not any(a.endswith(".elf") for a in args):
|
||||
cmd.append(elf_file)
|
||||
else:
|
||||
# Prefer an explicit core file if configured or present; else read from flash
|
||||
core_file = env.GetProjectOption("custom_esp_coredump_corefile", "")
|
||||
if not core_file:
|
||||
for name in ("coredump.bin", "coredump.b64"):
|
||||
cand = Path(get_project_dir()) / name
|
||||
if cand.is_file():
|
||||
core_file = str(cand)
|
||||
break
|
||||
|
||||
# Global options
|
||||
cmd.extend(["--chip", mcu])
|
||||
upload_port = env.subst("$UPLOAD_PORT")
|
||||
if upload_port:
|
||||
cmd.extend(["--port", upload_port])
|
||||
|
||||
# Subcommand and arguments
|
||||
cmd.append("info_corefile")
|
||||
if core_file:
|
||||
cmd.extend(["--core", core_file])
|
||||
if core_file.lower().endswith(".b64"):
|
||||
cmd.extend(["--core-format", "b64"])
|
||||
# ELF is the required positional
|
||||
cmd.append(elf_file)
|
||||
|
||||
# Set up ESP-IDF environment variables and ensure required packages are installed
|
||||
coredump_env = os.environ.copy()
|
||||
|
||||
# Check if ESP-IDF packages are available, install if missing
|
||||
_framework_pkg_dir = platform.get_package_dir("framework-espidf")
|
||||
_rom_elfs_dir = platform.get_package_dir("tool-esp-rom-elfs")
|
||||
|
||||
# Install framework-espidf if not available
|
||||
if not _framework_pkg_dir or not os.path.isdir(_framework_pkg_dir):
|
||||
print("ESP-IDF framework not found, installing...")
|
||||
try:
|
||||
platform.install_package("framework-espidf")
|
||||
_framework_pkg_dir = platform.get_package_dir("framework-espidf")
|
||||
except Exception as e:
|
||||
print(f"Warning: Failed to install framework-espidf: {e}")
|
||||
|
||||
# Install tool-esp-rom-elfs if not available
|
||||
if not _rom_elfs_dir or not os.path.isdir(_rom_elfs_dir):
|
||||
print("ESP ROM ELFs tool not found, installing...")
|
||||
try:
|
||||
platform.install_package("tool-esp-rom-elfs")
|
||||
_rom_elfs_dir = platform.get_package_dir("tool-esp-rom-elfs")
|
||||
except Exception as e:
|
||||
print(f"Warning: Failed to install tool-esp-rom-elfs: {e}")
|
||||
|
||||
# Set environment variables if packages are available
|
||||
if _framework_pkg_dir and os.path.isdir(_framework_pkg_dir):
|
||||
coredump_env['IDF_PATH'] = str(Path(_framework_pkg_dir).resolve())
|
||||
if _rom_elfs_dir and os.path.isdir(_rom_elfs_dir):
|
||||
coredump_env['ESP_ROM_ELF_DIR'] = str(Path(_rom_elfs_dir).resolve())
|
||||
|
||||
# Debug-Info if wanted
|
||||
if env.GetProjectOption("custom_esp_coredump_verbose", False):
|
||||
print(f"Running command: {' '.join(cmd)}")
|
||||
if 'IDF_PATH' in coredump_env:
|
||||
print(f"IDF_PATH: {coredump_env['IDF_PATH']}")
|
||||
print(f"ESP_ROM_ELF_DIR: {coredump_env.get('ESP_ROM_ELF_DIR', 'Not set')}")
|
||||
|
||||
# Execute esp-coredump with ESP-IDF environment
|
||||
result = subprocess.run(cmd, check=False, capture_output=False, env=coredump_env)
|
||||
|
||||
if result.returncode != 0:
|
||||
print(f"Warning: esp-coredump exited with code {result.returncode}")
|
||||
|
||||
except FileNotFoundError:
|
||||
print("Error: Python executable not found.")
|
||||
print("Check your Python installation.")
|
||||
except Exception as e:
|
||||
print(f"Error: Failed to run coredump analysis: {e}")
|
||||
print(f'Make sure esp-coredump is installed: uv pip install --python "{PYTHON_EXE}" esp-coredump')
|
||||
|
||||
#
|
||||
# Target: Build executable and linkable firmware or FS image
|
||||
#
|
||||
@@ -931,6 +1057,27 @@ env.AddCustomTarget(
|
||||
always_build=True,
|
||||
)
|
||||
|
||||
# Register Custom Target for coredump analysis
|
||||
env.AddCustomTarget(
|
||||
name="coredump",
|
||||
dependencies="$BUILD_DIR/${PROGNAME}.elf",
|
||||
actions=coredump_analysis,
|
||||
title="Coredump Analysis",
|
||||
description="Analyze coredumps using esp-coredump "
|
||||
"(supports CLI args after --)",
|
||||
always_build=True,
|
||||
)
|
||||
|
||||
# Additional Target without Build-Dependency when already compiled
|
||||
env.AddCustomTarget(
|
||||
name="coredump-only",
|
||||
dependencies=None,
|
||||
actions=coredump_analysis,
|
||||
title="Coredump Analysis (No Build)",
|
||||
description="Analyze coredumps without building first",
|
||||
always_build=True,
|
||||
)
|
||||
|
||||
# Override memory inspection behavior
|
||||
env.SConscript("sizedata.py", exports="env")
|
||||
|
||||
|
||||
+70
-40
@@ -21,6 +21,7 @@ import socket
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from platformio.package.version import pepver_to_semver
|
||||
from platformio.compat import IS_WINDOWS
|
||||
@@ -55,20 +56,49 @@ python_deps = {
|
||||
"ecdsa": ">=0.19.1",
|
||||
"bitstring": ">=4.3.1",
|
||||
"reedsolo": ">=1.5.3,<1.8",
|
||||
"esp-idf-size": ">=2.0.0"
|
||||
"esp-idf-size": ">=2.0.0",
|
||||
"esp-coredump": ">=1.14.0"
|
||||
}
|
||||
|
||||
|
||||
def has_internet_connection(host="1.1.1.1", port=53, timeout=2):
|
||||
def has_internet_connection(timeout=5):
|
||||
"""
|
||||
Checks if an internet connection is available (default: Cloudflare DNS server).
|
||||
Returns True if a connection is possible, otherwise False.
|
||||
Checks practical internet reachability for dependency installation.
|
||||
1) If HTTPS/HTTP proxy environment variable is set, test TCP connectivity to the proxy endpoint.
|
||||
2) Otherwise, test direct TCP connectivity to common HTTPS endpoints (port 443).
|
||||
|
||||
Args:
|
||||
timeout (int): Timeout duration in seconds for the connection test.
|
||||
|
||||
Returns:
|
||||
True if at least one path appears reachable; otherwise False.
|
||||
"""
|
||||
try:
|
||||
with socket.create_connection((host, port), timeout=timeout):
|
||||
# 1) Test TCP connectivity to the proxy endpoint.
|
||||
proxy = os.getenv("HTTPS_PROXY") or os.getenv("https_proxy") or os.getenv("HTTP_PROXY") or os.getenv("http_proxy")
|
||||
if proxy:
|
||||
try:
|
||||
u = urlparse(proxy if "://" in proxy else f"http://{proxy}")
|
||||
host = u.hostname
|
||||
port = u.port or (443 if u.scheme == "https" else 80)
|
||||
if host and port:
|
||||
socket.create_connection((host, port), timeout=timeout).close()
|
||||
return True
|
||||
except Exception:
|
||||
# If proxy connection fails, fall back to direct connection test
|
||||
pass
|
||||
|
||||
# 2) Test direct TCP connectivity to common HTTPS endpoints (port 443).
|
||||
https_hosts = ("pypi.org", "files.pythonhosted.org", "github.com")
|
||||
for host in https_hosts:
|
||||
try:
|
||||
socket.create_connection((host, 443), timeout=timeout).close()
|
||||
return True
|
||||
except OSError:
|
||||
return False
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
# Direct DNS:53 connection is abolished due to many false positives on enterprise networks
|
||||
# (add it at the end if necessary)
|
||||
return False
|
||||
|
||||
|
||||
def get_executable_path(penv_dir, executable_name):
|
||||
@@ -286,18 +316,18 @@ def install_python_deps(python_exe, external_uv_executable):
|
||||
for p in packages:
|
||||
result[p["name"].lower()] = pepver_to_semver(p["version"])
|
||||
else:
|
||||
print(f"Error: uv pip list failed with exit code {result_obj.returncode}")
|
||||
print(f"Warning: uv pip list failed with exit code {result_obj.returncode}")
|
||||
if result_obj.stderr:
|
||||
print(f"Error output: {result_obj.stderr.strip()}")
|
||||
|
||||
except subprocess.TimeoutExpired:
|
||||
print("Error: uv pip list command timed out")
|
||||
print("Warning: uv pip list command timed out")
|
||||
except (json.JSONDecodeError, KeyError) as e:
|
||||
print(f"Error: Could not parse package list: {e}")
|
||||
print(f"Warning: Could not parse package list: {e}")
|
||||
except FileNotFoundError:
|
||||
print("Error: uv command not found")
|
||||
print("Warning: uv command not found")
|
||||
except Exception as e:
|
||||
print(f"Error! Couldn't extract the list of installed Python packages: {e}")
|
||||
print(f"Warning! Couldn't extract the list of installed Python packages: {e}")
|
||||
|
||||
return result
|
||||
|
||||
@@ -306,39 +336,39 @@ def install_python_deps(python_exe, external_uv_executable):
|
||||
|
||||
if packages_to_install:
|
||||
packages_list = []
|
||||
package_map = {}
|
||||
for p in packages_to_install:
|
||||
spec = python_deps[p]
|
||||
if spec.startswith(('http://', 'https://', 'git+', 'file://')):
|
||||
packages_list.append(spec)
|
||||
package_map[spec] = p
|
||||
else:
|
||||
full_spec = f"{p}{spec}"
|
||||
packages_list.append(full_spec)
|
||||
package_map[full_spec] = p
|
||||
packages_list.append(f"{p}{spec}")
|
||||
|
||||
for package_spec in packages_list:
|
||||
cmd = [
|
||||
penv_uv_executable, "pip", "install",
|
||||
f"--python={python_exe}",
|
||||
"--quiet", "--upgrade",
|
||||
package_spec
|
||||
]
|
||||
try:
|
||||
subprocess.check_call(
|
||||
cmd,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.STDOUT,
|
||||
timeout=300
|
||||
)
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"Error: Installing package '{package_map.get(package_spec, package_spec)}' failed (exit code {e.returncode}).")
|
||||
except subprocess.TimeoutExpired:
|
||||
print(f"Error: Installing package '{package_map.get(package_spec, package_spec)}' timed out.")
|
||||
except FileNotFoundError:
|
||||
print("Error: uv command not found")
|
||||
except Exception as e:
|
||||
print(f"Error: Installing package '{package_map.get(package_spec, package_spec)}': {e}.")
|
||||
cmd = [
|
||||
penv_uv_executable, "pip", "install",
|
||||
f"--python={python_exe}",
|
||||
"--quiet", "--upgrade"
|
||||
] + packages_list
|
||||
|
||||
try:
|
||||
subprocess.check_call(
|
||||
cmd,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.STDOUT,
|
||||
timeout=300
|
||||
)
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"Error: Failed to install Python dependencies (exit code: {e.returncode})")
|
||||
return False
|
||||
except subprocess.TimeoutExpired:
|
||||
print("Error: Python dependencies installation timed out")
|
||||
return False
|
||||
except FileNotFoundError:
|
||||
print("Error: uv command not found")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"Error installing Python dependencies: {e}")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
Reference in New Issue
Block a user