5340 many periodic advertiser sync error -5

the code, i can create one periodic adv, and i can get 45 aoa sample, but when i turn on another tag, i got err -5 from "err = bt_le_per_adv_sync_create(&(obj.sync_create_param), &(obj.sync));". the error like:

how to understand the " <wrn> bt_hci_core: opcode 0x2044 status 0x07". this is my main.c code like:

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

#include <stddef.h>
#include <errno.h>
#include <zephyr/kernel.h>

#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/direction.h>

#include <zephyr/drivers/gpio.h>

// LOG日志模块引入
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(main, LOG_LEVEL_DBG);
#define LOG_D(format, ...) LOG_DBG(">(%d)" format, __LINE__, ##__VA_ARGS__)
#define LOG_W(format, ...) LOG_WRN(">(%d)" format, __LINE__, ##__VA_ARGS__)
#define LOG_E(format, ...) LOG_ERR(">(%d)" format, __LINE__, ##__VA_ARGS__)

#include "wk_list.h"
#include "uart.h"

#define DEVICE_NAME CONFIG_BT_DEVICE_NAME
#define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1)
#define PEER_NAME_LEN_MAX 30
/* BT Core 5.3 specification allows controller to wait 6 periodic advertising events for
 * synchronization establishment, hence timeout must be longer than that.
 */
#define SYNC_CREATE_TIMEOUT_INTERVAL_NUM 7
/* Maximum length of advertising data represented in hexadecimal format */
#define ADV_DATA_HEX_STR_LEN_MAX (BT_GAP_ADV_MAX_EXT_ADV_DATA_LEN * 2 + 1)

static struct bt_le_per_adv_sync *sync;
// 标签MAC地址
static bt_addr_le_t per_addr;
static volatile bool per_adv_found;
static bool scan_enabled;
// 周期性广播对象的SID
static uint8_t per_sid;
static uint32_t sync_create_timeout_ms;

static K_SEM_DEFINE(sem_per_adv, 0, 1);
static K_SEM_DEFINE(sem_per_sync, 0, 1);
static K_SEM_DEFINE(sem_per_sync_lost, 0, 1);

// LED模块引入
#define LED_SLEEP_TIME_MS 1000
#define LED0_NODE DT_ALIAS(led0)
#define LED1_NODE DT_ALIAS(led1)
static const struct gpio_dt_spec led0 = GPIO_DT_SPEC_GET(LED0_NODE, gpios);
static const struct gpio_dt_spec led1 = GPIO_DT_SPEC_GET(LED1_NODE, gpios);

// BTN模块引入
#define BUTTON_0_NODE DT_ALIAS(boot_button0)
#if DT_NODE_EXISTS(BUTTON_0_NODE) && DT_NODE_HAS_PROP(BUTTON_0_NODE, gpios)
static const struct gpio_dt_spec button0 = GPIO_DT_SPEC_GET(BUTTON_0_NODE, gpios);
#else
#error "button must be declared in device tree as 'mcuboot_button0'"
#endif

// 使能天线切换开关(接收端AoA)
#if defined(CONFIG_BT_DF_CTE_RX_AOA)
/* Example sequence of antenna switch patterns for antenna matrix designed by
 * Nordic. For more information about antenna switch patterns see README.rst.
 */
// Nordic官方参数.
// static const uint8_t ant_patterns[] = {0x2, 0x0, 0x5, 0x6, 0x1, 0x4, 0xC, 0x9, 0xE, 0xD, 0x8, 0xA};
// 吾控参数.默认在数组[0],即天线9上接收扩展广播,当周期性广播后面有CTE时,就顺序切换天线1~天线8
const static uint8_t ant_patterns[] = {0x04, 0x0B, 0x0C, 0x0E, 0x00, 0x02, 0x07, 0x08, 0x0A};
#endif /* CONFIG_BT_DF_CTE_RX_AOA */

/**
 * @brief 周期性广播间隔转毫秒
 *
 * @param interval
 * @return uint32_t
 */
static inline uint32_t adv_interval_to_ms(uint16_t interval)
{
	return interval * 5 / 4;
}

/**
 * @brief phy类型
 *
 * @param phy
 * @return const char*
 */
static const char *phy2str(uint8_t phy)
{
	switch (phy)
	{
	case 0:
		return "No packets";
	case BT_GAP_LE_PHY_1M:
		return "LE 1M";
	case BT_GAP_LE_PHY_2M:
		return "LE 2M";
	case BT_GAP_LE_PHY_CODED:
		return "LE Coded";
	default:
		return "Unknown";
	}
}

