nRF5340 Connectionless CTE not detected by Silicon Labs AoA Host (BRD4185A)

Hello team,

I am working on a Direction Finding (AoA) setup using nRF5340 DK as a CTE Transmitter (Tag) and a Silicon Labs BRD4185A (AoA board) as the Anchor/Receiver.

I have already 3 anchors(from silicon labs) and 2 tags (Silicon labs + Renesas-Dialog) working correctly with silicon labs AoA Host Application.

I am using the nRF Connect SDK v3.0.1 with Zephyr, running the connectionless beacon demo for CTE transmission.

Below are my logs from the nRF5340 side:
*** Booting nRF Connect SDK v3.0.1-9eb5615da66b ***
*** Using Zephyr OS v4.0.99-77f865b8f8d0 ***
Starting Connectionless Beacon Demo
Creating new ID failed (err -22)
Bluetooth initialization...success
GATT service registered successfully
CTE GATT service is registered.
Advertising set create...success
Set advertising data...success
Setting ext adv params... (skipped, already configured in bt_le_ext_adv_create)
Ext adv params set successfully
Ext adv start...Ext adv started successfully
Periodic advertising params set...success
Set periodic advertising data...success
Periodic advertising enable...Periodic advertising enabled
Attempting to enable CTE (attempt 1, length=0x14, count=2)...CTE params set successfully
Enable CTE...CTE enabled successfully
Started connectionless CTE!
Device name is set to Nordic ADV Demo
Device address: FC:93:0B:0D:C7:AC (random)

I am also attaching the working Dialog logs for reference.
Dialog (Working with silicon labs anchors) logs:
Hardware Initialized
BLE ADV Demo Task Started
Watchdog Registered
Before task creation
Before OS_TASK_CREATE for bleM
After OS_TASK_CREATE for bleM
Before ad_ble_event_queue_register
After ad_ble_event_queue_register
Before ble_peripheral_start()
After ble_peripheral_start()
CTE GATT service initialized and added to database
Setting ext adv params...
Ext adv params set successfully
Ext adv started successfully
Attempting to enable CTE (attempt 1, length=0x14, count=0x04)
CTE enabled successfully
Periodic advertising enabled with sync info for MODE_AUX_CTE_ADVA_IN_AUX
Started connectionless CTE!
Device name is set to Dialog ADV Demo

On the Silabs AoA Host side (BRD4185A), the board initializes correctly, connects to MQTT, but doesn't detects the nrf board, and also it does not receive any CTE packets from the nRF5340 tag.

I am attaching the Sniffer logs images for working Dialog tag with anchors along with nordic tag and its main.c logic.



/*
 * Copyright (c) 2021 Nordic Semiconductor ASA
 *
 * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
 */

#include <zephyr/types.h>
#include <stddef.h>
#include <errno.h>
#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>
#include <zephyr/bluetooth/addr.h>
#include <zephyr/bluetooth/gap.h>

#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/gatt.h>
#include <zephyr/logging/log.h>

#include <zephyr/bluetooth/direction.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/sys/util.h>

void update_cte_tx_params(void);

LOG_MODULE_REGISTER(ctes, LOG_LEVEL_INF);
/* forward declarations so helper functions can use them */
// extern struct bt_le_ext_adv *adv_set;
// extern struct bt_df_adv_cte_tx_param cte_params;

static ssize_t read_u8(struct bt_conn *conn, const struct bt_gatt_attr *attr,
                       void *buf, uint16_t len, uint16_t offset)
{
    const uint8_t *value = attr->user_data;
    return bt_gatt_attr_read(conn, attr, buf, len, offset, value, sizeof(*value));
}

static ssize_t read_u16(struct bt_conn *conn, const struct bt_gatt_attr *attr,
                        void *buf, uint16_t len, uint16_t offset)
{
    uint16_t tmp = sys_cpu_to_le16(*(uint16_t *)attr->user_data);
    return bt_gatt_attr_read(conn, attr, buf, len, offset, &tmp, sizeof(tmp));
}

