Bluetooth Low Energy Scanning and Advertising#

Introduction#

This workshop is a detailed description of how to use the advertising and scanning features of the TI BLE5-Stack. All tasks in this lab session are targeted to be completed within a 2-4 hour time frame. You should have an intermediate level of knowledge of the C programming language as well as experience with embedded software development to be able to complete the tasks.

The first section describes the GAP layer of the Bluetooth Low Energy (LE) protocol, and the subsequent tasks will explore the advertising and scanning and how to make some small changes to the Bluetooth LE application.

It is recommended to read the TI BLE5-Stack User’s Guide alongside this lab for details and further information. Some references will also be made to the Bluetooth Core Specification which specifies how Bluetooth LE devices should operate and communicate with other devices.

Prerequisites#

Hardware#

For this lab, you need two Bluetooth-enabled development boards. Supported devices are:

Software#

Getting started – Desktop#

Install the software#

Run the SimpleLink SDK installer. This gives you the SDK with TI-RTOS included at the default location

C:\ti\simplelink_cc13xx_cc26xx_sdk_x_xx_xx_xx

Modify/Load the software#

Note

Connecting multiple boards at the same time As mentioned before, you will use two boards for this lab. You can assign a specific LaunchPad to each CCS project. You can find instructions to do that here.

  • Load Board #1 with the simple_central project that supports the scanning procedure. The simple_central project is found in the ble5stack examples folder, e.g. <SDK_INSTALL_DIR>\examples\rtos\<BOARD_NAME>\ble5stack\simple_central\tirtos7

  • Load Board #2 with simple_peripheral project that supports the advertising procedure. The simple_peripheral project is found in the ble5stack examples folder, e.g. <SDK_INSTALL_DIR>\examples\rtos\<BOARD_NAME>\ble5stack\simple_peripheral\tirtos7

The following tasks will show how to modify the above projects to showcase scanning and advertising features in the respective projects. The user will have to modify these existing projects in the SimpleLink SDK to experiment with the tasks.

Background#

GAP#

The Generic Access Profile (GAP) layer is a top layer in the host protocol stack that defines how BLE devices behave in standby and connecting states. The purpose of GAP is to ensure interoperability with other Bluetooth LE devices. GAP also describes discovery, link establishment and security procedures. The GAP APIs can be called from the application layer.

The following figure shows the GAP layer in relation to other layers in the software hierarchy:

../../../_images/bt_gap_layers_figure.png

The following GAP roles are defined in the Bluetooth Core Specification and described in gap.h:

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.

The GAP layer API gives very fine-grained control of the device behavior. To help using GAP, the BLE5-Stack contains four modules with higher lever APIs the application can use:

Note

GAP Advertiser

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.

Note

GAP Scanner

The GAP Scanner performs Extended Scanning and Legacy Scanning operations. It controls the Scanner GAP state.

Note

GAP Initiator

The initiator module is used to initiate the connection to a peripheral device. 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.

Note

GAPBondMgr

The GAP Bond Manager is a configurable module that offloads most of the security mechanisms from the application.

In this SimpleLink Academy lab, we will elaborate on the the gap_advertiser and the gap_scanner modules.

### Quiz Which GAP Roles are connectionless (do not support the connected state)? Choose the correct answer(s). - [x] Broadcaster - [ ] Central - [x] Observer - [ ] Peripheral

Advertising#

Advertising Basics#

Bluetooth devices can send advertising packets PDUs (Protocol Data Units) to broadcast data, or to allow other Bluetooth devices to find them and connect to them.

Bluetooth Low Energy uses 40 different RF channels (an RF channel is defined by a frequency and bandwidth at which the data is sent). Three of these channels are called primary advertising channels, and are used for communication outside of connections. This includes:

  • advertisements

  • scan requests

  • scan responses

  • connection requests.

The remaining 37 channels are primarily used for data exchanges within BLE connections. This is illustrated in the following figure. The primary advertising channels are placed to avoid the Wi-Fi channels since Wi-Fi transmissions cause noise on the BLE channels.

../../../_images/BLEchannels.png

Hint

On advertisement channels, both advertisement, scan request and connection request packets are sent. So if there is too much noise on the advertisement channel, the devices will be unable to establish a connection. This is why the advertisement channels are placed the farthest away from the Wi-Fi channels. In addition, they are placed far away from each other.

With Bluetooth 5, new advertisement types are added that expand the possibilities of sending data in advertisement packets. The new advertisement packet types are listed in the Advertising Packets section. Bluetooth 5 opens for advertising on the 37 data channels. When advertisements are sent on the data channels, they are called secondary advertising channels.

### Quiz Which of the following PDUs can **not** be sent on a primary advertisement channel? - [x] A data packet - [ ] An advertisement packet - [ ] A connection request packet

Advertising Parameters#

The following table summarizes the parameters that can be configured for advertising.

Advertising Parameter

Description

Range

Advertising Interval

Time between the start of two consecutive advertising events

20 ms to 10.24 s

Advertising Types

Different PDUs are sent for different types of advertising

See table under Advertising Packets

Advertising Channels