/**
 * @brief CTE类型
 *
 * @param type
 * @return const char*
 */
static const char *cte_type2str(uint8_t type)
{
	switch (type)
	{
	case BT_DF_CTE_TYPE_AOA:
		return "AOA";
	case BT_DF_CTE_TYPE_AOD_1US:
		return "AOD 1 [us]";
	case BT_DF_CTE_TYPE_AOD_2US:
		return "AOD 2 [us]";
	case BT_DF_CTE_TYPE_NONE:
		return "";
	default:
		return "Unknown";
	}
}

/**
 * @brief 包状态
 *
 * @param status
 * @return const char*
 */
static const char *packet_status2str(uint8_t status)
{
	switch (status)
	{
	case BT_DF_CTE_CRC_OK:
		return "CRC OK";
	case BT_DF_CTE_CRC_ERR_CTE_BASED_TIME:
		return "CRC not OK, CTE Info OK";
	case BT_DF_CTE_CRC_ERR_CTE_BASED_OTHER:
		return "CRC not OK, Sampled other way";
	case BT_DF_CTE_INSUFFICIENT_RESOURCES:
		return "No resources";
	default:
		return "Unknown";
	}
}

/**
 * @brief 解析广播数据(三段式)
 *
 * @param data
 * @param user_data
 * @return true
 * @return false
 */
static bool data_cb(struct bt_data *data, void *user_data)
{
	char *name = user_data;
	uint8_t len;

	switch (data->type)
	{
	case BT_DATA_NAME_SHORTENED:
	case BT_DATA_NAME_COMPLETE:
		len = MIN(data->data_len, PEER_NAME_LEN_MAX - 1);
		memcpy(name, data->data, len);
		name[len] = '\0';
		return false;
	default:
		return true;
	}
}

/**
 * @brief 周期性广播同步完成后的回调函数(如果标签在基站附近,理论上只需要同步一次即可,也就是回调执行一次,后面就是收数据/关连接/IQ采样行为)
 *
 * @param sync
 * @param info
 */
static void sync_cb(struct bt_le_per_adv_sync *sync, struct bt_le_per_adv_sync_synced_info *info)
{
	char le_addr[BT_ADDR_LE_STR_LEN];

	bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));

	LOG_D("sync_cb[%u]: [DEVICE]: %s synced, "
		  "Interval 0x%04x (%u ms), PHY %s\n",
		  bt_le_per_adv_sync_get_index(sync), le_addr, info->interval, adv_interval_to_ms(info->interval), phy2str(info->phy));
	// 同步完成给出信号量(主函数中接收信号量,并使能CTE接收)
	k_sem_give(&sem_per_sync);
}

// 对象同步丢失FIFO
static K_FIFO_DEFINE(fifo_sync_lost_obj_data);
struct sync_lost_data_t
{
	// 对象
	struct bt_le_per_adv_sync *sync_obj;
	// 对象地址
	uint8_t addr[BT_ADDR_LE_STR_LEN];
};
/**
 * @brief 关闭周期性同步完成(标签断电/离开这个区域了)
 *
 * @param sync
 * @param info
 */
static void term_cb(struct bt_le_per_adv_sync *sync, const struct bt_le_per_adv_sync_term_info *info)
{
	struct sync_lost_data_t *sync_lost_data = k_malloc(sizeof(struct sync_lost_data_t));
	if (sync_lost_data)
	{
		sync_lost_data->sync_obj = sync;
		memcpy(sync_lost_data->addr, info->addr->a.val, BT_ADDR_SIZE);
	}
	// 关闭完成给出信号量(主函数中接收信号量,并删除该同步对象)
	k_fifo_put(&fifo_sync_lost_obj_data, sync_lost_data);
}

/**
 * @brief 周期性广播接收数据完成
 *
 * @param sync
 * @param info
 * @param buf
 */
static void recv_cb(struct bt_le_per_adv_sync *sync, const struct bt_le_per_adv_sync_recv_info *info, struct net_buf_simple *buf)
{
	static char data_str[ADV_DATA_HEX_STR_LEN_MAX];
	char le_addr[BT_ADDR_LE_STR_LEN];

	bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));
	bin2hex(buf->data, buf->len, data_str, sizeof(data_str));
	// LOG_D("recv_cb[%u]: [DEVICE]: %s, tx_power %i, RSSI %i, CTE %s, data length %u, data: %s\n", bt_le_per_adv_sync_get_index(sync), le_addr, info->tx_power, info->rssi, cte_type2str(info->cte_type), buf->len, data_str);
}

