[nrf52][zephyr] prepare for native builds (#16193)

Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
This commit is contained in:
tomaszduda23
2026-05-11 16:47:06 +02:00
committed by GitHub
parent 30e2f7e8e9
commit 8cf0eba043
4 changed files with 131 additions and 90 deletions
+90 -27
View File
@@ -9,6 +9,7 @@ import subprocess
from esphome import pins
import esphome.codegen as cg
from esphome.components.zephyr import (
add_extra_script,
copy_files as zephyr_copy_files,
zephyr_add_overlay,
zephyr_add_pm_static,
@@ -21,6 +22,7 @@ from esphome.components.zephyr import (
from esphome.components.zephyr.const import (
BOOTLOADER_MCUBOOT,
CONF_CDC_ACM,
KEY_BOARD,
KEY_BOOTLOADER,
KEY_ZEPHYR,
CdcAcm,
@@ -36,6 +38,7 @@ from esphome.const import (
CONF_OTA,
CONF_RESET_PIN,
CONF_SAFE_MODE,
CONF_TOOLCHAIN,
CONF_VERSION,
CONF_VOLTAGE,
KEY_CORE,
@@ -44,10 +47,12 @@ from esphome.const import (
KEY_TARGET_PLATFORM,
PLATFORM_NRF52,
ThreadModel,
Toolchain,
)
from esphome.core import CORE, CoroPriority, EsphomeError, coroutine_with_priority
from esphome.core.config import BOARD_MAX_LENGTH
import esphome.final_validate as fv
from esphome.helpers import write_file_if_changed
from esphome.storage_json import StorageJSON
from esphome.types import ConfigType
@@ -67,8 +72,35 @@ AUTO_LOAD = ["zephyr", "preferences"]
IS_TARGET_PLATFORM = True
_LOGGER = logging.getLogger(__name__)
FAKE_BOARD_MANIFEST = """
{
"frameworks": [
"zephyr"
],
"name": "esphome nrf52",
"upload": {
"maximum_ram_size": 248832,
"maximum_size": 815104,
"speed": 115200
},
"url": "https://esphome.io/",
"vendor": "esphome",
"build": {
"bsp": {
"name": "adafruit"
},
"softdevice": {
"sd_fwid": "0x00B6"
}
}
}
"""
def set_core_data(config: ConfigType) -> ConfigType:
# Resolve toolchain: CLI (already on CORE.toolchain) > YAML > default.
if CORE.toolchain is None:
CORE.toolchain = config.get(CONF_TOOLCHAIN, Toolchain.PLATFORMIO)
zephyr_set_core_data(config)
CORE.data[KEY_CORE][KEY_TARGET_PLATFORM] = PLATFORM_NRF52
CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK] = KEY_ZEPHYR
@@ -80,10 +112,18 @@ def set_core_data(config: ConfigType) -> ConfigType:
def set_framework(config: ConfigType) -> ConfigType:
if CONF_VERSION not in config[CONF_FRAMEWORK]:
default_version = "2.6.1-b" if CORE.using_toolchain_platformio else "2.9.2"
config = {
**config,
CONF_FRAMEWORK: {**config[CONF_FRAMEWORK], CONF_VERSION: default_version},
}
framework_ver = cv.Version.parse(
cv.version_number(config[CONF_FRAMEWORK][CONF_VERSION])
)
CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] = framework_ver
if not CORE.using_toolchain_platformio:
return config
if framework_ver < cv.Version(2, 9, 2):
return cv.require_framework_version(
nrf52_zephyr=cv.Version(2, 6, 1, "a"),
@@ -182,7 +222,7 @@ CONFIG_SCHEMA = cv.All(
default={},
): cv.Schema(
{
cv.Optional(CONF_VERSION, default="2.6.1-b"): cv.string_strict,
cv.Optional(CONF_VERSION): cv.string_strict,
cv.Optional(CONF_ADVANCED, default={}): cv.Schema(
{
cv.Optional(
@@ -238,40 +278,51 @@ FINAL_VALIDATE_SCHEMA = _final_validate
@coroutine_with_priority(CoroPriority.PLATFORM)
async def to_code(config: ConfigType) -> None:
"""Convert the configuration to code."""
cg.add_platformio_option("board", config[CONF_BOARD])
cg.add_build_flag("-DUSE_NRF52")
cg.add_define("ESPHOME_BOARD", config[CONF_BOARD])
cg.add_define("ESPHOME_VARIANT", "NRF52")
# nRF52 processors are single-core
cg.add_define(ThreadModel.SINGLE)
cg.add_platformio_option(CONF_FRAMEWORK, CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK])
cg.add_platformio_option(
"platform",
"https://github.com/tomaszduda23/platform-nordicnrf52/archive/refs/tags/v10.3.0-5.zip",
)
cg.add_platformio_option(
"platform_packages",
[
f"platformio/framework-zephyr@https://github.com/tomaszduda23/framework-sdk-nrf/archive/refs/tags/v{CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION]}.zip",
],
)
if CORE.using_toolchain_platformio:
cg.add_platformio_option("board", config[CONF_BOARD])
cg.add_platformio_option(
CONF_FRAMEWORK, CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK]
)
cg.add_platformio_option(
"platform",
"https://github.com/tomaszduda23/platform-nordicnrf52/archive/refs/tags/v10.3.0-5.zip",
)
cg.add_platformio_option(
"platform_packages",
[
f"platformio/framework-zephyr@https://github.com/tomaszduda23/framework-sdk-nrf/archive/refs/tags/v{CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION]}.zip",
],
)
if config[KEY_BOOTLOADER] != BOOTLOADER_MCUBOOT:
# make sure that firmware.zip is created
# for Adafruit_nRF52_Bootloader
cg.add_platformio_option("board_upload.protocol", "nrfutil")
cg.add_platformio_option("board_upload.use_1200bps_touch", "true")
cg.add_platformio_option("board_upload.require_upload_port", "true")
cg.add_platformio_option("board_upload.wait_for_upload_port", "true")
add_extra_script(
"pre",
"pre_build.py",
Path(__file__).parent / "pre_build.py.script",
)
# build is done by west so bypass board checking in platformio
cg.add_platformio_option("boards_dir", CORE.relative_build_path("boards"))
if config[KEY_BOOTLOADER] == BOOTLOADER_MCUBOOT:
cg.add_define("USE_BOOTLOADER_MCUBOOT")
else:
if "_sd" in config[KEY_BOOTLOADER]:
bootloader = config[KEY_BOOTLOADER].split("_")
sd_id = bootloader[2][2:]
cg.add_define("USE_SOFTDEVICE_ID", int(sd_id))
if (len(bootloader)) > 3:
sd_version = bootloader[3][1:]
cg.add_define("USE_SOFTDEVICE_VERSION", int(sd_version))
# make sure that firmware.zip is created
# for Adafruit_nRF52_Bootloader
cg.add_platformio_option("board_upload.protocol", "nrfutil")
cg.add_platformio_option("board_upload.use_1200bps_touch", "true")
cg.add_platformio_option("board_upload.require_upload_port", "true")
cg.add_platformio_option("board_upload.wait_for_upload_port", "true")
elif "_sd" in config[KEY_BOOTLOADER]:
bootloader = config[KEY_BOOTLOADER].split("_")
sd_id = bootloader[2][2:]
cg.add_define("USE_SOFTDEVICE_ID", int(sd_id))
if (len(bootloader)) > 3:
sd_version = bootloader[3][1:]
cg.add_define("USE_SOFTDEVICE_VERSION", int(sd_version))
zephyr_setup_preferences()
zephyr_to_code(config)
@@ -341,6 +392,16 @@ async def _dfu_to_code(dfu_config):
def copy_files() -> None:
"""Copy files to the build directory."""
if CORE.using_toolchain_platformio and (
zephyr_data()[KEY_BOOTLOADER] == BOOTLOADER_MCUBOOT
or zephyr_data()[KEY_BOARD] == "xiao_ble"
):
write_file_if_changed(
CORE.relative_build_path(f"boards/{zephyr_data()[KEY_BOARD]}.json"),
FAKE_BOARD_MANIFEST,
)
zephyr_copy_files()
@@ -415,6 +476,8 @@ def upload_program(config: ConfigType, args, host: str) -> bool:
if zephyr_data()[KEY_BOOTLOADER] == BOOTLOADER_MCUBOOT:
mcumgr_device = host
else:
if not CORE.using_toolchain_platformio:
raise EsphomeError("Not implemented yet")
result = _upload_using_platformio(config, host, ["-t", "upload"])
if result != 0:
raise EsphomeError(f"Upload failed with result: {result}")
+8
View File
@@ -25,6 +25,14 @@ BOARDS_ZEPHYR = {
BOOTLOADER_ADAFRUIT_NRF52_SD140_V6,
]
},
"adafruit_itsybitsy": {
KEY_BOOTLOADER: [
BOOTLOADER_ADAFRUIT_NRF52_SD140_V6,
BOOTLOADER_ADAFRUIT,
BOOTLOADER_ADAFRUIT_NRF52_SD132,
BOOTLOADER_ADAFRUIT_NRF52_SD140_V7,
]
},
}
# https://github.com/ffenix113/zigbee_home/blob/17bb7b9e9d375e756da9e38913f53303937fb66a/types/board/known_boards.go
+33 -63
View File
@@ -10,9 +10,7 @@ from esphome.helpers import copy_file_if_changed, write_file_if_changed
from esphome.types import ConfigType
from .const import (
BOOTLOADER_MCUBOOT,
CONF_CDC_ACM,
KEY_BOARD,
KEY_BOOTLOADER,
KEY_EXTRA_BUILD_FILES,
KEY_KCONFIG,
@@ -50,8 +48,8 @@ class Section:
class ZephyrData(TypedDict):
board: str
bootloader: str
prj_conf: dict[str, tuple[PrjConfValueType, bool]]
overlay: str
prj_conf: dict[str, dict[str, tuple[PrjConfValueType, bool]]]
overlay: dict[str, str]
extra_build_files: dict[str, Path]
pm_static: list[Section]
user: dict[str, list[str]]
@@ -63,7 +61,9 @@ def zephyr_set_core_data(config: ConfigType) -> None:
board=config[CONF_BOARD],
bootloader=config[KEY_BOOTLOADER],
prj_conf={},
overlay="",
overlay={
"": "",
}, # set empty to make sure that overlay is cleared after config change
extra_build_files={},
pm_static=[],
user={},
@@ -76,12 +76,14 @@ def zephyr_data() -> ZephyrData:
def zephyr_add_prj_conf(
name: str, value: PrjConfValueType, required: bool = True
name: str, value: PrjConfValueType, required: bool = True, image: str = ""
) -> None:
"""Set an zephyr prj conf value."""
if not name.startswith("CONFIG_"):
name = "CONFIG_" + name
prj_conf = zephyr_data()[KEY_PRJ_CONF]
if image not in zephyr_data()[KEY_PRJ_CONF]:
zephyr_data()[KEY_PRJ_CONF][image] = {}
prj_conf = zephyr_data()[KEY_PRJ_CONF][image]
if name not in prj_conf:
prj_conf[name] = (value, required)
return
@@ -94,8 +96,11 @@ def zephyr_add_prj_conf(
prj_conf[name] = (value, required)
def zephyr_add_overlay(content):
zephyr_data()[KEY_OVERLAY] += textwrap.dedent(content)
def zephyr_add_overlay(content: str, image: str = "") -> None:
data = zephyr_data()
if image not in data[KEY_OVERLAY]:
data[KEY_OVERLAY][image] = ""
data[KEY_OVERLAY][image] += textwrap.dedent(content)
def add_extra_build_file(filename: str, path: Path) -> bool:
@@ -118,8 +123,6 @@ def zephyr_to_code(config: ConfigType) -> None:
cg.add_build_flag("-DUSE_ZEPHYR")
cg.add_define("USE_NATIVE_64BIT_TIME")
cg.set_cpp_standard("gnu++20")
# build is done by west so bypass board checking in platformio
cg.add_platformio_option("boards_dir", CORE.relative_build_path("boards"))
# c++ support
zephyr_add_prj_conf("NEWLIB_LIBC", True)
zephyr_add_prj_conf("FPU", True)
@@ -132,18 +135,12 @@ def zephyr_to_code(config: ConfigType) -> None:
# <err> os: Illegal load of EXC_RETURN into PC
zephyr_add_prj_conf("MAIN_STACK_SIZE", 2048)
add_extra_script(
"pre",
"pre_build.py",
Path(__file__).parent / "pre_build.py.script",
)
CORE.add_job(_cdc_acm_to_code, config)
@coroutine_with_priority(CoroPriority.FINAL)
async def _cdc_acm_to_code(config: ConfigType) -> None:
if "CONFIG_CDC_ACM_DTE_RATE_CALLBACK_SUPPORT" in zephyr_data()[KEY_PRJ_CONF]:
if "CONFIG_CDC_ACM_DTE_RATE_CALLBACK_SUPPORT" in zephyr_data()[KEY_PRJ_CONF][""]:
var = cg.new_Pvariable(config[CONF_CDC_ACM])
await cg.register_component(var, {})
@@ -219,55 +216,28 @@ def copy_files():
"""
)
want_opts = zephyr_data()[KEY_PRJ_CONF]
prj_conf = (
"\n".join(
f"{name}={_format_prj_conf_val(value[0])}"
for name, value in sorted(want_opts.items())
for image, want_opts in zephyr_data()[KEY_PRJ_CONF].items():
prj_conf = (
"\n".join(
f"{name}={_format_prj_conf_val(value[0])}"
for name, value in sorted(want_opts.items())
)
+ "\n"
)
+ "\n"
)
write_file_if_changed(CORE.relative_build_path("zephyr/prj.conf"), prj_conf)
if image:
path = CORE.relative_build_path(f"sysbuild/{image}.conf")
else:
path = CORE.relative_build_path("zephyr/prj.conf")
write_file_if_changed(
CORE.relative_build_path("zephyr/app.overlay"),
zephyr_data()[KEY_OVERLAY],
)
write_file_if_changed(CORE.relative_build_path(path), prj_conf)
if (
zephyr_data()[KEY_BOOTLOADER] == BOOTLOADER_MCUBOOT
or zephyr_data()[KEY_BOARD] == "xiao_ble"
):
fake_board_manifest = """
{
"frameworks": [
"zephyr"
],
"name": "esphome nrf52",
"upload": {
"maximum_ram_size": 248832,
"maximum_size": 815104,
"speed": 115200
},
"url": "https://esphome.io/",
"vendor": "esphome",
"build": {
"bsp": {
"name": "adafruit"
},
"softdevice": {
"sd_fwid": "0x00B6"
}
}
}
"""
write_file_if_changed(
CORE.relative_build_path(f"boards/{zephyr_data()[KEY_BOARD]}.json"),
fake_board_manifest,
)
for image, content in zephyr_data()[KEY_OVERLAY].items():
if image:
path = CORE.relative_build_path(f"sysbuild/{image}.overlay")
else:
path = CORE.relative_build_path("zephyr/app.overlay")
write_file_if_changed(path, content)
for filename, path in zephyr_data()[KEY_EXTRA_BUILD_FILES].items():
copy_file_if_changed(