Legacy advertising packets are sent on three channels

Different combinations of channels 37, 38 and 39.

The advertising channels for legacy advertising are channel 37 (2402 MHz), channel 38 (2426 MHz), and channel 39 (2480 MHz). The device can advertise on one, two or three of these. The following diagram shows an advertising event when advertising on all three channels.

../../../_images/adv_channels_diag.png

Note that the same data (ADV_IND) is sent on all three channels. Since the packet is quite small (remember that the advertisement data is no more than 31 bytes), it takes less than 10 ms to send it. The device can be modified to advertise only on selected channels. Advertising on fewer channels will save power, however using more channels will increase the likelihood of a peer device receiving the packet. The user can configure the advertising interval based on the application use case. For example, if a door lock is advertising at a slower interval, it will take longer for the peer device to connect to the door lock which would adversely affect user experience.

### Quiz Which advertising interval would be better suited for a door lock application powered by battery? - [ ] 10 s > Hint: Latency would be too high, would take longer to connect on average. - [x] 1 s - [ ] 7.5 ms > Hint: Would draw too much current for a battery powered application. ### Which advertising interval would be better suited for a temperature sensor application powered by battery? - [x] 10 s - [ ] 1 s > Hint: Unnecessary use of power as temperature does not change that frequently. - [ ] 7.5 ms > Hint: Would draw too much current for a battery powered application.

Advertising Packets#

The advertising data consists up to 31 bytes (For Bluetooth 5 advertising packets, the adv data can be up to 254 bytes.) of user configurable data. There are eight different types of advertising packets, listed in the table below. The last four (in bold type) have been added with Bluetooth 5. They are called extended advertisement PDUs. The packets that start with ADV_ are transmitted on the primary advertising channels. The packets that start with AUX_ are transmitted on the secondary advertisement channels.

Advertising PDU

Description

Max adv data len

Allow scan req

Allow connect

Channel

ADV_IND

Used to send connectable undirected advertisement

31 bytes

Yes

Yes

Primary

ADV_DIRECT_IND

Used to send connectable directed advertisement

N/A (Directed advertisement only includes the 6-byte device address of the initiating device)

No

Yes

Primary

ADV_SCAN_IND

Used to send scannable (Scannable means that if a device receives a scan request, it can respond with a scan response) undirected advertisement

31 bytes

Yes

No

Primary

ADV_NONCONN_IND

Used to send non-connectable undirected advertisement

31 bytes

No

No

Primary

ADV_EXT_IND

Used to indicate that an advertisement will be sent on a secondary advertisement channel.

No adv data allowed.

No

No

Primary

AUX_ADV_IND

Used to send connectable directed advertisement on a secondary advertisement channel.

254 bytes

Yes*

Yes*

Secondary

AUX_SYNC_IND

Used for periodic advertisements on secondary advertisement channels.

254 bytes

No

No

Secondary

AUX_CHAIN_IND

Used to chain advertisement packets, allowing the advertisement data to extend beyond one packet.

254 bytes

N/A

N/A

Secondary

  • AUX_ADV_IND packets can be either scannable or connectable.

  • See the Bluetooth Core Specification Volume 6, Part B, Chapter 4.4 or the GAP chapter of the TI BLE5-Stack User’s Guide (located in BLE5-Stack → Generic Access Profile (GAP)) for more information about the different non-connected states.

For example, if you want to advertise on LE Coded PHY (long range), the device would first use the ADV_EXT_IND to advertise on the primary advertising channels. This ADV_EXT_IND packet would tell scanning devices what secondary channel to use to receive the following AUX_ADV_IND.

Another example: If the device wants to send a lot of advertisement data, it may want to send many advertisement packets chained together to hold more than the 31-byte limit. It may also choose to send them on the faster 2M PHY. To accomplish this, the device must first advertise with ADV_EXT_IND. The ADV_EXT_IND packet would tell scanning devices which secondary channel to listen on (in this case the 2M PHY) for the following AUX_ADV_IND. Then, the AUX_ADV_IND would tell scanning devices that there are chained advertisements coming containing the remaining adv data though AUX_CHAIN_IND PDU’s. In this case, you are not allowed to advertise in connectable or scannable mode.

The above table shows that the legacy advertising modes use the 31 payload bytes for advertisement data except directed advertising, which uses the 6-byte device address of the initiating device as adv data. Directed advertisements have an intended receiving device (scanner), while undirected advertisements do not have a specific intended receiver. In addition, all the legacy advertising types enable sending a scan response, except for the directed and non-connectable advertising. See the scanning section below for an illustration of the sequence of events when a scan response occurs.

It may be worth noting that for directed advertising, the advertisement duration cannot be higher than 1.28 s.

Note

Bluetooth 5 Advertisement PDUs

The rules for directed/undirected, scannable/non-scannable and connectable/non-connectable are a bit more complex for the extended advertising PDUs. The extended advertising PDUs contain an additional header where this information is stored.

The Common Extended Advertising Payload Format is given below. No fields of the extended header are mandatory.

../../../_images/CEAPF.png

Scannable and Connectable