/**
 * @brief IQ采样完成
 *
 * @param sync
 * @param report
 */
static void cte_recv_cb(struct bt_le_per_adv_sync *sync, struct bt_df_per_adv_sync_iq_samples_report const *report)
{
	LOG_D("cte_recv_cb[%u]: samples count %d, cte type %s, slot durations: %u [us], packet status %s, RSSI %i\n", bt_le_per_adv_sync_get_index(sync), report->sample_count, cte_type2str(report->cte_type), report->slot_durations, packet_status2str(report->packet_status), report->rssi);
}

// 周期性广播同步回调
static struct bt_le_per_adv_sync_cb sync_callbacks = {
	.synced = sync_cb,			  /* 同步已成功 */
	.term = term_cb,			  /* 同步已关闭(本机关闭/远程请求关闭/远距离丢数据引起的关闭) */
	.recv = recv_cb,			  /* 数据已提取 */
	.cte_report_cb = cte_recv_cb, /*IQ采样已成功*/
};

/**
 * @brief 扫描回调函数
 *
 * @param info
 * @param buf
 */
static void scan_recv(const struct bt_le_scan_recv_info *info, struct net_buf_simple *buf)
{
	char le_addr[BT_ADDR_LE_STR_LEN];
	char name[PEER_NAME_LEN_MAX];

	(void)memset(name, 0, sizeof(name));

	// 提取标签的名称
	bt_data_parse(buf, data_cb, name);
	// 提取标签的地址
	bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));

	// LOG_D("[DEVICE]: %s, AD evt type %u, Tx Pwr: %i, RSSI %i %s C:%u S:%u "
	// 	   "D:%u SR:%u E:%u Prim: %s, Secn: %s, Interval: 0x%04x (%u ms), "
	// 	   "SID: %u\n",
	// 	   le_addr, info->adv_type, info->tx_power, info->rssi, name,
	// 	   (info->adv_props & BT_GAP_ADV_PROP_CONNECTABLE) != 0,
	// 	   (info->adv_props & BT_GAP_ADV_PROP_SCANNABLE) != 0,
	// 	   (info->adv_props & BT_GAP_ADV_PROP_DIRECTED) != 0,
	// 	   (info->adv_props & BT_GAP_ADV_PROP_SCAN_RESPONSE) != 0,
	// 	   (info->adv_props & BT_GAP_ADV_PROP_EXT_ADV) != 0, phy2str(info->primary_phy),
	// 	   phy2str(info->secondary_phy), info->interval, adv_interval_to_ms(info->interval),
	// 	   info->sid);
	// LOG_D("[DEVICE]: %s\r\n", le_addr);


	// 有间隔则说明是周期性广播
	if (/*!per_adv_found && */ info->interval)
	{
		/* 记录新来的标签MAC,插入到list,如果长时间无周期性广播,则删除它 */
		gpio_pin_toggle_dt(&led0);
		// LOG_D("[DEVICE]: %s, AD evt type %u, Tx Pwr: %i, RSSI %i %s C:%u S:%u "
		// 	   "D:%u SR:%u E:%u Prim: %s, Secn: %s, Interval: 0x%04x (%u ms), "
		// 	   "SID: %u\n",
		// 	   le_addr, info->adv_type, info->tx_power, info->rssi, name,
		// 	   (info->adv_props & BT_GAP_ADV_PROP_CONNECTABLE) != 0,
		// 	   (info->adv_props & BT_GAP_ADV_PROP_SCANNABLE) != 0,
		// 	   (info->adv_props & BT_GAP_ADV_PROP_DIRECTED) != 0,
		// 	   (info->adv_props & BT_GAP_ADV_PROP_SCAN_RESPONSE) != 0,
		// 	   (info->adv_props & BT_GAP_ADV_PROP_EXT_ADV) != 0, phy2str(info->primary_phy),
		// 	   phy2str(info->secondary_phy), info->interval, adv_interval_to_ms(info->interval),
		// 	   info->sid);

		// 同步超时时间
		sync_create_timeout_ms = adv_interval_to_ms(info->interval) * SYNC_CREATE_TIMEOUT_INTERVAL_NUM;
		per_adv_found = true;
		per_sid = info->sid;
		bt_addr_le_copy(&per_addr, info->addr);
		// 给出信号量,立即开始同步
		k_sem_give(&sem_per_adv);
	}
}

/**
 * @brief 发现有了周期性广播,就创建一个东西(obj)来处理周期性广播同步
 *
 */
