import os from typing import Union import esphome.codegen as cg from esphome.core import CORE from esphome.helpers import ( write_file_if_changed, copy_file_if_changed, ) from esphome.const import ( CONF_VARIANT, CONF_BOARD, KEY_NAME, ) from .const import ( ZEPHYR_VARIANT_GENERIC, ZEPHYR_VARIANT_NRF_SDK, KEY_ZEPHYR, KEY_PRJ_CONF, KEY_OVERLAY, zephyr_ns, BOOTLOADER_MCUBOOT, KEY_EXTRA_BUILD_FILES, KEY_PATH, ) AUTO_LOAD = ["preferences"] KEY_BOARD = "board" KEY_BOOTLOADER = "bootloader" def zephyr_set_core_data(config): CORE.data[KEY_ZEPHYR] = {} CORE.data[KEY_ZEPHYR][KEY_BOARD] = config[CONF_BOARD] CORE.data[KEY_ZEPHYR][KEY_PRJ_CONF] = {} CORE.data[KEY_ZEPHYR][KEY_OVERLAY] = "" CORE.data[KEY_ZEPHYR][KEY_BOOTLOADER] = config[KEY_BOOTLOADER] CORE.data[KEY_ZEPHYR][KEY_EXTRA_BUILD_FILES] = {} return config PrjConfValueType = Union[bool, str, int] def zephyr_add_prj_conf(name: str, value: PrjConfValueType): """Set an zephyr prj conf value.""" if not CORE.using_zephyr: raise ValueError("Not an zephyr project") if not name.startswith("CONFIG_"): name = "CONFIG_" + name if name in CORE.data[KEY_ZEPHYR][KEY_PRJ_CONF]: old_value = CORE.data[KEY_ZEPHYR][KEY_PRJ_CONF][name] if old_value != value: raise ValueError( f"{name} alread set with value {old_value}, new value {value}" ) CORE.data[KEY_ZEPHYR][KEY_PRJ_CONF][name] = value def zephyr_add_overlay(content): if not CORE.using_zephyr: raise ValueError("Not an zephyr project") CORE.data[KEY_ZEPHYR][KEY_OVERLAY] += content def add_extra_build_file(filename: str, path: str) -> bool: """Add an extra build file to the project.""" if filename not in CORE.data[KEY_ZEPHYR][KEY_EXTRA_BUILD_FILES]: CORE.data[KEY_ZEPHYR][KEY_EXTRA_BUILD_FILES][filename] = { KEY_NAME: filename, KEY_PATH: path, } return True return False def add_extra_script(stage: str, filename: str, path: str): """Add an extra script to the project.""" key = f"{stage}:{filename}" if add_extra_build_file(filename, path): cg.add_platformio_option("extra_scripts", [key]) def zephyr_to_code(conf): cg.add(zephyr_ns.setup_preferences()) cg.add_build_flag("-DUSE_ZEPHYR") if conf[CONF_VARIANT] == ZEPHYR_VARIANT_GENERIC: cg.add_platformio_option( "platform_packages", [ "platformio/framework-zephyr@^2.30500.231204", ], ) elif conf[CONF_VARIANT] == ZEPHYR_VARIANT_NRF_SDK: cg.add_platformio_option( "platform_packages", [ "platformio/framework-zephyr@https://github.com/tomaszduda23/framework-sdk-nrf", "platformio/toolchain-gccarmnoneeabi@https://github.com/tomaszduda23/toolchain-sdk-ng", ], ) # build is done by west so bypass board checking in platformio cg.add_platformio_option("boards_dir", CORE.relative_build_path("boards")) else: raise NotImplementedError # c++ support zephyr_add_prj_conf("NEWLIB_LIBC", True) zephyr_add_prj_conf("CONFIG_FPU", True) zephyr_add_prj_conf("NEWLIB_LIBC_FLOAT_PRINTF", True) zephyr_add_prj_conf("CPLUSPLUS", True) zephyr_add_prj_conf("LIB_CPLUSPLUS", True) # preferences zephyr_add_prj_conf("SETTINGS", True) zephyr_add_prj_conf("NVS", True) zephyr_add_prj_conf("FLASH_MAP", True) zephyr_add_prj_conf("CONFIG_FLASH", True) # watchdog zephyr_add_prj_conf("WATCHDOG", True) zephyr_add_prj_conf("WDT_DISABLE_AT_BOOT", False) # disable console zephyr_add_prj_conf("UART_CONSOLE", False) zephyr_add_prj_conf("CONSOLE", False) # TODO debug only zephyr_add_prj_conf("DEBUG_THREAD_INFO", True) # zephyr_add_prj_conf("DEBUG", True) ### zephyr_add_prj_conf("USE_SEGGER_RTT", True) zephyr_add_prj_conf("RTT_CONSOLE", True) zephyr_add_prj_conf("LOG", True) zephyr_add_prj_conf("LOG_BLOCK_IN_THREAD", True) zephyr_add_prj_conf("LOG_BUFFER_SIZE", 4096) zephyr_add_prj_conf("SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL", True) zephyr_add_prj_conf("USB_CDC_ACM_LOG_LEVEL_WRN", True) add_extra_script( "pre", "pre_build.py", os.path.join(os.path.dirname(__file__), "pre_build.py.script"), ) add_extra_script( "post", "post_build.py", os.path.join(os.path.dirname(__file__), "post_build.py.script"), ) def _format_prj_conf_val(value: PrjConfValueType) -> str: if isinstance(value, bool): return "y" if value else "n" if isinstance(value, int): return str(value) if isinstance(value, str): return f'"{value}"' raise ValueError # Called by writer.py def copy_files(): want_opts = CORE.data[KEY_ZEPHYR][KEY_PRJ_CONF] contents = ( "\n".join( f"{name}={_format_prj_conf_val(value)}" for name, value in sorted(want_opts.items()) ) + "\n" ) write_file_if_changed(CORE.relative_build_path("zephyr/prj.conf"), contents) write_file_if_changed( CORE.relative_build_path("zephyr/app.overlay"), CORE.data[KEY_ZEPHYR][KEY_OVERLAY], ) write_file_if_changed( CORE.relative_build_path("zephyr/child_image/mcuboot.conf"), """ CONFIG_MCUBOOT_SERIAL=y CONFIG_BOOT_SERIAL_PIN_RESET=y CONFIG_UART_CONSOLE=n CONFIG_BOOT_SERIAL_ENTRANCE_GPIO=n CONFIG_BOOT_SERIAL_CDC_ACM=y CONFIG_UART_NRFX=n CONFIG_LOG=n CONFIG_ASSERT_VERBOSE=n CONFIG_BOOT_BANNER=n CONFIG_PRINTK=n CONFIG_CBPRINTF_LIBC_SUBSTS=n """, ) if CORE.data[KEY_ZEPHYR][KEY_BOOTLOADER] == BOOTLOADER_MCUBOOT: fake_board_manifest = """ { "frameworks": [ "zephyr" ], "name": "esphome nrf52", "upload": { "maximum_ram_size": 248832, "maximum_size": 815104 }, "url": "https://esphome.io/", "vendor": "esphome" } """ write_file_if_changed( CORE.relative_build_path(f"boards/{CORE.data[KEY_ZEPHYR][KEY_BOARD]}.json"), fake_board_manifest, ) for _, file in CORE.data[KEY_ZEPHYR][KEY_EXTRA_BUILD_FILES].items(): copy_file_if_changed( file[KEY_PATH], CORE.relative_build_path(file[KEY_NAME]), )