The extended advertisement PDUs can not be both scannable and connectable. They can be either scannable or connectable (or neither). This is indicated in the AdvMode field of the packet.

../../../_images/AdvMode.png

Scannable ADV_EXT_IND and AUX_ADV_IND can not contain any advertising data (just the header).

PHYs

ADV_EXT_IND can be sent on the LE 1M PHY, or a LE Coded PHY. The other extended advertising PDUs can be sent on any PHY.

### Quiz Which type of legacy advertising does not receive (RX) any packets (is transmit (TX) only)? Choose the correct answer(s). - [ ] ADV_IND - [ ] ADV_DIRECT_IND > Hint: Connectable directed advertisers receive connection requests. - [ ] ADV_SCAN_IND > Hint: Scannable advertisers receive scan requests. - [x] ADV_NONCONN_IND > Correct! Non-connectable ADV does not receive scan requests or connection requests. ### What information can the Common Extended Advertising Payload Format extended header contain? Choose the correct answer(s). - [ ] Whether the packet is scannable and connectable. > This information is found in the AdvMode field of the packet. - [x] Tx power of the advertisement packet > This info can be used for RSSI measurements. - [ ] The advertisement data > The advertisement data is not part of the header. - [x] The target device (in case of directed advertisements)

AUX_SYNC_IND is not supported in the SimpleLink CC13xx / CC26xx SDK, and will not be further explored in this SimpleLink Academy lab.

The following tasks will demonstrate advertising using the simple_peripheral project.

Advertising Task 1 – Change Advertising Parameters#

Settings related to advertising are found in the Broadcaster Configuration section of the BLE configuration SysConfig tab, under RF STACKS.

In some cases we want to save power. One way to do this is by increasing the advertisement interval (advertise less frequently), and just advertise on one channel. In this task, we will configure the device to advertise on one channel every 500 ms using the simple_peripheral project. Per default, the simple_peripheral project advertises with two different advertisement sets. The default values of the advertising set 1 are shown below:

../../../_images/set1params.png

In order to change the advertising interval, simply change the value for Primary  PHY Interval Minimum and Primary PHY Interval Maximum. In this case we will set them to 500 (ms).

../../../_images/set_adv_int.png

To change the advertisement channel, open the Primary Channel Map drop-down menu and un-check Channel 38 and Channel 39.

../../../_images/set_adv_channels.png

If you press the Show Generated Files button in the upper right corner of SysConfig and open ti_ble_config.c, you will see that the primIntMin, primIntMax (Note that in the BLE5-Stack, advertisement intervals are given in units of 0.625 ms. Thus, 500 ms is given as 800 * 0.625 ms.) and primChanMap values of the GapAdv_params_t advParams1 struct have changed:

ti_ble_config.c :: advParams1 – Generated code for Advertisement Set 1 configuration.#
// Advertisement Params 1
GapAdv_params_t advParams1 = {
  .eventProps =   GAP_ADV_PROP_CONNECTABLE | GAP_ADV_PROP_LEGACY | GAP_ADV_PROP_SCANNABLE,
  .primIntMin =   800,
  .primIntMax =   800,
  .primChanMap =  GAP_ADV_CHAN_37,
  .peerAddrType = PEER_ADDRTYPE_PUBLIC_OR_PUBLIC_ID,
  .peerAddr =     { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa },
  .filterPolicy = GAP_ADV_WL_POLICY_ANY_REQ,
  .txPower =      GAP_ADV_TX_POWER_NO_PREFERENCE,
  .primPhy =      GAP_ADV_PRIM_PHY_1_MBPS,
  .secPhy =       GAP_ADV_SEC_PHY_1_MBPS,
  .sid =          0
};

Build the project by pressing the hammer icon.

Warning

Changing Advertising Interval at Runtime To change advertising interval when advertising is enabled, you need to stop and re-start advertising again for the new changes to take effect. Please refer to Advertising Task 3 for an example on how to correctly change advertising interval runtime.

Advertising Task 2 – Change the Advertisement Data#

In this task, we will change the advertisement data. The advertisement data can be configured in the Advertisement Data 1 section of the BLE SysConfig tab (Located under BLE -> Broadcaster Configuration -> Advertisement Set 1). When you open Advertisement Data 1 you will be able to enable or disable all possible advertisement data types (AD types). You can read more about the different advertisement data types in gap.h.

After building the example, you can find the default advertisement data by navigating to the generated syscfg ti_ble_config.c located in Release -> syscfg -> ti_ble_config.c, as shown below:

ti_ble_config.c – Advertisement data variable.#
// Advertisement data
uint8_t advData1[] =
{
  0x03,
  GAP_ADTYPE_LOCAL_NAME_SHORT,
  'S',
  'P',

  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),

};

As you can see, the structure of the advertisement data always contains:

  • Length of the following data

  • GAP AD type

  • Data

For example the default advertisement data:

Length

GAP_ADTYPE

Explanation

Data

0x03

GAP_ADTYPE_LOCAL_NAME_SHORT

This flag displays the shortened version of the Local Name

‘S’,’P’

0x02

GAP_ADTYPE_FLAGS