static void create_sync(void)
{
	struct bt_le_per_adv_sync_param sync_create_param;
	int err;

	LOG_D("Creating Periodic Advertising Sync...");
	// 标签地址拷贝
	bt_addr_le_copy(&sync_create_param.addr, &per_addr);
	// 需要有CTE才同步(没有CTE则不同步)
	sync_create_param.options = 0;
	// 广播者的set id
	sync_create_param.sid = per_sid;
	// 通过“最大事件跳过”这个参数,扫描设备可以在成功接收到一个周期性广播事件后,选择跳过一定数量的连续周期性广播事件,以达到节省功耗或提高扫描效率的目的
	sync_create_param.skip = 0;
	// 同步超时100ms
	sync_create_param.timeout = 0x64; // 0xa;
	// 创建周期性广播同步对象,用于同步周期性广播的上报.
	err = bt_le_per_adv_sync_create(&sync_create_param, &sync);
}

// 扫描回调
static struct bt_le_scan_cb scan_callbacks = {
	.recv = scan_recv,
};

/**
 * @brief 删除处理周期性广播同步的obj
 *
 * @return int
 */
static int delete_sync(void)
{
	int err;

	LOG_D("Deleting Periodic Advertising Sync...");
	// 删除周期性广播同步对象
	err = bt_le_per_adv_sync_delete(sync);
	if (err)
	{
		LOG_D("failed (err %d)\n", err);
		return err;
	}
	LOG_D("success\n");

	return 0;
}

/**
 * @brief 使能cte
 *
 */
static void enable_cte_rx(void)
{
	int err;

	const struct bt_df_per_adv_sync_cte_rx_param cte_rx_params = {
		.max_cte_count = 5, /* CTE次数,与标签对应上即可 */
		/* BT_DF_CTE_TYPE_AOA   BT_DF_CTE_TYPE_ALL */
		.cte_types = BT_DF_CTE_TYPE_ALL,					/* 支持aoa/aod1us/aod2us三种类型 */
		.slot_durations = BT_DF_ANTENNA_SWITCHING_SLOT_2US, /* 开关切换使用2us槽 */
		.num_ant_ids = ARRAY_SIZE(ant_patterns),			/* 天线数量 */
		.ant_ids = ant_patterns,							/* 天线数组 */
	};

	LOG_D("%s():enable receiving of CTE.\r\n", __func__);
	// 使能接收,采样CTE信号
	err = bt_df_per_adv_sync_cte_rx_enable(sync, &cte_rx_params);
	if (err)
	{
		LOG_D("failed (err %d)\n", err);
		return;
	}
	LOG_D("success. CTE receive enabled.\n");
}

/**
 * @brief 扫描初始化
 *
 * @return int
 */
static int scan_init(void)
{
	LOG_D("Scan callbacks register...");
	// 注册扫描回调函数(primary channel)
	bt_le_scan_cb_register(&scan_callbacks);
	LOG_D("success.\n");

	LOG_D("Periodic Advertising callbacks register...");
	// 注册周期性广播回调函数(secondary channel)
	bt_le_per_adv_sync_cb_register(&sync_callbacks);
	LOG_D("success.\n");

	return 0;
}

/**
 * @brief 使能扫描
 *
 * @return int
 */
static int scan_enable(void)
{
	struct bt_le_scan_param param = {
		.type = BT_LE_SCAN_TYPE_ACTIVE, /*扫描,并request额外信息*/
		.options = BT_LE_SCAN_OPT_FILTER_DUPLICATE,
		/*BT_LE_SCAN_OPT_NONE*/ /*不过滤*/
		/*BT_LE_SCAN_OPT_FILTER_DUPLICATE*/
		.interval = BT_GAP_SCAN_FAST_INTERVAL, /* 扫描间隔60ms */
		.window = BT_GAP_SCAN_FAST_WINDOW,	   /* 扫描窗30ms */
		.timeout = 0U,
	};

	int err;

	if (!scan_enabled)
	{
		LOG_D("Start scanning...");
		err = bt_le_scan_start(&param, NULL);
		if (err)
		{
			LOG_D("failed (err %d)\n", err);
			return err;
		}
		LOG_D("success\n");
		scan_enabled = true;
	}

	return 0;
}

static void scan_disable(void)
{
	int err;

	LOG_D("Scan disable...");
	err = bt_le_scan_stop();
	if (err)
	{
		LOG_D("failed (err %d)\n", err);
		return;
	}
	LOG_D("Success.\n");

	scan_enabled = false;
}

/**
 * @brief 判定是否应该进入升级模式(button0开机就被按下)
 *
 * @return true -进入升级模式
 * @return false -不进入升级模式
 */
