Nordic nRF52840设置可扫描扩展广播后周期广播参数报错-22
问题分析与解决方案
问题描述
基于Nordic nRF52840开发周期性广播功能,搭配自研Android应用扫描设备时无法发现目标设备。排查Android端代码无异常后,发现将bt_le_ext_adv_create()的参数从BT_LE_EXT_ADV_NCONN_NAME(不可连接扩展广播)改为BT_LE_EXT_ADV_SCAN_NAME(可扫描扩展广播)后,调用bt_le_per_adv_set_param()会返回错误码-22(无效参数),相关代码如下:
/* * Copyright (c) 2020 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ #include <bluetooth/bluetooth.h> #define DEVICE_NAME "RECEIVER" #define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1) static uint8_t mfg_data[] = { 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x10, 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x10, 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x10, 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x10}; static const struct bt_data ad[] = { BT_DATA(BT_DATA_MANUFACTURER_DATA, mfg_data, 40), }; void main(void) { struct bt_le_ext_adv *adv; int err; printk("Starting Periodic Advertising Demo\n"); /* Initialize the Bluetooth Subsystem */ err = bt_enable(NULL); if (err) { printk("Bluetooth init failed (err %d)\n", err); return; } /* Create a non-connectable non-scannable advertising set */ err = bt_le_ext_adv_create(BT_LE_EXT_ADV_SCAN_NAME, NULL, &adv); // BT_LE_EXT_ADV_NCONN_NAME BT_LE_EXT_ADV_SCAN_NAME if (err) { printk("Failed to create advertising set (err %d)\n", err); return; } /* Set periodic advertising parameters */ err = bt_le_per_adv_set_param(adv, BT_LE_PER_ADV_DEFAULT); if (err) { printk("Failed to set periodic advertising parameters" " (err %d)\n", err); return; } /* Enable Periodic Advertising */ err = bt_le_per_adv_start(adv); if (err) { printk("Failed to enable periodic advertising (err %d)\n", err); return; } while (true) { printk("Start Extended Advertising..."); err = bt_le_ext_adv_start(adv, BT_LE_EXT_ADV_START_DEFAULT); if (err) { printk("Failed to start extended advertising " "(err %d)\n", err); return; } printk("done.\n"); k_sleep(K_SECONDS(10)); printk("Stop Extended Advertising..."); err = bt_le_ext_adv_stop(adv); if (err) { printk("Failed to stop extended advertising " "(err %d)\n", err); return; } printk("done.\n"); k_sleep(K_SECONDS(10)); } }
错误原因
错误码-22对应EINVAL,核心问题是可扫描扩展广播(BT_LE_EXT_ADV_SCAN_NAME)与周期性广播(Periodic Advertising)的参数组合不被Zephyr蓝牙栈允许。
根据蓝牙核心规范和Zephyr实现逻辑:
- 周期性广播的设计目标是无连接场景下周期性发送数据,要求绑定的广告集必须是不可连接类型;
BT_LE_EXT_ADV_SCAN_NAME属于可扫描广告集,允许外部设备发送扫描请求,与周期性广播的逻辑冲突,因此调用bt_le_per_adv_set_param()会触发无效参数错误。
解决方案
拆分两个独立的广告集,分别实现可扫描发现和周期性广播功能:
- 一个广告集用
BT_LE_EXT_ADV_SCAN_NAME配置,负责广播设备名称,确保Android设备能扫描发现; - 另一个广告集用
BT_LE_EXT_ADV_NCONN_NAME配置,绑定周期性广播,用于发送自定义厂商数据。
修改后的代码示例:
/* * Copyright (c) 2020 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ #include <bluetooth/bluetooth.h> #define DEVICE_NAME "RECEIVER" #define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1) // 自定义厂商数据 static uint8_t mfg_data[] = { 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x10, 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x10, 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x10, 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x10 }; // 可扫描广告数据:包含完整设备名称 static const struct bt_data scan_ad[] = { BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN), }; // 周期性广播数据:自定义厂商数据 static const struct bt_data periodic_ad[] = { BT_DATA(BT_DATA_MANUFACTURER_DATA, mfg_data, sizeof(mfg_data)), }; void main(void) { struct bt_le_ext_adv *scan_adv; struct bt_le_ext_adv *periodic_adv; int err; printk("Starting Periodic Advertising Demo\n"); // 初始化蓝牙子系统 err = bt_enable(NULL); if (err) { printk("Bluetooth init failed (err %d)\n", err); return; } // 创建可扫描扩展广告集 err = bt_le_ext_adv_create(BT_LE_EXT_ADV_SCAN_NAME, NULL, &scan_adv); if (err) { printk("Failed to create scan adv set (err %d)\n", err); return; } // 设置可扫描广告数据 err = bt_le_ext_adv_set_data(scan_adv, scan_ad, ARRAY_SIZE(scan_ad), NULL, 0); if (err) { printk("Failed to set scan adv data (err %d)\n", err); return; } // 启动可扫描广告(持续广播,确保被Android发现) err = bt_le_ext_adv_start(scan_adv, BT_LE_EXT_ADV_START_DEFAULT); if (err) { printk("Failed to start scan adv (err %d)\n", err); return; } printk("Scan advertising started.\n"); // 创建不可连接扩展广告集,用于绑定周期性广播 err = bt_le_ext_adv_create(BT_LE_EXT_ADV_NCONN_NAME, NULL, &periodic_adv); if (err) { printk("Failed to create periodic adv set (err %d)\n", err); return; } // 设置周期性广播数据 err = bt_le_ext_adv_set_data(periodic_adv, periodic_ad, ARRAY_SIZE(periodic_ad), NULL, 0); if (err) { printk("Failed to set periodic adv data (err %d)\n", err); return; } // 设置周期性广播参数 err = bt_le_per_adv_set_param(periodic_adv, BT_LE_PER_ADV_DEFAULT); if (err) { printk("Failed to set periodic adv params (err %d)\n", err); return; } // 启用周期性广播 err = bt_le_per_adv_start(periodic_adv); if (err) { printk("Failed to start periodic adv (err %d)\n", err); return; } // 周期性启停扩展广播(原逻辑保留) while (true) { printk("Start Periodic Extended Advertising..."); err = bt_le_ext_adv_start(periodic_adv, BT_LE_EXT_ADV_START_DEFAULT); if (err) { printk("Failed (err %d)\n", err); return; } printk("done.\n"); k_sleep(K_SECONDS(10)); printk("Stop Periodic Extended Advertising..."); err = bt_le_ext_adv_stop(periodic_adv); if (err) { printk("Failed (err %d)\n", err); return; } printk("done.\n"); k_sleep(K_SECONDS(10)); } }
说明
- 可扫描广告集持续广播设备名称,确保Android设备能正常发现设备;
- 周期性广播集独立运行,按设定周期发送自定义数据,避免参数冲突;
- 原代码中启停扩展广播的逻辑保留,可根据实际需求调整周期。
内容的提问来源于stack exchange,提问作者JayLin1119
相关产品推荐
相关产品推荐

