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

  • 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()
    

  • Hi, 

    We have checked from our side, and cannot reproduce the issue.

    Could you try the following steps?

           0. make sure we have stash/backup all the changes, or finish the commit

    1. in NCS, I add the git remote
      $ git remote add dual_mic https://github.com/rick1082/sdk-nrf.git
    2. fetch the example
      $ git fetch dual_mic dual_mic_sample_2_4_2
    3. checkout the branch
      $ git checkout dual_mic_sample_2_4_2
    4. run west update
      $ west update
    5. go to nrf/applications/nrf5340_audio/tools/buildprog and use buildprog.py to build the program and flash

    -Amanda H.

Related