static bool is_enter_boot_mode(void)
{
	int pin_active;

	if (!device_is_ready(button0.port))
	{
		return false;
	}

	gpio_pin_configure_dt(&button0, GPIO_INPUT);
	pin_active = gpio_pin_get_dt(&button0);

	LOG_DBG("0button0 = %d.", pin_active);

	if (pin_active)
	{
		k_sleep(K_MSEC(50));
		LOG_DBG("button0 = %d.", pin_active);
		if (pin_active == gpio_pin_get_dt(&button0))
			return true;
		else
			return false;
	}
	return false;
}

int main(void)
{
	/* 延时等待片外外设起来 */
	k_msleep(1000);

	int err;

	if (!gpio_is_ready_dt(&led0) || !gpio_is_ready_dt(&led1))
	{
		return 0;
	}

	gpio_pin_configure_dt(&led0, GPIO_OUTPUT_ACTIVE);
	gpio_pin_configure_dt(&led1, GPIO_OUTPUT_ACTIVE);
	gpio_pin_configure_dt(&button0, GPIO_INPUT);

	gpio_pin_set_dt(&led0, GPIO_ACTIVE_HIGH);
	gpio_pin_set_dt(&led1, GPIO_ACTIVE_HIGH);

	// 获取构建信息
	//  memset(bulid_msg, 0, sizeof(bulid_msg));
	//  sprintf(bulid_msg, "BR,%04d%02d%02d-%02d%02d%02d,%s\r\n", OS_YEAR, OS_MONTH, OS_DAY, OS_HOUR, OS_MINUTE, OS_SECOND, FIREWARE_VERSION);
	//  usart1SendStringDma(bulid_msg);

	LOG_INF("hello %s, build time:" __DATE__ " " __TIME__ "\n", CONFIG_BOARD);

	/* 检查是否是升级模式 */
	if (gpio_pin_get_dt(&button0))
	{
		gpio_pin_set_dt(&led1, GPIO_ACTIVE_LOW);
		LOG_INF("enter boot mode.");
		extern void wk_dfu_uart_thread_create(void);
		wk_dfu_uart_thread_create();
		return 0;
	}

	gpio_pin_set_dt(&led0, GPIO_ACTIVE_LOW);
	LOG_INF("enter application mode.");

	// 初始化串口
	uart_init();
	// 创建接收线程
	extern void uart_thread_create(void);
	uart_thread_create();

	uint8_t data[10] = {0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30};
	for (int i = 0; i < 100; i++)
	{
		uart_hex_print(data, sizeof(data));
		uart_print("hello app0 %d, build time:" __DATE__ " " __TIME__ "\r\n", i, CONFIG_BOARD);
	}

	k_msleep(1000);

	LOG_D("Starting Connectionless Locator Demo\n");
	LOG_D("Bluetooth initialization...");
	err = bt_enable(NULL);
	if (err)
	{
		LOG_D("failed (err %d)\n", err);
	}
	LOG_D("success\n");

	// 扫描初始化(处理扫描回调(primary)/处理周期性广播回调(secondary))
	scan_init();
	scan_enable();

	while (true)
	{
		k_msleep(100);
		gpio_pin_toggle_dt(&led1);
	}
}

/**
 * @brief 周期性广播对象创建
 *
 */
