Generic Access Profile (GAP)¶
The GAP layer of the Bluetooth Low Energy protocol stack is responsible for connection functionality. This layer handles the access modes and procedures of the device including device discovery, link establishment, link termination, initiation of security features, and device configuration. See GAP State Diagram. for more details.
Based on the role for which the device is configured, GAP State Diagram. shows the states of the device. The following describes these states.
Standby: The device is in the initial idle state upon reset.
Advertiser: The device is advertising with specific data letting any scanner/initiator devices know its existence. (This advertisement contains the device address and can contain some additional data such as the device name).
Scanner: When receiving the advertisement, the scanning device can send a scan request to the advertiser. The advertiser responds with a scan response. This process is called device discovery. The scanning device is aware of the advertising device and can initiate a connection with it.
Initiator: When initiating, the initiator must specify a peer device address to which to connect. If an advertisement is received matching that address of the peer device, the initiating device then sends out a request to establish a connection (link) with the advertising device with the connection parameters described in GAP Connection State.
Peripheral/Central: When a connection is formed, the device functions as a Peripheral if it is the advertiser and a Central if it is the initiator.
Bluetooth devices operate in one or more GAP roles based on the application use case. The GAP roles utilize one or more of the GAP states. Based on this configuration, many Bluetooth Low Energy protocol stack events are handled directly by the main application task and sometimes routed from the GAP Bond Manager. The application can register with the stack to be notified of certain events.
GAP Roles¶
Based on the configuration of the device, the GAP layer always operates in one of four (4) roles as defined by the specification:
Broadcaster - The device is an advertiser that is non connectable.
Observer - The device scans for advertisements but cannot initiate connections.
Peripheral - The device is an advertiser that is connectable and operates as Peripheral in a single link-layer connection.
Central - The device scans for advertisements and initiates connections and operates as a Central in a single or multiple link-layer connections.
The Bluetooth Core Specifications Version 5.3 allows for certain combinations of multiple roles, which are
supported by the Bluetooth Low Energy protocol stack.
The following modules will modify and examine the basic_ble
example project
included in the SDK. For configuration of the Bluetooth Low Energy stack
features, see Developing with SDK CC23xx and Stack Configurations.
Note
Bluetooth 5 also introduces new features including Extended Advertising. Bluetooth 5 supports legacy advertising maintaining backwards compatibility with previous versions. The following sections will describe how to operate the device for various GAP roles along with how to use the Extended Advertising features.
Warning
CC23xx devices do not support extended advertisement yet
The following sections will describe how to use the LE Advertising Extension feature. As its name suggests, the amount of advertising data that can be sent over the air has been extended from 31 bytes to 1650 bytes. Also multiple advertising sets can be created using a mix of legacy and extended advertisements. Extended advertisements introduces use the secondary advertising channels which uses the data channels to send extensive data in addition to the primary advertising channels (Ch. 37, 38, and 39) used in legacy advertisement.
GAP Constraints¶
GAP Scanner can only scan one PHY per scanning cycle.
The data length of the advertising data and the scan response data for Extended Advertising is limited to 1650 bytes. The connectable undirected advertising data is limited to 212 bytes. The connectable directed advertising data is limited to 206 bytes. For more information, refer to the Host Advertising Data section ([Vol 6], Part B, Section 2.3.4.9) of the Bluetooth Core Specifications Version 5.3.
GAP Advertiser¶
The application and profiles can directly call GAP API functions to perform Bluetooth Low Energy-related functions such as advertising or connecting. The GAP layer functionality is mostly defined in library code. The function headers can be found in gap.h. The advertising specific implementation is found in gap_advertiser.h. Most of these functions can be called directly.
Note
Here we will use the BLEAppUtil framework as example code which abstracts the direct gap api interaction, therefore, you may not see the actual gap APIs from the following code snippets.
The GAP Advertiser module lets you create advertisement sets. The application can then enable and disable the sets. This way, the application can quickly change the advertisement parameters and advertisement data. For example, an application can create an advertisement set for legacy advertising and one for long range advertising. If both sets are enabled, the application advertises both on the primary advertising channels with legacy advertising, and on a LE Coded PHY with the long range set. With long range advertising, more advertising data can be sent and connections can be formed from larger distances compared to legacy advertising.
Note
The advertisement data is de-coupled from the advertisement sets, so multiple sets can use the same advertisement data.
Multiple active advertising sets can set different advertising intervals. The
figure below illustrate how two advertising sets with different intervals are
scheduled. The intervals do not have to be multiples of each other. The
advertising interval can be set in the .primIntMin
and .primIntMax
Advertisement Params (GapAdv_params_t
) member before the advertisement set
is enabled or during advertising as described in Changing advertising parameters.
1 advSetInitParamsSet_1.advParam->primIntMin = 160; //100ms 2 advSetInitParamsSet_1.advParam->primIntMax = 160; //100ms 3 4 status = BLEAppUtil_initAdvSet(&peripheralAdvHandle_1, &advSetInitParamsSet_1);
Following shows an alternative for changing the advertising interval after the advertising set is initialized by using gap_advertiser.h.
1 status = BLEAppUtil_initAdvSet(&peripheralAdvHandle_1, &advSetInitParamsSet_1); 2 3 uint32_t advInt = 160; //160*0.625ms = 100ms 4 5 GapAdv_setParam(peripheralAdvHandle_1, GAP_ADV_PARAM_PRIMARY_INTERVAL_MIN, &advInt); 6 GapAdv_setParam(peripheralAdvHandle_1, GAP_ADV_PARAM_PRIMARY_INTERVAL_MAX, &advInt);
The following list describes how to configure the GAP layer for advertising. The basic_ble example project will be used as an example.
Note
The advertisement set creation can be done using SysConfig.
Create Advertisement¶
Create Advertisement set with GapAdv_create(). This function expects application to register a callback, pass in advertising parameter and allocate a handle for the associated advertising set.
The first step is to create a callback function. By using BLEAppUtil framework, the advertising callback function is already created.
1 void BLEAppUtil_advCB(uint32_t event, GapAdv_data_t *pBuf, uint32_t *arg) 2 { 3 BLEAppUtil_AdvEventData_t *pData = BLEAppUtil_malloc(sizeof(BLEAppUtil_AdvEventData_t)); 4 5 if (pData) 6 { 7 pData->event = event; 8 pData->pBuf = pBuf; 9 pData->arg = arg; 10 11 // Enqueue the event 12 if (BLEAppUtil_enqueueMsg(BLEAPPUTIL_EVT_ADV_CB_EVENT, pData) != SUCCESS) 13 { 14 BLEAppUtil_free(pData); 15 } 16 } 17 }
Next we need to create advertising parameters with GapAdv_params_t struct which by default is generated using SysConfig tool.
1 GapAdv_params_t advParams1 = { 2 .eventProps = GAP_ADV_PROP_CONNECTABLE | GAP_ADV_PROP_LEGACY | GAP_ADV_PROP_SCANNABLE, 3 .primIntMin = 160, 4 .primIntMax = 160, 5 .primChanMap = GAP_ADV_CHAN_ALL, 6 .peerAddrType = PEER_ADDRTYPE_PUBLIC_OR_PUBLIC_ID, 7 .peerAddr = { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa }, 8 .filterPolicy = GAP_ADV_AL_POLICY_ANY_REQ, 9 .txPower = GAP_ADV_TX_POWER_NO_PREFERENCE, 10 .primPhy = GAP_ADV_PRIM_PHY_1_MBPS, 11 .secPhy = GAP_ADV_SEC_PHY_1_MBPS, 12 .sid = 0 13 };
The Tx Power of legacy advertisements cannot be individually set.
Lastly, we need to create a handle for this advertising set.
//! Store handle needed for each advertise set uint8 peripheralAdvHandle_1;
Now we have all the pieces ready, let’s see how the BLEAppUtil framework creates an advertising set.
// Initialize Advertising Set status = BLEAppUtil_initAdvSet(&peripheralAdvHandle_1, &advSetInitParamsSet_1);
1 bStatus_t BLEAppUtil_initAdvSet(uint8 *advHandle, const BLEAppUtil_AdvInit_t *advInitInfo) 2 { 3 return bleStk_initAdvSet(BLEAppUtil_advCB, advHandle, GAP_ADV_EVT_MASK_ALL, 4 advInitInfo->advParam, advInitInfo->advDataLen , 5 advInitInfo->advData, advInitInfo->scanRespDataLen, 6 advInitInfo->scanRespData); 7 }
1 bStatus_t bleStk_initAdvSet(pfnBleStkAdvCB_t advCallback, uint8_t *advHandle, 2 GapAdv_eventMaskFlags_t eventMask, 3 GapAdv_params_t *advParams, 4 uint16_t advDataLen ,uint8_t advData[], 5 uint16_t scanRespDataLen, uint8_t scanRespData[]) 6 { 7 bStatus_t status; 8 9 #ifdef ERPC_SERVER 10 // keep the remote eRPC app callback 11 remote_bleApp_GapAdvCb = advCallback; 12 13 // Create Advertisement set and assign handle 14 status = GapAdv_create((pfnGapCB_t)local_bleApp_GapAdvCb, advParams, advHandle); 15 #else 16 status = GapAdv_create((pfnGapCB_t)advCallback, advParams, advHandle); 17 #endif 18 if (status != SUCCESS) 19 { 20 return status; 21 } 22 // ..... 23 // ..... 24 }
Set Advertiser’s virtual address set with GapAdv_setVirtualAdvAddr(). The handle for the advertisement set, returned from GapAdv_create(), can also be used to create a custom private address for that advertisement set. This allows different advertising sets to have different addresses.
Warning
This is only applicable for legacy non-connectable and non-scannable advertising sets and it is optional.
Load Advertisement and Scan Response data. Advertising and scan response data is decoupled from the advertising set. That is, it is possible for multiple advertising sets to use the same memory for advertising/scan response data. An advertising set could also use the same memory for its advertising and scan response data. (Note that this requires diligence as there are some data types that are only allowed once in the advertisement and scan response data. See the Bluetooth Core Specification Supplement (CSSv7) for specifics.)
If your advertisement set is not scannable, you can of course skip the last step (load scan response data). Example advertising and scan response data is shown in Listing 37. and Listing 38.
1 bStatus_t bleStk_initAdvSet(pfnBleStkAdvCB_t advCallback, uint8_t *advHandle, 2 GapAdv_eventMaskFlags_t eventMask, 3 GapAdv_params_t *advParams, 4 uint16_t advDataLen ,uint8_t advData[], 5 uint16_t scanRespDataLen, uint8_t scanRespData[]) 6 { 7 bStatus_t status; 8 //... 9 //... 10 11 // Load advertising data for set that is statically allocated by the app 12 status = GapAdv_loadByHandle(*advHandle, GAP_ADV_DATA_TYPE_ADV, advDataLen, advData); 13 14 if (status != SUCCESS) 15 { 16 return status; 17 } 18 19 // Load scan response data for set that is statically allocated by the app 20 if (scanRespData != NULL) 21 { 22 status = GapAdv_loadByHandle(*advHandle, GAP_ADV_DATA_TYPE_SCAN_RSP, scanRespDataLen, scanRespData); 23 if (status != SUCCESS) 24 { 25 return status; 26 } 27 } 28 //.... 29 //.... 30 }
uint8_t advData1[] = { 0x02, GAP_ADTYPE_FLAGS, GAP_ADTYPE_FLAGS_BREDR_NOT_SUPPORTED | GAP_ADTYPE_FLAGS_GENERAL, 0x03, GAP_ADTYPE_16BIT_MORE, LO_UINT16(0xfff0), HI_UINT16(0xfff0), };
uint8_t scanResData1[] = { 0x12, GAP_ADTYPE_LOCAL_NAME_COMPLETE, 'B', 'a', 's', 'i', 'c', ' ', 'B', 'L', 'E', ' ', 'p', 'r', 'o', 'j', 'e', 'c', 't', 0x02, GAP_ADTYPE_POWER_LEVEL, 0, 0x05, GAP_ADTYPE_PERIPHERAL_CONN_INTERVAL_RANGE, LO_UINT16(80), HI_UINT16(80), LO_UINT16(104), HI_UINT16(104), };
Set events of interest to send to application. The events will be sent to the callback function given in GapAdv_create().
1 bStatus_t BLEAppUtil_initAdvSet(uint8 *advHandle, const BLEAppUtil_AdvInit_t *advInitInfo) 2 { 3 // BLEAppUtil_advCB is the callback function 4 // The mask is to register all GAP_ADV_EVT_MASK_ALL 5 // That means in BLEAppUtil 6 return bleStk_initAdvSet(BLEAppUtil_advCB, advHandle, GAP_ADV_EVT_MASK_ALL, 7 advInitInfo->advParam, advInitInfo->advDataLen , 8 advInitInfo->advData, advInitInfo->scanRespDataLen, 9 advInitInfo->scanRespData); 10 }
As you can see that the registered events for advertising callback is all. However, this only applies to the BLEAppUtil layer. In the application layer, you can further refine it to what you are interested in knowing. You can also just update the GAP_ADV_EVT_MASK_ALL to something else.
If you don’t want to modify the BLEAppUtil Framework, then you can use
BLEAppUtil_registerEventHandler
to register the advertising events of interest on top of the BLEAppUtil framework. The aforementioned API expectsBLEAppUtil_EventHandler_t
struct.1 BLEAppUtil_EventHandler_t peripheralAdvHandler = 2 { 3 .handlerType = BLEAPPUTIL_GAP_ADV_TYPE, 4 .pEventHandler = Peripheral_AdvEventHandler, 5 .eventMask = BLEAPPUTIL_ADV_START_AFTER_ENABLE | 6 BLEAPPUTIL_ADV_END_AFTER_DISABLE 7 };
The first parameter is highlighted in Listing 41.. Here we are interested in advertising, thus we choose BLEAPPUTIL_GAP_ADV_TYPE. The second one is declaration of the function name, Listing 42., of where/how the events will be processed. Lastly, it takes the event masks Listing 43. which are mapped to the GAP Event masks.
1 // Event Handler Types 2 typedef enum BLEAppUtil_eventHandlerType_e 3 { 4 BLEAPPUTIL_GAP_CONN_TYPE, 5 BLEAPPUTIL_CONN_NOTI_TYPE, 6 BLEAPPUTIL_GAP_ADV_TYPE, 7 BLEAPPUTIL_GAP_SCAN_TYPE, 8 BLEAPPUTIL_GAP_PERIODIC_TYPE, 9 BLEAPPUTIL_GATT_TYPE, 10 BLEAPPUTIL_PASSCODE_TYPE, 11 BLEAPPUTIL_PAIR_STATE_TYPE, 12 BLEAPPUTIL_L2CAP_DATA_TYPE, 13 BLEAPPUTIL_L2CAP_SIGNAL_TYPE, 14 BLEAPPUTIL_HCI_DATA_TYPE, 15 BLEAPPUTIL_HCI_GAP_TYPE, 16 BLEAPPUTIL_HCI_SMP_TYPE, 17 BLEAPPUTIL_HCI_SMP_META_TYPE, 18 BLEAPPUTIL_HCI_CTRL_TO_HOST_TYPE 19 } BLEAppUtil_eventHandlerType_e;
1void Peripheral_AdvEventHandler(uint32 event, BLEAppUtil_msgHdr_t *pMsgData) 2{ 3 switch(event) 4 { 5 case BLEAPPUTIL_ADV_START_AFTER_ENABLE: 6 { 7 MenuModule_printf(APP_MENU_ADV_EVENT, 0, "Adv status: Started - handle: " 8 MENU_MODULE_COLOR_YELLOW "%d" MENU_MODULE_COLOR_RESET, 9 ((BLEAppUtil_AdvEventData_t *)pMsgData)->pBuf->advHandle); 10 break; 11 } 12 13 case BLEAPPUTIL_ADV_END_AFTER_DISABLE: 14 { 15 MenuModule_printf(APP_MENU_ADV_EVENT, 0, "Adv status: Ended - handle: " 16 MENU_MODULE_COLOR_YELLOW "%d" MENU_MODULE_COLOR_RESET, 17 ((BLEAppUtil_AdvEventData_t *)pMsgData)->pBuf->advHandle); 18 break; 19 } 20 21 default: 22 { 23 break; 24 } 25 } 26 }
1typedef enum BLEAppUtil_GAPAdvEventMaskFlags_e 2{ 3 // Gets advertising handle using pBuf 4 BLEAPPUTIL_ADV_START_AFTER_ENABLE = GAP_EVT_ADV_START_AFTER_ENABLE, 5 // Gets advertising handle using pBuf 6 BLEAPPUTIL_ADV_END_AFTER_DISABLE = GAP_EVT_ADV_END_AFTER_DISABLE, 7 // Gets advertising handle using pBuf 8 BLEAPPUTIL_ADV_START = GAP_EVT_ADV_START, 9 // Gets advertising handle using pBuf 10 BLEAPPUTIL_ADV_END = GAP_EVT_ADV_END, 11 // Gets @ref GapAdv_setTerm_t using pBuf 12 BLEAPPUTIL_ADV_SET_TERMINATED = GAP_EVT_ADV_SET_TERMINATED, 13 // Gets @ref GapAdv_scanReqReceived_t using pBuf 14 BLEAPPUTIL_SCAN_REQ_RECEIVED = GAP_EVT_SCAN_REQ_RECEIVED, 15 // Gets @ref GapAdv_truncData_t using pBuf 16 BLEAPPUTIL_ADV_DATA_TRUNCATED = GAP_EVT_ADV_DATA_TRUNCATED, 17 BLEAPPUTIL_ADV_INSUFFICIENT_MEMORY = GAP_EVT_INSUFFICIENT_MEMORY 18} BLEAppUtil_GAPAdvEventMaskFlags_e;
Enable advertising. Calling GapAdv_enable() to start advertising. Note that
BLEAppUtil_advStart
is a wrapper forGapAdv_enable
1 // Enable advertising 2 status = BLEAppUtil_advStart(peripheralAdvHandle_1, &advSetStartParamsSet_1);
1 bStatus_t BLEAppUtil_advStart(uint8 handle, const BLEAppUtil_AdvStart_t *advStartInfo) 2 { 3 return GapAdv_enable(handle, advStartInfo->enableOptions , 4 advStartInfo->durationOrMaxEvents); 5 }
The above steps can be repeated to create multiple advertising sets. There is an upper limit of 20 advertising sets (defined in the controller). The heap may also restrict the number of advertising sets you can create in one application.
Changing advertising parameters¶
In order to change an individual parameter after advertising has been enabled, advertising must first be disabled. Re-enable advertising after the parameter is modified. Here we will illustrate the sequence using BLEAppUtil framework.
1 // Stop advertising
2 BLEAppUtil_advStop(peripheralAdvHandle_1);
3
4 // Set a parameter
5 uint32_t newAdvInt = 200;
6 GapAdv_setParam(peripheralAdvHandle_1, GAP_ADV_PARAM_PRIMARY_INTERVAL_MIN, &newAdvInt);
7 GapAdv_setParam(peripheralAdvHandle_1, GAP_ADV_PARAM_PRIMARY_INTERVAL_MAX, &newAdvInt);
8
9 // Re-enable advertising
10 status = BLEAppUtil_advStart(peripheralAdvHandle_1, &advSetStartParamsSet_1);
Ending Advertisement¶
An advertising set can be disabled with GapAdv_disable() and deleted (such that all memory related to the set is freed) with GapAdv_destroy().
1 // Stop advertising
2 BLEAppUtil_advStop(peripheralAdvHandle_1);
3
4 // Free all parameter settings related to advertising set
5 // Keep the advertising / scan response data
6 // Note that this can be called while advertising is still enabled
7 GapAdv_destroy(peripheralAdvHandle_1, GAP_ADV_FREE_OPTION_DONT_FREE);
Warning
In the out of box example, the advertising and scan response data are declared as global array using sysconfig tool, thus the BLE5-Stack has not allocated memory to the advertising and scan response data. If attempt to free the data using GAP_ADV_FREE_OPTION_ADV_DATA or GAP_ADV_FREE_OPTION_SCAN_RESP_DATA, it will cause hardfault.
Updating Advertisement/Scan Response Data¶
Again, since the advertising and scan response data is decoupled from the advertising set, it is possible for multiple advertising sets to use the same memory for advertising/scan response data. An advertising set could also use the same memory for its advertising and scan response data. The memory used for advertising/scan response data is referred to as a “buffer” throughout this section.
The preferred and safest method to update a buffer is to use the prepare/load latching mechanism. This will ensure the following:
No buffer is freed if it is used by another advertising set.
Advertising is always disabled before the buffer is modified, thus ensuring that no corrupted advertisement packets are transmitted.
Prevent double-copying.
The following offers several ways to update the advertising and scan response data.
Update the Advertising/Scan Response Data of a Single Handle¶
If the application wants to modify a few bytes of advertising/scan response data that is used by a single advertisement set, use GapAdv_prepareLoadByHandle() and GapAdv_loadByHandle().
1 bStatus_t status;
2
3 // Don't free anything since we're going to use the same buffer to re-load
4 status = GapAdv_prepareLoadByHandle(peripheralAdvHandle_1, GAP_ADV_FREE_OPTION_DONT_FREE);
5
6 // Only update the data when return is successful
7 if (status == SUCCESS)
8 {
9 // Sample buffer modification
10 advData1[3] = 0xAA;
11
12 // Reload buffer to handle
13 // Here the advDataLen = sizeof(advData1) and it is setup in the app\_peripheral.c
14 // under advSetInitParamsSet_1
15 GapAdv_loadByHandle(peripheralAdvHandle_1, GAP_ADV_DATA_TYPE_ADV, advSetInitParamsSet_1->advDataLen, advData1);
16 }
The GapAdv_prepareLoadByHandle() will perform the following here:
Check that this buffer isn’t used by other handles. If GapAdv_prepareLoadByHandle() returns success, the application will know that it can now safely modify this buffer.
Automatically disable advertising and mark it for re-enabling
GapAdv_loadByHandle() will automatically re-enable advertising with the updated buffer. This method can also be used to use a subset of the original advertising data. In this case, simply update the data length parameter of GapAdv_loadByHandle().
Warning
The above methods of changing advertising or scan response data have one limitation. A special procedure is required when the data length is currently set to zero and application wants to change that to a non-zero length. In this case the application needs to
Destroy the advertisement set, please see Ending Advertisement
Re-create it with the desired data, please see Create Advertisement
Load a New Buffer to a Single Advertising Handle¶
If the application wants to load a new buffer to a single advertising handle without double-copying, use GapAdv_prepareLoadByHandle() and GapAdv_loadByHandle(). This way, the advertising data exists in only one place in memory that is used by the GAP Advertiser. In this case we will free the buffer and allocate a new one.
1 // Free the buffer (to avoid double copying) since we're loading a new buffer
2 // However, only free the buffer if it's allocated in the application.
3 // Using out of box example, the buffer is a global array and can not be freed
4 GapAdv_prepareLoadByHandle(advHandleLegacy, GAP_ADV_FREE_OPTION_ADV_DATA);
5
6 // Allocate new buffer (and then fill it as desired)
7 uint8_t *advertData2= ICall_malloc(ADV_DATA2_LEN);
8
9 // Load the new buffer to the advertisement set handle
10 GapAdv_loadByHandle(peripheralAdvHandle_1, GAP_ADV_DATA_TYPE_ADV, ADV_DATA2_LEN, advertData2);
GapAdv_prepareLoadByHandle() will perform the following here:
Check that the buffer isn’t used by any other advertisement handles. If GapAdv_prepareLoadByHandle() returns success, the application will know that it can now safely modify this buffer.
Automatically disable advertising and mark it for re-enabling
Free the original buffer
The GapAdv_loadByHandle() will automatically re-enable advertising on the handle with the new buffer.
Update Advertising/Scan Response Data that is Used for Multiple Advertising Handles¶
This is the case where the application wants to modify a few bytes of advertising or scan response data that is shared among advertisement sets.
1 // Don't free anything since we're going to use the same buffer to reload
2 GapAdv_prepareLoadByBuffer(advertData, FALSE);
3
4 // Sample buffer modification
5 advertData[3] = 0xAA;
6
7 // Reload the buffer to be used by the advertisement set handles
8 GapAdv_loadByBuffer(ADV_DATA_LEN, advertData);
GapAdv_prepareLoadByBuffer() will automatically disable advertising for all advertising handles that use this buffer. GapAdv_loadByBuffer() will automatically re-enable advertising for all handles that use this buffer.
Load a New Buffer To Multiple Advertising Handles¶
This is the case where the applications wants to load a new buffer to all advertisement set handles that are using this buffer.
1 // Free buffer (to avoid double copying) since we're loading a new buffer
2 // However, only free the buffer if it's allocated in the application.
3 // Using out of box example, the buffer is a global array and can not be freed
4 GapAdv_prepareLoadByBuffer(advertData, TRUE);
5
6 // Allocate new buffer (and then fill as desired)
7 uint8_t *advertData2= ICall_malloc(ADV_DATA2_LEN);
8
9 // Reload the buffer to be used by all the handles
10 GapAdv_loadByBuffer(ADV_DATA2_LEN, advertData2);
GapAdv_loadByBuffer() will perform the following here:
Automatically disable advertising and mark it for re-enabling on all handles that use this buffer
Free the original buffer
GapAdv_loadByBuffer() will automatically re-enable advertising on all handles that used the original buffer.
Directly Manipulating a Buffer While Advertising is Enabled¶
Since the application owns the advertising and scan response buffers and thus has access to the memory where the buffers are stored, there is nothing preventing it from directly modifying this memory. While discouraged, there are scenarios where this can be useful, such as updating the data after each advertisement in the most power-efficient manner. The main drawback to this is that there is no way to guarantee that this update won’t occur while an advertising packet is being transmitted that is using this buffer, potentially resulting in a corrupted advertising packet. This is especially true if multiple advertising sets are using the same buffer. If the application accepts the risk of potentially corrupting an advertising packet, there is a recommended way to do this with minimized risk.
The buffer should be updated directly in the advertising callback (in the stack context)
when the GAP_EVT_ADV_END
or BLEAPPUTIL_ADV_END
is received. Generally,
it is recommended to minimize processing in these callbacks and, instead, to
post an event to process in the application context. It should be noted that
any processing in the stack context has the potential to break the timing of
the controller. This should not be a problem if the only processing is to
update the buffer. Also, for both this reason and because it will lead to
a semaphore that never gets posted due to calling an ICall API from the stack
context, it should be noted that no stack API calls can be made from the stack
context. To reiterate for clarity, no BLE-Stack APIs can be called from the callback (stack) context.
1 void Peripheral_AdvEventHandler(uint32 event, BLEAppUtil_msgHdr_t *pMsgData)
2 {
3 switch(event)
4 {
5 // Advertisement just ended so it should be safe to update buffer here
6 // without corrupting an advertisement.
7 case BLEAPPUTIL_ADV_END:
8 {
9 advertData[3] = 0xAA;
10 }
11 }
12 }
Note that this can not be guaranteed to work all the time without corrupting an
advertisement. Also, if the buffer is used by multiple advertisement handles, the
application would need to track the GAP_EVT_ADV_END
or BLEAPPUTIL_ADV_END
across all of these handles to find an ideal time to update the buffer.
If this processing is added to the callback in the stack context, this
increases the risk of breaking the timing of the controller.
Also, remember to include GAP_EVT_ADV_END
or BLEAPPUTIL_ADV_END
in the event mask,
please refer to Listing 40..
Limited Advertising¶
The above code can set the advertising for limited or general
advertising modes. By default, the peripheral advertises in general
discoverable mode. To use limited discoverable mode, the
corresponding fields inside the Listing 37. should be
corresponding fields inside the Listing 37. should be
changed by changing GAP_ADTYPE_FLAGS_GENERAL
to
GAP_ADTYPE_FLAGS_LIMITED
. This can be achieved by using BLE5-Stack SysConfig Features.
Periodic Advertising¶
Warning
This functionnality is not yet implemented in the stack.
Directed Advertisements as GATT Server¶
In BLE5-Stack 3.02.04.00, Privacy is always enabled. Most of the privacy features are handled by the GAP bond manager in the stack. To conserve flash memory, by default, the GAP bond manager does not enable GATT client features. The implication of these disabled GATT client features is that the GAP bond manager will not query the Central Address Resolution characteristic of the remote device.
In order to perform a directed advertisement when the initiator’s address is set to Private Resolvable Address, the peripheral device must read the Central Address Resolution characteristic of its remote device to make sure address resolution is supported. Failure to do so before sending directed advertisements violates the Bluetooth Core Specifications Version 5.3.
By default, sample applications such as basic_ble does not define
GATT_NO_CLIENT
but initializes the GATT Client as shown below:
1 if (role & (GAP_PROFILE_PERIPHERAL | GAP_PROFILE_CENTRAL)) 2 { 3 // Initialize GATT Client 4 GATT_InitClient(); 5 }
Note
GATT_NO_CLIENT
can be set using BLE5-Stack SysConfig Features.
GAP Peripheral¶
The Peripheral role demonstrates the use of the advertising and
connection states. The steps to use this role will be demonstrated by
examining the basic_ble
example project and related GAP APIs.
Note
Many of the GAP default values may configured using syscfg
. The examples
below show how to make these modifications when syscfg
is not desired.
Initialize the GAP parameters. This initialization typically occurs in the application initialization function by calling
GAP_SetParamValue()
. In thebasic_ble
example project included in the SDK, this initialization occurs inbleStack_initGap()
as shown below.1{ 2 bStatus_t status = SUCCESS; 3 // Pass all parameter update requests to the app for it to decide 4 GAP_SetParamValue(GAP_PARAM_LINK_UPDATE_DECISION, paramUpdateDecision); 5 6 //... 7 return status; 8}
Additional calls to
GAP_SetParamValue()
may be made inbleStack_initGap()
to override default GAP parameters when usingbasic_ble
. A list of configurable parameters may be located in the BLE Stack API Reference GAP section.Modifing GAP initialization parameters in
basic_ble
. By default,basic_ble
will send all GAP parameter updates to the application. To change this behavior, modify theappMainPeriCentParams
structure located inapp_main.c
.1BLEAppUtil_PeriCentParams_t appMainPeriCentParams = 2{ 3 .connParamUpdateDecision = DEFAULT_PARAM_UPDATE_REQ_DECISION, 4 .gapBondParams = &gapBondParams 5};
Initialize the application task using
GAP_DeviceInit
for the Peripheral Role and register to receive GAP events. To modify the default GAP device initialization parameters inbasic_ble
, modify theappMainParams
structure located inapp_main.c
.1// Parameters that should be given as input to the BLEAppUtil_init function 2BLEAppUtil_GeneralParams_t appMainParams = 3{ 4 .taskPriority = 1, 5 .taskStackSize = 1024, 6 .profileRole = (BLEAppUtil_Profile_Roles_e)(HOST_CONFIG), 7 .addressMode = DEFAULT_ADDRESS_MODE, 8 .deviceNameAtt = attDeviceName, 9};
1//... 2 3status = GAP_DeviceInit(BLEAppUtilLocal_GeneralParams->profileRole, 4 BLEAppUtilSelfEntity, 5 BLEAppUtilLocal_GeneralParams->addressMode, 6 BLEAppUtilLocal_GeneralParams->pDeviceRandomAddress); 7 8//...
Now you can send commands from the application. The following is an example of the application initiating PHY change using HCI_LE_SetPhyCmd().
1 bStatus_t BLEAppUtil_setConnPhy(BLEAppUtil_ConnPhyParams_t *phyParams) 2 { 3 // Set Phy Preference on the current connection. Apply the same value 4 // for RX and TX. For more information, see the LE 2M PHY section in the User's Guide: 5 // http://software-dl.ti.com/lprf/ble5stack-latest/ 6 return HCI_LE_SetPhyCmd(phyParams->connHandle, phyParams->allPhys, phyParams->txPhy, phyParams->rxPhy, phyParams->phyOpts); 7 }
The following shows the software flow when the user chooses to set the PHY preference from the terminal in the
basic_ble
example:As shown in the diagram above, the actual PHY change is returned asynchronously and is passed to the application with event code HCI_BLE_PHY_UPDATE_COMPLETE_EVENT.
The application task processes most of the GAP-related events passed to it from the Bluetooth Low Energy protocol stack. For example, when a link is terminated, the application removes the connection information and displays the terminated device’s peer address and connection handle number. The following code snippet can be found in
app_connection.c
:1void Connection_ConnEventHandler(uint32 event, BLEAppUtil_msgHdr_t *pMsgData) 2{ 3 switch(event) 4 { 5 // ... 6 case BLEAPPUTIL_LINK_TERMINATED_EVENT: 7 { 8 gapTerminateLinkEvent_t *gapTermMsg = (gapTerminateLinkEvent_t *)pMsgData; 9 10 // Remove the connection from the conneted device list 11 Connection_removeConnInfo(gapTermMsg->connectionHandle); 12 13 /*! Print the peer address and connection handle number */ 14 MenuModule_printf(APP_MENU_CONN_EVENT, 0, "Conn status: Terminated - " 15 "connectionHandle = " MENU_MODULE_COLOR_YELLOW "%d " MENU_MODULE_COLOR_RESET 16 "reason = " MENU_MODULE_COLOR_YELLOW "%d" MENU_MODULE_COLOR_RESET, 17 gapTermMsg->connectionHandle, gapTermMsg->reason); 18 19 /*! Print the number of current connections */ 20 MenuModule_printf(APP_MENU_NUM_CONNS, 0, "Connections number: " 21 MENU_MODULE_COLOR_YELLOW "%d " MENU_MODULE_COLOR_RESET, 22 linkDB_NumActive()); 23 24 break; 25 } 26 // ...
GAP Scanner¶
The GAP Scanner performs Extended Scanning, Legacy Scanning and
Synchronization with periodic advertisements operations as
defined by the Bluetooth Core Specifications Version 5.3. It controls the Scanner GAP state (see
GAP State Diagram.). The Central and Observer role uses the scanning
state implemented by the GAP Scanner. The GAP Scanner is demonstrated in the
app_central.c
and app_observer.c
source files in the basic_ble
example project. The periodic advertisement synchronization capability of the
GAP Scanner is demonstrated in the rtls_master example project. See the
BLE Stack API Reference for the full GAP Scanner API including commands,
configurable parameters, events, and callbacks. The steps to use this module are
listed in the following, along with example code from basic_ble
.
Start a Central or Observer GAP role. In this case we will use the GAP Central role. For configuration of the Bluetooth Low Energy stack features, and changing the role in
basic_ble
, see Developing with SDK CC23xx and Stack Configurations.Register an event callback handler for scanner events. In the
basic_ble
project, an event handler is registered usingBLEAppUtil_registerEventHandler
. ThecentralScanHandler
structure contains the event mask and callback function to handle GAP scan events.// Events handlers struct, contains the handlers and event masks // of the application central role module BLEAppUtil_EventHandler_t centralScanHandler = { .handlerType = BLEAPPUTIL_GAP_SCAN_TYPE, // Event handler type .pEventHandler = Central_ScanEventHandler, // Callback to event handler .eventMask = BLEAPPUTIL_SCAN_ENABLED | // Event mask BLEAPPUTIL_SCAN_DISABLED };
This structure determines what events will be passed to the callback handler,
Central_ScanEventHandler()
. For a full list of GAP scan events, please seeBLEAppUtil_GAPScanEventMaskFlags_e
enum located inbleapputil_api.h
. Before taking a look at the event handler, it must be registered so that the application recieves GAP scan events.// Register callback to process Scanner events bStatus_t Central_start() { bStatus_t status = SUCCESS; status = BLEAppUtil_registerEventHandler(¢ralScanHandler); if(status != SUCCESS) { // Return status value return(status); } // ... }
Handling events in the event handler. The code block below shows the general callback structure to handle events.
void Central_ScanEventHandler(uint32 event, BLEAppUtil_msgHdr_t *pMsgData) { BLEAppUtil_ScanEventData_t *scanMsg = (BLEAppUtil_ScanEventData_t *)pMsgData; switch (event) { case BLEAPPUTIL_SCAN_ENABLED: { //...code... break; } case BLEAPPUTIL_SCAN_DISABLED: { //...code... break; } default: { //...more code... break; } } }
Set scan parameters. It’s worth noting that the parameters are split into PHY-related parameters (set with GapScan_setPhyParams()) and non-PHY-related parameters (set with GapScan_setParam()). The
basic_ble
example project abstracts these calls throughBLEAppUtil_scanInit()
andBLEAppUtil_setConnParams()
with their definitions located inbleapputil_init.c
. Thebasic_ble
example project provides structures to modify the initial scan and PHY parameters.
Note
Make sure the scanning window is at least twice the length of the expected advertising interval to ensure at least 1 advertising event is captured in each scanning window. Otherwise, the scanning device may not find the advertising device.
const BLEAppUtil_ScanInit_t centralScanInitParams =
{
/*! Opt SCAN_PRIM_PHY_1M | SCAN_PRIM_PHY_CODED */
.primPhy = DEFAULT_SCAN_PHY,
/*! Opt SCAN_TYPE_ACTIVE | SCAN_TYPE_PASSIVE */
.scanType = DEFAULT_SCAN_TYPE,
/*! Scan interval shall be greater than or equal to scan window */
.scanInterval = DEFAULT_SCAN_INTERVAL, /* Units of 625 us */
/*! Scan window shall be less than or equal to scan interval */
.scanWindow = DEFAULT_SCAN_WINDOW, /* Units of 625 us */
/*! Select which fields of an advertising report will be stored */
/*! in the AdvRptList, For mor field see @ref Gap_scanner.h */
.advReportFields = ADV_RPT_FIELDS,
/*! Opt SCAN_PRIM_PHY_1M | SCAN_PRIM_PHY_CODED */
.scanPhys = DEFAULT_SCAN_PHY,
/*! Opt SCAN_FLT_POLICY_ALL | SCAN_FLT_POLICY_AL | */
/*! SCAN_FLT_POLICY_ALL_RPA | SCAN_FLT_POLICY_AL_RPA */
.fltPolicy = SCANNER_FILTER_POLICY,
/*! For more filter PDU @ref Gap_scanner.h */
.fltPduType = SCANNER_FILTER_PDU_TYPE,
/*! Opt SCAN_FLT_RSSI_ALL | SCAN_FLT_RSSI_NONE */
.fltMinRssi = SCANNER_FILTER_MIN_RSSI,
/*! Opt SCAN_FLT_DISC_NONE | SCAN_FLT_DISC_GENERAL | SCAN_FLT_DISC_LIMITED
* | SCAN_FLT_DISC_ALL | SCAN_FLT_DISC_DISABLE */
.fltDiscMode = SCANNER_FILTER_DISC_MODE,
/*! Opt SCAN_FLT_DUP_ENABLE | SCAN_FLT_DUP_DISABLE | SCAN_FLT_DUP_RESET */
.fltDup = SCANNER_DUPLICATE_FILTER
};
const BLEAppUtil_ConnParams_t centralConnInitParams =
{
/*! Opt INIT_PHY_ALL | INIT_PHY_1M | INIT_PHY_2M | INIT_PHY_CODED */
.initPhys = DEFAULT_INIT_PHY,
.scanInterval = INIT_PHYPARAM_SCAN_INT, /* Units of 0.625ms */
.scanWindow = INIT_PHYPARAM_SCAN_WIN, /* Units of 0.625ms */
.minConnInterval = INIT_PHYPARAM_MIN_CONN_INT, /* Units of 1.25ms */
.maxConnInterval = INIT_PHYPARAM_MAX_CONN_INT, /* Units of 1.25ms */
.connLatency = INIT_PHYPARAM_CONN_LAT,
.supTimeout = INIT_PHYPARAM_SUP_TO /* Units of 10ms */
};
These structures are then passed to BLEAppUtil_scanInit()
and
BLEAppUtil_setConnParams()
respectively. These initialization functions may
be found in Central_start()
.
Start Scanning.
Menu_scanStartCB()
callback triggers when selected in thebasic_ble
menu. This callback callsBLEAppUtil_scanStart()
, which starts scanning for advertisement reports and scan responses.Menu_scanStartCB()
contains parameters for the scan period, duration, and the maximum number of advertisement reports.void Menu_scanStartCB(uint8 index) { bStatus_t status; const BLEAppUtil_ScanStart_t centralScanStartParams = { /*! Zero for continuously scanning */ .scanPeriod = DEFAULT_SCAN_PERIOD, /* Units of 1.28sec */ /*! Scan Duration shall be greater than to scan interval,*/ /*! Zero continuously scanning. */ .scanDuration = DEFAULT_SCAN_DURATION, /* Units of 10ms */ /*! If non-zero, the list of advertising reports will be */ /*! generated and come with @ref GAP_EVT_SCAN_DISABLED. */ .maxNumReport = APP_MAX_NUM_OF_ADV_REPORTS }; // Will start scanning by calling GapScan_enable. status = BLEAppUtil_scanStart(¢ralScanStartParams); // Print the status of the scan MenuModule_printf(APP_MENU_GENERAL_STATUS_LINE, 0, "Call Status: ScanStart = " MENU_MODULE_COLOR_BOLD MENU_MODULE_COLOR_RED "%d" MENU_MODULE_COLOR_RESET, status); }
BLEAppUtil_scanStart()
callsGapScan_enable()
with the parameters of thecentralScanStartParams
structure.bStatus_t BLEAppUtil_scanStart(const BLEAppUtil_ScanStart_t *scanStartInfo) { return GapScan_enable(scanStartInfo->scanPeriod, scanStartInfo->scanDuration, scanStartInfo->maxNumReport); }
The return status from the protocol stack call of GapScan_enable() indicates only whether or not the attempt to perform device discovery was successful. The actual device discovered is returned asynchronously as a
BLEAPPUTIL_ADV_REPORT
forwarded through the GAP Scanner callbacks registered by the application (here:Central_ScanEventHandler()
). This is described below.The GAP Scanner performs some processing on the GAP events it receives from the protocol stack. The task also forwards some events to the application. Figure 69. shows this and how the
BLEAPPUTIL_ADV_REPORT
is processed from the protocol stack to the application.
Note that during scanning, individual advertisements and scan responses are
returned as BLEAPPUTIL_ADV_REPORT
. This is defined by the Bluetooth Core Specifications Version 5.3. By
default, duplicate reports are filtered such that only one event is returned to
the application per peer device BDA. This can be configured via the
SCAN_PARAM_FLT_DUP GAP Scanner parameter (You can change scanning
parameters with GapScan_setParam() or by modifying the
centralScanInitParams
structure within app_central.c
).
BLEAPPUTIL_SCAN_ENABLED
indicates the start of scanning. After the scan
has completed, a summary of discovered reports will be returned to the
application with BLEAPPUTIL_SCAN_DISABLED
. You can see the implementation
of this in Central_ScanEventHandler()
.
void Central_ScanEventHandler(uint32 event, BLEAppUtil_msgHdr_t *pMsgData)
{
BLEAppUtil_ScanEventData_t *scanMsg = (BLEAppUtil_ScanEventData_t *)pMsgData;
switch (event)
{
case BLEAPPUTIL_SCAN_ENABLED:
{
centralScanIndex = 0;
MenuModule_printf(APP_MENU_SCAN_EVENT, 0, "Scan status: Scan started...");
break;
}
case BLEAPPUTIL_SCAN_DISABLED:
{
uint8 i;
for(int i = 0; i < APP_MAX_NUM_OF_ADV_REPORTS; i++)
{
memset(¢ralScanRes[i], 0, sizeof(App_scanResults));
}
// Go over the advertise reports that was saved in the host level and save it
for (i = 0; i < scanMsg->pBuf->pScanDis.numReport; i++)
{
GapScan_Evt_AdvRpt_t advReport;
// Get the address from the report
GapScan_getAdvReport(i, &advReport);
// Add the report to the scan list
Central_addScanRes(&advReport);
}
MenuModule_printf(APP_MENU_SCAN_EVENT, 0, "Scan status: Scan disabled - "
"Reason: " MENU_MODULE_COLOR_YELLOW "%d " MENU_MODULE_COLOR_RESET
"Num results: " MENU_MODULE_COLOR_YELLOW "%d " MENU_MODULE_COLOR_RESET,
scanMsg->pBuf->pScanDis.reason,
scanMsg->pBuf->pScanDis.numReport);
break;
}
default:
{
break;
}
}
}
The maximum amount of scan responses that can be discovered during one scan can
be set with the DEFAULT_MAX_SCAN_RES
parameter that is passed into the
maxNumReport
parameter of GapScan_enable().
In an environment saturated with advertisements and scan responses, this can have a drastic impact on heap usage to the point of potentially breaking the stack. Therefore, it is essential to profile your application for the worst-case scenario where the maximum amount of scan responses are discovered during a scan.
Note that some scanning parameters will not be updated before scanning has been disabled and re-enabled. This is true for the following parameters:
SCAN_PARAM_FLT_PDU_TYPE - Filter by PDU Type
SCAN_PARAM_FLT_MIN_RSSI - Filter by Minimum RSSI
SCAN_PARAM_FLT_DISC_MODE - Filter by Discoverable Mode
SCAN_PARAM_RPT_FIELDS - Advertising Report Fields
Advertising Report Recording¶
The Advertising Report Recording feature allows a developer to specify what
information is received and recorded in an Advertisement Report. This is useful
in the use case where the application does not require the payload of the
Advertising Report, but requires specific information such as address type,
address, RSSI, etc. while eleminating the BLEAPPUTIL_ADV_REPORT
event from
GAP Scanner.
To prepare recording, GAP Scanner allocates necessary amount of memory when
calling GapScan_enable()
. Whenever a new packet arrives, GAP
Scanner puts the specified fields of the information in the Advertising Report
List in packed form. The list resides in RAM after the scanning ends until the
application calls GapScan_discardAdvReportList()
or a new scanning session
starts. While the list is available, the application may retrieve the
information by using GapScan_getAdvReport()
. When the list fills,
GAP Scanner issues BLEAPPUTIL_ADV_REPORT_FULL
notifying the application
that no more Advertising Reports will be recorded. The application may specify
which fields of the Advertising Report information by using GapScan_setParam()
with the parameter ID SCAN_PARAM_RPT_FIELDS
and the associated bitmap.
The number of an Advertising Report’s information recorded may be found in the
numReport
field of the data buffer of type GapScan_Evt_End_t
, coming
with BLEAPPUTIL_SCAN_DISABLED
event. Alternatively, if the application
didn’t mask BLEAPPUTIL_SCAN_DISABLED
, GapScan_getParam()
with
parameter ID SCAN_PARAM_NUM_ADV_RPT may be used.
Obtain Advertising Channel from Advertising Report¶
The application can extract the advertising channel from the advertising report with the following modifications:
Enable the advertising channel in the advertising report:
With SysConfig: Enable the setting
Add Advertisement Channel Number
insideObserver Configuration
, see Observer ConfigurationsWithout SysConfig: Add
-DADV_RPT_INC_CHANNEL=1
in the fileti_ble_app_config.opt
Use the following example code in
SimpleCentral_processAppMsg()
to access the channel informationcase SC_EVT_ADV_REPORT: { GapScan_Evt_AdvRpt_t* pAdvRpt = (GapScan_Evt_AdvRpt_t*) (pMsg->pData); volatile uint8_t advChannel = pAdvRpt->secPhy >> 2; //...
Filtering¶
The GAP Scanner module provides 5 different types of filter to reduce the amount of advertising report notifications to the application and eventually save memory and power consumption. The result of the filtering affects both advertising reports and advertising report recording. The packets filtered out by the filter configurations are discarded and will neither be reported nor recorded. The filters are set by using the API GapScan_setParam() with the following parameter IDs:
All parameter IDs are described in the following sections. The GAP Scanner allows the application to apply any combination of the individual filters at the same time to narrow the scope of packets to receive.
Filter by LL Filter Policy¶
This filter is a link layer-level filter. The associated parameter ID used in GapScan_setParam() is SCAN_PARAM_FLT_POLICY. The parameter accompanying SCAN_PARAM_FLT_POLICY is passed to the link layer when GapScan_enable() is called before actually enabling scanning. Since the filtering is done by the link layer, the application only receives advertising report events that have passed the filter. The parameter value can be one of the following:
Name |
Value |
Description |
---|---|---|
SCAN_FLT_POLICY_ALL |
0 |
Accept all Advs except directed Adv not addressed to this device. |
SCAN_FLT_POLICY_WL |
1 |
Accept only Advs from devices where the advertiser’s address is in the FIlter Accept List. |
SCAN_FLT_POLICY_ALL_RPA |
2 |
Accept all Advs except directed Adv not addressed to this device and any packet addressed to this device or addressed to a private resolvable address. |
SCAN_FLT_POLICY_WL_RPA |
3 |
Accept only Advs from devices where the advertiser’s address is in the FIlter Accept List and any packet addressed to this device or addressed to a private resolvable address. |
Filter by PDU Type¶
This filter is based on the Event_Type
parameter coming with the LE Extended
Advertising Report Event. The associated parameter ID is
SCAN_PARAM_FLT_PDY_TYPE
. The parameter value specifies packets
classified in six categories:
Connectable/Non-Connectable
Scannable/Non-Scannable
Directed/Undirected
ScanRsp/Adv
Legacy/Extended
Complete/Incomplete.
Every incoming packet has exactly one attribute in each category. For example,
SCAN_FLT_PDU_NONSCANNABLE_ONLY and
SCAN_FLT_PDU_SCANNABLE_ONLY cannot be chosen together since they
represent scannable and non-scannable packets. Only either one can be used. If
neither type is selected in a set, the filter will not care about that category.
For example, if neither SCAN_FLT_PDU_NONCONNECTABLE_ONLY nor
SCAN_FLT_PDU_CONNECTABLE_ONLY is set in the parameter value, the GAP
Scanner will notify the application of both connectable packets and
non-connectable packets. It will also record both connectable packets and
non-connectable packets. The SCAN_PARAM_FLT_PDY_TYPE
parameter value
can be any combination of the following individual values (except individual
values that are mutually exclusive):
Name |
Value |
Description |
---|---|---|
SCAN_FLT_PDU_NONCONNECTABLE_ONLY |
0x0001 |
Non-connectable packets only. Mutually exclusive with SCAN_FLT_PDU_CONNECTABLE_ONLY |
SCAN_FLT_PDU_CONNECTABLE_ONLY |
0x0002 |
Connectable packets only. Mutually exclusive with SCAN_FLT_PDU_NONCONNECTABLE_ONLY |
SCAN_FLT_PDU_NONSCANNABLE_ONLY |
0x0004 |
Non-scannable packets only. Mutually exclusive with SCAN_FLT_PDU_SCANNABLE_ONLY |
SCAN_FLT_PDU_SCANNABLE_ONLY |
0x0008 |
Scannable packets only. Mutually exclusive with SCAN_FLT_PDU_NONSCANNABLE_ONLY |
SCAN_FLT_PDU_UNDIRECTED_ONLY |
0x0010 |
Undirected packets only. Mutually exclusive with SCAN_FLT_PDU_DIRECTIED_ONLY |
SCAN_FLT_PDU_DIRECTED_ONLY |
0x0020 |
Directed packets only. Mutually exclusive with SCAN_FLT_PDU_UNDIRECTED_ONLY |
SCAN_FLT_PDU_ADV_ONLY |
0x0040 |
Advertisement packets only. Mutually exclusive with SCAN_FLT_PDU_SCANRSP_ONLY |
SCAN_FLT_PDU_SCANRSP_ONLY |
0x0080 |
Scan Response packets only. Mutually exclusive with SCAN_FLT_PDU_ADV_ONLY |
SCAN_FLT_PDU_EXTENDED_ONLY |
0x0100 |
Extended packets only. Mutually exclusive with SCAN_FLT_PDU_LEGACY_ONLY |
SCAN_FLT_PDU_LEGACY_ONLY |
0x0200 |
Legacy packets only. Mutually exclusive with SCAN_FLT_PDU_EXTENDED_ONLY |
SCAN_FLT_PDU_TRUNCATED_ONLY |
0x0400 |
Truncated packets only. Mutually exclusive with SCAN_FLT_PDU_COMPLETE_ONLY |
SCAN_FLT_PDU_COMPLETE_ONLY |
0x0800 |
Complete packets only. Mutually exclusive with SCAN_FLT_PDU_TRUNCATED_ONLY |
Filter by Minimum RSSI¶
This filter is based on the RSSI parameter coming with the LE Extended Advertising Report Event. The associated parameter ID used in GapScan_setParam() is SCAN_PARAM_FLT_MIN_RSSI. The GAP Scanner will discard LE Extended Advertising Report Event whose RSSI parameter value is smaller than the minimum RSSI value given by the application. The available range is -128 dBm to 127 dBm.
Filter by Discoverable Mode¶
This filter is based on the Flags AD
type value in the payload (in
the Data
parameter) of the LE Extended Advertising Report Event. The
associated parameter ID used in GapScan_setParam() is
SCAN_PARAM_FLT_DISC_MODE. This filter is applied after the GAP
Scanner reassembles all the fragmented payloads delivered by multiple LE
Extended Advertising Report Events. The GAP Scanner will discard the
defragmented packet if the found Flags AD type value doesn’t match the parameter
value given by the application. The parameter value can be one of the following:
Name |
Value |
Description |
---|---|---|
SCAN_FLT_DISC_NONE |
0 |
Accept only Non-Discoverable mode |
SCAN_FLT_DISC_GENERAL |
1 |
Accept only General Discoverable mode |
SCAN_FLT_DISC_LIMITED |
2 |
Accept only Limited Discoverable mode |
SCAN_FLT_DISC_ALL |
3 |
Accept both General and Limited Discoverable mode |
SCAN_FLT_DISC_DISABLE |
4 |
Discoverable Mode Filter Off (Don’t care about the Flags AD type value) |
Filter by Duplicates¶
Like the Link Layer Scanner Filter Policy, this filter is a link layer-level filter.
The associated parameter ID used in GapScan_setParam() is
SCAN_PARAM_FLT_DUP. The parameter accompanying the
SCAN_PARAM_FLT_DUP parameter ID is passed to the link layer when
GapScan_enable() internally calls LE_SetExtScanEnable
to
enable scanning. Since the filtering is done by the link layer, GAP Scanner
receives only the LE Extended Advertising Report Events that have passed the
filter. The parameter value can be one of the following:
Name |
Value |
Description |
---|---|---|
SCAN_FLT_DUP_DISABLE |
0 |
Disable duplicate filtering. |
SCAN_FLT_DUP_ENABLE |
1 |
Enable duplicate filtering. |
SCAN_FLT_DUP_RESET |
2 |
Enable duplicate filtering. Reset for each scan period. |
Warning
When the duplicate filtering is enabled, the maximum amount of devices that can be found is 16. If more than 16 devices need to be discovered, the duplicate filtering needs to be disabled.
Synchronize with a Periodic Advertising Train¶
Warning
This functionnality is not yet implemented in the stack.
GAP Initiator¶
The initiator module is used to initiate the connection to a peripheral device. An initiator generally scans for advertisements then connects to a specific device. The initiator is a short lived state that transitions to the Central Role after a connection is established. Unlike the GAP Advertiser and GAP Scanner modules, GAP Initiator doesn’t have any callback function to call or to be called. Only a device initiated with the GAP Central Role can become an initiator.
In the basic_ble
example, developers choose the device role in
Sysconfig
. This is covered in Developing with SDK CC23xx. Like all other
BLE roles, after the stack is initialized, handlers are registered in the role’s
respective start function.
1bStatus_t Central_start()
2{
3 bStatus_t status = SUCCESS;
4
5 status = BLEAppUtil_registerEventHandler(¢ralScanHandler);
6 if(status != SUCCESS)
7 {
8 // Return status value
9 return(status);
10 }
11
12 status = BLEAppUtil_scanInit(¢ralScanInitParams);
13 if(status != SUCCESS)
14 {
15 // Return status value
16 return(status);
17 }
18
19 status = BLEAppUtil_setConnParams(¢ralConnInitParams);
20 if(status != SUCCESS)
21 {
22 // Return status value
23 return(status);
24 }
25
26 // Return status value
27 return(status);
28}
Initiiating connections is handled through the application menu, located in
app_menu.c
.
1void Menu_connectToDeviceCB(uint8 index)
2{
3 bStatus_t status;
4
5 // Get the scan results list
6 App_scanResults *menuScanRes;
7 uint8 size = Scan_getScanResList(&menuScanRes);
8
9 // Set the connection parameters
10 BLEAppUtil_ConnectParams_t connParams =
11 {
12 .peerAddrType = menuScanRes[index].addressType,
13 .phys = INIT_PHY_1M,
14 .timeout = 1000
15 };
16
17 // Copy the selected address
18 memcpy(connParams.pPeerAddress, menuScanRes[index].address, B_ADDR_LEN);
19 status = BLEAppUtil_connect(&connParams);
20
21 // Print the status of the connect call
22 MenuModule_printf(APP_MENU_GENERAL_STATUS_LINE, 0, "Call Status: Connect = "
23 MENU_MODULE_COLOR_BOLD MENU_MODULE_COLOR_RED "%d" MENU_MODULE_COLOR_RESET,
24 status);
25
26 // Go back to the last menu
27 MenuModule_goBack();
28}
Note that the call to BLEAppUtil_connect()
serves as a wrapper to the GAP
API that connects to the address and parameters specified in the
BLEAppUtil_ConnectParams_t
structure. Taking a closer look at the
BLEAppUtil_connect()
function will reveal the GAP API GapInit_connect()
.
1 bStatus_t BLEAppUtil_connect(BLEAppUtil_ConnectParams_t *connParams)
2 {
3 return GapInit_connect(connParams->peerAddrType, connParams->pPeerAddress,
4 connParams->phys, connParams->timeout);
5 }
If the on-going connection attempt is intended to be canceled by either timeout
or a user request (the application calling GapInit_cancelConnect() or
BLEAppUtil_cancelConnect()
), the stack notifies the application of the
cancellation completion with a BLEAPPUTIL_CONNECTING_CANCELLED_EVENT.
Attention
When initiating timeout is set to 0 (wait indefinitely), the CC23xx will
stay in initiator role until a connection is successfully established.
If the peripheral device disappeared from the network before the connection
is established, the only method for initiator to exit this state is
to call GapInit_cancelConnect() or BLEAppUtil_cancelConnect()
.
GAP Central¶
The GAP Central role is demonstrated in app_central.c
. The Central
role demonstrates the use of the scanning and initiating states and supports
connections to peripheral devices. The steps to use this module are as follows.
For configuration of the Bluetooth Low Energy stack features, see Developing with SDK CC23xx and Stack Configurations.
#. After the stack is initialized, the Central_start()
function initializes
all the handlers necessary to implement the Central role.
bStatus_t Central_start() { bStatus_t status = SUCCESS; status = BLEAppUtil_registerEventHandler(¢ralScanHandler); if(status != SUCCESS) { // Return status value return(status); } status = BLEAppUtil_scanInit(¢ralScanInitParams); if(status != SUCCESS) { // Return status value return(status); } status = BLEAppUtil_setConnParams(¢ralConnInitParams); if(status != SUCCESS) { // Return status value return(status); } // Return status value return(status); }
GAP Connection State¶
A BLE device can be either in the Central or Peripheral role in the Connection State. After devices enter connection state, the devices exchange data under specific time within a specific time slot. This slot where the data is exchanged is called a Connection Event.
The beginning of the Connection Event is called an Anchor Point. During the connection event, the central and peripheral device will meet at the anchor point and the central will transmit the first packet, while the peripheral will have woken up to listen for the central devices transmitted packet.
Note
There is no limitation on the number of packet transactions per connection event other than MAX_NUM_PDU define in TI’s BLE5-Stack. It can either be one packet per direction or multiple packets per direction.
Connection Event Callback¶
Knowing what happened during the connection event can be beneficial for the system design. For example, by knowing which channels has higher CRC error, the application layer can decide to do a channelmap update to avoid those channels to ensure better communication.
TI’s BLE5-Stack provides the ability for application to obtain a detailed report of the connection event. The report will be sent at the end of the connection event and it contains status and statistics of the most recent connection event.
The report contains the following parameters:
status - Status of the last connection event.
status
Description
GAP_CONN_EVT_STAT_SUCCESS
Both central and peripheral show up for the conenction event and exchange packets.
GAP_CONN_EVT_STAT_CRC_ERROR
Packets were exchanged but the failed at the CRC checkout.
GAP_CONN_EVT_STAT_MISSED
The peer device did not show up for the connection event.
handle - Connection event handle which is used to identify the most recent connection event when there are multilple connections per device.
channel - The RF channel which the most recent connection event used.
phy - The PHY which the most recent connection event used.
lastRssi - The RSSI value measured on the last packet of the most recent connection event.
packets - Number of packets received during the most recent connection event.
errors - Accumulated number of packets with CRC error for this connection handle.
nextTaskType - The next task the link layer is going to run.
nextTaskTime - The time to next task.
eventCounter - The event counter of the most recent connection event.
timeStamp - The anchor point of the most recent connection event.
eventType - There are 3 types of events returning from BLE5-Stack.
eventType
Description
GAP_CB_CONN_ESTABLISHED
This event is returned when connection is established. When this returns, the eventCount will be 0.
GAP_CB_PHY_UPDATE
This event is returned when last connection event received LL_PHY_UPDATE_IND packet.
GAP_CB_CONN_EVENT_ALL
This event covers the above and rest of the scenarios.
To enable the application layer to receive connection report, you can use
BLEAppUtil_registerConnNotifHandler()
located in bleapputil_init.c
.
The following code is an example of how to register connection event report for all types of event within certain connection.
1// Assuming connHandle was assigned when the connection established.
2uint16_t connHandle;
3
4BLEAppUtil_registerConnNotifHandler(connHandle);
BLEAppUtil_registerConnNotifHandler()
serves as a wrapper for
Gap_registerConnEventCb()
and defaults to all events. For finer control,
a developer can call Gap_registerConnEventCb()
directly with the bit mask
desired or modify the function call directly within
BLEAppUtil_registerConnNotifHandler()
.
The available bit masks may be found in gap.h
.
1/**
2 * Connection Event type to register
3 */
4typedef enum
5{
6 GAP_CB_EVENT_INVALID = 0x00, //!< Invalid event type
7 GAP_CB_CONN_ESTABLISHED = 0x01, //!< Connection established, procedure 0
8 GAP_CB_PHY_UPDATE = 0x02, //!< PHY was updated, procedure 1
9 GAP_CB_CONN_EVENT_ALL = 0xFF //!< All connection Events
10} GAP_CB_Event_e;
1bStatus_t BLEAppUtil_registerConnNotifHandler(uint16_t connHandle)
2{
3 return Gap_RegisterConnEventCb(BLEAppUtil_connEventCB, GAP_CB_REGISTER, GAP_CB_CONN_EVENT_ALL, connHandle);
4}
Whenever a connection event is received, BLEAPPUTIL_EVT_CONN_EVENT_CB
will
queued to the application.
Connection Parameters¶
This section describes the connection parameters which are sent by the initiating device with the connection request and can be modified by either device when the connection is established. These parameters are as follows:
Connection Interval - In Bluetooth Low Energy connections, a frequency-hopping scheme is used. The two devices each send and receive data from one another only on a specific channel at a specific time. These devices meet a specific amount of time later at a new channel (the link layer of the Bluetooth Low Energy protocol stack handles the channel switching). This meeting where the two devices send and receive data is known as a
connection event
. If there is no application data to be sent or received, the two devices exchange link layer data with empty application payload to maintain the connection. The connection interval is the amount of time between two connection events in units of 1.25 ms. The connection interval can range from a minimum value of 6 (7.5 ms) to a maximum of 3200 (4.0 s). See Connection Event and Interval for more details.
Different applications may require different connection intervals. As described in Connection Parameter Considerations, these requirements affect the power consumption of the device. For more detailed information on power consumption, see the Measuring Bluetooth Smart Power Consumption Application Report (SWRA478).
Peripheral Latency - Formerly known as ‘Slave Latency’. This parameter gives the Peripheral device the option of skipping a number of connection events. This ability gives the peripheral device some flexibility. If the peripheral does not have any data to send, it can skip connection events, stay asleep, and save power. The peripheral device selects whether to wake or not on a per connection event basis. The peripheral can skip connection events but must not skip more than allowed by the Peripheral latency parameter or the connection fails. See picture below for more details.
Supervision Time-out - This time-out is the maximum amount of time between two successful connection events. If this time passes without a successful connection event, the device terminates the connection and returns to an unconnected state. This parameter value is represented in units of 10 ms. The supervision time-out value can range from a minimum of 10 (100 ms) to 3200 (32.0 s). The time-out must be larger than the effective connection interval (see Effective Connection Interval for more details).
Effective Connection Interval¶
The effective connection interval is equal to the amount of time between two connection events, assuming that the Peripheral skips the maximum number of possible events if Peripheral latency is allowed (the effective connection interval is equal to the actual connection interval if Peripheral latency is set to 0).
The Peripheral Latency value represents the maximum number of events that can be skipped. This number can range from a minimum value of 0 (meaning that no connection events can be skipped) to a maximum of 499. The maximum value must not make the effective connection interval (see the following formula) greater than 16 s. The interval can be calculated using the following formula:
Effective Connection Interval = (Connection Interval) * (1 + [Peripheral Latency])
Consider the following example:
Connection Interval: 80 (100 ms)
Peripheral Latency: 4
Effective Connection Interval: (100 ms) * (1 + 4) = 500 ms
When no data is being sent from the Peripheral to the Central, the Peripheral transmits during a connection event once every 500 ms.
Connection Parameter Considerations¶
In many applications, the Peripheral skips the maximum number of connection events. Consider the effective connection interval when selecting or requesting connection parameters. Selecting the correct group of connection parameters plays an important role in power optimization of the Bluetooth Low Energy device. The following list gives a general summary of the trade-offs in connection parameter settings.
Reducing the connection interval does as follows:
Increases the power consumption for both devices
Increases the throughput in both directions
Reduces the time for sending data in either direction
Increasing the connection interval does as follows:
Reduces the power consumption for both devices
Reduces the throughput in both directions
Increases the time for sending data in either direction
Reducing the Peripheral latency (or setting it to zero) does as follows:
Increases the power consumption for the peripheral device
Reduces the time for the peripheral device to receive the data sent from a central device
Increasing the Peripheral latency does as follows:
Reduces power consumption for the peripheral during periods when the peripheral has no data to send to the central device
Increases the time for the peripheral device to receive the data sent from the central device
Connection Parameter Update¶
In some cases, the central device requests a connection with a
peripheral device containing connection parameters that are
unfavorable to the peripheral device. In other cases, a peripheral
device might have the desire to change connection parameters in the middle of a
connection, based on the peripheral application. The peripheral
device can request the central device to change the connection
parameters by sending a Connection Parameter Update Request
.
For Bluetooth 4.1, 4.2, 5.0 and 5.1-capable devices, this request is handled directly by
the Link Layer. For Bluetooth 4.0 devices, the L2CAP layer of the
protocol stack handles the request. The Bluetooth Low Energy stack
automatically selects the update method.
The Connection Parameter Update Request
contains four parameters:
Minimum connection interval
Maximum connection interval
Peripheral latency
Supervision time-out
These values represent the parameters that the peripheral device wants for
the connection. The connection interval is given as a range. When
the central device receives the Connection Parameter Update Request
request,
it can accept or reject the new parameters.
Sending a Connection Parameter Update Request
is optional and it is not
required for the central device to accept or apply the requested
parameters. Some applications try to establish a connection at a
faster connection interval to allow for a faster service discovery
and initial setup. These applications later request a longer
(slower) connection interval for optimal power usage.
Regardless of the roles (peripheral or central), connection parameter updates can
be sent asynchronously with the GAP_UpdateLinkParamReq() command. The
simple_peripheral application can be configured to automatically send a
parameter update a certain amount of time after establishing a connection. For
example, the simple_peripheral application uses the following symbols, defined
in simple_peripheral.c
:
#define DEFAULT_DESIRED_MIN_CONN_INTERVAL 80
#define DEFAULT_DESIRED_MAX_CONN_INTERVAL 800
#define DEFAULT_DESIRED_SLAVE_LATENCY 0
#define DEFAULT_DESIRED_CONN_TIMEOUT 1000
#define SP_SEND_PARAM_UPDATE_DELAY 6000
In simple_peripheral, six seconds after a connection is established the application automatically sends a GAP Update Link Parameter Request:
1 static void SimplePeripheral_processParamUpdate(uint16_t connHandle)
2 {
3 gapUpdateLinkParamReq_t req;
4 req.connectionHandle = connHandle;
5 req.connLatency = DEFAULT_DESIRED_SLAVE_LATENCY;
6 req.connTimeout = DEFAULT_DESIRED_CONN_TIMEOUT;
7 req.intervalMin = DEFAULT_DESIRED_MIN_CONN_INTERVAL;
8 req.intervalMax = DEFAULT_DESIRED_MAX_CONN_INTERVAL;
9
10 // Send parameter update
11 bStatus_t status = GAP_UpdateLinkParamReq(&req);
When the peer device receives this update request, the GAP parameter GAP_PARAM_LINK_UPDATE_DECISION determines how it responds. See GAP Peripheral for an explanation of how this parameter is configured.
Connection Termination¶
Either the Central or the Peripheral can terminate a connection for any reason. One side initiates termination and the other side must respond before both devices exit the connected state. Use the GAP_TerminateLinkReq() command to terminate an existing connection.