Summary

In this article I discuss BLE “Observing” and add that functionality to my PSoC 6 – CYW43xxx AnyCloud BLE Adverting Scanner project.

Story

In part1 of this series I discussed the pieces parts required to get the AnyCloud Bluetooth Stack operating using the AnyCloud SDK running on a PSoC 6 with a CY43xxx combo.  Then in part 2 I built a project with those parts and started up the Bluetooth Host stack.  The project didn’t really do anything, actually nothing, so it wasn’t very interesting, but it was going.  In this article I will discuss BLE advertising scanning, how to configure it in the AnyCloud project and finally how to add it to the project.

There are

Article Topic
AnyCloud Bluetooth Advertising Scanner (Part 1) Introduction to AnyCloud Bluetooth Advertising
AnyCloud Bluetooth Advertising Scanner (Part 2) Creating an AnyCloud Bluetooth project
AnyCloud Bluetooth Advertising Scanner (Part 3) Adding Observing functionality to the project
AnyCloud Bluetooth Utilities Library A set of APIs for enhancement of the AnyCloud Library
AnyCloud Bluetooth Advertising Scanner (Part 4) Adding a command line to the scanner
AnyCloud Bluetooth Advertising Scanner (Part 5) Adding a history database to the scanner
AnyCloud Bluetooth Advertising Scanner (Part 6) Decoding advertising packets
AnyCloud Bluetooth Advertising Scanner (Part 7) Adding recording commands to the command line
AnyCloud Bluetooth Advertising Scanner (Part 8) Adding filtering to the scanner
AnyCloud Bluetooth Advertising Scanner (Part 9) Improve the print and add packet age
AnyCloud Bluetooth Advertising Scanner (Part 10) Sort the database

All of the code can be found at git@github.com:iotexpert/AnyCloudBLEScanner.git and https://github.com/iotexpert/AnyCloudBLEScanner.git

There are git tags in place starting at part 5 so that you can look at just that version of the code.  "git tag" to list the tags.  And "git checkout part6" to look at the part 6 version of the code.

You can also create a new project with this is a template if you have the IoT Expert Manifest Files installed

Explain BLE Advertising – Scanner/Observer

You might recall that there are four roles that a BLE device can perform

  • Peripheral – low power devices that broadcast advertisements, then accept a single connection
  • Central – devices like cell phones that connect to peripherals.  They may run multiple connections at a time.
  • Broadcaster – a nonconnectable peripheral that sends out advertisements
  • Observer – A central-like device that listens for broadcasters (or advertising peripherals)

And you might remember that advertisements are short, up to 31-byte, packets of data that give information which can be used for one or more of:

  • advertising the availability to connect
  • advertising services
  • advertising the name
  • advertising vendor specific information
  • advertising beacon data (like temperature or …)
  • advertising location

And, if you forgot, BLE operates on 40 channels.  But to save power in peripherals, all of the advertising happens on channel 37, 38 and 39.  Specifically a peripheral or broadcaster will send out the advertising data on channel 37, then 38 then 39, then wait… then do it again.  But why one channel at a time?  Because BLE radio’s can be tuned to transmit and receive on only one channel at a time (a power saving and complexity reducing feature)

Inside of the Central/Observer it will listen on channel 37 for a “window” amount of time.  Then it will do nothing for an interval-window amount of time.  Then it will do that same thing on channel 28 then 39.  But why only one channel at a time?  Same reason as above, it saves power and simplifies the design.  Why not have the window and the interval be the same?  Once again, it saves power.

Here is a picture:

But, what happens if you are not listening when the advertiser advertises?  You missed it.  Tough shit.  It turns out that setting the scan window and interval will greatly impact the probability that you hear advertisements.  And, you are more likely to hear advertisements because they are sent on three channels.  But it seems like it will never work.  Will it? … yes, of course, or they wouldn’t have done it that way 🙂

BLE Advertising – The Advertiser Peripheral or Broadcaster

So what exactly is inside of an advertising packet?  Volume 6 part B Section 2.3 of the bluetooth core spec describes the advertising protocol data unit (PDU)

But what is inside of the header?

This leaves us with what is inside of the “payload”.  The answer is that the ADV_IND Payload Data Unit (PDU) contains an address of 6-bytes plus up to 31 bytes of data.

The AdvA field shall contain the advertiser’s public or random device address as indicated by TxAdd.

The actual AdvData field is further broken up into “AD Structures” like this:

And what is the “AD Type”, well it is a one byte of one of the following:

And then where do you find the assigned numbers for the field types?  In the “Assigned Numbers and GAP“.  Here is a clip from the spec.