void create_sync_thread(void)
{
	int err;
	k_msleep(100);

	while (1)
	{
		// 等待周期性广播信号量(可能会丢对象)
		err = k_sem_take(&sem_per_adv, K_FOREVER);
		if (err)
		{
			LOG_D("failed (err %d)\n", err);
			continue;
		}

		// 临时对象
		struct per_adv_obj obj;

		/* 遍历链表确认是否已经存在该对象 */
		// 提取对象地址
		memcpy(obj.mac.addr, per_addr.a.val, BT_ADDR_SIZE);
		// 遍历
		err = wk_list_search_per_adv_obj(obj);
		if (!err)
		{
			// LOG_D("obj already exist continue.");
			continue;
		}
		LOG_D("new obj comming.");
		// 周期性广播对象的参数
		struct bt_le_per_adv_sync_param sync_create_param;
		// 标签地址拷贝
		bt_addr_le_copy(&(obj.sync_create_param.addr), &per_addr);
		// 需要有CTE才同步(没有CTE则不同%E6%AD%A5)
		obj.sync_create_param.options = 0;
		// 广播者的set id
		obj.sync_create_param.sid = per_sid;
		// 通过“最大事件跳过”这个参数,扫描设备可以在成功接收到一个周期性广播事件后,选择跳过一定数量的连续周期性广播事件,以达到节省功耗或提高扫描效率的目的
		obj.sync_create_param.skip = 0;
		// 同步超时100ms
		obj.sync_create_param.timeout = 0x64; // 0xa;
		// 创建周期性广播同步对象,用于同步周期性广播的上报.
		err = bt_le_per_adv_sync_create(&(obj.sync_create_param), &(obj.sync));
		if (err)
		{
			LOG_D("sync create failed (err %d)\n", err);
			continue;
		}

		/* 等待周期性广播成功 */
		err = k_sem_take(&sem_per_sync, K_MSEC(sync_create_timeout_ms));
		if (err)
		{
			// 删除周期性广播同步对象
			bt_le_per_adv_sync_delete(obj.sync);
			continue;
		}
		LOG_D("success. Periodic sync established.\n");

		/* 使能CTE */
		const struct bt_df_per_adv_sync_cte_rx_param cte_rx_params = {
			.max_cte_count = 5, /* CTE次数,与标签对应上即可 */
			/* BT_DF_CTE_TYPE_AOA   BT_DF_CTE_TYPE_ALL */
			.cte_types = BT_DF_CTE_TYPE_ALL,					/* 支持aoa/aod1us/aod2us三种类型 */
			.slot_durations = BT_DF_ANTENNA_SWITCHING_SLOT_2US, /* 开关切换使用2us槽 */
			.num_ant_ids = ARRAY_SIZE(ant_patterns),			/* 天线数量 */
			.ant_ids = ant_patterns,							/* 天线数组 */
		};
		// 使能接收,采样CTE信号
		err = bt_df_per_adv_sync_cte_rx_enable(obj.sync, &cte_rx_params);
		if (err)
		{
			LOG_D("enable cet rx failed (err %d)\n", err);
			continue;
		}

		/* 将对象添加到链表 */
		wk_list_add_per_adv_obj(obj);
		// 遍历链表
		wk_list_foreach_per_adv_obj();
	}
}
K_THREAD_DEFINE(create_sync_thread_id, 2024, create_sync_thread, NULL, NULL, NULL, 9, 0, 0);

/**
 * @brief 周期性广播对象删除
 *
 */
void delete_sync_thread(void)
{
	int err;
	k_msleep(100);

	while (1)
	{
		// 一直等待获取同步关闭的对象(标签断电/离开这个区域了)
		struct sync_lost_data_t *sync_lost_obj = k_fifo_get(&fifo_sync_lost_obj_data, K_FOREVER);
		// 删除周期性广播同步对象
		err = bt_le_per_adv_sync_delete(sync_lost_obj->sync_obj);
		LOG_D("bt_le_per_adv_sync_delete err = %d", err);

		// 临时对象
		struct per_adv_obj obj;
		// 提取对象地址
		memcpy(obj.mac.addr, sync_lost_obj->addr, BT_ADDR_SIZE);
		// 释放内存
		k_free(sync_lost_obj);
		// 删除链表
		err = wk_list_del_per_adv_obj(obj);
		LOG_D("wk_list_del_per_adv_obj err = %d", err);
	}
}
K_THREAD_DEFINE(delete_sync_thread_id, 2024, delete_sync_thread, NULL, NULL, NULL, 9, 0, 0);

how can i write the code for many tags.