#define BT_UUID_CTE_SERVICE_VAL   0x184A
#define BT_UUID_CTE_ENABLE_VAL    0x2BAD
#define BT_UUID_CTE_ADV_MIN_VAL   0x2BAE
#define BT_UUID_CTE_ADV_MIN_LEN_VAL 0x2BAF
#define BT_UUID_CTE_ADV_TX_COUNT_VAL 0x2BB0
#define BT_UUID_CTE_ADV_INTERVAL_VAL  0x2BB1
#define BT_UUID_CTE_ADV_PHY_VAL   0x2BB2

#define BT_UUID_CTE_SERVICE       BT_UUID_DECLARE_16(BT_UUID_CTE_SERVICE_VAL)
#define BT_UUID_CTE_ENABLE        BT_UUID_DECLARE_16(BT_UUID_CTE_ENABLE_VAL)
#define BT_UUID_CTE_ADV_MIN       BT_UUID_DECLARE_16(BT_UUID_CTE_ADV_MIN_VAL)
#define BT_UUID_CTE_ADV_MIN_LEN   BT_UUID_DECLARE_16(BT_UUID_CTE_ADV_MIN_LEN_VAL)
#define BT_UUID_CTE_ADV_TX_COUNT  BT_UUID_DECLARE_16(BT_UUID_CTE_ADV_TX_COUNT_VAL)
#define BT_UUID_CTE_ADV_INTERVAL  BT_UUID_DECLARE_16(BT_UUID_CTE_ADV_INTERVAL_VAL)
#define BT_UUID_CTE_ADV_PHY       BT_UUID_DECLARE_16(BT_UUID_CTE_ADV_PHY_VAL)


static bool cte_enabled;
static uint16_t adv_cte_interval = 0x20;  /* 32 * 1.25ms = 40ms default */
static uint8_t adv_cte_min_len = 0x02;    /* in 8us units */
static uint8_t adv_cte_tx_count = 0x02;
static uint8_t adv_cte_phy = BT_GAP_LE_PHY_1M;
static uint8_t adv_cte_tx_dur = 0x14;     /* in 8us units */

#define MIN_SUPPORTED_INTERVAL 0x10

static ssize_t write_cte_enable(struct bt_conn *conn,
                                const struct bt_gatt_attr *attr,
                                const void *buf, uint16_t len, uint16_t offset,
                                uint8_t flags)
{
    if (len < 1) return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
    uint8_t value = *((uint8_t *)buf);

    LOG_INF("CTE Enable: 0x%02X (conn_idx=%p)", value, conn);

	extern struct bt_le_ext_adv *adv_set;
	extern struct bt_df_adv_cte_tx_param cte_params;

	int err;
	if (value) {
		err = bt_df_set_adv_cte_tx_param(adv_set, &cte_params);
		if (err) {
			LOG_ERR("Failed to set CTE params (%d)", err);
			return BT_GATT_ERR(BT_ATT_ERR_WRITE_REQ_REJECTED);
		}

		err = bt_df_adv_cte_tx_enable(adv_set);
		if (err) {
			LOG_ERR("Failed to enable CTE TX (%d)", err);
			return BT_GATT_ERR(BT_ATT_ERR_WRITE_REQ_REJECTED);
		}
		cte_enabled = true;
	} else {
		err = bt_df_adv_cte_tx_disable(adv_set);
		if (err) {
			LOG_ERR("Failed to disable CTE TX (%d)", err);
			return BT_GATT_ERR(BT_ATT_ERR_WRITE_REQ_REJECTED);
		}
		cte_enabled = false;
	}
	update_cte_tx_params();
    return len;
}

static ssize_t write_cte_interval(struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                  const void *buf, uint16_t len, uint16_t offset,
                                  uint8_t flags)
{
    if (len < sizeof(uint16_t)) return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
    uint16_t val = sys_get_le16(buf);
    LOG_INF("CTE interval write: 0x%04X", val);

    if (val < MIN_SUPPORTED_INTERVAL)
        return BT_GATT_ERR(BT_ATT_ERR_WRITE_REQ_REJECTED);

    adv_cte_interval = val;
	update_cte_tx_params();
    return len;
}

static ssize_t write_cte_min_len(struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                 const void *buf, uint16_t len, uint16_t offset,
                                 uint8_t flags)
{
    if (len < 1) return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
    adv_cte_min_len = *((uint8_t *)buf);
    LOG_INF("CTE min length: 0x%02X", adv_cte_min_len);
    update_cte_tx_params();
    return len;
}

