nRF5340 LE Audio CIS bidirectional TWS (NCS v2.5.0)

Hi, 

I'm considering bidirectional TWS in CIS.

In other words, I want the gateway to be able to output the microphone audio from the L and R headsets.

It seems that the nRF5340 Audio DK can only render either L or R due to the codec implemented, but our custom board implements a stereo output codec.

However, only audio from the L side can be played.

I am developing based on CIS's WALKIE-TALKIE.

I found a description in the le_audio_rx_data_handler function of le_audio_rx.c that allows only the Lch signal to pass.

When I changed AUDIO_CH_L to AUDIO_CH_R, the audio from the R side headset was now played, but the audio from the L side was no longer played.

I tried disabling all of this description, but only the audio from the L side is played, and the audio from the R side is not played.

How should I edit the code to mix and play both L and R audio?

The source for realizing this application seems to have been released, but it is difficult to use as a reference because the NCS version is old.

Regards, 

Gotoda

Parents Reply Children
  • Hi, 

    Rather than implementing two microphones in one receiver, I would like the gateway to output microphone signals from two headset boards at the same time, as shown in the figure below.

    Is it possible to achieve this with code editing for nrf5340_audio?

    Regards, 

    Gotoda

  • This is exactly what the above demo code would like to achieve. 

  • I'm sorry it's taken some time to reply.

    I downloaded the project files you introduced, but the following build error occurs.

     *  Executing task: nRF Connect: Build: nrf5340_audio_DualMic/build (active) 
    
    Building nrf5340_audio_DualMic
    west build --build-dir c:/Works/nRF5/vscode/nrf5340_audio_DualMic/build c:/Works/nRF5/vscode/nrf5340_audio_DualMic
    
    -- west build: generating a build system
    Loading Zephyr default modules (Zephyr base (cached)).
    -- Application: C:/Works/nRF5/vscode/nrf5340_audio_DualMic
    -- CMake version: 3.20.5
    -- Cache files will be written to: C:/Works/nRF5/ncs/v2.5.0/zephyr/.cache
    -- Zephyr version: 3.4.99 (C:/Works/nRF5/ncs/v2.5.0/zephyr)
    -- Found west (found suitable version "1.1.0", minimum required is "0.14.0")
    -- Board: nrf5340_audio_dk_nrf5340_cpuapp
    -- Found host-tools: zephyr 0.16.1 (C:/Works/nRF5/ncs/toolchains/c57af46cb7/opt/zephyr-sdk)
    -- Found toolchain: zephyr 0.16.1 (C:/Works/nRF5/ncs/toolchains/c57af46cb7/opt/zephyr-sdk)
    -- Found BOARD.dts: C:/Works/nRF5/ncs/v2.5.0/zephyr/boards/arm/nrf5340_audio_dk_nrf5340/nrf5340_audio_dk_nrf5340_cpuapp.dts
    -- Generated zephyr.dts: C:/Works/nRF5/vscode/nrf5340_audio_DualMic/build/zephyr/zephyr.dts
    -- Generated devicetree_generated.h: C:/Works/nRF5/vscode/nrf5340_audio_DualMic/build/zephyr/include/generated/devicetree_generated.h
    -- Including generated dts.cmake file: C:/Works/nRF5/vscode/nrf5340_audio_DualMic/build/zephyr/dts.cmake
    
    warning: BT_BACKGROUND_SCAN_INTERVAL (defined at
    C:/Works/nRF5/vscode/nrf5340_audio_DualMic\src/bluetooth\bt_management\scanning\Kconfig.defaults:9,
    C:/Works/nRF5/ncs/v2.5.0/zephyr/subsys/bluetooth/host/Kconfig:740,
    subsys/bluetooth\host/Kconfig:740) was assigned the value '25' but got the value ''. Check these
    unsatisfied dependencies: ((BT_OBSERVER && NRF5340_AUDIO) || (BT_OBSERVER && BT_HCI_HOST &&
    BT_RPC_STACK) || (BT_OBSERVER && BT_HCI_HOST && BT_HCI && BT)) (=n). See
    http://docs.zephyrproject.org/latest/kconfig.html#CONFIG_BT_BACKGROUND_SCAN_INTERVAL and/or look up
    BT_BACKGROUND_SCAN_INTERVAL in the menuconfig/guiconfig interface. The Application Development
    Primer, Setting Configuration Values, and Kconfig - Tips and Best Practices sections of the manual
    might be helpful too.
    
    
    warning: BT_BACKGROUND_SCAN_WINDOW (defined at
    C:/Works/nRF5/vscode/nrf5340_audio_DualMic\src/bluetooth\bt_management\scanning\Kconfig.defaults:12,
    Parsing C:/Works/nRF5/vscode/nrf5340_audio_DualMic/Kconfig
    Loaded configuration 'C:/Works/nRF5/ncs/v2.5.0/zephyr/boards/arm/nrf5340_audio_dk_nrf5340/nrf5340_audio_dk_nrf5340_cpuapp_defconfig'
    Merged configuration 'C:/Works/nRF5/vscode/nrf5340_audio_DualMic/prj.conf'
    C:/Works/nRF5/ncs/v2.5.0/zephyr/subsys/bluetooth/host/Kconfig:744,
    subsys/bluetooth\host/Kconfig:744) was assigned the value '25' but got the value ''. Check these
    unsatisfied dependencies: ((BT_OBSERVER && NRF5340_AUDIO) || (BT_OBSERVER && BT_HCI_HOST &&
    BT_RPC_STACK) || (BT_OBSERVER && BT_HCI_HOST && BT_HCI && BT)) (=n). See
    http://docs.zephyrproject.org/latest/kconfig.html#CONFIG_BT_BACKGROUND_SCAN_WINDOW and/or look up
    BT_BACKGROUND_SCAN_WINDOW in the menuconfig/guiconfig interface. The Application Development Primer,
    Setting Configuration Values, and Kconfig - Tips and Best Practices sections of the manual might be
    helpful too.
    
    
    warning: SHELL_VT100_COLORS (defined at subsys/shell/Kconfig:141) was assigned the value 'y' but got
    the value 'n'. Check these unsatisfied dependencies: SHELL_VT100_COMMANDS (=n). See
    http://docs.zephyrproject.org/latest/kconfig.html#CONFIG_SHELL_VT100_COLORS and/or look up
    SHELL_VT100_COLORS in the menuconfig/guiconfig interface. The Application Development Primer,
    Setting Configuration Values, and Kconfig - Tips and Best Practices sections of the manual might be
    helpful too.
    
    
    warning: Deprecated symbol NFCT_PINS_AS_GPIOS is enabled.
    
    
    warning: Experimental symbol NRF5340_AUDIO is enabled.
    
    
    warning: Experimental symbol BT_LL_ACS_NRF53 is enabled.
    
    
    warning: Experimental symbol BT_ISO_PERIPHERAL is enabled.
    
    
    warning: Experimental symbol BT_AUDIO is enabled.
    
    
    warning: Experimental symbol BT_BAP_UNICAST_SERVER is enabled.
    
    
    warning: Experimental symbol BT_VCP_VOL_REND is enabled.
    
    
    warning: Experimental symbol BT_CSIP_SET_MEMBER is enabled.
    
    
    warning: Experimental symbol BT_MCC is enabled.
    
    
    warning: Experimental symbol BT_CAP_ACCEPTOR is enabled.
    
    
    warning: ZBUS_RUNTIME_OBSERVERS_POOL_SIZE (defined at C:/Works/nRF5/vscode/nrf5340_audio_DualMic\Kconfig.defaults:53) defined without a type
    
    error: Aborting due to Kconfig warnings
    
    CMake Error at C:/Works/nRF5/ncs/v2.5.0/zephyr/cmake/modules/kconfig.cmake:348 (message):
      command failed with return code: 1
    Call Stack (most recent call first):
      C:/Works/nRF5/ncs/v2.5.0/nrf/cmake/modules/kconfig.cmake:29 (include)
      C:/Works/nRF5/ncs/v2.5.0/zephyr/cmake/modules/zephyr_default.cmake:129 (include)
      C:/Works/nRF5/ncs/v2.5.0/zephyr/share/zephyr-package/cmake/ZephyrConfig.cmake:66 (include)
      C:/Works/nRF5/ncs/v2.5.0/zephyr/share/zephyr-package/cmake/ZephyrConfig.cmake:97 (include_boilerplate)
      CMakeLists.txt:46 (find_package)
    
    
    -- Configuring incomplete, errors occurred!
    FATAL ERROR: command exited with status 1: 'C:\Works\nRF5\ncs\toolchains\c57af46cb7\opt\bin\cmake.EXE' -DWEST_PYTHON=C:/Works/nRF5/ncs/toolchains/c57af46cb7/opt/bin/python.exe '-Bc:\Works\nRF5\vscode\nrf5340_audio_DualMic\build' -GNinja '-Sc:\Works\nRF5\vscode\nrf5340_audio_DualMic'
    
     *  The terminal process terminated with exit code: 1. 
     *  Terminal will be reused by tasks, press any key to close it. 
    

    How should I resolve this?

  • That sample is for v2.4.2. Could you build it with NCS v2.4.2?

  • I changed to v2.4.2 and built it, but the same error occurs.

    error: Aborting due to Kconfig warnings
    
    CMake Error at C:/Works/nRF5/ncs/v2.4.2/zephyr/cmake/modules/kconfig.cmake:339 (message):
      command failed with return code: 1
    Call Stack (most recent call first):
      C:/Works/nRF5/ncs/v2.4.2/nrf/cmake/modules/kconfig.cmake:29 (include)
      C:/Works/nRF5/ncs/v2.4.2/zephyr/cmake/modules/zephyr_default.cmake:115 (include)
      C:/Works/nRF5/ncs/v2.4.2/zephyr/share/zephyr-package/cmake/ZephyrConfig.cmake:66 (include)
      C:/Works/nRF5/ncs/v2.4.2/zephyr/share/zephyr-package/cmake/ZephyrConfig.cmake:92 (include_boilerplate)
      CMakeLists.txt:46 (find_package)
    

    I looked at the relevant line in kconfig.cmake (attached file), but I don't know why the return value of execute_process is 1.

    # SPDX-License-Identifier: Apache-2.0
    
    include_guard(GLOBAL)
    
    include(extensions)
    include(python)
    
    # autoconf.h is generated by Kconfig and placed in <build>/zephyr/include/generated/autoconf.h.
    # A project may request a custom location by setting AUTOCONF_H explicitly before
    # calling 'find_package(Zephyr)' or loading this module.
    set_ifndef(AUTOCONF_H ${PROJECT_BINARY_DIR}/include/generated/autoconf.h)
    # Re-configure (Re-execute all CMakeLists.txt code) when autoconf.h changes
    set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${AUTOCONF_H})
    
    # Folders needed for conf/mconf files (kconfig has no method of redirecting all output files).
    # conf/mconf needs to be run from a different directory because of: GH-3408
    file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/kconfig/include/generated)
    file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/kconfig/include/config)
    
    set_ifndef(KCONFIG_NAMESPACE "CONFIG")
    
    set_ifndef(KCONFIG_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/Kconfig)
    file(MAKE_DIRECTORY ${KCONFIG_BINARY_DIR})
    
    # Support multiple SOC_ROOT, remove ZEPHYR_BASE as that is always sourced.
    set(kconfig_soc_root ${SOC_ROOT})
    list(REMOVE_ITEM kconfig_soc_root ${ZEPHYR_BASE})
    set(OPERATION WRITE)
    foreach(root ${kconfig_soc_root})
      file(${OPERATION} ${KCONFIG_BINARY_DIR}/Kconfig.soc.defconfig
           "osource \"${root}/soc/$(ARCH)/*/Kconfig.defconfig\"\n"
      )
      file(${OPERATION} ${KCONFIG_BINARY_DIR}/Kconfig.soc
           "osource \"${root}/soc/$(ARCH)/*/Kconfig.soc\"\n"
      )
      file(${OPERATION} ${KCONFIG_BINARY_DIR}/Kconfig.soc.arch
           "osource \"${root}/soc/$(ARCH)/Kconfig\"\n"
           "osource \"${root}/soc/$(ARCH)/*/Kconfig\"\n"
      )
      set(OPERATION APPEND)
    endforeach()
    
    # Support multiple shields in BOARD_ROOT, remove ZEPHYR_BASE as that is always sourced.
    set(kconfig_board_root ${BOARD_ROOT})
    list(REMOVE_ITEM kconfig_board_root ${ZEPHYR_BASE})
    set(OPERATION WRITE)
    foreach(root ${kconfig_board_root})
      file(${OPERATION} ${KCONFIG_BINARY_DIR}/Kconfig.shield.defconfig
           "osource \"${root}/boards/shields/*/Kconfig.defconfig\"\n"
      )
      file(${OPERATION} ${KCONFIG_BINARY_DIR}/Kconfig.shield
           "osource \"${root}/boards/shields/*/Kconfig.shield\"\n"
      )
      set(OPERATION APPEND)
    endforeach()
    
    if(KCONFIG_ROOT)
      # Perform any variable substitutions if they are present
      string(CONFIGURE "${KCONFIG_ROOT}" KCONFIG_ROOT)
    
      zephyr_file(APPLICATION_ROOT KCONFIG_ROOT)
      # KCONFIG_ROOT has either been specified as a CMake variable or is
      # already in the CMakeCache.txt. This has precedence.
    elseif(EXISTS   ${APPLICATION_SOURCE_DIR}/Kconfig)
      set(KCONFIG_ROOT ${APPLICATION_SOURCE_DIR}/Kconfig)
    else()
      set(KCONFIG_ROOT ${ZEPHYR_BASE}/Kconfig)
    endif()
    
    set_ifndef(BOARD_DEFCONFIG ${BOARD_DIR}/${BOARD}_defconfig)
    if((DEFINED BOARD_REVISION) AND EXISTS ${BOARD_DIR}/${BOARD}_${BOARD_REVISION_STRING}.conf)
      set_ifndef(BOARD_REVISION_CONFIG ${BOARD_DIR}/${BOARD}_${BOARD_REVISION_STRING}.conf)
    endif()
    set(DOTCONFIG                  ${PROJECT_BINARY_DIR}/.config)
    set(PARSED_KCONFIG_SOURCES_TXT ${PROJECT_BINARY_DIR}/kconfig/sources.txt)
    
    if(CONF_FILE)
      string(CONFIGURE "${CONF_FILE}" CONF_FILE_EXPANDED)
      string(REPLACE " " ";" CONF_FILE_AS_LIST "${CONF_FILE_EXPANDED}")
    endif()
    
    if(EXTRA_CONF_FILE)
      string(CONFIGURE "${EXTRA_CONF_FILE}" EXTRA_CONF_FILE_EXPANDED)
      string(REPLACE " " ";" EXTRA_CONF_FILE_AS_LIST "${EXTRA_CONF_FILE_EXPANDED}")
    endif()
    
    
    # DTS_ROOT_BINDINGS is a semicolon separated list, this causes
    # problems when invoking kconfig_target since semicolon is a special
    # character in the C shell, so we make it into a question-mark
    # separated list instead.
    string(REPLACE ";" "?" DTS_ROOT_BINDINGS "${DTS_ROOT_BINDINGS}")
    
    # Export each `ZEPHYR_<module>_MODULE_DIR` to Kconfig.
    # This allows Kconfig files to refer relative from a modules root as:
    # source "$(ZEPHYR_FOO_MODULE_DIR)/Kconfig"
    foreach(module_name ${ZEPHYR_MODULE_NAMES})
      zephyr_string(SANITIZE TOUPPER MODULE_NAME_UPPER ${module_name})
      list(APPEND
           ZEPHYR_KCONFIG_MODULES_DIR
           "ZEPHYR_${MODULE_NAME_UPPER}_MODULE_DIR=${ZEPHYR_${MODULE_NAME_UPPER}_MODULE_DIR}"
      )
    
      if(ZEPHYR_${MODULE_NAME_UPPER}_KCONFIG)
        list(APPEND
             ZEPHYR_KCONFIG_MODULES_DIR
             "ZEPHYR_${MODULE_NAME_UPPER}_KCONFIG=${ZEPHYR_${MODULE_NAME_UPPER}_KCONFIG}"
      )
      endif()
    endforeach()
    
    # A list of common environment settings used when invoking Kconfig during CMake
    # configure time or menuconfig and related build target.
    string(REPLACE ";" "\\\;" SHIELD_AS_LIST_ESCAPED "${SHIELD_AS_LIST}")
    # cmake commands are escaped differently
    string(REPLACE ";" "\\;" SHIELD_AS_LIST_ESCAPED_COMMAND "${SHIELD_AS_LIST}")
    
    if(TOOLCHAIN_HAS_NEWLIB)
      set(_local_TOOLCHAIN_HAS_NEWLIB y)
    else()
      set(_local_TOOLCHAIN_HAS_NEWLIB n)
    endif()
    
    set(COMMON_KCONFIG_ENV_SETTINGS
      PYTHON_EXECUTABLE=${PYTHON_EXECUTABLE}
      srctree=${ZEPHYR_BASE}
      KERNELVERSION=${KERNELVERSION}
      APPVERSION=${APP_VERSION_STRING}
      CONFIG_=${KCONFIG_NAMESPACE}_
      KCONFIG_CONFIG=${DOTCONFIG}
      # Set environment variables so that Kconfig can prune Kconfig source
      # files for other architectures
      ARCH=${ARCH}
      ARCH_DIR=${ARCH_DIR}
      BOARD_DIR=${BOARD_DIR}
      BOARD_REVISION=${BOARD_REVISION}
      KCONFIG_BINARY_DIR=${KCONFIG_BINARY_DIR}
      APPLICATION_SOURCE_DIR=${APPLICATION_SOURCE_DIR}
      ZEPHYR_TOOLCHAIN_VARIANT=${ZEPHYR_TOOLCHAIN_VARIANT}
      TOOLCHAIN_KCONFIG_DIR=${TOOLCHAIN_KCONFIG_DIR}
      TOOLCHAIN_HAS_NEWLIB=${_local_TOOLCHAIN_HAS_NEWLIB}
      EDT_PICKLE=${EDT_PICKLE}
      # Export all Zephyr modules to Kconfig
      ${ZEPHYR_KCONFIG_MODULES_DIR}
    )
    
    # Allow out-of-tree users to add their own Kconfig python frontend
    # targets by appending targets to the CMake list
    # 'EXTRA_KCONFIG_TARGETS' and setting variables named
    # 'EXTRA_KCONFIG_TARGET_COMMAND_FOR_<target>'
    #
    # e.g.
    # cmake -DEXTRA_KCONFIG_TARGETS=cli
    # -DEXTRA_KCONFIG_TARGET_COMMAND_FOR_cli=cli_kconfig_frontend.py
    
    set(EXTRA_KCONFIG_TARGET_COMMAND_FOR_menuconfig
      ${ZEPHYR_BASE}/scripts/kconfig/menuconfig.py
      )
    
    set(EXTRA_KCONFIG_TARGET_COMMAND_FOR_guiconfig
      ${ZEPHYR_BASE}/scripts/kconfig/guiconfig.py
      )
    
    set(EXTRA_KCONFIG_TARGET_COMMAND_FOR_hardenconfig
      ${ZEPHYR_BASE}/scripts/kconfig/hardenconfig.py
      )
    
    set_ifndef(KCONFIG_TARGETS menuconfig guiconfig hardenconfig)
    
    foreach(kconfig_target
        ${KCONFIG_TARGETS}
        ${EXTRA_KCONFIG_TARGETS}
        )
      add_custom_target(
        ${kconfig_target}
        ${CMAKE_COMMAND} -E env
        ZEPHYR_BASE=${ZEPHYR_BASE}
        ${COMMON_KCONFIG_ENV_SETTINGS}
        "SHIELD_AS_LIST=${SHIELD_AS_LIST_ESCAPED}"
        DTS_POST_CPP=${DTS_POST_CPP}
        DTS_ROOT_BINDINGS=${DTS_ROOT_BINDINGS}
        ${PYTHON_EXECUTABLE}
        ${EXTRA_KCONFIG_TARGET_COMMAND_FOR_${kconfig_target}}
        ${KCONFIG_ROOT}
        WORKING_DIRECTORY ${PROJECT_BINARY_DIR}/kconfig
        USES_TERMINAL
        COMMAND_EXPAND_LISTS
        )
    endforeach()
    
    # Support assigning Kconfig symbols on the command-line with CMake
    # cache variables prefixed according to the Kconfig namespace.
    # This feature is experimental and undocumented until it has undergone more
    # user-testing.
    unset(EXTRA_KCONFIG_OPTIONS)
    if(SYSBUILD)
      get_property(sysbuild_variable_names TARGET sysbuild_cache PROPERTY "SYSBUILD_CACHE:VARIABLES")
      zephyr_get(SYSBUILD_MAIN_APP)
      zephyr_get(SYSBUILD_NAME)
    
      foreach (name ${sysbuild_variable_names})
        if("${name}" MATCHES "^${SYSBUILD_NAME}_${KCONFIG_NAMESPACE}_")
          string(REGEX REPLACE "^${SYSBUILD_NAME}_" "" org_name ${name})
          get_property(${org_name} TARGET sysbuild_cache PROPERTY ${name})
          list(APPEND cache_variable_names ${org_name})
        elseif(SYSBUILD_MAIN_APP AND "${name}" MATCHES "^${KCONFIG_NAMESPACE}_")
          get_property(${name} TARGET sysbuild_cache PROPERTY ${name})
          list(APPEND cache_variable_names ${name})
        endif()
      endforeach()
    else()
      get_cmake_property(cache_variable_names CACHE_VARIABLES)
    endif()
    
    foreach (name ${cache_variable_names})
      if("${name}" MATCHES "^CLI_${KCONFIG_NAMESPACE}_")
        # Variable was set by user in earlier invocation, let's append to extra
        # config unless a new value has been given.
        string(REGEX REPLACE "^CLI_" "" org_name ${name})
        if(NOT DEFINED ${org_name})
          set(EXTRA_KCONFIG_OPTIONS
            "${EXTRA_KCONFIG_OPTIONS}\n${org_name}=${${name}}"
          )
        endif()
      elseif("${name}" MATCHES "^${KCONFIG_NAMESPACE}_")
        # When a cache variable starts with the 'KCONFIG_NAMESPACE' value, it is
        # assumed to be a Kconfig symbol assignment from the CMake command line.
        set(EXTRA_KCONFIG_OPTIONS
          "${EXTRA_KCONFIG_OPTIONS}\n${name}=${${name}}"
          )
        set(CLI_${name} "${${name}}")
        list(APPEND cli_config_list ${name})
      endif()
    endforeach()
    
    if(EXTRA_KCONFIG_OPTIONS)
      set(EXTRA_KCONFIG_OPTIONS_FILE ${PROJECT_BINARY_DIR}/misc/generated/extra_kconfig_options.conf)
      file(WRITE
        ${EXTRA_KCONFIG_OPTIONS_FILE}
        ${EXTRA_KCONFIG_OPTIONS}
        )
    endif()
    
    # Bring in extra configuration files dropped in by the user or anyone else;
    # make sure they are set at the end so we can override any other setting
    file(GLOB config_files ${APPLICATION_BINARY_DIR}/*.conf)
    list(SORT config_files)
    set(
      merge_config_files
      ${BOARD_DEFCONFIG}
      ${BOARD_REVISION_CONFIG}
      ${CONF_FILE_AS_LIST}
      ${shield_conf_files}
      ${EXTRA_CONF_FILE_AS_LIST}
      ${EXTRA_KCONFIG_OPTIONS_FILE}
      ${config_files}
    )
    
    # Create a list of absolute paths to the .config sources from
    # merge_config_files, which is a mix of absolute and relative paths.
    set(merge_config_files_with_absolute_paths "")
    foreach(f ${merge_config_files})
      if(IS_ABSOLUTE ${f})
        set(path ${f})
      else()
        set(path ${APPLICATION_CONFIG_DIR}/${f})
      endif()
    
      list(APPEND merge_config_files_with_absolute_paths ${path})
    endforeach()
    set(merge_config_files ${merge_config_files_with_absolute_paths})
    
    foreach(f ${merge_config_files})
      if(NOT EXISTS ${f} OR IS_DIRECTORY ${f})
        message(FATAL_ERROR "File not found: ${f}")
      endif()
    endforeach()
    
    # Calculate a checksum of merge_config_files to determine if we need
    # to re-generate .config
    set(merge_config_files_checksum "")
    foreach(f ${merge_config_files})
      file(MD5 ${f} checksum)
      set(merge_config_files_checksum "${merge_config_files_checksum}${checksum}")
    endforeach()
    
    # Create a new .config if it does not exists, or if the checksum of
    # the dependencies has changed
    set(merge_config_files_checksum_file ${PROJECT_BINARY_DIR}/.cmake.dotconfig.checksum)
    set(CREATE_NEW_DOTCONFIG 1)
    # Check if the checksum file exists too before trying to open it, though it
    # should under normal circumstances
    if(EXISTS ${DOTCONFIG} AND EXISTS ${merge_config_files_checksum_file})
      # Read out what the checksum was previously
      file(READ
        ${merge_config_files_checksum_file}
        merge_config_files_checksum_prev
        )
      if(
          ${merge_config_files_checksum} STREQUAL
          ${merge_config_files_checksum_prev}
          )
        # Checksum is the same as before
        set(CREATE_NEW_DOTCONFIG 0)
      endif()
    endif()
    
    if(CREATE_NEW_DOTCONFIG)
      set(input_configs_are_handwritten --handwritten-input-configs)
      set(input_configs ${merge_config_files})
    else()
      set(input_configs ${DOTCONFIG})
    endif()
    
    cmake_path(GET AUTOCONF_H PARENT_PATH autoconf_h_path)
    if(NOT EXISTS ${autoconf_h_path})
      file(MAKE_DIRECTORY ${autoconf_h_path})
    endif()
    
    execute_process(
      COMMAND ${CMAKE_COMMAND} -E env
      ${COMMON_KCONFIG_ENV_SETTINGS}
      SHIELD_AS_LIST=${SHIELD_AS_LIST_ESCAPED_COMMAND}
      ${PYTHON_EXECUTABLE}
      ${ZEPHYR_BASE}/scripts/kconfig/kconfig.py
      --zephyr-base=${ZEPHYR_BASE}
      ${input_configs_are_handwritten}
      ${KCONFIG_ROOT}
      ${DOTCONFIG}
      ${AUTOCONF_H}
      ${PARSED_KCONFIG_SOURCES_TXT}
      ${input_configs}
      WORKING_DIRECTORY ${APPLICATION_SOURCE_DIR}
      # The working directory is set to the app dir such that the user
      # can use relative paths in CONF_FILE, e.g. CONF_FILE=nrf5.conf
      RESULT_VARIABLE ret
      )
    if(NOT "${ret}" STREQUAL "0")
      message(FATAL_ERROR "command failed with return code: ${ret}")
    endif()
    
    if(CREATE_NEW_DOTCONFIG)
      # Write the new configuration fragment checksum. Only do this if kconfig.py
      # succeeds, to avoid marking zephyr/.config as up-to-date when it hasn't been
      # regenerated.
      file(WRITE ${merge_config_files_checksum_file}
                 ${merge_config_files_checksum})
    endif()
    
    # Read out the list of 'Kconfig' sources that were used by the engine.
    file(STRINGS ${PARSED_KCONFIG_SOURCES_TXT} PARSED_KCONFIG_SOURCES_LIST)
    
    # Force CMAKE configure when the Kconfig sources or configuration files changes.
    foreach(kconfig_input
        ${merge_config_files}
        ${DOTCONFIG}
        ${PARSED_KCONFIG_SOURCES_LIST}
        )
      set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${kconfig_input})
    endforeach()
    
    add_custom_target(config-twister DEPENDS ${DOTCONFIG})
    
    # Remove the CLI Kconfig symbols from the namespace and
    # CMakeCache.txt. If the symbols end up in DOTCONFIG they will be
    # re-introduced to the namespace through 'import_kconfig'.
    foreach (name ${cli_config_list})
      unset(${name})
      unset(${name} CACHE)
    endforeach()
    
    # Import the .config file and make all settings available in CMake processing.
    import_kconfig(${KCONFIG_NAMESPACE} ${DOTCONFIG})
    
    # Cache the CLI Kconfig symbols that survived through Kconfig, prefixed with CLI_.
    # Remove those who might have changed compared to earlier runs, if they no longer appears.
    foreach (name ${cli_config_list})
      if(DEFINED ${name})
        set(CLI_${name} ${CLI_${name}} CACHE INTERNAL "")
      else()
        unset(CLI_${name} CACHE)
      endif()
    endforeach()
    

Related