Parents
  • Hi,

    It seems you have exceeded the maximum amount of simultaneous tags. The status code you get (0x7) is BT_HCI_ERR_MEM_CAPACITY_EXCEEDED. Probably your CONFIG_BT_PER_ADV_SYNC_MAX is set to 45?

    45 is a high number, though. How many tags do you intend to support simultaneously?

  • i can get 45 aoa sample

    the '45' means 45 IQ samples for aoa algorithm, not for periodic adv!  in my prj.conf, 'CONFIG_BT_PER_ADV_SYNC_MAX=6'

    How many tags do you intend to support simultaneously?

    10Hz about 20 tags is ok for me. this is my prj.conf file:

    #
    # Copyright (c) 2021 Nordic Semiconductor ASA
    #
    # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
    #
    
    CONFIG_BT=y
    CONFIG_BT_DEVICE_NAME="DF Connectionless Locator App"
    
    CONFIG_BT_EXT_ADV=y
    CONFIG_BT_PER_ADV_SYNC=y
    CONFIG_BT_OBSERVER=y
    
    # Enable Direction Finding Feature including AoA and AoD
    CONFIG_BT_DF=y
    CONFIG_BT_DF_CONNECTIONLESS_CTE_RX=y
    
    
    
    ###############################################
    # ----------------------------------CONSOLE功能配置
    # 开启控制台
    CONFIG_CONSOLE=y
    # # 使能printk作为控制台输出
    CONFIG_PRINTK=y
    
    # # 方案1.使用jlink的rtt作为输出
    # # 1.1使能jlink
    CONFIG_USE_SEGGER_RTT=y
    # # 1.2使能jlink作为控制台输出
    CONFIG_RTT_CONSOLE=y
    
    # # 方案2.使能uart作为控制台输出
    # # 2.1使能uart
    # CONFIG_SERIAL=n
    # # 2.2使能uart作为控制台输出
    # CONFIG_UART_CONSOLE=n
    
    # ----------------------------------LOG功能配置
    # 使能LOG
    CONFIG_LOG=y
    # 使用jlink的rtt作为LOG的后端
    CONFIG_LOG_BACKEND_RTT=n
    # 使用uart作为LOG的后端
    CONFIG_LOG_BACKEND_UART=n
    
    # ----------------------------------SHELL功能配置
    # 使能shell
    CONFIG_SHELL=y
    CONFIG_SHELL_MINIMAL=n
    CONFIG_SHELL_STACK_SIZE=4024
    CONFIG_SHELL_BACKEND_SERIAL=n
    CONFIG_THREAD_MONITOR=y
    CONFIG_BOOT_BANNER=y
    CONFIG_THREAD_NAME=y
    CONFIG_SHELL_BACKEND_RTT=y
    CONFIG_SHELL_PROMPT_RTT=""
    
    # ----------------------------------DFU
    # 使能串口
    CONFIG_SERIAL=y
    # 使能异步API
    CONFIG_UART_ASYNC_API=y
    CONFIG_NRFX_UARTE0=y
    # 使能串口中断(和异步API只能二选一)
    # CONFIG_UART_INTERRUPT_DRIVEN=y
    
    
    # Enable the LittleFS file system.
    # CONFIG_FILE_SYSTEM=y
    CONFIG_FLASH=y
    # CONFIG_FLASH_PAGE_LAYOUT=y
    CONFIG_FLASH_MAP=y
    # CONFIG_FILE_SYSTEM_LITTLEFS=y
    
    CONFIG_REBOOT=y
    
    CONFIG_STREAM_FLASH=y
    CONFIG_IMG_MANAGER=y
    
    # ----------------------------------其它重要配置
    # 堆大小设置,使用k_malloc时从此2048上获取
    CONFIG_HEAP_MEM_POOL_SIZE=40240
    CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=1025
    
    ########################################
    CONFIG_BT_PER_ADV_SYNC_MAX=6
    
    # 仅支持串口升级
    CONFIG_BOOTLOADER_MCUBOOT=y
    

    now i have 2 tags, only one tag can be synced. it is confused me.

  • What repo are you using code from when testing this? For AoA receiver, we really only support using nRF Connect SDK 2.1.0, anything older should not be expected to work, anything newer won't have a valid Bluetooth QDID (but may work still).

  • sorry, i use sdk 2.4.0,i can not find any documents about, i do as this . i will use 2.1.0 later. thanks.

  • we really only support using nRF Connect SDK 2.1.0

    yes, i test v2.1.0. but get error. rx use 5240dk, tx use 52833dk.

    [11:26:30.556]收←◆success. Found periodic advertising.
    Creating Periodic Advertising Sync...success.
    Waiting for periodic sync...
    
    [11:26:31.786]收←◆PER_ADV_SYNC[0]: [DEVICE]: 0C:E8:88:C4:34:D2 (random) synced, Interval 0x0780 (2400 ms), PHY LE 2M
    success. Periodic sync established.
    Enable receiving of CTE...
    
    [11:26:41.802]收←◆ASSERTION FAIL [err == 0] @ WEST_TOPDIR/zephyr/subsys/bluetooth/host/hci_core.c:327
    	k_sem_take failed with err -11
    [11:26:42.068]收←◆*** Booting Zephyr OS build v3.1.99-ncs1  ***
    Starting Connectionless Locator Demo
    Bluetooth initialization...success
    Scan callbacks register...success.
    Periodic Advertising callbacks register...success.
    Start scanning...success
    Waiting for periodic advertising...
    
    [11:26:42.435]收←◆success. Found periodic advertising.
    Creating Periodic Advertising Sync...success.
    Waiting for periodic sync...
    
    [11:26:43.787]收←◆PER_ADV_SYNC[0]: [DEVICE]: 0C:E8:88:C4:34:D2 (random) synced, Interval 0x0780 (2400 ms), PHY LE 2M
    success. Periodic sync established.
    Enable receiving of CTE...
    
    [11:26:53.801]收←◆ASSERTION FAIL [err == 0] @ WEST_TOPDIR/zephyr/subsys/bluetooth/host/hci_core.c:327
    	k_sem_take failed with err -11
    [11:26:54.069]收←◆*** Booting Zephyr OS build v3.1.99-ncs1  ***
    Starting Connectionless Locator Demo
    Bluetooth initialization...success
    Scan callbacks register...success.
    Periodic Advertising callbacks register...success.
    Start scanning...success
    Waiting for periodic advertising...
    
    [11:26:54.315]收←◆success. Found periodic advertising.
    Creating Periodic Advertising Sync...success.
    Waiting for periodic sync...
    
    [11:26:55.785]收←◆PER_ADV_SYNC[0]: [DEVICE]: 0C:E8:88:C4:34:D2 (random) synced, Interval 0x0780 (2400 ms), PHY LE 2M
    success. Periodic sync established.
    Enable receiving of CTE...

  • it seems, v2.4.0 and v2.5.0 can support aoa, but only for 52833? not for 5340?

  • added CONFIG_BT_PER_ADV_SYNC_MAX=2 to hci_rpmsg.conf, as that makes you run out of RAM on the network core. So other optimizations are needed, for instance adjust CONFIG_HEAP_MEM_POOL_SIZE=2048 (I did not test extensively but I did not get any problems immediately with this configuration).

    i set app core and net core "CONFIG_BT_PER_ADV_SYNC_MAX=2" , set net core " CONFIG_HEAP_MEM_POOL_SIZE=2048" .  i get two perodic adv, but when i set CONFIG_BT_PER_ADV_SYNC_MAX=3, ram overflow, so it is difficult to support many tags per adv, such as 10 tags or more. it is need about 12.4K ram for one tag. almost 10 tag = 10*12.4K = 124K, but net core only 64K, how to solve this problem? thanks.