And conveniently enough we enumerated them for you inside of the SDK header file wiced_bt_ble.h

/** Advertisement data types */
enum wiced_bt_ble_advert_type_e {
BTM_BLE_ADVERT_TYPE_FLAG                        = 0x01,                 /**< Advertisement flags */
BTM_BLE_ADVERT_TYPE_16SRV_PARTIAL               = 0x02,                 /**< List of supported services - 16 bit UUIDs (partial) */
BTM_BLE_ADVERT_TYPE_16SRV_COMPLETE              = 0x03,                 /**< List of supported services - 16 bit UUIDs (complete) */
BTM_BLE_ADVERT_TYPE_32SRV_PARTIAL               = 0x04,                 /**< List of supported services - 32 bit UUIDs (partial) */
BTM_BLE_ADVERT_TYPE_32SRV_COMPLETE              = 0x05,                 /**< List of supported services - 32 bit UUIDs (complete) */
BTM_BLE_ADVERT_TYPE_128SRV_PARTIAL              = 0x06,                 /**< List of supported services - 128 bit UUIDs (partial) */
BTM_BLE_ADVERT_TYPE_128SRV_COMPLETE             = 0x07,                 /**< List of supported services - 128 bit UUIDs (complete) */
BTM_BLE_ADVERT_TYPE_NAME_SHORT                  = 0x08,                 /**< Short name */
BTM_BLE_ADVERT_TYPE_NAME_COMPLETE               = 0x09,                 /**< Complete name */
BTM_BLE_ADVERT_TYPE_TX_POWER                    = 0x0A,                 /**< TX Power level  */
BTM_BLE_ADVERT_TYPE_DEV_CLASS                   = 0x0D,                 /**< Device Class */
BTM_BLE_ADVERT_TYPE_SIMPLE_PAIRING_HASH_C       = 0x0E,                 /**< Simple Pairing Hash C */
BTM_BLE_ADVERT_TYPE_SIMPLE_PAIRING_RAND_C       = 0x0F,                 /**< Simple Pairing Randomizer R */
BTM_BLE_ADVERT_TYPE_SM_TK                       = 0x10,                 /**< Security manager TK value */
BTM_BLE_ADVERT_TYPE_SM_OOB_FLAG                 = 0x11,                 /**< Security manager Out-of-Band data */
BTM_BLE_ADVERT_TYPE_INTERVAL_RANGE              = 0x12,                 /**< Slave connection interval range */
BTM_BLE_ADVERT_TYPE_SOLICITATION_SRV_UUID       = 0x14,                 /**< List of solicitated services - 16 bit UUIDs */
BTM_BLE_ADVERT_TYPE_128SOLICITATION_SRV_UUID    = 0x15,                 /**< List of solicitated services - 128 bit UUIDs */
BTM_BLE_ADVERT_TYPE_SERVICE_DATA                = 0x16,                 /**< Service data - 16 bit UUID */
BTM_BLE_ADVERT_TYPE_PUBLIC_TARGET               = 0x17,                 /**< Public target address */
BTM_BLE_ADVERT_TYPE_RANDOM_TARGET               = 0x18,                 /**< Random target address */
BTM_BLE_ADVERT_TYPE_APPEARANCE                  = 0x19,                 /**< Appearance */
BTM_BLE_ADVERT_TYPE_ADVERT_INTERVAL             = 0x1a,                 /**< Advertising interval */
BTM_BLE_ADVERT_TYPE_LE_BD_ADDR                  = 0x1b,                 /**< LE device bluetooth address */
BTM_BLE_ADVERT_TYPE_LE_ROLE                     = 0x1c,                 /**< LE role */
BTM_BLE_ADVERT_TYPE_256SIMPLE_PAIRING_HASH      = 0x1d,                 /**< Simple Pairing Hash C-256 */
BTM_BLE_ADVERT_TYPE_256SIMPLE_PAIRING_RAND      = 0x1e,                 /**< Simple Pairing Randomizer R-256 */
BTM_BLE_ADVERT_TYPE_32SOLICITATION_SRV_UUID     = 0x1f,                 /**< List of solicitated services - 32 bit UUIDs */
BTM_BLE_ADVERT_TYPE_32SERVICE_DATA              = 0x20,                 /**< Service data - 32 bit UUID */
BTM_BLE_ADVERT_TYPE_128SERVICE_DATA             = 0x21,                 /**< Service data - 128 bit UUID */
BTM_BLE_ADVERT_TYPE_CONN_CONFIRM_VAL            = 0x22,                 /**< LE Secure Connections Confirmation Value */
BTM_BLE_ADVERT_TYPE_CONN_RAND_VAL               = 0x23,                 /**< LE Secure Connections Random Value */
BTM_BLE_ADVERT_TYPE_URI                         = 0x24,                 /**< URI */
BTM_BLE_ADVERT_TYPE_INDOOR_POS                  = 0x25,                 /**< Indoor Positioning */
BTM_BLE_ADVERT_TYPE_TRANS_DISCOVER_DATA         = 0x26,                 /**< Transport Discovery Data */
BTM_BLE_ADVERT_TYPE_SUPPORTED_FEATURES          = 0x27,                 /**< LE Supported Features */
BTM_BLE_ADVERT_TYPE_UPDATE_CH_MAP_IND           = 0x28,                 /**< Channel Map Update Indication */
BTM_BLE_ADVERT_TYPE_PB_ADV                      = 0x29,                 /**< PB-ADV */
BTM_BLE_ADVERT_TYPE_MESH_MSG                    = 0x2A,                 /**< Mesh Message */
BTM_BLE_ADVERT_TYPE_MESH_BEACON                 = 0x2B,                 /**< Mesh Beacon */
BTM_BLE_ADVERT_TYPE_PSRI                        = 0x2E,                 /**< Generic Audio Provate Set Random Identifier */
BTM_BLE_ADVERT_TYPE_3D_INFO_DATA                = 0x3D,                 /**< 3D Information Data */
BTM_BLE_ADVERT_TYPE_MANUFACTURER                = 0xFF                  /**< Manufacturer data */
};