static ssize_t write_cte_tx_count(struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                  const void *buf, uint16_t len, uint16_t offset,
                                  uint8_t flags)
{
    if (len < 1) return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
    adv_cte_tx_count = *((uint8_t *)buf);
    LOG_INF("CTE TX count: 0x%02X", adv_cte_tx_count);
    update_cte_tx_params();
    return len;
}

static ssize_t write_cte_phy(struct bt_conn *conn, const struct bt_gatt_attr *attr,
                             const void *buf, uint16_t len, uint16_t offset,
                             uint8_t flags)
{
    if (len < 1) return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
    adv_cte_phy = *((uint8_t *)buf);
    LOG_INF("CTE PHY: 0x%02X", adv_cte_phy);
    update_cte_tx_params();
    return len;
}

static ssize_t write_cte_tx_dur(struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                const void *buf, uint16_t len, uint16_t offset,
                                uint8_t flags)
{
    if (len < 1) return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
    adv_cte_tx_dur = *((uint8_t *)buf);
    LOG_INF("CTE TX duration: 0x%02X", adv_cte_tx_dur);
    update_cte_tx_params();
    return len;
}

static struct bt_gatt_attr ctes_attrs[] = {
    BT_GATT_PRIMARY_SERVICE(BT_UUID_CTE_SERVICE),

    BT_GATT_CHARACTERISTIC(BT_UUID_CTE_ENABLE,
        BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
        BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
        read_u8, write_cte_enable, &cte_enabled),

    BT_GATT_CHARACTERISTIC(BT_UUID_CTE_ADV_INTERVAL,
        BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
        BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
        read_u16, write_cte_interval, &adv_cte_interval),

    BT_GATT_CHARACTERISTIC(BT_UUID_CTE_ADV_MIN_LEN,
        BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
        BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
        read_u8, write_cte_min_len, &adv_cte_min_len),

    BT_GATT_CHARACTERISTIC(BT_UUID_CTE_ADV_TX_COUNT,
        BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
        BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
        read_u8, write_cte_tx_count, &adv_cte_tx_count),

    BT_GATT_CHARACTERISTIC(BT_UUID_CTE_ADV_PHY,
        BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
        BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
        read_u8, write_cte_phy, &adv_cte_phy),

    BT_GATT_CHARACTERISTIC(BT_UUID_CTE_ADV_MIN, 
        BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
        BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
        read_u8, write_cte_tx_dur, &adv_cte_tx_dur)
};

static struct bt_gatt_service ctes_svc = BT_GATT_SERVICE(ctes_attrs);


/* Length of CTE in unit of 8[us] */
#define CTE_LEN (0x14U)
/* Number of CTE send in single periodic advertising train */
#define PER_ADV_EVENT_CTE_COUNT 2

static const struct bt_data ad[] = {
	BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME, sizeof(CONFIG_BT_DEVICE_NAME) - 1),
};

static void adv_sent_cb(struct bt_le_ext_adv *adv,
			struct bt_le_ext_adv_sent_info *info);

const static struct bt_le_ext_adv_cb adv_callbacks = {
	.sent = adv_sent_cb,
};

struct bt_le_ext_adv *adv_set;

const static struct bt_le_adv_param param =
		BT_LE_ADV_PARAM_INIT(BT_LE_ADV_OPT_EXT_ADV | BT_LE_ADV_OPT_USE_IDENTITY | BT_LE_ADV_OPT_USE_TX_POWER,
				     0xF0,
				     0xF0,
				     NULL);

static struct bt_le_ext_adv_start_param ext_adv_start_param = {
	.timeout = 0,
	.num_events = 0,
};

const static struct bt_le_per_adv_param per_adv_param = {
    .interval_min = 0x78,
    .interval_max = 0x78,
    .options = BT_LE_ADV_OPT_USE_TX_POWER | BT_LE_PER_ADV_OPT_INCLUDE_ADI,
};

#if defined(CONFIG_BT_DF_CTE_TX_AOD)
/* Example sequence of antenna switch patterns for antenna matrix designed by
 * Nordic. For more information about antenna switch patterns see README.rst.
 */
