Arduino core 3.2.1

This commit is contained in:
Jason2866
2025-07-03 17:12:25 +02:00
parent da5cb384a5
commit 9bf8b3f10a
76 changed files with 493576 additions and 1697 deletions
+3 -3
View File
@@ -110,14 +110,14 @@ env.Append(
" ".join(
[
"riscv32-esp-elf-objcopy"
if mcu in ("esp32c2","esp32c3","esp32c6","esp32h2","esp32p4")
if mcu in ("esp32c2","esp32c3","esp32c5","esp32c6","esp32h2","esp32p4")
else "xtensa-%s-elf-objcopy" % mcu,
"--input-target",
"binary",
"--output-target",
"elf32-littleriscv" if mcu in ("esp32c2","esp32c3","esp32c6","esp32h2","esp32p4") else "elf32-xtensa-le",
"elf32-littleriscv" if mcu in ("esp32c2","esp32c3","esp32c5","esp32c6","esp32h2","esp32p4") else "elf32-xtensa-le",
"--binary-architecture",
"riscv" if mcu in ("esp32c2","esp32c3","esp32c6","esp32h2","esp32p4") else "xtensa",
"riscv" if mcu in ("esp32c2","esp32c3","esp32c5","esp32c6","esp32h2","esp32p4") else "xtensa",
"--rename-section",
".data=.rodata.embedded",
"$SOURCE",
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+271 -185
View File
@@ -40,22 +40,22 @@ from SCons.Script import (
DefaultEnvironment,
)
from platformio import fs, __version__
from platformio import fs
from platformio.compat import IS_WINDOWS
from platformio.proc import exec_command
from platformio.builder.tools.piolib import ProjectAsLibBuilder
from platformio.project.config import ProjectConfig
from platformio.package.version import get_original_version, pepver_to_semver
# Added to avoid conflicts between installed Python packages from
# the IDF virtual environment and PlatformIO Core
# Note: This workaround can be safely deleted when PlatformIO 6.1.7 is released
if os.environ.get("PYTHONPATH"):
del os.environ["PYTHONPATH"]
env = DefaultEnvironment()
env.SConscript("_embed_files.py", exports="env")
# remove maybe existing old map file in project root
map_file = os.path.join(env.subst("$PROJECT_DIR"), env.subst("$PROGNAME") + ".map")
if os.path.exists(map_file):
os.remove(map_file)
def install_standard_python_deps():
def _get_installed_standard_pip_packages():
result = {}
@@ -83,7 +83,10 @@ def install_standard_python_deps():
deps = {
"wheel": ">=0.35.1",
"rich-click": ">=1.8.6",
"PyYAML": ">=6.0.2"
"PyYAML": ">=6.0.2",
"intelhex": ">=2.3.0",
"rich": ">=14.0.0",
"esp-idf-size": ">=1.6.1"
}
installed_packages = _get_installed_standard_pip_packages()
@@ -100,7 +103,7 @@ def install_standard_python_deps():
env.Execute(
env.VerboseAction(
(
'"$PYTHONEXE" -m pip install -U '
'"$PYTHONEXE" -m pip install -U -q -q -q '
+ " ".join(
[
'"%s%s"' % (p, deps[p])
@@ -147,18 +150,20 @@ TOOLCHAIN_DIR = platform.get_package_dir(
assert os.path.isdir(FRAMEWORK_DIR)
assert os.path.isdir(TOOLCHAIN_DIR)
if (
["espidf"] == env.get("PIOFRAMEWORK")
and semantic_version.Version.coerce(__version__)
<= semantic_version.Version("6.1.10")
and "__debug" in COMMAND_LINE_TARGETS
):
print("Warning! Debugging an IDF project requires PlatformIO Core >= 6.1.11!")
def create_silent_action(action_func):
"""Create a silent SCons action that suppresses output"""
silent_action = env.Action(action_func)
silent_action.strfunction = lambda target, source, env: ''
return silent_action
# Arduino framework as a component is not compatible with ESP-IDF >5.3
if "arduino" in env.subst("$PIOFRAMEWORK"):
ARDUINO_FRAMEWORK_DIR = platform.get_package_dir("framework-arduinoespressif32")
ARDUINO_FRMWRK_LIB_DIR = platform.get_package_dir("framework-arduinoespressif32-libs")
if mcu == "esp32c2":
ARDUINO_FRMWRK_C2_LIB_DIR = join(ARDUINO_FRMWRK_LIB_DIR, mcu)
if not os.path.exists(ARDUINO_FRMWRK_C2_LIB_DIR):
ARDUINO_C2_DIR = join(platform.get_package_dir("framework-arduino-c2-skeleton-lib"),mcu)
shutil.copytree(ARDUINO_C2_DIR, ARDUINO_FRMWRK_C2_LIB_DIR, dirs_exist_ok=True)
# Possible package names in 'package@version' format is not compatible with CMake
if "@" in os.path.basename(ARDUINO_FRAMEWORK_DIR):
new_path = os.path.join(
@@ -168,6 +173,7 @@ if "arduino" in env.subst("$PIOFRAMEWORK"):
os.rename(ARDUINO_FRAMEWORK_DIR, new_path)
ARDUINO_FRAMEWORK_DIR = new_path
assert ARDUINO_FRAMEWORK_DIR and os.path.isdir(ARDUINO_FRAMEWORK_DIR)
arduino_libs_mcu = join(platform.get_package_dir("framework-arduinoespressif32-libs"),mcu)
BUILD_DIR = env.subst("$BUILD_DIR")
PROJECT_DIR = env.subst("$PROJECT_DIR")
@@ -178,6 +184,18 @@ SDKCONFIG_PATH = os.path.expandvars(board.get(
os.path.join(PROJECT_DIR, "sdkconfig.%s" % env.subst("$PIOENV")),
))
def contains_path_traversal(url):
"""Check for Path Traversal patterns"""
dangerous_patterns = [
'../', '..\\', # Standard Path Traversal
'%2e%2e%2f', '%2e%2e%5c', # URL-encoded
'..%2f', '..%5c', # Mixed
'%252e%252e%252f', # Double encoded
]
url_lower = url.lower()
return any(pattern in url_lower for pattern in dangerous_patterns)
#
# generate modified Arduino IDF sdkconfig, applying settings from "custom_sdkconfig"
#
@@ -192,180 +210,208 @@ if "espidf.custom_sdkconfig" in board:
flag_custom_sdkonfig = True
def HandleArduinoIDFsettings(env):
"""
Handles Arduino IDF settings configuration with custom sdkconfig support.
"""
def get_MD5_hash(phrase):
"""Generate MD5 hash for checksum validation."""
import hashlib
return hashlib.md5((phrase).encode('utf-8')).hexdigest()[:16]
return hashlib.md5(phrase.encode('utf-8')).hexdigest()[:16]
def custom_sdkconfig_file(string):
if not config.has_option("env:"+env["PIOENV"], "custom_sdkconfig"):
def load_custom_sdkconfig_file():
"""Load custom sdkconfig from file or URL if specified."""
if not config.has_option("env:" + env["PIOENV"], "custom_sdkconfig"):
return ""
sdkconfig_entrys = env.GetProjectOption("custom_sdkconfig").splitlines()
for file in sdkconfig_entrys:
if "http" in file and "://" in file:
response = requests.get(file.split(" ")[0])
if response.ok:
target = str(response.content.decode('utf-8'))
sdkconfig_entries = env.GetProjectOption("custom_sdkconfig").splitlines()
for file_entry in sdkconfig_entries:
# Handle HTTP/HTTPS URLs
if "http" in file_entry and "://" in file_entry:
url = file_entry.split(" ")[0]
# Path Traversal protection
if contains_path_traversal(url):
print(f"Path Traversal detected: {url} check your URL path")
else:
print("Failed to download:", file)
return ""
return target
if "file://" in file:
file_path = join(PROJECT_DIR,file.lstrip("file://").split(os.path.sep)[-1])
try:
response = requests.get(file_entry.split(" ")[0], timeout=10)
if response.ok:
return response.content.decode('utf-8')
except requests.RequestException as e:
print(f"Error downloading {file_entry}: {e}")
except UnicodeDecodeError as e:
print(f"Error decoding response from {file_entry}: {e}")
return ""
# Handle local files
if "file://" in file_entry:
file_ref = file_entry[7:] if file_entry.startswith("file://") else file_entry
filename = os.path.basename(file_ref)
file_path = join(PROJECT_DIR, filename)
if os.path.exists(file_path):
with open(file_path, 'r') as file:
target = file.read()
try:
with open(file_path, 'r') as f:
return f.read()
except IOError as e:
print(f"Error reading file {file_path}: {e}")
return ""
else:
print("File not found:", file_path)
print("File not found, check path:", file_path)
return ""
return target
return ""
def extract_flag_name(line):
"""Extract flag name from sdkconfig line."""
line = line.strip()
if line.startswith("#") and "is not set" in line:
return line.split(" ")[1]
elif not line.startswith("#") and "=" in line:
return line.split("=")[0]
return None
custom_sdk_config_flags = ""
board_idf_config_flags = ""
sdkconfig_file_flags = ""
custom_sdkconfig_file_str = ""
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)
# 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
if config.has_option("env:" + env["PIOENV"], "custom_sdkconfig"):
custom_flags = env.GetProjectOption("custom_sdkconfig").rstrip("\n")
if custom_flags:
flags.append(custom_flags)
return "\n".join(flags) + "\n" if flags else ""
if config.has_option("env:"+env["PIOENV"], "custom_sdkconfig"):
flag_custom_sdkonfig = True
custom_sdk_config_flags = (env.GetProjectOption("custom_sdkconfig").rstrip("\n")) + "\n"
custom_sdkconfig_file_str = custom_sdkconfig_file(sdkconfig_file_flags)
if "espidf.custom_sdkconfig" in board:
board_idf_config_flags = ('\n'.join([element for element in board.get("espidf.custom_sdkconfig", "")])).rstrip("\n") + "\n"
flag_custom_sdkonfig = True
if flag_custom_sdkonfig == True: # TDOO duplicated
print("*** Add \"custom_sdkconfig\" settings to IDF sdkconfig.defaults ***")
idf_config_flags = custom_sdk_config_flags
if custom_sdkconfig_file_str != "":
sdkconfig_file_flags = custom_sdkconfig_file_str + "\n"
idf_config_flags = sdkconfig_file_flags + idf_config_flags
idf_config_flags = board_idf_config_flags + idf_config_flags
def add_flash_configuration(config_flags):
"""Add flash frequency and mode configuration."""
if flash_frequency != "80m":
idf_config_flags = idf_config_flags + "# CONFIG_ESPTOOLPY_FLASHFREQ_80M is not set\n"
esptool_flashfreq_y = "CONFIG_ESPTOOLPY_FLASHFREQ_%s=y\n" % flash_frequency.upper()
esptool_flashfreq_M = "CONFIG_ESPTOOLPY_FLASHFREQ=\"%s\"\n" % flash_frequency
idf_config_flags = idf_config_flags + esptool_flashfreq_y + esptool_flashfreq_M
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":
idf_config_flags = idf_config_flags + "# CONFIG_ESPTOOLPY_FLASHMODE_QIO is not set\n"
esptool_flashmode = "CONFIG_ESPTOOLPY_FLASHMODE_%s=y\n" % flash_mode.upper()
if esptool_flashmode not in idf_config_flags:
idf_config_flags = idf_config_flags + esptool_flashmode
if mcu in ("esp32") and "CONFIG_FREERTOS_UNICORE=y" in idf_config_flags:
idf_config_flags = idf_config_flags + "# CONFIG_SPIRAM is not set\n"
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
# ESP32 specific SPIRAM configuration
if mcu == "esp32" and "CONFIG_FREERTOS_UNICORE=y" in config_flags:
config_flags += "# CONFIG_SPIRAM is not set\n"
return config_flags
idf_config_flags = idf_config_flags.splitlines()
sdkconfig_src = join(ARDUINO_FRMWRK_LIB_DIR,mcu,"sdkconfig")
def get_flag(line):
if line.startswith("#") and "is not set" in line:
return line.split(" ")[1]
elif not line.startswith("#") and len(line.split("=")) > 1:
return line.split("=")[0]
else:
return None
with open(sdkconfig_src) as src:
sdkconfig_dst = os.path.join(PROJECT_DIR, "sdkconfig.defaults")
dst = open(sdkconfig_dst,"w")
dst.write("# TASMOTA__"+ get_MD5_hash(''.join(custom_sdk_config_flags).strip() + mcu) +"\n")
while line := src.readline():
flag = get_flag(line)
if flag is None:
def write_sdkconfig_file(idf_config_flags, checksum_source):
if "arduino" not in env.subst("$PIOFRAMEWORK"):
print("Error: Arduino framework required for sdkconfig processing")
return
"""Write the final sdkconfig.defaults file with checksum."""
sdkconfig_src = join(arduino_libs_mcu, "sdkconfig")
sdkconfig_dst = join(PROJECT_DIR, "sdkconfig.defaults")
# Generate checksum for validation (maintains original logic)
checksum = get_MD5_hash(checksum_source.strip() + mcu)
with open(sdkconfig_src, 'r', encoding='utf-8') as src, open(sdkconfig_dst, 'w', encoding='utf-8') as dst:
# Write checksum header (critical for compilation decision logic)
dst.write(f"# TASMOTA__{checksum}\n")
processed_flags = set()
# Process each line from source sdkconfig
for line in src:
flag_name = extract_flag_name(line)
if flag_name is None:
dst.write(line)
else:
no_match = True
for item in idf_config_flags:
if flag == get_flag(item.replace("\'", "")):
dst.write(item.replace("\'", "")+"\n")
no_match = False
print("Replace:",line,"with:",item.replace("\'", ""))
idf_config_flags.remove(item)
if no_match:
dst.write(line)
for item in idf_config_flags: # are there new flags?
print("Add:",item.replace("\'", ""))
dst.write(item.replace("\'", "")+"\n")
dst.close()
return
else:
continue
# Check if we have a custom replacement for this flag
flag_replaced = False
for custom_flag in idf_config_flags[:]: # Create copy for safe removal
custom_flag_name = extract_flag_name(custom_flag.replace("'", ""))
if flag_name == custom_flag_name:
cleaned_flag = custom_flag.replace("'", "")
dst.write(cleaned_flag + "\n")
print(f"Replace: {line.strip()} with: {cleaned_flag}")
idf_config_flags.remove(custom_flag)
processed_flags.add(custom_flag_name)
flag_replaced = True
break
if not flag_replaced:
dst.write(line)
# Add any remaining new flags
for remaining_flag in idf_config_flags:
cleaned_flag = remaining_flag.replace("'", "")
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:
return
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()]
# Write final configuration file with checksum
custom_sdk_config_flags = ""
if config.has_option("env:" + env["PIOENV"], "custom_sdkconfig"):
custom_sdk_config_flags = env.GetProjectOption("custom_sdkconfig").rstrip("\n") + "\n"
write_sdkconfig_file(idf_config_list, custom_sdk_config_flags)
def HandleCOMPONENTsettings(env):
if flag_custom_component_add == True or flag_custom_component_remove == True: # todo remove duplicated
import yaml
from yaml import SafeLoader
print("*** \"custom_component\" is used to select managed idf components ***")
if flag_custom_component_remove == True:
idf_custom_component_remove = env.GetProjectOption("custom_component_remove").splitlines()
else:
idf_custom_component_remove = ""
if flag_custom_component_add == True:
idf_custom_component_add = env.GetProjectOption("custom_component_add").splitlines()
else:
idf_custom_component_add = ""
from component_manager import ComponentManager
component_manager = ComponentManager(env)
# search "idf_component.yml" file
try: # 1.st in Arduino framework
idf_component_yml_src = os.path.join(ARDUINO_FRAMEWORK_DIR, "idf_component.yml")
shutil.copy(join(ARDUINO_FRAMEWORK_DIR,"idf_component.yml"),join(ARDUINO_FRAMEWORK_DIR,"idf_component.yml.orig"))
yml_file_dir = idf_component_yml_src
except: # 2.nd Project source
try:
idf_component_yml_src = os.path.join(PROJECT_SRC_DIR, "idf_component.yml")
shutil.copy(join(PROJECT_SRC_DIR,"idf_component.yml"),join(PROJECT_SRC_DIR,"idf_component.yml.orig"))
yml_file_dir = idf_component_yml_src
except: # no idf_component.yml in Project source -> create
idf_component_yml_src = os.path.join(PROJECT_SRC_DIR, "idf_component.yml")
yml_file_dir = idf_component_yml_src
idf_component_yml_str = """
dependencies:
idf: \">=5.1\"
"""
idf_component_yml = yaml.safe_load(idf_component_yml_str)
with open(idf_component_yml_src, 'w',) as f :
yaml.dump(idf_component_yml,f)
if flag_custom_component_add or flag_custom_component_remove:
actions = [action for flag, action in [
(flag_custom_component_add, "select"),
(flag_custom_component_remove, "deselect")
] if flag]
action_text = " and ".join(actions)
print(f"*** \"custom_component\" is used to {action_text} managed idf components ***")
yaml_file=open(idf_component_yml_src,"r")
idf_component=yaml.load(yaml_file, Loader=SafeLoader)
idf_component_str=json.dumps(idf_component) # convert to json string
idf_component_json=json.loads(idf_component_str) # convert string to json dict
if idf_custom_component_remove != "":
for entry in idf_custom_component_remove:
# checking if the entry exists before removing
if entry in idf_component_json["dependencies"]:
print("*** Removing component:",entry)
del idf_component_json["dependencies"][entry]
if idf_custom_component_add != "":
for entry in idf_custom_component_add:
if len(str(entry)) > 4: # too short or empty entry
# add new entrys to json
if "@" in entry:
idf_comp_entry = str(entry.split("@")[0]).replace(" ", "")
idf_comp_vers = str(entry.split("@")[1]).replace(" ", "")
else:
idf_comp_entry = str(entry).replace(" ", "")
idf_comp_vers = "*"
if idf_comp_entry not in idf_component_json["dependencies"]:
print("*** Adding component:", idf_comp_entry, idf_comp_vers)
new_entry = {idf_comp_entry: {"version": idf_comp_vers}}
idf_component_json["dependencies"].update(new_entry)
idf_component_yml_file = open(yml_file_dir,"w")
yaml.dump(idf_component_json, idf_component_yml_file)
idf_component_yml_file.close()
# print("JSON from modified idf_component.yml:")
# print(json.dumps(idf_component_json))
component_manager.handle_component_settings(
add_components=flag_custom_component_add,
remove_components=flag_custom_component_remove
)
return
return
if flag_custom_component_add == True or flag_custom_component_remove == True:
if "arduino" in env.subst("$PIOFRAMEWORK"):
HandleCOMPONENTsettings(env)
if flag_custom_sdkonfig == True and "arduino" in env.subst("$PIOFRAMEWORK"):
if flag_custom_sdkonfig == True and "arduino" in env.subst("$PIOFRAMEWORK") and "espidf" not in env.subst("$PIOFRAMEWORK"):
HandleArduinoIDFsettings(env)
LIB_SOURCE = os.path.join(ProjectConfig.get_instance().get("platformio", "platforms_dir"), "espressif32", "builder", "build_lib")
if not bool(os.path.exists(os.path.join(PROJECT_DIR, ".dummy"))):
@@ -397,7 +443,6 @@ def get_project_lib_includes(env):
return paths
def is_cmake_reconfigure_required(cmake_api_reply_dir):
cmake_cache_file = os.path.join(BUILD_DIR, "CMakeCache.txt")
cmake_txt_files = [
@@ -536,6 +581,8 @@ def populate_idf_env_vars(idf_env):
if "IDF_TOOLS_PATH" in idf_env:
del idf_env["IDF_TOOLS_PATH"]
idf_env["ESP_ROM_ELF_DIR"] = platform.get_package_dir("tool-esp-rom-elfs")
def get_target_config(project_configs, target_index, cmake_api_reply_dir):
target_json = project_configs.get("targets")[target_index].get("jsonFile", "")
@@ -595,6 +642,8 @@ def extract_defines(compile_group):
define_string = define_string.strip()
if "=" in define_string:
define, value = define_string.split("=", maxsplit=1)
if define == "OPENTHREAD_BUILD_DATETIME":
return None
if any(char in value for char in (' ', '<', '>')):
value = f'"{value}"'
elif '"' in value and not value.startswith("\\"):
@@ -1531,11 +1580,10 @@ def install_python_deps():
# https://github.com/platformio/platformio-core/issues/4614
"urllib3": "<2",
# https://github.com/platformio/platform-espressif32/issues/635
"cryptography": "~=41.0.1",
"future": ">=0.18.3",
"cryptography": "~=44.0.0",
"pyparsing": ">=3.1.0,<4",
"idf-component-manager": "~=2.0.1",
"esp-idf-kconfig": ">=2.5.0"
"esp-idf-kconfig": "~=2.5.0"
}
if sys_platform.system() == "Darwin" and "arm" in sys_platform.machine().lower():
@@ -1556,7 +1604,7 @@ def install_python_deps():
env.Execute(
env.VerboseAction(
(
'"%s" -m pip install -U ' % python_exe_path
'"%s" -m pip install -U -q -q -q ' % python_exe_path
+ " ".join(['"%s%s"' % (p, deps[p]) for p in packages_to_install])
),
"Installing ESP-IDF's Python dependencies",
@@ -1566,7 +1614,7 @@ def install_python_deps():
if IS_WINDOWS and "windows-curses" not in installed_packages:
env.Execute(
env.VerboseAction(
'"%s" -m pip install windows-curses' % python_exe_path,
'"%s" -m pip install -q -q -q windows-curses' % python_exe_path,
"Installing windows-curses package",
)
)
@@ -1786,18 +1834,36 @@ if "arduino" in env.subst("$PIOFRAMEWORK"):
LIBSOURCE_DIRS=[os.path.join(ARDUINO_FRAMEWORK_DIR, "libraries")]
)
# Set ESP-IDF version environment variables (needed for proper Kconfig processing)
framework_version = get_framework_version()
major_version = framework_version.split('.')[0] + '.' + framework_version.split('.')[1]
os.environ["ESP_IDF_VERSION"] = major_version
# Configure CMake arguments with ESP-IDF version
extra_cmake_args = [
"-DIDF_TARGET=" + idf_variant,
"-DPYTHON_DEPS_CHECKED=1",
"-DEXTRA_COMPONENT_DIRS:PATH=" + ";".join(extra_components),
"-DPYTHON=" + get_python_exe(),
"-DSDKCONFIG=" + SDKCONFIG_PATH,
f"-DESP_IDF_VERSION={major_version}",
f"-DESP_IDF_VERSION_MAJOR={framework_version.split('.')[0]}",
f"-DESP_IDF_VERSION_MINOR={framework_version.split('.')[1]}",
]
# This will add the linker flag for the map file
extra_cmake_args.append(
f'-DCMAKE_EXE_LINKER_FLAGS=-Wl,-Map={os.path.join(BUILD_DIR, env.subst("$PROGNAME") + ".map")}'
)
# Add any extra args from board config
extra_cmake_args += click.parser.split_arg_string(board.get("build.cmake_extra_args", ""))
print("Reading CMake configuration...")
project_codemodel = get_cmake_code_model(
PROJECT_DIR,
BUILD_DIR,
[
"-DIDF_TARGET=" + idf_variant,
"-DPYTHON_DEPS_CHECKED=1",
"-DEXTRA_COMPONENT_DIRS:PATH=" + ";".join(extra_components),
"-DPYTHON=" + get_python_exe(),
"-DSDKCONFIG=" + SDKCONFIG_PATH,
]
+ click.parser.split_arg_string(board.get("build.cmake_extra_args", "")),
extra_cmake_args
)
# At this point the sdkconfig file should be generated by the underlying build system
@@ -1973,7 +2039,7 @@ env.Prepend(
(
board.get(
"upload.bootloader_offset",
"0x1000" if mcu in ["esp32", "esp32s2"] else ("0x2000" if mcu in ["esp32p4"] else "0x0"),
"0x1000" if mcu in ["esp32", "esp32s2"] else ("0x2000" if mcu in ["esp32c5", "esp32p4"] else "0x0"),
),
os.path.join("$BUILD_DIR", "bootloader.bin"),
),
@@ -2063,6 +2129,15 @@ extra_elf2bin_flags = "--elf-sha256-offset 0xb0"
# For chips that support configurable MMU page size feature
# If page size is configured to values other than the default "64KB" in menuconfig,
mmu_page_size = "64KB"
if sdk_config.get("MMU_PAGE_SIZE_8KB", False):
mmu_page_size = "8KB"
elif sdk_config.get("MMU_PAGE_SIZE_16KB", False):
mmu_page_size = "16KB"
elif sdk_config.get("MMU_PAGE_SIZE_32KB", False):
mmu_page_size = "32KB"
else:
mmu_page_size = "64KB"
if sdk_config.get("SOC_MMU_PAGE_SIZE_CONFIGURABLE", False):
if board_flash_size == "2MB":
mmu_page_size = "32KB"
@@ -2091,7 +2166,7 @@ if os.path.isdir(ulp_dir) and os.listdir(ulp_dir) and mcu not in ("esp32c2", "es
# Compile Arduino IDF sources
#
if "arduino" in env.get("PIOFRAMEWORK") and "espidf" not in env.get("PIOFRAMEWORK"):
if ("arduino" in env.subst("$PIOFRAMEWORK")) and ("espidf" not in env.subst("$PIOFRAMEWORK")):
def idf_lib_copy(source, target, env):
env_build = join(env["PROJECT_BUILD_DIR"],env["PIOENV"])
sdkconfig_h_path = join(env_build,"config","sdkconfig.h")
@@ -2147,9 +2222,14 @@ if "arduino" in env.get("PIOFRAMEWORK") and "espidf" not in env.get("PIOFRAMEWOR
print("*** Original Arduino \"idf_component.yml\" restored ***")
except:
print("*** Original Arduino \"idf_component.yml\" couldnt be restored ***")
env.AddPostAction("checkprogsize", idf_lib_copy)
# Restore original pioarduino-build.py
from component_manager import ComponentManager
component_manager = ComponentManager(env)
component_manager.restore_pioarduino_build_py()
silent_action = create_silent_action(idf_lib_copy)
env.AddPostAction("checkprogsize", silent_action)
if "espidf" in env.get("PIOFRAMEWORK") and (flag_custom_component_add == True or flag_custom_component_remove == True):
if "espidf" in env.subst("$PIOFRAMEWORK") and (flag_custom_component_add == True or flag_custom_component_remove == True):
def idf_custom_component(source, target, env):
try:
shutil.copy(join(ARDUINO_FRAMEWORK_DIR,"idf_component.yml.orig"),join(ARDUINO_FRAMEWORK_DIR,"idf_component.yml"))
@@ -2163,8 +2243,14 @@ if "espidf" in env.get("PIOFRAMEWORK") and (flag_custom_component_add == True or
os.remove(join(PROJECT_SRC_DIR,"idf_component.yml"))
print("*** pioarduino generated \"idf_component.yml\" removed ***")
except:
print("*** \"idf_component.yml\" couldnt be removed ***")
env.AddPostAction("checkprogsize", idf_custom_component)
print("*** no custom \"idf_component.yml\" found for removing ***")
if "arduino" in env.subst("$PIOFRAMEWORK"):
# Restore original pioarduino-build.py, only used with Arduino
from component_manager import ComponentManager
component_manager = ComponentManager(env)
component_manager.restore_pioarduino_build_py()
silent_action = create_silent_action(idf_custom_component)
env.AddPostAction("checkprogsize", silent_action)
#
# Process OTA partition and image
#
@@ -2219,7 +2305,7 @@ def _parse_size(value):
partitions_csv = env.subst("$PARTITIONS_TABLE_CSV")
result = []
next_offset = 0
bound = int(board.get("upload.offset_address", "0x10000"), 16) # default 0x10000
bound = 0x10000
with open(partitions_csv) as fp:
for line in fp.readlines():
line = line.strip()
+1 -1
View File
@@ -37,7 +37,7 @@ def prepare_ulp_env_vars(env):
toolchain_path = platform.get_package_dir(
"toolchain-xtensa-esp-elf"
if idf_variant not in ("esp32c6", "esp32p4")
if idf_variant not in ("esp32c5","esp32c6", "esp32p4")
else "toolchain-riscv32-esp"
)
+333 -127
View File
@@ -12,31 +12,47 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import locale
import os
import re
import shlex
import subprocess
import sys
from os.path import isfile, join
from SCons.Script import (
ARGUMENTS, COMMAND_LINE_TARGETS, AlwaysBuild, Builder, Default,
DefaultEnvironment)
ARGUMENTS,
COMMAND_LINE_TARGETS,
AlwaysBuild,
Builder,
Default,
DefaultEnvironment,
)
from platformio.project.helpers import get_project_dir
from platformio.util import get_serial_ports
# Initialize environment and configuration
env = DefaultEnvironment()
platform = env.PioPlatform()
projectconfig = env.GetProjectConfig()
terminal_cp = locale.getpreferredencoding().lower()
#
# Helpers
#
# Framework directory path
FRAMEWORK_DIR = platform.get_package_dir("framework-arduinoespressif32")
def BeforeUpload(target, source, env):
"""
Prepare the environment before uploading firmware.
Handles port detection and special upload configurations.
"""
upload_options = {}
if "BOARD" in env:
upload_options = env.BoardConfig().get("upload", {})
env.AutodetectUploadPort()
if not env.subst("$UPLOAD_PORT"):
env.AutodetectUploadPort()
before_ports = get_serial_ports()
if upload_options.get("use_1200bps_touch", False):
@@ -47,6 +63,10 @@ def BeforeUpload(target, source, env):
def _get_board_memory_type(env):
"""
Determine the memory type configuration for the board.
Returns the appropriate memory type string based on board configuration.
"""
board_config = env.BoardConfig()
default_type = "%s_%s" % (
board_config.get("build.flash_mode", "dio"),
@@ -62,24 +82,33 @@ def _get_board_memory_type(env):
),
)
def _normalize_frequency(frequency):
"""
Convert frequency value to normalized string format (e.g., "40m").
Removes 'L' suffix and converts to MHz format.
"""
frequency = str(frequency).replace("L", "")
return str(int(int(frequency) / 1000000)) + "m"
def _get_board_f_flash(env):
"""Get the flash frequency for the board."""
frequency = env.subst("$BOARD_F_FLASH")
return _normalize_frequency(frequency)
def _get_board_f_image(env):
"""Get the image frequency for the board, fallback to flash frequency."""
board_config = env.BoardConfig()
if "build.f_image" in board_config:
return _normalize_frequency(board_config.get("build.f_image"))
return _get_board_f_flash(env)
def _get_board_f_boot(env):
"""Get the boot frequency for the board, fallback to flash frequency."""
board_config = env.BoardConfig()
if "build.f_boot" in board_config:
return _normalize_frequency(board_config.get("build.f_boot"))
@@ -88,10 +117,11 @@ def _get_board_f_boot(env):
def _get_board_flash_mode(env):
if _get_board_memory_type(env) in (
"opi_opi",
"opi_qspi",
):
"""
Determine the appropriate flash mode for the board.
Handles special cases for OPI memory types.
"""
if _get_board_memory_type(env) in ("opi_opi", "opi_qspi"):
return "dout"
mode = env.subst("$BOARD_FLASH_MODE")
@@ -101,6 +131,10 @@ def _get_board_flash_mode(env):
def _get_board_boot_mode(env):
"""
Determine the boot mode for the board.
Handles special cases for OPI memory types.
"""
memory_type = env.BoardConfig().get("build.arduino.memory_type", "")
build_boot = env.BoardConfig().get("build.boot", "$BOARD_FLASH_MODE")
if memory_type in ("opi_opi", "opi_qspi"):
@@ -109,6 +143,10 @@ def _get_board_boot_mode(env):
def _parse_size(value):
"""
Parse size values from various formats (int, hex, K/M suffixes).
Returns the size in bytes as an integer.
"""
if isinstance(value, int):
return value
elif value.isdigit():
@@ -122,16 +160,23 @@ def _parse_size(value):
def _parse_partitions(env):
"""
Parse the partition table CSV file and return partition information.
Also sets the application offset for the environment.
"""
partitions_csv = env.subst("$PARTITIONS_TABLE_CSV")
if not isfile(partitions_csv):
sys.stderr.write("Could not find the file %s with partitions "
"table.\n" % partitions_csv)
sys.stderr.write(
"Could not find the file %s with partitions table.\n"
% partitions_csv
)
env.Exit(1)
return
result = []
next_offset = 0
app_offset = int(board.get("upload.offset_address", "0x10000"), 16) # default 0x10000
app_offset = 0x10000 # Default address for firmware
with open(partitions_csv) as fp:
for line in fp.readlines():
line = line.strip()
@@ -148,25 +193,34 @@ def _parse_partitions(env):
"subtype": tokens[2],
"offset": tokens[3] or calculated_offset,
"size": tokens[4],
"flags": tokens[5] if len(tokens) > 5 else None
"flags": tokens[5] if len(tokens) > 5 else None,
}
result.append(partition)
next_offset = _parse_size(partition["offset"])
if (partition["subtype"] == "ota_0"):
if partition["subtype"] == "ota_0":
app_offset = next_offset
next_offset = next_offset + _parse_size(partition["size"])
# Configure application partition offset
env.Replace(ESP32_APP_OFFSET=str(hex(app_offset)))
# Propagate application offset to debug configurations
env["INTEGRATION_EXTRA_DATA"].update({"application_offset": str(hex(app_offset))})
env["INTEGRATION_EXTRA_DATA"].update(
{"application_offset": str(hex(app_offset))}
)
return result
def _update_max_upload_size(env):
"""
Update the maximum upload size based on partition table configuration.
Prioritizes user-specified partition names.
"""
if not env.get("PARTITIONS_TABLE_CSV"):
return
sizes = {
p["subtype"]: _parse_size(p["size"]) for p in _parse_partitions(env)
p["subtype"]: _parse_size(p["size"])
for p in _parse_partitions(env)
if p["type"] in ("0", "app")
}
@@ -177,12 +231,15 @@ def _update_max_upload_size(env):
if custom_app_partition_name:
selected_partition = partitions.get(custom_app_partition_name, {})
if selected_partition:
board.update("upload.maximum_size", _parse_size(selected_partition["size"]))
board.update(
"upload.maximum_size", _parse_size(selected_partition["size"])
)
return
else:
print(
"Warning! Selected partition `%s` is not available in the partition " \
"table! Default partition will be used!" % custom_app_partition_name
"Warning! Selected partition `%s` is not available in the "
"partition table! Default partition will be used!"
% custom_app_partition_name
)
for p in partitions.values():
@@ -191,20 +248,23 @@ def _update_max_upload_size(env):
break
def _to_unix_slashes(path):
"""Convert Windows-style backslashes to Unix-style forward slashes."""
return path.replace("\\", "/")
#
# Filesystem helpers
#
def fetch_fs_size(env):
"""
Extract filesystem size and offset information from partition table.
Sets FS_START, FS_SIZE, FS_PAGE, and FS_BLOCK environment variables.
"""
fs = None
for p in _parse_partitions(env):
if p["type"] == "data" and p["subtype"] in ("spiffs", "fat", "littlefs"):
if p["type"] == "data" and p["subtype"] in (
"spiffs",
"fat",
"littlefs",
):
fs = p
if not fs:
sys.stderr.write(
@@ -213,6 +273,7 @@ def fetch_fs_size(env):
)
env.Exit(1)
return
env["FS_START"] = _parse_size(fs["offset"])
env["FS_SIZE"] = _parse_size(fs["size"])
env["FS_PAGE"] = int("0x100", 16)
@@ -226,20 +287,37 @@ def fetch_fs_size(env):
def __fetch_fs_size(target, source, env):
"""Wrapper function for fetch_fs_size to be used as SCons emitter."""
fetch_fs_size(env)
return (target, source)
def check_lib_archive_exists():
"""
Check if lib_archive is set in platformio.ini configuration.
Returns True if found, False otherwise.
"""
for section in projectconfig.sections():
if "lib_archive" in projectconfig.options(section):
return True
return False
# Initialize board configuration and MCU settings
board = env.BoardConfig()
mcu = board.get("build.mcu", "esp32")
toolchain_arch = "xtensa-%s" % mcu
filesystem = board.get("build.filesystem", "spiffs")
if mcu in ("esp32c2", "esp32c3", "esp32c6", "esp32h2", "esp32p4"):
filesystem = board.get("build.filesystem", "littlefs")
# Set toolchain architecture for RISC-V based ESP32 variants
if mcu in ("esp32c2", "esp32c3", "esp32c5", "esp32c6", "esp32h2", "esp32p4"):
toolchain_arch = "riscv32-esp"
# Initialize integration extra data if not present
if "INTEGRATION_EXTRA_DATA" not in env:
env["INTEGRATION_EXTRA_DATA"] = {}
# Configure build tools and environment variables
env.Replace(
__get_board_boot_mode=_get_board_boot_mode,
__get_board_f_flash=_get_board_f_flash,
@@ -247,7 +325,6 @@ env.Replace(
__get_board_f_boot=_get_board_f_boot,
__get_board_flash_mode=_get_board_flash_mode,
__get_board_memory_type=_get_board_memory_type,
AR="%s-elf-gcc-ar" % toolchain_arch,
AS="%s-elf-as" % toolchain_arch,
CC="%s-elf-gcc" % toolchain_arch,
@@ -255,7 +332,14 @@ env.Replace(
GDB=join(
platform.get_package_dir(
"tool-riscv32-esp-elf-gdb"
if mcu in ("esp32c2", "esp32c3", "esp32c6", "esp32h2", "esp32p4")
if mcu in (
"esp32c2",
"esp32c3",
"esp32c5",
"esp32c6",
"esp32h2",
"esp32p4",
)
else "tool-xtensa-esp-elf-gdb"
)
or "",
@@ -265,20 +349,14 @@ env.Replace(
OBJCOPY=join(platform.get_package_dir("tool-esptoolpy") or "", "esptool.py"),
RANLIB="%s-elf-gcc-ranlib" % toolchain_arch,
SIZETOOL="%s-elf-size" % toolchain_arch,
ARFLAGS=["rc"],
SIZEPROGREGEXP=r"^(?:\.iram0\.text|\.iram0\.vectors|\.dram0\.data|\.flash\.text|\.flash\.rodata|)\s+([0-9]+).*",
SIZEPROGREGEXP=r"^(?:\.iram0\.text|\.iram0\.vectors|\.dram0\.data|"
r"\.flash\.text|\.flash\.rodata|)\s+([0-9]+).*",
SIZEDATAREGEXP=r"^(?:\.dram0\.data|\.dram0\.bss|\.noinit)\s+([0-9]+).*",
SIZECHECKCMD="$SIZETOOL -A -d $SOURCES",
SIZEPRINTCMD="$SIZETOOL -B -d $SOURCES",
ERASEFLAGS=[
"--chip", mcu,
"--port", '"$UPLOAD_PORT"'
],
ERASECMD='"$PYTHONEXE" "$OBJCOPY" $ERASEFLAGS erase_flash',
ERASEFLAGS=["--chip", mcu, "--port", '"$UPLOAD_PORT"'],
ERASECMD='"$PYTHONEXE" "$OBJCOPY" $ERASEFLAGS erase-flash',
# mkspiffs package contains two different binaries for IDF and Arduino
MKFSTOOL="mk%s" % filesystem
+ (
@@ -293,46 +371,61 @@ env.Replace(
if filesystem == "spiffs"
else ""
),
# Legacy `ESP32_SPIFFS_IMAGE_NAME` is used as the second fallback value for
# backward compatibility
# Legacy `ESP32_SPIFFS_IMAGE_NAME` is used as the second fallback value
# for backward compatibility
ESP32_FS_IMAGE_NAME=env.get(
"ESP32_FS_IMAGE_NAME", env.get("ESP32_SPIFFS_IMAGE_NAME", filesystem)
"ESP32_FS_IMAGE_NAME",
env.get("ESP32_SPIFFS_IMAGE_NAME", filesystem),
),
ESP32_APP_OFFSET=env.get("INTEGRATION_EXTRA_DATA").get(
"application_offset"
),
ESP32_APP_OFFSET=env.get("INTEGRATION_EXTRA_DATA").get("application_offset"),
ARDUINO_LIB_COMPILE_FLAG="Inactive",
PROGSUFFIX=".elf"
PROGSUFFIX=".elf",
)
# Check if lib_archive is set in platformio.ini and set it to False
# if not found. This makes weak defs in framework and libs possible.
if not check_lib_archive_exists():
env_section = "env:" + env["PIOENV"]
projectconfig.set(env_section, "lib_archive", "False")
# Allow user to override via pre:script
if env.get("PROGNAME", "program") == "program":
env.Replace(PROGNAME="firmware")
# Configure build actions and builders
env.Append(
BUILDERS=dict(
ElfToBin=Builder(
action=env.VerboseAction(" ".join([
'"$PYTHONEXE" "$OBJCOPY"',
"--chip", mcu, "elf2image",
"--flash_mode", "${__get_board_flash_mode(__env__)}",
"--flash_freq", "${__get_board_f_image(__env__)}",
"--flash_size", board.get("upload.flash_size", "4MB"),
"-o", "$TARGET", "$SOURCES"
]), "Building $TARGET"),
suffix=".bin"
action=env.VerboseAction(
" ".join(
[
'"$PYTHONEXE" "$OBJCOPY"',
"--chip",
mcu,
"elf2image",
"--flash-mode",
"${__get_board_flash_mode(__env__)}",
"--flash-freq",
"${__get_board_f_image(__env__)}",
"--flash-size",
board.get("upload.flash_size", "4MB"),
"-o",
"$TARGET",
"$SOURCES",
]
),
"Building $TARGET",
),
suffix=".bin",
),
DataToBin=Builder(
action=env.VerboseAction(
" ".join(
['"$MKFSTOOL"', "-c", "$SOURCES", "-s", "$FS_SIZE"]
+ (
[
"-p",
"$FS_PAGE",
"-b",
"$FS_BLOCK",
]
["-p", "$FS_PAGE", "-b", "$FS_BLOCK"]
if filesystem in ("littlefs", "spiffs")
else []
)
@@ -347,9 +440,76 @@ env.Append(
)
)
# Load framework-specific configuration
if not env.get("PIOFRAMEWORK"):
env.SConscript("frameworks/_bare.py", exports="env")
def firmware_metrics(target, source, env):
"""
Custom target to run esp-idf-size with support for command line parameters
Usage: pio run -t metrics -- [esp-idf-size arguments]
"""
if terminal_cp != "utf-8":
print("Firmware metrics can not be shown. Set the terminal codepage to \"utf-8\"")
return
map_file = os.path.join(env.subst("$BUILD_DIR"), env.subst("$PROGNAME") + ".map")
if not os.path.isfile(map_file):
# map file can be in project dir
map_file = os.path.join(get_project_dir(), env.subst("$PROGNAME") + ".map")
if not os.path.isfile(map_file):
print(f"Error: Map file not found: {map_file}")
print("Make sure the project is built first with 'pio run'")
return
try:
import subprocess
import sys
import shlex
cmd = [env.subst("$PYTHONEXE"), "-m", "esp_idf_size", "--ng"]
# Parameters from platformio.ini
extra_args = env.GetProjectOption("custom_esp_idf_size_args", "")
if extra_args:
cmd.extend(shlex.split(extra_args))
# 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:]
cmd.extend(cli_args)
# Add CLI arguments before the map file
if cli_args:
cmd.extend(cli_args)
# Map-file as last argument
cmd.append(map_file)
# Debug-Info if wanted
if env.GetProjectOption("custom_esp_idf_size_verbose", False):
print(f"Running command: {' '.join(cmd)}")
# Call esp-idf-size
result = subprocess.run(cmd, check=False, capture_output=False)
if result.returncode != 0:
print(f"Warning: esp-idf-size exited with code {result.returncode}")
except ImportError:
print("Error: esp-idf-size module not found.")
print("Install with: pip install esp-idf-size")
except FileNotFoundError:
print("Error: Python executable not found.")
print("Check your Python installation.")
except Exception as e:
print(f"Error: Failed to run firmware metrics: {e}")
print("Make sure esp-idf-size is installed: pip install esp-idf-size")
#
# Target: Build executable and linkable firmware or FS image
#
@@ -364,6 +524,10 @@ if "nobuild" in COMMAND_LINE_TARGETS:
target_firm = join("$BUILD_DIR", "${PROGNAME}.bin")
else:
target_elf = env.BuildProgram()
silent_action = env.Action(firmware_metrics)
# Hack to silence scons command output
silent_action.strfunction = lambda target, source, env: ""
env.AddPostAction(target_elf, silent_action)
if set(["buildfs", "uploadfs", "uploadfsota"]) & set(COMMAND_LINE_TARGETS):
target_firm = env.DataToBin(
join("$BUILD_DIR", "${ESP32_FS_IMAGE_NAME}"), "$PROJECT_DATA_DIR"
@@ -371,26 +535,27 @@ else:
env.NoCache(target_firm)
AlwaysBuild(target_firm)
else:
target_firm = env.ElfToBin(
join("$BUILD_DIR", "${PROGNAME}"), target_elf)
target_firm = env.ElfToBin(join("$BUILD_DIR", "${PROGNAME}"), target_elf)
env.Depends(target_firm, "checkprogsize")
env.AddPlatformTarget("buildfs", target_firm, target_firm, "Build Filesystem Image")
# Configure platform targets
env.AddPlatformTarget(
"buildfs", target_firm, target_firm, "Build Filesystem Image"
)
AlwaysBuild(env.Alias("nobuild", target_firm))
target_buildprog = env.Alias("buildprog", target_firm, target_firm)
# update max upload size based on CSV file
# Update max upload size based on CSV file
if env.get("PIOMAINPROG"):
env.AddPreAction(
"checkprogsize",
env.VerboseAction(
lambda source, target, env: _update_max_upload_size(env),
"Retrieving maximum program size $SOURCES"))
"Retrieving maximum program size $SOURCES",
),
)
#
# Target: Print binary size
#
target_size = env.AddPlatformTarget(
"size",
target_elf,
@@ -399,25 +564,25 @@ target_size = env.AddPlatformTarget(
"Calculate program size",
)
#
# Target: Upload firmware or FS image
#
upload_protocol = env.subst("$UPLOAD_PROTOCOL")
debug_tools = board.get("debug.tools", {})
upload_actions = []
# Compatibility with old OTA configurations
if (upload_protocol != "espota"
and re.match(r"\"?((([0-9]{1,3}\.){3}[0-9]{1,3})|[^\\/]+\.local)\"?$",
env.get("UPLOAD_PORT", ""))):
if upload_protocol != "espota" and re.match(
r"\"?((([0-9]{1,3}\.){3}[0-9]{1,3})|[^\\/]+\.local)\"?$",
env.get("UPLOAD_PORT", ""),
):
upload_protocol = "espota"
sys.stderr.write(
"Warning! We have just detected `upload_port` as IP address or host "
"name of ESP device. `upload_protocol` is switched to `espota`.\n"
"Please specify `upload_protocol = espota` in `platformio.ini` "
"project configuration file.\n")
"project configuration file.\n"
)
# Configure upload protocol: ESP OTA
if upload_protocol == "espota":
if not env.subst("$UPLOAD_PORT"):
sys.stderr.write(
@@ -425,32 +590,45 @@ if upload_protocol == "espota":
"using `upload_port` for build environment or use "
"global `--upload-port` option.\n"
"See https://docs.platformio.org/page/platforms/"
"espressif32.html#over-the-air-ota-update\n")
"espressif32.html#over-the-air-ota-update\n"
)
env.Replace(
UPLOADER=join(FRAMEWORK_DIR,"tools", "espota.py"),
UPLOADER=join(FRAMEWORK_DIR, "tools", "espota.py"),
UPLOADERFLAGS=["--debug", "--progress", "-i", "$UPLOAD_PORT"],
UPLOADCMD='"$PYTHONEXE" "$UPLOADER" $UPLOADERFLAGS -f $SOURCE'
UPLOADCMD='"$PYTHONEXE" "$UPLOADER" $UPLOADERFLAGS -f $SOURCE',
)
if set(["uploadfs", "uploadfsota"]) & set(COMMAND_LINE_TARGETS):
env.Append(UPLOADERFLAGS=["--spiffs"])
upload_actions = [env.VerboseAction("$UPLOADCMD", "Uploading $SOURCE")]
# Configure upload protocol: esptool
elif upload_protocol == "esptool":
env.Replace(
UPLOADER=join(
platform.get_package_dir("tool-esptoolpy") or "", "esptool.py"),
platform.get_package_dir("tool-esptoolpy") or "", "esptool.py"
),
UPLOADERFLAGS=[
"--chip", mcu,
"--port", '"$UPLOAD_PORT"',
"--baud", "$UPLOAD_SPEED",
"--before", board.get("upload.before_reset", "default_reset"),
"--after", board.get("upload.after_reset", "hard_reset"),
"write_flash", "-z",
"--flash_mode", "${__get_board_flash_mode(__env__)}",
"--flash_freq", "${__get_board_f_image(__env__)}",
"--flash_size", "detect"
"--chip",
mcu,
"--port",
'"$UPLOAD_PORT"',
"--baud",
"$UPLOAD_SPEED",
"--before",
board.get("upload.before_reset", "default-reset"),
"--after",
board.get("upload.after_reset", "hard-reset"),
"write-flash",
"-z",
"--flash-mode",
"${__get_board_flash_mode(__env__)}",
"--flash-freq",
"${__get_board_f_image(__env__)}",
"--flash-size",
"detect",
],
UPLOADCMD='"$PYTHONEXE" "$UPLOADER" $UPLOADERFLAGS $ESP32_APP_OFFSET $SOURCE'
UPLOADCMD='"$PYTHONEXE" "$UPLOADER" $UPLOADERFLAGS '
"$ESP32_APP_OFFSET $SOURCE",
)
for image in env.get("FLASH_EXTRA_IMAGES", []):
env.Append(UPLOADERFLAGS=[image[0], env.subst(image[1])])
@@ -458,27 +636,36 @@ elif upload_protocol == "esptool":
if "uploadfs" in COMMAND_LINE_TARGETS:
env.Replace(
UPLOADERFLAGS=[
"--chip", mcu,
"--port", '"$UPLOAD_PORT"',
"--baud", "$UPLOAD_SPEED",
"--before", board.get("upload.before_reset", "default_reset"),
"--after", board.get("upload.after_reset", "hard_reset"),
"write_flash", "-z",
"--flash_mode", "${__get_board_flash_mode(__env__)}",
"--flash_freq", "${__get_board_f_image(__env__)}",
"--flash_size", "detect",
"$FS_START"
"--chip",
mcu,
"--port",
'"$UPLOAD_PORT"',
"--baud",
"$UPLOAD_SPEED",
"--before",
board.get("upload.before_reset", "default-reset"),
"--after",
board.get("upload.after_reset", "hard-reset"),
"write-flash",
"-z",
"--flash-mode",
"${__get_board_flash_mode(__env__)}",
"--flash-freq",
"${__get_board_f_image(__env__)}",
"--flash-size",
"detect",
"$FS_START",
],
UPLOADCMD='"$PYTHONEXE" "$UPLOADER" $UPLOADERFLAGS $SOURCE',
)
upload_actions = [
env.VerboseAction(BeforeUpload, "Looking for upload port..."),
env.VerboseAction("$UPLOADCMD", "Uploading $SOURCE")
env.VerboseAction("$UPLOADCMD", "Uploading $SOURCE"),
]
# Configure upload protocol: DFU
elif upload_protocol == "dfu":
hwids = board.get("build.hwids", [["0x2341", "0x0070"]])
vid = hwids[0][0]
pid = hwids[0][1]
@@ -493,17 +680,18 @@ elif upload_protocol == "dfu":
"-d",
",".join(["%s:%s" % (hwid[0], hwid[1]) for hwid in hwids]),
"-Q",
"-D"
"-D",
],
UPLOADCMD='"$UPLOADER" $UPLOADERFLAGS "$SOURCE"',
)
# Configure upload protocol: Debug tools (OpenOCD)
elif upload_protocol in debug_tools:
_parse_partitions(env)
openocd_args = ["-d%d" % (2 if int(ARGUMENTS.get("PIOVERBOSE", 0)) else 1)]
openocd_args.extend(
debug_tools.get(upload_protocol).get("server").get("arguments", []))
debug_tools.get(upload_protocol).get("server").get("arguments", [])
)
openocd_args.extend(
[
"-c",
@@ -531,7 +719,9 @@ elif upload_protocol in debug_tools:
f.replace(
"$PACKAGE_DIR",
_to_unix_slashes(
platform.get_package_dir("tool-openocd-esp32") or ""))
platform.get_package_dir("tool-openocd-esp32") or ""
),
)
for f in openocd_args
]
env.Replace(
@@ -541,55 +731,71 @@ elif upload_protocol in debug_tools:
)
upload_actions = [env.VerboseAction("$UPLOADCMD", "Uploading $SOURCE")]
# custom upload tool
# Configure upload protocol: Custom
elif upload_protocol == "custom":
upload_actions = [env.VerboseAction("$UPLOADCMD", "Uploading $SOURCE")]
else:
sys.stderr.write("Warning! Unknown upload protocol %s\n" % upload_protocol)
# Register upload targets
env.AddPlatformTarget("upload", target_firm, upload_actions, "Upload")
env.AddPlatformTarget("uploadfs", target_firm, upload_actions, "Upload Filesystem Image")
env.AddPlatformTarget(
"uploadfsota", target_firm, upload_actions, "Upload Filesystem Image OTA")
"uploadfs", target_firm, upload_actions, "Upload Filesystem Image"
)
env.AddPlatformTarget(
"uploadfsota",
target_firm,
upload_actions,
"Upload Filesystem Image OTA",
)
#
# Target: Erase Flash and Upload
#
env.AddPlatformTarget(
"erase_upload",
target_firm,
[
env.VerboseAction(BeforeUpload, "Looking for upload port..."),
env.VerboseAction("$ERASECMD", "Erasing..."),
env.VerboseAction("$UPLOADCMD", "Uploading $SOURCE")
env.VerboseAction("$UPLOADCMD", "Uploading $SOURCE"),
],
"Erase Flash and Upload",
)
#
# Target: Erase Flash
#
env.AddPlatformTarget(
"erase",
None,
[
env.VerboseAction(BeforeUpload, "Looking for upload port..."),
env.VerboseAction("$ERASECMD", "Erasing...")
env.VerboseAction("$ERASECMD", "Erasing..."),
],
"Erase Flash",
)
#
# Override memory inspection behavior
#
# Register Custom Target for firmware metrics
env.AddCustomTarget(
name="metrics",
dependencies="$BUILD_DIR/${PROGNAME}.elf",
actions=firmware_metrics,
title="Firmware Size Metrics",
description="Analyze firmware size using esp-idf-size "
"(supports CLI args after --)",
always_build=True,
)
# Additional Target without Build-Dependency when already compiled
env.AddCustomTarget(
name="metrics-only",
dependencies=None,
actions=firmware_metrics,
title="Firmware Size Metrics (No Build)",
description="Analyze firmware size without building first",
always_build=True,
)
# Override memory inspection behavior
env.SConscript("sizedata.py", exports="env")
#
# Default targets
#
# Set default targets
Default([target_buildprog, target_size])