How does scanning work in the AnyCloud Bluetooth Stack?

To turn on observing/scanning you need to call the function:

wiced_bt_dev_status_t wiced_bt_ble_observe (wiced_bool_t start, uint8_t duration, wiced_bt_ble_scan_result_cback_t *p_scan_result_cback);

Which will cause the host stack to tell the controller to start scanning for advertising packets.  It will set the scan window and scan interval the low duty scan settings from the bluetooth configuration structure… which we setup with the Bluetooth configurator.

 .low_duty_scan_interval          = CY_BT_LOW_DUTY_SCAN_INTERVAL,                              /**< Low duty scan interval */
.low_duty_scan_window            = CY_BT_LOW_DUTY_SCAN_WINDOW,                                /**< Low duty scan window */
.low_duty_scan_duration          = CY_BT_LOW_DUTY_SCAN_DURATION,                              /**< Low duty scan duration in seconds (0 for infinite) */

When the controller hears an advertising packet, it will send the HCI advertising report to the Bluetooth host stack, which with then call you back.  Specifically it will call you back by calling the p_scan_result_cback” function.

You provide the callback function which has the prototype:

typedef void (wiced_bt_ble_scan_result_cback_t) (wiced_bt_ble_scan_results_t *p_scan_result, uint8_t *p_adv_data);

which contains two parameters, p_scan_result which is a structure that has the mac address and some thing data plus the p_adv_data which has the raw bytes of the advertising packet.

Add Observing to our Project

OK.  Lets add this to our project by creating a callback function like this:

Lines 5-9: Just prints out the raw bytes of the MAC address of the remote device, the one advertising

To print out the raw advertising data you need to remember that it is formatted as

  1. A length (of all of the data of the field)
  2. A type
  3. The rest of the data

When you find a field of length of 0 you know that you have reached the end of the data

On Lines 13-20: I print out one field at a time and the raw data

//
void obv_callback(wiced_bt_ble_scan_results_t *p_scan_result, uint8_t *p_adv_data)
{
// Print the MAC Address
printf("MAC: ");
for(int i=0;i<6;i++)
{
printf("%02X:",p_scan_result->remote_bd_addr[i]);
}
// Print the RAW Data of the ADV Packet
printf(" Data: ");
int i=0;
while(p_adv_data[i])
{
for(int j=0;j<p_adv_data[i];j++)
{
printf("%02X ",p_adv_data[i+1+j]);
}
i = i + p_adv_data[i]+1;
}
printf("\n");
}

