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(¶m, 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.

