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.



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


  • TI BLE5-Stack Quick Start
  • The GAP chapter of the [TI BLE5-Stack User's Guide] (located in BLE5-Stack → Generic Access Profile (GAP)) can also provide further insight into the topics discussed in this SimpleLink Academy lab.

Getting started – Desktop

Install the software

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


Modify/Load the software

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.



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.

  • You can read about GAP in the [TI BLE5-Stack User's Guide] and proceed to BLE5-Stack → Generic Access Profile (GAP)*

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

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:

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.

GAP Scanner

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

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.


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.


Which GAP Roles are connectionless (do not support the connected state)? Choose the correct answer(s).


Advertising Basics

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

Bluetooth Low Energy uses 40 different RF channels. 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.

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.


Which of the following PDUs can not be sent on a primary advertisement channel?

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.

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.


Which advertising interval would be better suited for a door lock application powered by battery?

Which advertising interval would be better suited for a temperature sensor application powered by battery?

Advertising Packets

The advertising data consists up to 31 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 No Yes Primary
ADV_SCAN_IND Used to send scannable 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.

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.

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.

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


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.


Which type of legacy advertising does not receive (RX) any packets (is transmit (TX) only)? Choose the correct answer(s).

What information can the Common Extended Advertising Payload Format extended header contain? Choose the correct answer(s).

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:

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

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

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 and primChanMap values of the GapAdv_params_t advParams1 struct have changed:

// Advertisement Params 1
GapAdv_params_t advParams1 = {
  .primIntMin =   800,
  .primIntMax =   800,
  .primChanMap =  GAP_ADV_CHAN_37,
  .peerAddr =     { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa },
  .filterPolicy = GAP_ADV_WL_POLICY_ANY_REQ,
  .primPhy =      GAP_ADV_PRIM_PHY_1_MBPS,
  .secPhy =       GAP_ADV_SEC_PHY_1_MBPS,
  .sid =          0

ti_ble_config.c :: advParams1 – Generated code for Advertisement Set 1 configuration.

Build the project by pressing the hammer icon.

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. 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:

// Advertisement data
uint8_t advData1[] =




ti_ble_config.c – Advertisement data variable.

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.

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:

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

        if (*(uint8_t *)(pEventData->pBuf) == advHandleLegacy)
            bStatus_t status = FAILURE;

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

            // 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;
            // 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 |

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

simple_peripheral.c :: SimplePeripheral_processAdvEvent() :: GAP_EVT_ADV_START_AFTER_ENABLE – Change advertisement data.

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.

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:

// 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);

Limited advertising in time

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

// 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);

Limited number of advertising events

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.

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 Advertising 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:

  // 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 |

simple_peripheral.c :: SimplePeripheral_processGapMessage() :: GAP_DEVICE_INIT_DONE_EVENT – Configuring the GapAdv event mask.

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.
        // 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)

simple_peripheral.c :: SimplePeripheral_processAdvEvent() :: GAP_EVT_SCAN_REQ_RECEIVED – The first time the device receives a scan request, disable advertising.

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. 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.

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

simple_peripheral.c :: SimplePeripheral_processAdvEvent() :: GAP_EVT_ADV_END_AFTER_DISABLE – Configuring the advertising interval at runtime.

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 or 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.


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.

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 scans on one channel 10ms to 10.24s
Scan Duration The duration in which the device stays in the scanning state 10ms to infinity

The following diagram visually shows the scanning parameters:

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.


Which type of scanning does not transmit (TX) any packets, only receives (RX) advertisement packets? Choose the correct answer(s).

After receiving which type of advertisement can an active scanner send a scan request? Choose the correct answer(s).

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 + 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.


Which of the scan PDUs can be sent on the LE 2M PHY?

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.

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 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.

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

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;

gap_scanner.h :: 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.

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;

util.c – Add a function to convert bytes to a string.

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

extern char *Util_convertBytes2Str(uint8_t *pData, uint8_t length);

util.h – Add a function prototype to convert bytes to string.

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

      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",
             Display_print1(dispHandle, 5, 0, "ScanResponseData: %s",
                            Util_convertBytes2Str(pAdvRpt->pData, pAdvRpt->dataLen));
             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) 
      // ...

simple_central.c :: SimpleCentral_processAppMsg() :: SC_EVT_ADV_REPORT – Print the received advertisement and scan response packets.

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. Why?

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

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

// === 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 ====

simple_central.c :: SimpleCentral_doDiscoverDevices() – Configuring scan duration and scan period to scan indefinitely.

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.


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:

// ...
    // 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);
// ...

simple_central.c :: SimpleCentral_processGapMsg() :: GAP_DEVICE_INIT_DONE_EVENT – Turn off the duplicate scanning filter.

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.

  • You can read about all the different scanning filters available in gap_scanner.h in the [TI BLE5-Stack User's Guide].

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.

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.


[TI BLE5-Stack User's Guide]

Bluetooth Core Specification

Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.