Then update the management callback to start the scanner after the stack is successfully started

    switch (event)
{
case BTM_ENABLED_EVT:
if (WICED_BT_SUCCESS == p_event_data->enabled.status)
{
printf("Started BT Stack Succesfully\n");
wiced_bt_ble_observe(WICED_TRUE,0,obv_callback);
}

Program and Test

Now when I run the program data comes blasting out of the screen because there are a boatload of ble devices in my house

AnyCloud> Unhandled Bluetooth Management Event: 0x16
Started BT Stack Succesfully
MAC: 76:99:58:E8:8B:1F:Data: 01 1A 0A 0C FF 4C 00 10 06 13 1A 54 F7 5A 7A 
MAC: 9E:7B:EF:0B:74:20:Data: 01 06 16 F7 FD 01 0C C2 81 CE 0C 74 58 77 19 C8 E3 84 A3 42 50 98 00 00 00 00 03 
MAC: 6F:11:7C:FF:02:13:Data: 01 1A 0A 05 FF 4C 00 10 06 03 1E BA 24 58 3D 
MAC: 3F:64:BE:4E:29:0C:Data: 01 04 FF 00 4C 02 15 26 86 F3 9C BA DA 46 58 85 4A A6 2E 7E 5E 8B 8D 00 01 00 00 C9 
MAC: 47:4B:F1:53:2C:84:Data: 01 06 FF 4C 00 10 05 08 18 79 1E C2 
MAC: C8:69:CD:18:BC:E6:Data: 01 1A 0A 0C FF 4C 00 10 05 0C 14 17 BF E9 
MAC: 27:F6:6F:1E:7A:78:Data: 01 1A FF 4C 00 09 06 03 12 C0 A8 20 0D 
MAC: 6F:AE:84:F6:6A:9F:Data: 01 06 FF 4C 00 10 05 08 18 79 1E C2 
MAC: 3F:64:BE:4E:29:0C:Data: 01 04 FF 00 4C 02 15 26 86 F3 9C BA DA 46 58 85 4A A6 2E 7E 5E 8B 8D 00 01 00 00 C9 
MAC: 41:EE:B4:9C:5C:5F:Data: 01 1A 0A 07 FF 4C 00 10 06 33 1A 49 59 46 B4 
MAC: 9E:7B:EF:0B:74:20:Data: 01 06 16 F7 FD 01 0C C2 81 CE 0C 74 58 77 19 C8 E3 84 A3 42 50 98 00 00 00 00 03 
MAC: C8:EB:ED:C8:AC:1C:Data: 01 0A 03 66 66 19 D0 07 FF EE 03 1C AC C8 ED EB C8 
MAC: 76:99:58:E8:8B:1F:Data: 01 1A 0A 0C FF 4C 00 10 06 13 1A 54 F7 5A 7A

In the next article Ill add some more smarts to manage the data to be easier to look at.

For your information here is all of the file bluetoothManager.c

#include <stdio.h>
#include <stdlib.h>
#include "cybsp.h"
#include "FreeRTOS.h"
#include "bluetoothManager.h"
#include "wiced_bt_stack.h"
#include "wiced_bt_dev.h"
#include "wiced_bt_trace.h"
//
void obv_callback(wiced_bt_ble_scan_results_t *p_scan_result, uint8_t *p_adv_data)
{
// Print the MAC Address
printf("MAC: ");
for(int i=0;i<6;i++)
{
printf("%02X:",p_scan_result->remote_bd_addr[i]);
}
// Print the RAW Data of the ADV Packet
printf(" Data: ");
int i=0;
while(p_adv_data[i])
{
for(int j=0;j<p_adv_data[i];j++)
{
printf("%02X ",p_adv_data[i+1+j]);
}
i = i + p_adv_data[i]+1;
}
printf("\n");
}
/**************************************************************************************************
* Function Name: app_bt_management_callback()
***************************************************************************************************
* Summary:
*   This is a Bluetooth stack event handler function to receive management events from
*   the BLE stack and process as per the application.
*
* Parameters:
*   wiced_bt_management_evt_t event             : BLE event code of one byte length
*   wiced_bt_management_evt_data_t *p_event_data: Pointer to BLE management event structures
*
* Return:
*  wiced_result_t: Error code from WICED_RESULT_LIST or BT_RESULT_LIST
*
*************************************************************************************************/
wiced_result_t app_bt_management_callback(wiced_bt_management_evt_t event, wiced_bt_management_evt_data_t *p_event_data)
{
wiced_result_t result = WICED_BT_SUCCESS;
switch (event)
{
case BTM_ENABLED_EVT:
if (WICED_BT_SUCCESS == p_event_data->enabled.status)
{
printf("Started BT Stack Succesfully\n");
wiced_bt_ble_observe(WICED_TRUE,0,obv_callback);
}
else
{
printf("Error enabling BTM_ENABLED_EVENT\n");
}
break;
default:
printf("Unhandled Bluetooth Management Event: 0x%x\n", event);
break;
}
return result;
}

 

Recommended Posts

No comment yet, add your voice below!


Add a Comment

Your email address will not be published. Required fields are marked *