十、GAP - arduino
1.1 背景
GAP(Generic Access Profile)位于主机协议栈的最顶层,用来定义BLE设备在待机或者连接状态中的行为,该Profile保证不同的Bluetooth产品可以互相发现对方并建立连接。GAP定义了:
蓝牙设备如何发现和建立与其他设备的安全/不安全连接;
处理一些一般模式的业务(如询问、命名和搜索)和一些安全性问题(如担保) ;
处理一些有关连接的业务(如链路建立、信道和连接建立);
下图直观地展示了GAP处于蓝牙协议栈的层次关系。
GAP Role |
Description |
BROADCASTER |
A device that only sends advertising events. |
OBSERVER |
A device that only receives advertising events. |
PERIPHERAL |
A device that accepts the establishment of an LE physical link using the connection establishment procedure. |
CENTRAL |
A device that supports the Central role initiates the establishment of a physical connection. |
1.2 广播(advertising)
蓝牙设备通过在广播数据包中发送广播包(PDUS)来允许其他设备(scanners)发现并连接它,这些广播数据最大长度支持31字节的可配置数据,针对到来的scan request,在scan response中,蓝牙设备(Broadcaster)可以发送额外的31字节数据。GAP一共定义了四种不同类型的广播数据包类型:
Advertising PDU |
Description |
Max Adv Data Len |
Max Scan Res Len |
Allow Connect |
ADV_IND |
用于发送可连接,非定向的广播 |
31 bytes |
31 bytes |
Yes |
ADV_DIRECT_IND |
用于发送可连接,定向广播 |
N/A |
N/A |
Yes |
ADV_SCAN_IND |
用于发送扫描,非定向广播 |
31 bytes |
31 bytes |
No |
ADV_NONCONN_IND |
用于发送非可连接,非定向广播 |
31 bytes |
N/A |
No |
在上面的表格中,除了直接广播(ADV_DIRECT_IND),其余广播PDU携带广播数据报文最大长度是31字节。对于直接广播来说,其只携带了6字节的目标设备地址,该目标设备地址的预期被连接的设备地址。另外,所有的广播类型允许对于到来的scan response做出scan response,除了直接广播(ADV_DIRECT_IND)和非连接广播(ADV_NONCONN_IND),更多信息,请参考BT Core Spec V4.2 Vol. 6, Part B, Section 4.4。
下面的表格总结了广播过程中可以配置的参数:
Parameter |
Description |
Range |
Advertising Interval |
Time between the start of two consecutive advertising events |
20ms to 10.24s |
Advertising Types |
Different PDUs are sent for different types of advertising |
Connectable undirected, connectable directed, scannable directed, non-connectable |
Advertising Channels |
Advertising packets are sent on three channels |
Different combinations of channels 37, 38 and 39. |
蓝牙设备有三个特定的广播通道,分别是:通道37 (2402 MHz), 通道 38 (2426 MHz), 和通道39 (2480 MHz)。这三个通道选择能够将与WIFI干扰降低到最小,下图展示了一个广播事件,该事件使用了全部三个广播通道。
需要注意的是:在这三个广播通道上,相同的数据被传递,GAP允许设备只广播在某一个或者两个通道上。在更少的通道上进行广播,意味这激着更小的能量消耗。但是,会降低被对端设备侦听到的概率。
1.3 扫描(Scanning)
当设备处于连接状态时,其或可以发送广播包对外广播其存在,或扫描周边正在广播的设备。扫描周边设备的过程称为设备发现。GAP定义了两种扫描类型——主动(active)和(被动),两者的区别是,前者可以发送scan request从广播者获取更多额外的信息,而后者仅仅能够接收广播者的广播数据。如无特殊语境,“发现”和“扫描”两个术语讲不加区分,交替使用。下图演示了一个设备对广播者发送扫描请求以及广播者做出应答的时序图。
在扫描过程中,你需要熟悉一些扫描参数,每个参数都有特定的范围,下表罗列了这些参数的语义以及可配置范围。
Parameter |
Description |
Range |
Scan Interval |
Interval between the start of two consecutive scan windows |
10ms to 10.24s |
Scan Window |
The duration in which the Link Layer scans on one channel |
10ms to 10.24s |
Scan Duration |
The duration in which the device stays in the scanning state |
10ms to infinity |
GAP并没有规定扫描过程中,对三个广播通道的扫描顺序,扫描者在每个扫描间隔内,在一个扫描窗口期间内,依次扫描37,38,39通道。下图形象的展示了这些扫描参数的内涵:
1.4 模式/状态
一、发现模式(Discoverablity Modes), 对应于inquiry(inquiry request/response)
– Non-discoverable Mode: 不响应inquiry;
– Discoverable Mode: 是下面两种模式的总称, 设备进入INQUIRY_SCAN状态, 响应inquiry;
– Limited discoverable Mode: 响应LIAC inquiry;
– General discoverable Mode: 响应GIAC inquiry;
二、连接模式(Connectability Modes), 对应于paging(paging request/response)
– Non-connectable Mode: 不响应paging;
– Connectable Mode: 设备进入PAGE_SCAN状态, 响应paging;
三、配对模式(Bondable Modes), 对应于bonding/paring, 需要和SSP(Secure Simple Pairing)配合使用
– Non-bondable Mode: 设备不可接受来自远端设备的绑定/配对请求;
– Bondable Mode: 设备可接受来自远端设备的绑定/配对请求;
1.5 ADK信号时序
以ADK4.4为例,简单的罗列了设备在初始化时,如何进行扫描,广播初始化的,具体细节请见ADK源代码。
/*Power on -> start scan -> start(fast)advertising -> start(slow)advertising (if not connected)*/
sink_ble.c
sinkBleInitialiseDevice()–>
ConnectionDmBleAddTdlDevicesToWhiteListReq(BLE_ONLY) /* Setup whitelist*/
sinkGattInitInitialiseDevice(); /* Initialise GATT */
sinkBleGapInitialise();–>
sinkBleGapInitGapConnFlag();
gapSetAdvSpeed(ble_gap_adv_speed_fast);
sinkBlePowerOnEvent()–>
sinkBleGapEvent(ble_gap_event_power_on)–>
gapStateIdleHandleEvent()–>
sinkBleSetGapState(ble_gap_state_scanning_advertising)
sinkBleCheckNoConnectionsEvent()–>BLE_INTERNAL_MESSAGE_EVENT_NO_CONNECTIONS
–>sinkBleGapEvent(ble_gap_event_no_connections)
gapStateScanAdvHandleEvent(ble_gap_event_no_connections)–>
sinkBleGapStartReadLocalName(ble_gap_read_name_advertising)
gapStartScanning(TRUE)–>
bleStartScanning()–>
ConnectionDmBleSetScanParametersReq()–>DM_HCI_ULP_SET_SCAN_PARAMETERS_REQ
ConnectionDmBleSetScanEnable(TRUE)
bluestack_handler.c
DM_HCI_READ_LOCAL_NAME_CFM–>
connectionHandleLocalNameComplete()–>
localNameComplete()–>CL_DM_LOCAL_NAME_COMPLETE
sinkBleHandleCLMessage()–>CL_DM_LOCAL_NAME_COMPLETE–>
sinkBleGapReadLocalNameComplete()–>
bleSetupAdvertisingData(ble_gap_read_name_advertising)–>
ConnectionDmBleSetAdvertisingDataReq()–>
DM_HCI_ULP_SET_ADVERTISING_DATA_REQ
DM_HCI_ULP_SET_ADVERTISING_DATA_CFM–>
connectionHandleDmBleSetAdvertisingDataCfm()–>CL_DM_BLE_SET_ADVERTISING_DATA_CFM–>
bleHandleSetAdvertisingData()–>
sinkBleGapEvent(ble_gap_event_set_advertising_complete)
gapStartAdvertising();–>
gapStartAdvertising()–>
sinkBleGetAdvertisingParameters()
sinkBleSetAdvertisingParamsDefault()
sinkGattManagerStartAdvertising()–>
sinkEnableGattConnectable()–>
enablePageScan(sink_scan_connectable_gatt)
gapStartFastAdvTimer()–>
MessageSendLater(BLE_INTERNAL_MESSAGE_FAST_ADV_TIMER)
BLE_INTERNAL_MESSAGE_FAST_ADV_TIMER–>
sinkBleGapEvent(ble_gap_event_fast_adv_timeout)–>
gapStateScanAdvHandleEvent()–>
gapStopAdvertising(ble_gap_adv_speed_slow)–>
gapSetAdvSpeed(new_speed) /* First stop fast advertising*/
sinkGattManagerStopAdvertising()–> /* Then start slow advertising */
GattManagerCancelWaitForRemoteClient()
GATT_MANAGER_CANCEL_REMOTE_CLIENT_CONNECT_CFM–>
handleGattManagerCancelRemoteClientConnectCfm()–>
sinkBleCancelAdvertisingEvent()–>
sinkBleGapEvent(ble_gap_event_cancelled_advertising)–>
gapStateScanAdvHandleEvent()–>
sinkBleGapStartReadLocalName(ble_gap_read_name_advertising)