Reply
  • added CONFIG_BT_PER_ADV_SYNC_MAX=2 to hci_rpmsg.conf, as that makes you run out of RAM on the network core. So other optimizations are needed, for instance adjust CONFIG_HEAP_MEM_POOL_SIZE=2048 (I did not test extensively but I did not get any problems immediately with this configuration).

    i set app core and net core "CONFIG_BT_PER_ADV_SYNC_MAX=2" , set net core " CONFIG_HEAP_MEM_POOL_SIZE=2048" .  i get two perodic adv, but when i set CONFIG_BT_PER_ADV_SYNC_MAX=3, ram overflow, so it is difficult to support many tags per adv, such as 10 tags or more. it is need about 12.4K ram for one tag. almost 10 tag = 10*12.4K = 124K, but net core only 64K, how to solve this problem? thanks.

Children
  • Hi,

    Did you try to modify the definition of SYNC_IQ_REPORT_CNT so that it is not a multiple of CONFIG_BT_PER_ADV_SYNC_MAX? That will reduce the RAM usage. Note that we have not tested with multiple, but some customers have done it (like in this thread).

    Regarding lacking qualification for AoO the nRF3540 with SDK version 2.4.0 and 2.5.0 that does not mean that it will not work for prototyping, but it mans it is not qualified and cannot be used in an end product (unless it is qualified at a later point, if so, that will change).

  • #if defined(CONFIG_BT_PER_ADV_SYNC_MAX) && defined(CONFIG_BT_CTLR_DF_PER_SCAN_CTE_NUM_MAX)
    #define SYNC_IQ_REPORT_CNT (CONFIG_BT_PER_ADV_SYNC_MAX * CONFIG_BT_CTLR_DF_PER_SCAN_CTE_NUM_MAX * 2)
    #else
    #define SYNC_IQ_REPORT_CNT 0U
    #endif

    yes, i find varible "mem_iq_report" in ull_df.c consume many memerys, so 5340 net core only 64K not fit aoa rx, 52833 maybe fit for aoa rx?

  • Yes, the nRF52833 will not have this limitation. You will stil have a limited amount of RAM though (128 kB for the whole system), but no split between the host and controller. So the will be a limit to how many tags you can support concurrently, though there are probably more RAM optimizations that can be made.

    Edit: I know that ublox have a solution where they support up to 20 advertisers on the nRF52833, but that is with the direction finding algorithm running off chip to offload the CPU. You can learn more about it in the Integrating Bluetooth Direction Finding with u-blox webinar.

Related