Describes the advertisement mode: Limited, General etc.

GAP_ADTYPE_FLAGS_BREDR_NOT_SUPPORTED and GAP_ADTYPE_FLAGS_GENERAL

0x03

GAP_ADTYPE_16BIT_MORE

This flag contains some of the 16bit Service UUIDs of the device; more UUIDs are available.

LO_UINT16(SIMPLEPROFILE_SERV_UUID), HI_UINT16(SIMPLEPROFILE_SERV_UUID)

The length always includes the length of the GAP_ADTYPE. Note that advertising data should follow a certain format described in the Bluetooth Core Specification. The different data types that can be used in the advertisement data are further explained in the Core Specification Supplement (CSSv7, Part A, Chapter 1) along with examples.

Note

Changing the Advertisement Data There are several ways to change the advertisement data. You can read about them in the TI BLE5-Stack User’s Guide. See BLE5-Stack → Generic Access Profile (GAP)

Here, we will change the advertisement data when advertising is first enabled. This is shown in the flow diagram below:

../../../_images/update_advdata.png

When advertising is enabled, the application receives an event from GAP Advertiser, GAP_EVT_ADV_START_AFTER_ENABLE. We will update the advertisement data with the Load a New Buffer To Multiple Advertising Handles (We have to use this method since there are two advertisement sets using this data; the legacy advertising set that we have already looked at, and a long range advertisement set.) method described in the GAP Chapter of the TI BLE5-Stack User’s Guide. Copy the following code into the GAP_EVT_ADV_START_AFTER_ENABLE case of the SimplePeripheral_processAdvEvent function in Simple_Peripheral.c

simple_peripheral.c :: SimplePeripheral_processAdvEvent() :: GAP_EVT_ADV_START_AFTER_ENABLE – Change advertisement data.#
case GAP_EVT_ADV_START_AFTER_ENABLE:
    if (*(uint8_t *)(pEventData->pBuf) == advHandleLegacy)
    {
        bStatus_t status = FAILURE;

        // Disable advertising and prepare the buffer.
        status = GapAdv_prepareLoadByBuffer(advData1, FALSE);
        SIMPLEPERIPHERAL_ASSERT(status == SUCCESS);

        // Allocate new buffer (and then fill as desired)
        uint8_t ADV_DATA2_LEN = 11;
        uint8_t *advertData2= ICall_malloc(ADV_DATA2_LEN);

        advertData2[0] = 0x02;                   // Length of this data
        advertData2[1] = GAP_ADTYPE_FLAGS;
        advertData2[2] = GAP_ADTYPE_FLAGS_BREDR_NOT_SUPPORTED | GAP_ADTYPE_FLAGS_GENERAL;
        // service UUID, to notify central devices what services are included
        // in this peripheral
        advertData2[3] = 0x03;                  // Length of this data
        advertData2[4] = GAP_ADTYPE_16BIT_MORE; // some of the UUID's, but not all
        advertData2[5] = LO_UINT16(SIMPLEPROFILE_SERV_UUID);
        advertData2[6] = HI_UINT16(SIMPLEPROFILE_SERV_UUID);
        // Add Manufacturer specific data
        advertData2[7] = 0x03;                  //Length of this data
        advertData2[8] = GAP_ADTYPE_MANUFACTURER_SPECIFIC;
        advertData2[9] = 0xAA;
        advertData2[10] = 0xBB;


        // Set event mask -> No longer get event for "advertisement started".
        status = GapAdv_setEventMask(advHandleLegacy,
                                     GAP_ADV_EVT_MASK_END_AFTER_DISABLE |
                                     GAP_ADV_EVT_MASK_SET_TERMINATED);
        SIMPLEPERIPHERAL_ASSERT(status == SUCCESS);

        // Reload buffer and re-enable advertising
        status = GapAdv_loadByBuffer(ADV_DATA2_LEN, advertData2);
        SIMPLEPERIPHERAL_ASSERT(status == SUCCESS);
    }
    Display_printf(dispHandle, SP_ROW_ADVSTATE, 0, "Adv Set %d Enabled",
                   *(uint8_t *)(pEventData->pBuf));
  break;

In the code snippet above, the following occurs:

  • GapAdv_prepareLoadByBuffer() disables advertising for all advertisement sets who uses the advData1 buffer.

  • ICall_malloc() allocates memory in the ICall heap for the new advertisement data.

  • By using the GAP_ADTYPE_MANUFACTURER_SPECIFIC data type, we can put whatever we want in the advertisement data. (In this case it’s 0xAA and 0xBB but you can change it to something else if you want.)

  • GapAdv_loadByBuffer() loads the advertData2 to the relevant advertisement sets and re-enables advertising.

Make sure the code builds without errors. If successful, you should see the advertisement data updated accordingly.

Hint

Limited Advertising Limited advertising can be used to conserve power by advertising for a limited amount of time. General advertisers will continue advertising indefinitely, while limited advertisers advertise for a given amount of times, e.g. 30 s, then stop. This is usually enough time for any devices trying to scan and/or connect to the device to discover it. You can also set a number of advertisement events. In this case, the device will advertise until the number of advertisement events is reached (or until something else disables advertising).