static uint8_t ant_patterns[] = {0x2, 0x0, 0x5, 0x6, 0x1, 0x4, 0xC, 0x9, 0xE,
				 0xD, 0x8, 0xA};
#endif /* CONFIG_BT_DF_CTE_TX_AOD */

struct bt_df_adv_cte_tx_param cte_params = { .cte_len = CTE_LEN,
					     .cte_count = PER_ADV_EVENT_CTE_COUNT,
#if defined(CONFIG_BT_DF_CTE_TX_AOD)
					     .cte_type = BT_DF_CTE_TYPE_AOA,
					     .num_ant_ids = ARRAY_SIZE(ant_patterns),
					     .ant_ids = ant_patterns
#else
					     .cte_type = BT_DF_CTE_TYPE_AOA,
					     .num_ant_ids = 0,
					     .ant_ids = NULL
#endif /* CONFIG_BT_DF_CTE_TX_AOD */
};

static void adv_sent_cb(struct bt_le_ext_adv *adv,
			struct bt_le_ext_adv_sent_info *info)
{
	printk("Advertiser[%d] %p sent %d\n", bt_le_ext_adv_get_index(adv),
	       adv, info->num_sent);
}
void update_cte_tx_params(void)
{
    int err;

    err = bt_df_set_adv_cte_tx_param(adv_set, &cte_params);
    if (err) {
        LOG_ERR("Failed to update CTE TX params (%d)", err);
    } else {
        LOG_INF("CTE TX params updated successfully");
    }
}

int main(void)
{
	cte_params = (struct bt_df_adv_cte_tx_param){
		.cte_len        = adv_cte_tx_dur,
		.cte_count      = adv_cte_tx_count,
		.cte_type       = BT_DF_CTE_TYPE_AOA,
		.num_ant_ids    = 0,
		.ant_ids        = NULL,
	};

    char addr_s[BT_ADDR_LE_STR_LEN];
    struct bt_le_oob oob_local;
    int err;

    printk("Starting Connectionless Beacon Demo\n");
    LOG_INF("Starting Nordic CTES GATT example...");

    bt_addr_le_t addr;
    err = bt_addr_le_from_str("FF:EE:DD:CC:BB:AA", "public", &addr);
    if (err) {
        printk("Invalid BT address (err %d)\n", err);
    }

    err = bt_id_create(&addr, NULL);
    if (err < 0) {
        printk("Creating new ID failed (err %d)\n", err);
    }

    /* Initialize the Bluetooth Subsystem */
    printk("Bluetooth initialization...");
    err = bt_enable(NULL);
    if (err) {
        printk("failed (err %d)\n", err);
        return 0;
    }
    printk("success\n");

	err = bt_gatt_service_register(&ctes_svc);
	if (err) {
		printk("Failed to register GATT service (err %d)\n", err);
	} else {
		printk("GATT service registered successfully\n");
	}
	if (bt_gatt_service_is_registered(&ctes_svc)) {
		printk("CTE GATT service is registered.\n");
	} else {
		printk("CTE GATT service is NOT registered.\n");
	}
    /* Create extended advertising set */
    printk("Advertising set create...");
    err = bt_le_ext_adv_create(&param, &adv_callbacks, &adv_set);
    if (err) {
        printk("failed (err %d)\n", err);
        return 0;
    }
    printk("success\n");

    /* Set extended advertising data */
    printk("Set advertising data...");
    err = bt_le_ext_adv_set_data(adv_set, ad, ARRAY_SIZE(ad), NULL, 0);
    if (err) {
        printk("failed (err %d)\n", err);
        return 0;
    }
    printk("success\n");

    /* Extended advertising parameters are already configured via bt_le_ext_adv_create() */
    printk("Setting ext adv params... (skipped, already configured in bt_le_ext_adv_create)\n");
    printk("Ext adv params set successfully\n");

    /* START extended advertising first — this matches Dialog startup flow */
    printk("Ext adv start...");
    err = bt_le_ext_adv_start(adv_set, &ext_adv_start_param);
    if (err) {
        printk("failed (err %d)\n", err);
        return 0;
    }
    printk("Ext adv started successfully\n");
	/* Configure and start periodic advertising using the global per_adv_param */
     printk("Periodic advertising params set...");
     err = bt_le_per_adv_set_param(adv_set, &per_adv_param);
     if (err) {
         printk("failed (err %d)\n", err);
         return err;
     }
     printk("success\n");
    /* set periodic advertising data (Dialog does this) */
    static const struct bt_data per_ad[] = {
     BT_DATA(BT_DATA_FLAGS, "\x06", 1),
        BT_DATA(BT_DATA_UUID16_ALL, "\x4A\x18", 2), /* 0x184A CTE service */
        BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME, sizeof(CONFIG_BT_DEVICE_NAME) - 1),
     };
    printk("Set periodic advertising data...");
    err = bt_le_per_adv_set_data(adv_set, per_ad, ARRAY_SIZE(per_ad));
    if (err) {
     printk("failed to set periodic adv data (err %d)\n", err);
        return 0;
     }
    printk("success\n");

    printk("Periodic advertising enable...");
    err = bt_le_per_adv_start(adv_set);
    if (err) {
        printk("failed (err %d)\n", err);
        return 0;
    }
    printk("Periodic advertising enabled\n");

    /* Now set CTE parameters (length, type, switching, etc.) */
    printk("Attempting to enable CTE (attempt 1, length=0x14, count=%d)...", PER_ADV_EVENT_CTE_COUNT);
    err = bt_df_set_adv_cte_tx_param(adv_set, &cte_params);
    if (err) {
        printk("failed (err %d)\n", err);
        return 0;
    }
    printk("CTE params set successfully\n");

    /* Enable CTE transmission (after periodic adv started) */
    printk("Enable CTE...");
    err = bt_df_adv_cte_tx_enable(adv_set);
    if (err) {
        printk("failed (err %d)\n", err);
        return 0;
    }
    printk("CTE enabled successfully\n");

    /* Mirror Dialog final message */
    bt_le_ext_adv_oob_get_local(adv_set, &oob_local);
    bt_addr_le_to_str(&oob_local.addr, addr_s, sizeof(addr_s));
    printk("Started connectionless CTE!\n");
    printk("Device name is set to Nordic ADV Demo\n");

	bt_le_oob_get_local(BT_ID_DEFAULT, &oob_local);
	bt_addr_le_to_str(&oob_local.addr, addr_s, sizeof(addr_s));
	printk("Device address: %s\n", addr_s);

    return 0;
}

