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.