To use limited advertising in simple_peripheral.c, change the calls to GapAdv_enable() to have the following parameters:

Limited advertising in time#
// To advertise for a limited amount of time, use the option GAP_ADV_ENABLE_OPTIONS_USE_DURATION
GapAdv_enableOptions_t myOption = GAP_ADV_ENABLE_OPTIONS_USE_DURATION;
// The time to advertise before stopping is given in units of 10 ms. The range is 10 ms - 655 540 ms
uint16 advDuration = 3000; //Advertise for 30 s (30 000 / 10 ms = 3000 )

GapAdv_enable(advHandleLegacy, myOption, advDuration);

To limit the number of advertisement events, change the calls to GapAdv_enable() to have the following parameters:

Limited number of advertising events#
// To advertise for a limited amount of advertisement events, use the option GAP_ADV_ENABLE_OPTIONS_USE_MAX_EVENTS
GapAdv_enableOptions_t myOption = GAP_ADV_ENABLE_OPTIONS_USE_MAX_EVENTS;
// The maximum number of advertisements to send before stopping has the range 1-256
uint16 advDuration = 60;

GapAdv_enable(advHandleLegacy, myOption, advDuration);

Note that GapAdv_enable is called multiple times in simple_peripheral.c.

When an advertisement set is terminated (because the time/number of adv events is used up), a GAP_EVT_ADV_SET_TERMINATED event is posted. Go ahead and change the GAP_EVT_ADV_SET_TERMINATED event in simple_peripheral.c to print out something fun!

In addition, the flag in the advertisement data should be changed to GAP_ADTYPE_FLAGS_LIMITED. You can do this directly in the advertisement data.

../../../_images/limited_adflag.png

Limited advertising can be used to classify/filter devices. For example, devices that are scanning in limited discovery mode can only receive advertisement data from limited advertising devices. See the Bluetooth Core Specification for more information on the limited discovery procedure.

Advertising Task 3 – Change Adverstising Interval at Runtime#

In this task we will advertise as previously, but when the device is discovered it will change the advertising interval to advertise more frequently. Imagine a BLE beacon that transmits data to passing phones. If no phones are around there is no reason to advertise often, since every advertisement packet consumes power. When the beacon receives a scan request it knows that someone is around and it makes sense to advertise more frequently. In the first task we changed the advertising interval to 500 ms. We will keep this, but when the device is discovered for the first time it will start advertising with 100 ms interval. You can use a Bluetooth packet sniffer to observe the advertising interval. You can also use EnergyTrace™ to indirectly measure the advertising interval by monitoring the device’s current consumption. See the Measuring Current Consumption App Note for details and setup.

We want this change to happen at runtime, thus it cannot be configured in SysConfig. Open the application file (simple_peripheral.c). In order to see if Simple Peripheral has received any scan requests, we have to add GAP_ADV_EVT_MASK_SCAN_REQ_NOTI to the GapAdv event mask. Replace the existing call to GapAdv_setEventMask() with the following:

simple_peripheral.c :: SimplePeripheral_processGapMessage() :: GAP_DEVICE_INIT_DONE_EVENT – Configuring the GapAdv event mask.#
  // Set event mask for set #1
  status = GapAdv_setEventMask(advHandleLegacy,
                           GAP_ADV_EVT_MASK_SCAN_REQ_NOTI |
                           GAP_ADV_EVT_MASK_START_AFTER_ENABLE |
                           GAP_ADV_EVT_MASK_END_AFTER_DISABLE |
                           GAP_ADV_EVT_MASK_SET_TERMINATED);

Whenever a scan request is received, an event will be sent to the GapAdv callback function (SimplePeripheral_advCallback()), which in turn will post it to the SimplePeripheral_processAdvEvent() function. We can use this event to set the advertisement interval, using the GapAdv_setParam() API. However, before the GapAdv_setParam() is used, advertising has to be disabled.

The scan request event is received in SimplePeripheral_processAdvEvent(), as GAP_EVT_SCAN_REQ_RECEIVED.

  • Inside this event we need to disable advertising (because GapAdv_setParam() can not be used while advertising is enabled)

  • The event is posted every time the device receives a scan request. However we only want to update the advertisement interval once, so check whether this one is already updated.

Solution for GAP_EVT_SCAN_REQ_RECEIVED
simple_peripheral.c :: SimplePeripheral_processAdvEvent() :: GAP_EVT_SCAN_REQ_RECEIVED – The first time the device receives a scan request, disable advertising.#
case GAP_EVT_SCAN_REQ_RECEIVED:
{
	// Check whether the advertisement interval has been changed already.
	uint8_t advInterval = 0;
	GapAdv_getParam(advHandleLegacy, GAP_ADV_PARAM_PRIMARY_INTERVAL_MIN, &advInterval);

	if (advInterval != 160)
	{
    	GapAdv_disable(advHandleLegacy);
  	}
}
break;

