DA14531-蓝牙应用篇-主机BLE SCAN使用详解

本文详细介绍了DA14531单片机在物联网中作为BLE主机进行SCAN操作的步骤,包括参数配置、扫描功能的开启与关闭,以及广播包的解析。内容适合嵌入式系统、单片机和物联网固件工程师阅读,旨在帮助工程师快速掌握BLE主机SCAN功能的使用。
摘要由CSDN通过智能技术生成

下面是当前开发基于的版本

名称说明
SDK6.0.18.1182.1

# 本教程主要适用于低功耗产品,DA14531它体积小,非常适用于对体积敏感的设备,如无线可穿戴、传感器、助听器等设备。本文经过作者发大量时间来记录开发项目的调试方法,里面有许多笔者开发心得体会,旨在让读者工程师快速上手,减少摸索时间,提高软件质量。


读者对象
本文档主要适用以下工程师:
嵌入式系统工程师
单片机软件工程师
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功能,可以通过以下步骤进行操作:

  1. 停止当前BLE主机正在进行的任何连接操作。
  2. 调用BLE主机的停止扫描函数,调用user_ble_gap_stop_scan()函数。
  3. 等待一段时间回调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(&central_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
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小武编程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值