下面是当前开发基于的版本
名称 | 说明 |
---|---|
SDK | 6.0.18.1182.1 |
读者对象
本文档主要适用以下工程师:
嵌入式系统工程师
单片机软件工程师
IOT固件工程师
BLE固件工程师
前言
如何使用BLE SCAN功能?BLE SCAN是来搜索周围BLE从机设备的,本文是基于DA14531 SDK主机工程代码,首先讲述主机搜索器参数配置、接着是搜索器开启和关闭接口、再来是搜索器广播接口、广播数据格式和广播数据解析等。
1、主机SCAN参数配置
static const struct scan_configuration user_scan_conf ={
/// Operation code.
.code = GAPM_SCAN_ACTIVE,
/// Own BD address source of the device
.addr_src = GAPM_CFG_ADDR_PUBLIC,
/// Scan interval
.interval = MS_TO_BLESLOTS(300),
/// Scan window size
.window = MS_TO_BLESLOTS(250),
/// Scanning mode
.mode = GAP_OBSERVER_MODE,
/// Scan filter policy
.filt_policy = SCAN_ALLOW_ADV_ALL,
/// Scan duplicate filtering policy
.filter_duplic = SCAN_FILT_DUPLIC_DIS
};
code :主动搜索或者被动搜索,主动搜索可以扫描的广播Scan包
addr_src:地址类型,详见gapm_addr_type。公共或者私有的MAC地址
interval : 扫描间隔,单位625us
window: 扫描窗口,单位625us,窗口必须小于或等于扫描间隔
mode: 模式,详见gap_scan_mode,一般搜索器或观察者
filt_policy: 扫描过滤策略,详见scan_filter_policy。
filter_duplic: 扫描副本过滤策略,详见scan_dup_filter_policy。
BLE扫描参数配置是指在进行BLE扫描时,需要设置的参数,以获取适当的扫描结果。常见的参数配置包括:
扫描模式:包括主动扫描和被动扫描。在主动扫描模式下,设备会周期性发送扫描请求,以获取周围BLE设备的广播数据,而在被动扫描模式下,设备只能接收周围BLE设备的广播数据。
扫描窗口和间隔:扫描窗口是指设备在一个时间段内接收扫描响应的时间,扫描间隔是指设备在两次扫描之间的时间间隔。通常情况下,扫描窗口越大,可以接收到的广播数据越多,但会增加功耗和延长扫描时间。
超时时间:指设备在扫描一段时间后,如果没有扫描到目标设备,则会停止扫描。超时时间的设置应根据实际场景和需求进行调整。
扫描过滤:可以根据设备名称、服务UUID、设备地址等信息对扫描结果进行过滤,以减少不必要的扫描结果。
扫描功率:扫描功率的设置会影响设备在扫描时的信号强度,从而影响设备的扫描距离和功耗。
扫描模式:扫描模式有三种:低功耗模式、平衡模式、低延迟模式。低功耗模式能够在一定程度上节约功耗,但会降低扫描的成功率,而低延迟模式则能够提高扫描的成功率,但会消耗更多的功耗。
以上是常见的BLE扫描参数配置,根据需要和实际场景进行设置可以更好地满足设备的需求。
2、主机SCAN开启
构造搜索器开启函数
void user_ble_gap_start_scan(void)
{
struct gapm_start_scan_cmd *cmd = KE_MSG_ALLOC(GAPM_START_SCAN_CMD,
TASK_GAPM,TASK_APP,
gapm_start_scan_cmd);
//GAPM requested operation:
cmd->op.code = user_scan_conf.code;
cmd->op.addr_src = user_central_conf.addr_src;
cmd->op.state = 0;
// Scan interval
cmd->interval = user_scan_conf.interval;
// Scan window size
cmd->window = user_scan_conf.window;
// Scanning mode :
// - GAP_GEN_DISCOVERY: General discovery mode
// - GAP_LIM_DISCOVERY: Limited discovery mode
// - GAP_OBSERVER_MODE: Observer mode
cmd->mode = user_scan_conf.mode;
// Scan filter policy:
// - SCAN_ALLOW_ADV_ALL: Allow advertising packets from anyone
// - SCAN_ALLOW_ADV_WLST: Allow advertising packets from White List devices only
cmd->filt_policy = user_scan_conf.filt_policy;
// Scan duplicate filtering policy:
// - SCAN_FILT_DUPLIC_DIS: Disable filtering of duplicate packets
// - SCAN_FILT_DUPLIC_EN: Enable filtering of duplicate packets
cmd->filter_duplic = user_scan_conf.filter_duplic;
arch_printf( "scan conf[%d %d %d %d %d %d %d %d ]\r\n",
cmd->op.code,
cmd->op.addr_src,
cmd->op.state,
cmd->interval,
cmd->window,
cmd->mode,
cmd->filt_policy,
cmd->filter_duplic);
ke_msg_send(cmd);
// We are now connectable
ke_state_set(TASK_APP, APP_CONNECTABLE);
arch_printf( "SCAN START\r\n");
}
3、主机SCAN关闭
构造搜索器关闭函数
void user_ble_gap_stop_scan(void)
{
ble_stop_air_operation();
}
BLE主机的SCAN指的是扫描周围的BLE从机并获取其广播包信息的功能。要关闭BLE主机的SCAN功能,可以通过以下步骤进行操作:
- 停止当前BLE主机正在进行的任何连接操作。
- 调用BLE主机的停止扫描函数,调用user_ble_gap_stop_scan()函数。
- 等待一段时间回调app_on_scanning_completed(),确保BLE主机已经停止了扫描操作。
请注意,在关闭BLE主机的SCAN功能后,BLE主机将无法再扫描周围的BLE从机,也无法获取其广播包信息。如果需要再次使用BLE主机进行扫描操作,需要重新调用启动扫描函数来重启SCAN功能。
其他函数接口
.app_on_scanning_completed = user_on_scanning_completed,
.app_on_adv_report_ind = user_on_adv_report_ind,
4、主机SCAN广播包解析
文件位于user_ble_gap.c中,获取广播数据有多少种类型
uint8_t user_ble_gap_get_adv_num_elements(const uint8_t *data, uint8_t len)
{
uint8_t i;
//start with 1 to comply with BT SIG
uint8_t num_elements = 1;
i=data[0] + 1;
while( i<len)
{
num_elements++;
i += data[i] + 1;
}
return num_elements;
}
定义存放广播数据数组
ble_gap_adv_struct_t adv_data_structs[num_ad_elements];
解析广播包并存放到数组中
void user_ble_gap_parse_adv_data(uint8_t in_len, const uint8_t *in_data, ble_gap_adv_struct_t *ad_elements)
{
uint8_t i = 0;
uint8_t elem_count = 0;
while( i<in_len)
{
ad_elements[elem_count].len = in_data[i] - 1;
ad_elements[elem_count].type = in_data[i+1];
ad_elements[elem_count].data = (uint8_t *)in_data + i + 2;
i += in_data[i] + 1;
elem_count++;
}
}
打印用户需要的广播数据
void user_on_adv_report_ind(struct gapm_adv_report_ind const * adv_ind)
{
bool dev_found = false;
#if CONNECT_TO_PERIPHERAL == 1
bool conn_to_device = false;
struct bd_addr dev_addr = adv_ind->report.adv_addr;
#endif
uint8_t num_ad_elements = user_ble_gap_get_adv_num_elements(adv_ind->report.data,
adv_ind->report.data_len);
ble_gap_adv_struct_t adv_data_structs[num_ad_elements];
user_ble_gap_parse_adv_data(adv_ind->report.data_len, adv_ind->report.data, adv_data_structs);
uint8_t i;
for(i = 0 ; i < num_ad_elements; i++)
{
switch(adv_data_structs[i].type)
{
#if SCAN_FILTER == SCAN_FILTER_NONE
case GAP_AD_TYPE_FLAGS:
{
dev_found = true;
dbg_block_printf("GAP FLAGS: %s\r\n",
format_hex_string(adv_data_structs[i].data, adv_data_structs[i].len) );
break;
}
#endif
#if (SCAN_FILTER == SCAN_FILTER_NONE)
case GAP_AD_TYPE_MORE_16_BIT_UUID:
{
dev_found = true;
dbg_block_printf("INCOMP LIST 16-BIT SVC: %s\r\n",
format_hex_string(adv_data_structs[i].data, adv_data_structs[i].len) );
break;
}
#endif
#if (SCAN_FILTER == (SCAN_FILTER_NAME) || SCAN_FILTER == SCAN_FILTER_NONE)
case GAP_AD_TYPE_COMPLETE_NAME:
{
dev_found = true;
char local_name[adv_data_structs[i].len + 1];
format_adv_string(adv_data_structs[i].data,adv_data_structs[i].len, local_name);
dbg_block_printf("Device Local Name: %s\r\n", local_name);
#if CONNECT_TO_PERIPHERAL == 1
conn_to_device = memcmp(PERIPH_MATCH_DATA, adv_data_structs[i].data, PERIPH_MATCH_DATA_LEN) ? false : true;
#endif
break;
}
#endif
#if SCAN_FILTER == SCAN_FILTER_NONE
case GAP_AD_TYPE_TRANSMIT_POWER:
{
dev_found = true;
int8_t power;
memcpy(&power, adv_data_structs[i].data, 1);
dbg_block_printf("TX_POWER: %i dBm\r\n", power);
break;
}
#endif
#if (SCAN_FILTER == SCAN_FILTER_16_BIT_SVC_DATA || SCAN_FILTER == SCAN_FILTER_NONE)
case GAP_AD_TYPE_SERVICE_16_BIT_DATA:
{
dev_found = true;
uint16_t UUID;
memcpy(&UUID, adv_data_structs[i].data, sizeof(uint16_t));
dbg_block_printf("GAP_TYPE: SVC_DATA_16_BIT: UUID:%04X DATA:%s\r\n",
UUID, format_hex_string(adv_data_structs[i].data+2, adv_data_structs[i].len-2) );
break;
}
#endif
#if (SCAN_FILTER == SCAN_FILTER_MFG_DATA || SCAN_FILTER == SCAN_FILTER_NONE)
case GAP_AD_TYPE_MANU_SPECIFIC_DATA:
{
if((adv_data_structs[i].data[0] ==0x80) && (adv_data_structs[i].data[1] ==0x81))
{ dev_found = true;
dbg_block_printf("MFG_SPECIFIC_DATA: %s\r\n",
format_hex_string(adv_data_structs[i].data, adv_data_structs[i].len) );
}
break;
}
#endif
default:
{
#if SCAN_FILTER == SCAN_FILTER_NONE
dev_found = true;
dbg_block_printf("GAP Type: %02x, Data: %s\r\n", adv_data_structs[i].type,
format_hex_string(adv_data_structs[i].data, adv_data_structs[i].len) );
#endif
break;
}
}
}
if(dev_found)
{
uint8_t rssi_abs = 256 - adv_ind->report.rssi;
dbg_block_printf("RSSI: -%d\r\n", rssi_abs);
dbg_block_printf("BD_ADDR:%s \r\n---------------END_ADV-----------\r\n",
format_bd_address(&adv_ind->report.adv_addr) );
}
#if CONNECT_TO_PERIPHERAL
if(conn_to_device)
{
dbg_block_printf("Connecting to Device...\r\n", NULL);
central_app_env.connect_to_periph = true;
memcpy(¢ral_app_env.connect_to_addr, &dev_addr, sizeof(struct bd_addr));
central_app_env.connect_to_addr_type = adv_ind->report.adv_addr_type;
user_ble_gap_stop_scan();
}
#endif
}