When advertisement is disabled, GAP posts an event, GAP_EVT_ADV_END_AFTER_DISABLE. Inside this event we know that advertising is disabled, so we can use GapAdv_setParam().

  • Find the list of advertising parameters that can be changed with GapAdv_setParam() in gap_advertiser.h (Hint: Look for the type definition of GapAdv_ParamId_t.). Find the parameter you would like to change.

  • Again, the advertisement interval is given in units of 0.625 ms. Calculate the correct value for 100 ms advertisement interval.

  • Make sure the advertisement interval is only updated once.

  • Remember to re-enable advertising.

  • You can read about GapAdv_setParam() in gap_advertiser.h or go to the API Reference Guide by navigating to the TI BLE5-Stack User’s Guide and go to API References → GAP.

Solution for GAP_EVT_ADV_END_AFTER_DISABLE
simple_peripheral.c :: SimplePeripheral_processAdvEvent() :: GAP_EVT_ADV_END_AFTER_DISABLE – Configuring the advertising interval at runtime.#
case GAP_EVT_ADV_END_AFTER_DISABLE:
{
  Display_printf(dispHandle, SP_ROW_ADVSTATE, 0, "Adv Set %d Disabled",
                 *(uint8_t *)(pEventData->pBuf));
  // Check whether the advertisement interval has been changed
  uint8_t advInterval = 0;
  GapAdv_getParam(advHandleLegacy, GAP_ADV_PARAM_PRIMARY_INTERVAL_MIN, &advInterval);
  if (advInterval != 160)
  {
    // Set the advertisement interval to 100 ms. 100 ms / 0.625 ms = 160.
    advInterval = 160;
    GapAdv_setParam(advHandleLegacy, GAP_ADV_PARAM_PRIMARY_INTERVAL_MIN, &advInterval);
    GapAdv_setParam(advHandleLegacy, GAP_ADV_PARAM_PRIMARY_INTERVAL_MAX, &advInterval);

    // Re-enable advertising.
    GapAdv_enable(advHandleLegacy, GAP_ADV_ENABLE_OPTIONS_USE_MAX , 0);
  }
}
break;

If you haven’t loaded the simple_peripheral project into your device yet - do it now!

After you have completed Scanning Task 1, come back to this quiz and adjust advertising interval accordingly in simple_peripheral. Also refer to the tips in Choosing advertising interval: Fast of slow? and Optimizing Scanning: How to get data fast?.

### Which advertising interval is best suited to get an acceptable detection rate for the settings used in Scanning Task 1? Choose the correct answer. - [ ] 160 ms > Hint: Not slow, but what happens to detection rate if scanner has scan window smaller than advertising interval + 10 ms? - [x] 140 ms > This is ok, but for even faster detection you can reduce the interval even further at the expense of higher power consumption. - [ ] 1 s > Too slow! Slow advertising intervals mean less opportunity for the scanner to intercept an advertisement.

Scanning#

Scanning Basics#

When not connected, Bluetooth Low Energy devices can either advertise their presence by transmitting advertisement packets or scan for nearby devices that are advertising. The process of scanning for devices is called device discovery. There are two types of scanning; active and passive. The difference is that an active scanner can send a scan request to request additional information from the advertiser, while a passive scanner can only receive data from advertising device. Note that the terms discovery and scanning may be used interchangeably. The figure below shows the sequence where the scanner sends a scan request to an advertiser during an advertising event.

../../../_images/scanning_flowchart.png

When it comes to the timing of a scan, there are a few parameters you need to get familiar with. Each of these parameters has a range dictated by the Bluetooth Core Specification. The Time Inter Frame Space (T_IFS) is the time interval between two consecutive packets on the same channel index and is set to 150us by the BLE specification.

Scan Parameter

Description

Range

Scan Interval

The interval between the start of two consecutive scan windows

10ms to 10.24s

Scan Window

The duration in which the Link Layer (The Link Layer is a layer in the controller, see the figure in the Background section.) scans on one channel

10ms to 10.24s

Scan Duration

The duration in which the device stays in the scanning state

10ms to infinity (Max value is 65534ms but if this value is set to 0, the device scans indefinitely)

The following diagram visually shows the scanning parameters:

../../../_images/scanning_diagram.png

Note that the order of the scanning channels are not configurable. The device will scan on channel 37 (2402 MHz), then channel 38 (2426 MHz), and then channel 39 (2480 MHz) respectively and in that order on every scanning interval for the length of time defined by the scanning window.

### Quiz Which type of scanning does not transmit (TX) any packets, only receives (RX) advertisement packets? Choose the correct answer(s). - [ ] active scanning > Hint: Active scanning allows sending scan requests. - [x] passive scanning ### After receiving which type of advertisement can an active scanner send a scan request? Choose the correct answer(s). - [x] Connectable undirected advertisement - [ ] Connectable directed advertisement > Hint: Only two of them are correct. - [x] Scannable undirected advertisement - [ ] Non-connectable undirected advertisement

Scanning Packets#

Scannable advertisement packets on secondary advertising channels can also incite a scan request and scan response. These are called AUX_SCAN_REQ and AUX_SCAN_RSP. All scanning related packets are summarized in the following table:

Scanning PDU

Transmitting device