What I’ve verified:

  • CTE transmission with GATT Database on nRF5340 is successfully enabled (no errors).

  • Advertising PHY is set to 1M for all advertisements and periodic packets.

  • Silabs AoA Host is configured for connectionless CTE with 1M PHY, CTE length = 20, slot = 1µs.

  • Anchor setup works fine when tested with the Silabs AoA example tags and Dialog tags(e.g., BRD4180A).

If anyone has tried inter-operating Nordic and Silabs direction-finding setups, any insights or working configurations would be appreciated.

Please let me know if anything more is required from my side.

Thanks & Regards,
Vidhi Shah

Parents
  • Hello Vidhi,
    the first thought here is that the error code you get from the nRF5340 "Creating new ID failed (err -22)" in the log indicate that the network core of the nRF5340 isn't programmed correctly. 
    Q1: How are you building the code example and is the programming log showing success with writing to the network core?
    Q2: Have you tried a different, non-AoA, BLE example to check that the chip and DK works as you plan? BLE uart peripheral or something like that. just to make sure the network core is programmed and running before going into thr AoA example.
    Q3: boards/nrf5340dk_nrf5340_cpuapp.overlay in the AoA, how is that looking compared to a different BLE code example overlay file?
    Best regards
    Asbjørn
Reply
  • Hello Vidhi,
    the first thought here is that the error code you get from the nRF5340 "Creating new ID failed (err -22)" in the log indicate that the network core of the nRF5340 isn't programmed correctly. 
    Q1: How are you building the code example and is the programming log showing success with writing to the network core?
    Q2: Have you tried a different, non-AoA, BLE example to check that the chip and DK works as you plan? BLE uart peripheral or something like that. just to make sure the network core is programmed and running before going into thr AoA example.
    Q3: boards/nrf5340dk_nrf5340_cpuapp.overlay in the AoA, how is that looking compared to a different BLE code example overlay file?
    Best regards
    Asbjørn
Children
No Data
Related