一、发起扫描scan
在收到GAPM_SET_DEV_CONFIG消息后,开始回调void app_set_dev_config_complete_func(void)函数。 该函数在设备配置完成时调用。主机和从机从此处开始分道扬镳,各自执行不同的任务代码。
主机是ke_state_set()函数把状态设置为可连接,然后发起扫描app_start_scanning()。
从机是ke_state_set()函数把状态设置为初始化数据库,然后建立服务和特征数据库,完成后再发起广播。
当然,以上这些操作并不是直接就执行了,也是通过消息传递和对应的回调函数来处理。
可以看出,主机作为扫描者,并不需要创建服务和特征数据库。
主机的(扫描者,连接后是Client):
void app_set_dev_config_complete_func(void)
{
ke_state_set(TASK_APP, APP_CONNECTABLE);
#if BLE_INTEGRATED_HOST_GTL
struct app_ready_ind *ind = KE_MSG_ALLOC(APP_READY_IND, TASK_GTL, TASK_APP, app_ready_ind);
ke_msg_send(ind);
#else
app_start_scanning();//发起扫描
#endif //BLE_INTEGRATED_HOST_GTL
return;
}
下面是从机的(广播者,连接后是server)
void app_set_dev_config_complete_func(void)
{
ke_state_set(TASK_APP, APP_DB_INIT);
if (app_db_init())
{
// No service to add in the DB -> Start Advertising
app_adv_start();//发起广播
}
#if BLE_INTEGRATED_HOST_GTL
struct app_ready_ind *ind = KE_MSG_ALLOC(APP_READY_IND, TASK_GTL, TASK_APP, app_ready_ind);
ke_msg_send(ind);
#endif //BLE_INTEGRATED_HOST_GTL
return;
}
BLE扫描分为2种方式,主动扫描和被动扫描。主动扫描和被动扫描的区别在于:主动扫描可以获得广播数据和扫描回应数据。而被动扫描只能获得广播数据。在实际项目中,如果需要获得对方对扫描的响应,需要主机设置为主动扫描。如果仅仅是需要广播数据则设置为被动扫描。被动扫描是完全只监听3个广播信道。主动扫描是监听3个广播信道收到某个设备的广播数据后,主动发出信息询问该设备。
app_start_scanning(void)函数初始化scan参数结构体,然后发出扫描请求消息。
//向GAP任务管理器发出扫描设备请求消息
void app_start_scanning(void)
{
#if !BLE_INTEGRATED_HOST_GTL
periph_found = 0;
#endif
GPIO_SetInactive(ADV_LED_PORT,ADV_LED_PIN);
struct gapm_start_scan_cmd *msg = KE_MSG_ALLOC(GAPM_START_SCAN_CMD, TASK_GAPM, TASK_APP, gapm_start_scan_cmd);
//填充扫描参数
msg->mode = GAP_GEN_DISCOVERY;//普通发现模式
msg->op.code = GAPM_SCAN_PASSIVE;//被动扫描
msg->op.addr_src = GAPM_PUBLIC_ADDR;//公用地址
msg->filter_duplic = SCAN_FILT_DUPLIC_EN;//开启重复过滤
msg->interval = 10;//扫描间隔
msg->window = 5;//扫描窗口
ke_msg_send(msg);//发出请求扫描消息
return;
}
/// Set scan mode Command扫描参数结构体
struct gapm_start_scan_cmd
{
/// GAPM requested operation:
/// - GAPM_SCAN_ACTIVE: Start active scan operation
/// - GAPM_SCAN_PASSIVE: Start passive scan operation
struct gapm_air_operation op;
/// Scan interval
uint16_t interval;
/// Scan window size
uint16_t window;
/// Scanning mode :
/// - GAP_GEN_DISCOVERY: General discovery mode
/// - GAP_LIM_DISCOVERY: Limited discovery mode
/// - GAP_OBSERVER_MODE: Observer mode
uint8_t 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
uint8_t filt_policy;
/// Scan duplicate filtering policy:
/// - SCAN_FILT_DUPLIC_DIS: Disable filtering of duplicate packets
/// - SCAN_FILT_DUPLIC_EN: Enable filtering of duplicate packets
uint8_t filter_duplic;
};
/// Air operation default parameters
struct gapm_air_operation
{
/// Operation code.
uint8_t code;
/** Own BD address source of the device:
* - GAPM_PUBLIC_ADDR: Public Address
* - GAPM_PROVIDED_RND_ADDR: Provided random address
* - GAPM_GEN_STATIC_RND_ADDR: Generated static random address
* - GAPM_GEN_RSLV_ADDR: Generated resolvable private random address
* - GAPM_GEN_NON_RSLV_ADDR: Generated non-resolvable private random address
* - GAPM_PROVIDED_RECON_ADDR: Provided Reconnection address (only for GAPM_ADV_DIRECT)
*/
uint8_t addr_src;
/// Dummy data use to retrieve internal operation state (should be set to 0).
uint16_t state;
/// Duration of resolvable address before regenerate it.
uint16_t renew_dur;
/// Provided own static private random address (addr_src = 1 or 5)
struct bd_addr addr;
};
发现模式(Discovery Mode)
Non-discoverable mode(不可发现模式)
Limited discoverable mode(受限发现模式)
General discoverable mode(普通发现模式)
Broadcast mode(广播器模式)
发现模式与广播模式相互关联,“不可发现模式”下,只能选择Scannable undirected advertising和Non-connectable undirected advertising两种广播模式,即这种发现模式下,BLE设备不可以被连接。“广播器模式”下也只能选择不可被连接的广播模式。“受限发现模式”和“普通发现模式”下,可以任意选择广播模式。
第一种不可发现模式,使广播设备不被发现,参考 。注意,这里不被发现是指不被执行“普通发现规程”的Central设备发现。如果Central设备执行观察规程(obverving procedure),仍然能够看到广播信号。而手机APP等软件默认为观察规程,所以假如使用手机BLE扫描APP来测试,依旧可以看到设为“不可发现模式”的广播设备。正确的测试方法是使用CySmart(PC),将主机的扫描规程设置为“普通”或“受限”,而不是“观察者”,就能够观察到正确现象。
第二种受限发现模式,这里“受限”是指受时间限制。与普通发现模式相比,这个模式好像只是强制要求广播超时参数,其他相同。但是二者的“含义”是不同的,一个受限发现的设备,即有超时限制的设备,给Central传达一个消息,我很快就不行了,要连接应赶紧。所以Central设备如果发现了多个广播设备,在显示设备列表的时候,应该有意识的将受限发现模式的设备放在前面,而普通发现模式的设备放在后面。广播数据中有两个标志位:LE General Discovery Mode和LE Limited Discovery Mode,分别设置这两个标志位即可设置发现模式。
第三种为普通发现模式,可以选择任意的广播模式,然后自由处理各项参数。
发现规程(Discovery Procedure)
**Limited discovery:受限发现规程
General discovery:普通发现规程
Observation discovery:观察器规程**
前两个发现规程,基本上是为了匹配上面前三种发现模式而设计的。如果Central使用普通发现规程,则能够发现“受限发现模式”和“普通发现模式”的设备,不能发现“不可发现模式”设备。如果Central使用受限发现规程,则仅能够发现“受限发现模式”的设备。对于观察期规程,则能发现各种发现模式下的广播设备。
扫描参数设置里:
msg->mode = GAP_GEN_DISCOVERY;//普通发现模式
msg->op.code = GAPM_SCAN_PASSIVE;//被动扫描
就是执行普通发现规程,使用被动扫描方式。
二、扫描完成处理scan_complete
当主机扫描到一个广播信号后,会发出GAPM_ADV_REPORT_IND事件。该事件的默认回调函数是gapm_adv_report_ind_handler( );
{GAPM_ADV_REPORT_IND, (ke_msg_func_t)gapm_adv_report_ind_handler}, 下面看一下该函数:
/**
****************************************************************************************
* @brief Handles Reception of advertise indication message during scanning.
****************************************************************************************
*/
int gapm_adv_report_ind_handler(ke_msg_id_t msgid,
struct gapm_adv_report_ind *param,
ke_task_id_t dest_id,
ke_task_id_t src_id);
{
app_adv_report_ind_func(param);//只处理gapm_adv_report_ind消息,msgid、dest_id、src_id全部舍去了。
return (KE_MSG_CONSUMED);
}
这个GAP层的函数只是简单调用了用户APP层的函数app_adv_report_ind_func( )。可见该函数只是一个接口,具体实现功能留在APP层。先看一下gapm_adv_report_ind消息。
//广播或扫描报告 消息事件
/// Advertising or scanning report information event
struct gapm_adv_report_ind
{
/// Advertising report structure
struct adv_report report;
};
该结构体下定义了 adv_report report结构体。
///Advertising report structure
struct adv_report
{
///Event type:四种类型广播包
/// - ADV_CONN_UNDIR: Connectable Undirected advertising
/// - ADV_CONN_DIR: Connectable directed advertising
/// - ADV_DISC_UNDIR: Discoverable undirected advertising
/// - ADV_NONCONN_UNDIR: Non-connectable undirected advertising
uint8_t evt_type;
///Advertising address type: public/random
uint8_t adv_addr_type;
///Advertising address value
struct bd_addr adv_addr;
///Data length in advertising packet
uint8_t data_len;
///Data of advertising packet
uint8_t data[ADV_DATA_LEN];
///RSSI value for advertising packet
uint8_t rssi;
};
adv_report report结构体定义了扫描到的广播包的参数。
Event type参数是事件类型,分为四种:可连接非定向、可连接定向、可发现非定向、不可连接非定向。它们对应四种广播类型:ADV_IND——通用广播、ADV_DIRECT_IND——定向连接广播、ADV_NONCONN_IND——不可连接广播、ADV_SCAN_IND——可扫描广播。
了解了gapm_adv_report_ind消息后,再来看app_adv_report_ind_func( )函数:前面的#if BLE_INTEGRATED_HOST_GTL是预定义使用外部处理器的情况。我们只看后面#else部分:
void app_adv_report_ind_func(struct gapm_adv_report_ind *param)
{
#if BLE_INTEGRATED_HOST_GTL
struct app_ext_adv_report_ind *cmd = KE_MSG_ALLOC(APP_EXT_ADV_REPORT_IND,
TASK_GTL, TASK_APP, app_ext_adv_report_ind);
cmd->addr_type = param->report.adv_addr_type;
memcpy ((void *)cmd->addr, (void *)¶m->report.adv_addr, BD_ADDR_LEN);
// Send the message
ke_msg_send(cmd);
#else
periph_found = 1;//发现广播者,置标志位
periph_addr_type = param->report.adv_addr_type;//广播地址类型,public或随机
memcpy ((void *)periph_addr, (void *)¶m->report.adv_addr, BD_ADDR_LEN);//复制广播者的蓝牙设备地址6*8bit,48位
//cancel scan procedure,发出消息请求取消扫描任务,这里是为了释放内存,准备建立连接了
struct gapm_cancel_cmd *cmd = KE_MSG_ALLOC(GAPM_CANCEL_CMD,
TASK_GAPM, TASK_APP,
gapm_cancel_cmd);//GAPM_CANCEL_CMD:Cancel ongoing operation,取消正在进行的任务
cmd->operation = GAPM_CANCEL;
// Send the message
ke_msg_send(cmd);//发出取消scan的消息
#endif
}
该APP函数主要干了三件事情:一是置一个标志位periph_found,说明扫描到一个广播信号了。第二是复制了广播者的BD地址。第三是发消息申请取消扫描。这里是为建立连接做准备。当然,开发者可以根据不同的需求功能,自己写这个函数。
void app_scanning_completed_func(void)
{
#if BLE_INTEGRATED_HOST_GTL //使用外部MCU时
/* EXT Processor - TASK APP over UART */
/* send scan completed indication to external app*/
void *cmd = ke_msg_alloc( APP_EXT_SCAN_CMP_IND, TASK_GTL, TASK_APP, 0);
// Send the message
ke_msg_send(cmd);
#else
if (periph_found)//发现广播者了
{
app_connect(periph_addr_type, periph_addr, 80);
}
else
{
app_start_scanning();//再次配置参数并扫描
}
#endif
}