Payload

SCAN_REQ

Scanner

Scanner’s address + advertiser’s address

SCAN_RSP

Advertiser

Advertiser’s address + 0-31 bytes scan response data

AUX_SCAN_REQ

Scanner

Scanner’s address + advertiser’s address

AUX_SCAN_RSP

Advertiser

Header (Common Extended Advertising Payload Format header) + 0-254 bytes data

Scan requests and scan responses are always sent on the same channel and PHY as the advertising packet that prompted them.

### Quiz Which of the scan PDUs can be sent on the LE 2M PHY? - [ ] SCAN_REQ - [ ] SCAN_RSP - [x] AUX_SCAN_REQ > If the device is scanning on LE 2M PHY and receives a scannable AUX_ADV_IND, it can send a scan request on LE 2M PHY. - [x] AUX_SCAN_RSP

The following tasks will demonstrate scanning using the simple_central project. If you haven’t already done so, import simple_central to your workspace!

Scanning Task 1 – Change Scanning Parameters#

Before we change the scan parameters, let’s find out where to change this in SysConfig. Open simple_central.syscfg and scroll down to the Observer section of the BLE configuration tab. The following is a screenshot of the default scan settings.

../../../_images/observer_config.png

In this task, we will scan continuously with the following scan parameters:

  • Scan interval: 150 ms

  • Scan window: 150 ms

  • Scan duration: 5 s

  • Scan type: Passive

  • Scan PHY: LE 1M PHY (Legacy scanning)

Once you have changed the settings as above, open the Show Generated Files view (Look for the “<>” icon at the top right of the SysConfig window!) to see the changes in ti_ble_config.h.

  • In gap_scanner.h you can find a full description on all parameters that can be configured for the GAP Scanner module.

Warning

Two Kinds of Discovery

Note that this discovery procedure to discover nearby devices is not to be confused with the characteristic discovery procedure which can be started after a connection is established.

If you haven’t loaded the simple_central project into your device yet – now is the time!

Scanning Task 2 – Print Scanning Results#

Every time the device receives an advertisement or scan response packet, the application receives a GAP_EVT_ADV_REPORT event from the GAP layer. This event is caught in SimpleCentral_scanCb() (You can see and change what GAP Scanner events SimpleCentral_scanCb() will receive in GapScan_setEventMask()), then sent it to SimpleCentral_processAppMsg().

The information received during a scan, used to discover devices advertising, is encapsulated in the GapScan_Evt_AdvRpt_t structure:

gap_scanner.h :: GapScan_Evt_AdvRpt_t#
typedef struct {
uint8_t  evtType;                 // Bits 0 to 4 indicate connectable, scannable, directed, scan rsp, and legacy respectively
  	GAP_Addr_Types_t addrType;        // Public, random, public ID, random ID, or anonymous
  	uint8_t  addr[B_ADDR_LEN];        // Address of the advertising device
  	GapScan_ScannedPhy_t primPhy;     // PHY of the primary advertising channel
  	GapScan_ScannedPhy_t secPhy;      // PHY of the secondary advertising channel
  	uint8_t  advSid;                  // SID(0x00-0x0f) of the advertising PDU
  	int8_t   txPower;                 // -127 dBm <= TX power <= 126 dBm
  	int8_t   rssi;                    // -127 dBm <= RSSI <= 20 dBm
  	GAP_Addr_Types_t directAddrType;  // Type of TargetA address in the directed advertising PDU
  	uint8_t  directAddr[B_ADDR_LEN];  // TargetA address
  	uint16_t periodicAdvInt;          // Periodic advertising interval. 0 means no periodic advertising.
  	uint16_t dataLen;                 // Length of the data
  	uint8_t  *pData;                  // Pointer to advertising or scan response data
} GapScan_Evt_AdvRpt_t;

The following code snippets demonstrate printing the received advertising data. First, create a function named Util_convertBytes2Str() to convert bytes to a string. Create this function in util.c.

util.c – Add a function to convert bytes to a string.#
char *Util_convertBytes2Str(uint8_t *pData, uint8_t length)
{
  uint8_t     charCnt;
  char        hex[] = "0123456789ABCDEF";
  static char str[(3*31)+1];
  char        *pStr = str;

  for (charCnt = 0; charCnt < length; charCnt++)
  {
*pStr++ = hex[*pData >> 4];
*pStr++ = hex[*pData++ & 0x0F];
if(!((charCnt+1) >= length))
{
    *pStr++ = ':';
}
  }
  str[(3*length)-1] = '\0';
  pStr = NULL;

  return str;
}

Then create a function prototype in util.h for Util_convertBytes2Str().

util.h – Add a function prototype to convert bytes to string.#
extern char *Util_convertBytes2Str(uint8_t *pData, uint8_t length);

Then add the following code to the SimpleCentral_processAppMsg()::SC_EVT_ADV_REPORT case statement to print the received advertisement and scan response packets.

simple_central.c :: SimpleCentral_processAppMsg() :: SC_EVT_ADV_REPORT – Print the received advertisement and scan response packets.#
case SC_EVT_ADV_REPORT:
{
  GapScan_Evt_AdvRpt_t* pAdvRpt = (GapScan_Evt_AdvRpt_t*) (pMsg->pData);

// === SOLUTION [Print scan results] ===
     //Print scan response data or advertising data
     if (pAdvRpt->evtType &= ADV_RPT_EVT_TYPE_SCAN_RSP)
     {
         Display_print1(dispHandle, 4, 0, "ScanResponseAddr: %s",
                        Util_convertBdAddr2Str(pAdvRpt->addr));
         Display_print1(dispHandle, 5, 0, "ScanResponseData: %s",
                        Util_convertBytes2Str(pAdvRpt->pData, pAdvRpt->dataLen));
     }
     else
     {
         Display_print2(dispHandle, 6, 0, "Advertising Addr: %s RSSI: %d",
                        Util_convertBdAddr2Str(pAdvRpt->addr), pAdvRpt->rssi);
         Display_print1(dispHandle, 7, 0, "Advertising Data: %s",
                        Util_convertBytes2Str(pAdvRpt->pData, pAdvRpt->dataLen));
     }
// === END SOLUTION [Print scan results] ===

  //Auto connect is enabled
  if (autoConnect) 
  // ...

Above, the function Util_convertBdAddr2Str() is used at points convert the BD address to a string. This is defined in util.h.

Re-build and flash the project. You may notice that you only receive advertisement packets and no scan response packets (In the last task we configured the device to do passive scanning, i.e. we’re not sending out scan requests).

Scanning Task 3 – Scan Indefinitely#

When the application needs to scan for nearby devices at all times, i.e. always be in the scanning state, set DEFAULT_SCAN_DURATION to 0. This will set the device in continuous scanning. Alternatively, we can set the device to scan continuously for a while, then take a break periodically. We will do this by setting the scan duration and scan period as follows:

  • Scan duration: 1 s

  • Scan period: 5 s

../../../_images/scanduationperiod.png

Scan duration can be set in SysConfig, but the scan period must be set in the application.

simple_central.c :: SimpleCentral_doDiscoverDevices() – Configuring scan duration and scan period to scan indefinitely.#
// === SOLUTION [Scan indefinitely] ===
//Scan period. Ignored if duration is zero. 1.28 sec unit
uint16_t scanPeriod = 4; // 4 * 1.28 s = 5.12 s.
GapScan_enable(scanPeriod, DEFAULT_SCAN_DURATION, 0);
// ==== END SOLUTION ====

This will make the device scan for 1 s, then pause for 4 s, before scanning for 1 s, etc.

Note that even in indefinite scanning, the number of scanned results will still be limited by DEFAULT_MAX_SCAN_RES. In this case, DEFAULT_MAX_SCAN_RES is set to 0. When zero, this parameter is ignored.

Note

Regardless of the number of devices the scanner has discovered, the application will return “0 Devices Found” when discovery is ended.

Filter Duplicate Advertisers#

For some applications, processing multiple advertisements by the same peer device can be useful. E.g. when collecting information from a beacon that updates its advertisement data frequently. By default, the scanning device will filter advertising data. You can turn this off by navigating to the Observer Configuration tab in SysConfig and setting Duplicate Filter to disabled.

To do this at runtime using the following API:

simple_central.c :: SimpleCentral_processGapMsg() :: GAP_DEVICE_INIT_DONE_EVENT – Turn off the duplicate scanning filter.#
// ...
// Set Advertising report fields to keep
temp16 = ADV_RPT_FIELDS;
GapScan_setParam(SCAN_PARAM_RPT_FIELDS, &temp16);
// Set LL Duplicate Filter
GapScan_FilterDuplicate_t scanFilter = SCAN_FLT_DUP_DISABLE;
GapScan_setParam(SCAN_PARAM_FLT_DUP, &scanFilter);
// ...

This will enable the controller to receive all the advertisement packets it sees over the air, and send it to the application for further processing.

Example UART Output from Tasks 1-3#

The figure below shows 2 serial COM ports emulators (PuTTY) which show the printed result of simple_peripheral and simple_central due to the above changes in both Scanning Task 1-3 and Advertising Task 1-3.

Once the updated projects are loaded, remember to use the right and left buttons (BTN1 and BTN2) to navigate the menu.

../../../_images/advertiser_scanner_example_output.png

Miscellaneous Guidelines#

Choosing advertising interval: Fast or slow?#

The advertising interval should be set based on use case. Both fast and slow advertising have pros and cons:

  • Slow advertising: higher advertising interval, lower power consumption, low probability of short discovery time.

  • Fast advertising: lower advertising interval, higher power consumption, high probability of short discovery time.

Optimizing Scanning: How to get data fast?#

In a time-constrained application, when the user needs to receive the data as fast as possible, make sure that the scan window is more than the advertising interval + 10ms to guarantee discovery. (The 10 ms extra account for the 0 ms to 10 ms of pseudo-random delay in between each advertising event, assuming no interference.) Following this rule will increase the chance of receiving the advertising packet on the first scan.

References#

TI BLE5-Stack User’s Guide

Bluetooth Core Specification