Main Content

Modeling of Bluetooth LE Devices with Heart Rate Profile

This example shows you how to model Bluetooth® low energy (LE) devices with the Heart Rate Profile (HRP).

Using this example, you can:

  • Create and configure a Bluetooth LE HRP client-server scenario with a smartphone as the client and a wrist band with a heart rate sensor as the server.

  • Configure the client to perform service and characteristic discovery.

  • Configure the client to enable receiving notifications for a characteristic from the server.

  • Export the generated protocol data units (PDUs) to a file with .pcap extension.

  • Visualize the generated PDUs by using a third party packet analyzer such as Wireshark [ 4 ].

Background

The Bluetooth core specification [ 2 ] includes a Low Energy version for low-rate wireless personal area networks, that is referred to as Bluetooth Low Energy (Bluetooth LE) or Bluetooth Smart. The Bluetooth LE stack consists of: Generic Attribute Profile (GATT), Attribute Protocol (ATT), Security Manager Protocol (SMP), Logical Link Control and Adaptation Protocol (L2CAP), Link Layer (LL) and Physical layer (PHY). Bluetooth LE was added to the standard for low energy devices generating small amounts of data, such as notification alerts used in such applications as home automation, health-care, fitness, and Internet of Things (IoT).

Attribute Protocol

The ATT is built on top of the L2CAP layer of Bluetooth LE. ATT defines a set of Protocol Data Units (PDUs) that are used for data exchange in GATT-based profiles.

Generic Attribute Profile

The GATT is a service framework built using ATT. GATT handles the generation of requests or responses based on application data from the higher layers or ATT PDU received from the lower layer. It stores the information in the form of services, characteristics, and characteristic descriptors. It uses a client-server architecture.

GATT Terminology:

  • Service: A service is a collection of data and associated behaviors to accomplish a particular function or feature. Example: A heart rate service that allows measurement of a heart rate.

  • Characteristic: A characteristic is a value used in a service along with its permissions. Example: A heart rate measurement characteristic contains information about the measured heart rate value.

  • Characteristic descriptor: Descriptors of the characteristic behavior. Example: A Client Characteristic Configuration Descriptor (CCCD), describes whether or not the server has to notify the client in a response containing the characteristic value.

  • GATT-Client: Initiates commands and requests to the server, and receives responses, indications and notifications sent by the server.

  • GATT-Server: Accepts incoming commands and requests from a client, and sends responses, indications, and notifications to the client.

BLEGATTClientServerModel.png

Heart Rate Profile

Heart Rate Profile (HRP) [ 3 ] is a GATT-based low energy profile defined by the Bluetooth Special Interest Group (SIG). The HRP defines the communication between a GATT-server of a heart rate sensor device, such as a wrist band, and a GATT-client, such as a smart phone or tablet. The HRP is widely used in fitness applications to collect heart rate measurements.

Bluetooth LE HRP Client-Server Scenario

In this scenario, the GATT-server is a wrist band with a heart rate sensor and the GATT-client is a smart phone.

% Create objects for GATT-server and GATT-client devices
gattServer = helperBLEGATTServer;
gattClient = helperBLEGATTClient;

Initially, the HRP client discovers the services, characteristics, characteristic descriptors defined at the server. After discovery, the client subscribes for heart rate measurement notifications.

Service Discovery

Clients perform a service discovery operation to get information about the available services. In service discovery, the client invokes 'Discover all primary services' by sending a Read by group type request ATT PDU. The server responds with the available services and their associated handles by sending a 'Read by group type response' ATT PDU. A handle is a unique identifier of an attribute that are dynamically assigned by the server.

Client request for services at Server

The generateATTPDU object function generates an ATT PDU corresponding to the given sub-procedure as specified in the Bluetooth core specification.

% Preallocate a variable to store the generated link layer packets
pcapPackets = cell(1, 9);
count = 1;

% Configure a GATT client to discover services available at the server
gattClient.SubProcedure = 'Discover all primary services';
serviceDiscReqPDU = generateATTPDU(gattClient);

% Transmit the application data (|serviceDiscReqPDU|) to the server through
% PHY
[bleWaveform, pcapPackets{count}] = helperBLETransmitData(serviceDiscReqPDU);
count = count+1;

Receive Client request at Server

The server receives a Read by group type request from the client and sends the list of available services in a Read by group type response ATT PDU.

The receiveData object function of the helperBLEGATTServer helper object decodes the incoming PDU as a GATT-server and returns the corresponding ATT PDU configuration object and the appropriate response PDU.

% Decode the received Bluetooth LE waveform and retrieve the application
% data
receivedPDU = helperBLEDecodeData(bleWaveform);

% Decode the received ATT PDU and generate response PDU, if applicable
[attServerRespPDU, serviceDiscReqCfg, gattServer] = receiveData(gattServer, receivedPDU);

fprintf("Received service discovery request at the server:\n")
Received service discovery request at the server:
serviceDiscReqCfg
serviceDiscReqCfg = 
  bleATTPDUConfig with properties:

           Opcode: 'Read by group type request'
      StartHandle: '0001'
        EndHandle: 'FFFF'
    AttributeType: '2800'

% Transmit the application response data (|attServerRespPDU|) to the client
% through PHY
[bleWaveform, pcapPackets{count}] = helperBLETransmitData(attServerRespPDU);
count = count+1;

Receive Server response at Client

The receiveData object function decodes the incoming PDU as a GATT-client and returns the corresponding ATT PDU configuration object and the appropriate response PDU, if applicable.

% Decode the received Bluetooth LE waveform and retrieve the application
% data
receivedPDU = helperBLEDecodeData(bleWaveform);

% Decode received ATT PDU and generate response PDU, if applicable
[~, serviceDiscRespCfg] = receiveData(gattClient, receivedPDU);
gattClient.StartHandle = serviceDiscRespCfg.StartHandle;
gattClient.EndHandle = serviceDiscRespCfg.EndHandle;

% Expected response from the server: |'Read by group type response'| or
% |'Error response'|
if strcmp(serviceDiscRespCfg.Opcode, 'Error response')
    fprintf("Received error response at the client:\n")
    serviceDiscRespCfg
    serviceDiscRespMsg = ['Error response(''' serviceDiscRespCfg.ErrorMessage ''')'];
else
    fprintf("Received service discovery response at the client:\n")
    serviceDiscRespCfg
    service = helperBluetoothID.getBluetoothName(serviceDiscRespCfg.AttributeValue);
    serviceDiscRespMsg = ['Service discovery response(''' service ''')'];
end
Received service discovery response at the client:
serviceDiscRespCfg = 
  bleATTPDUConfig with properties:

            Opcode: 'Read by group type response'
       StartHandle: '0001'
         EndHandle: '0006'
    AttributeValue: [2x2 char]

Characteristics Discovery

A service consists of multiple characteristics. For each service, there are information elements exchanged between a client and server. Each information element may contain descriptors of its behavior. A characteristic contains a value and its associated descriptors. After discovering the service, clients perform characteristics discovery to learn about the characteristics defined in the service. In characteristic discovery, the client invokes 'Discover all characteristics of service' by sending 'Read by type request' ATT PDU. The server responds with the available characteristics and their associated handles by sending a 'Read by type response' ATT PDU.

Client request for characteristics at Server

% Configure a GATT client to discover all the available characteristics at
% the server
gattClient.SubProcedure = 'Discover all characteristics of service';
chrsticDiscReqPDU = generateATTPDU(gattClient);

% Transmit the application data (|chrsticDiscReqPDU|) to the server through
% PHY
[bleWaveform, pcapPackets{count}] = helperBLETransmitData(chrsticDiscReqPDU);
count = count+1;

Receive Client request at Server

Decodes the received request and return the list of available characteristics in a Read by type response ATT PDU.

% Decode the received Bluetooth LE waveform and retrieve the application
% data
receivedPDU = helperBLEDecodeData(bleWaveform);

% Decode received ATT PDU and generate response PDU, if applicable
[chrsticDiscRespPDU, chrsticDiscReqCfg, gattServer] = receiveData(gattServer, receivedPDU);

fprintf("Received characteristic discovery request at the server:\n")
Received characteristic discovery request at the server:
chrsticDiscReqCfg
chrsticDiscReqCfg = 
  bleATTPDUConfig with properties:

           Opcode: 'Read by type request'
      StartHandle: '0001'
        EndHandle: '0006'
    AttributeType: '2803'

% Transmit the application response data (|chrsticDiscRespPDU|) to the
% client through PHY
[bleWaveform, pcapPackets{count}] = helperBLETransmitData(chrsticDiscRespPDU);
count = count+1;

Receive Server response at Client

% Decode the received Bluetooth LE waveform and retrieve the application
% data
receivedPDU = helperBLEDecodeData(bleWaveform);

% Decode received ATT PDU and generate response PDU, if applicable
[~, chrsticDiscRespCfg] = receiveData(gattClient, receivedPDU);

% Expected response from the server: |'Read by type response'| or |'Error
% response'|
if strcmp(chrsticDiscRespCfg.Opcode, 'Error response')
    fprintf("Received error response at the client:\n")
    chrsticDiscRespCfg
    chrsticDescRespMsg = ['Error response(''' chrsticDiscRespCfg.ErrorMessage ''')'];
else
    fprintf("Received characteristic discovery response at the client:\n")
    attributeValueCfg = helperBLEDecodeAttributeValue(...
        chrsticDiscRespCfg.AttributeValue, 'Characteristic');
    attributeValueCfg
    chrsticDescRespMsg = ['Characteristic discovery response(''' attributeValueCfg.CharacteristicType ''')'];
end
Received characteristic discovery response at the client:
attributeValueCfg = 
  helperBLEAttributeValueConfig with properties:

                    AttributeType: 'Characteristic'
                    BroadcastFlag: 'False'
                         ReadFlag: 'False'
         WriteWithoutResponseFlag: 'False'
                        WriteFlag: 'False'
                       NotifyFlag: 'True'
                     IndicateFlag: 'False'
    AuthenticatedSignedWritesFlag: 'False'
           ExtendedPropertiesFlag: 'False'
        CharacteristicValueHandle: '0003'
               CharacteristicType: 'Heart rate measurement'

Characteristic Descriptor Discovery

A characteristic may consists of multiple characteristic descriptors. After discovering the characteristic, clients perform characteristic descriptors discovery to learn about the list of descriptors and their handles. In characteristic descriptor discovery, the client invokes 'Discover all descriptors' by sending 'Information request' ATT PDU. The server responds with the available characteristic descriptors and their associated handles by sending a 'Information response' ATT PDU.

Client request for characteristic descriptors at Server

% Configure a GATT client to discover all the available characteristic
% descriptors at the server
gattClient.SubProcedure = 'Discover all descriptors';
gattClient.StartHandle = dec2hex(hex2dec(chrsticDiscRespCfg.AttributeHandle)+1, 4);
chrsticDescDiscReqPDU = generateATTPDU(gattClient);

% Transmit the application data (|chrsticDescDiscReqPDU|) to the client
% through PHY
[bleWaveform, pcapPackets{count}] = helperBLETransmitData(chrsticDescDiscReqPDU);
count = count+1;

Receive Client request at Server

Decodes the received request and returns the list of available characteristic descriptors in a Information response ATT PDU.

% Decode the received Bluetooth LE waveform and retrieve the application
% data
receivedPDU = helperBLEDecodeData(bleWaveform);

% Decode received ATT PDU and generate response PDU, if applicable
[chrsticDescDiscRespPDU, chrsticDescDiscReqCfg, gattServer] = receiveData(gattServer, receivedPDU);

fprintf("Received characteristic descriptor discovery request at the server:\n")
Received characteristic descriptor discovery request at the server:
chrsticDescDiscReqCfg
chrsticDescDiscReqCfg = 
  bleATTPDUConfig with properties:

         Opcode: 'Information request'
    StartHandle: '0003'
      EndHandle: '0006'

% Transmit the application response data (|chrsticDescDiscRespPDU|) to the
% client through PHY
[bleWaveform, pcapPackets{count}] = helperBLETransmitData(chrsticDescDiscRespPDU);
count = count+1;

Receive Server response at Client

% Decode the received Bluetooth LE waveform and retrieve the application
% data
receivedPDU = helperBLEDecodeData(bleWaveform);

% Decode received ATT PDU and generate response PDU, if applicable
[~, chrsticDescDiscRespCfg] = receiveData(gattClient, receivedPDU);

% Expected response from the server: |'Information response'| or |'Error
% response'|
if strcmp(chrsticDescDiscRespCfg.Opcode, 'Error response')
    fprintf("Received error response at the client:\n")
    chrsticDescDiscRespCfg
    chrsticDescDiscRespMsg = ['Error response(''' chrsticDescDiscRespCfg.ErrorMessage ''')'];
else
    fprintf("Received characteristic descriptor discovery response at the client:\n")
    chrsticDescDiscRespCfg
    descriptor = helperBluetoothID.getBluetoothName(chrsticDescDiscRespCfg.AttributeType);
    chrsticDescDiscRespMsg = ['Characteristic descriptor discovery response(''' descriptor ''')'];
end
Received characteristic descriptor discovery response at the client:
chrsticDescDiscRespCfg = 
  bleATTPDUConfig with properties:

             Opcode: 'Information response'
             Format: '16 bit'
    AttributeHandle: '0004'
      AttributeType: '2902'

Subscribe for Notifications

After discovering the characteristic descriptors, the client may enable or disable notifications for its characteristic value. To enable notifications, the client must set the notification bit (first bit) of Client Characteristic Configuration Descriptor (CCCD) value by invoking 'Write characteristic value' sub-procedure.

Client subscribe for notifications at Server

% Configure a GATT client to enable the notifications of Heart rate
% measurement characteristic
gattClient.SubProcedure = 'Write characteristic value';
gattClient.AttributeHandle = chrsticDescDiscRespCfg.AttributeHandle;
gattClient.AttributeValue = '0100';
enableNotificationReqPDU = generateATTPDU(gattClient);

% Transmit the application data (|enableNotificationReqPDU|) to the client
% through PHY
[bleWaveform, pcapPackets{count}] = helperBLETransmitData(enableNotificationReqPDU);
count = count+1;

Receive Client request at Server

Decodes the received request and sends the response in a Write response ATT PDU.

% Decode the received Bluetooth LE waveform and retrieve the application
% data
receivedPDU = helperBLEDecodeData(bleWaveform);

% Decode received ATT PDU and generate response PDU, if applicable
[enableNotificationRespPDU, enableNotificationReqCfg, gattServer] = receiveData(gattServer, receivedPDU);

fprintf("Received enable notification request at the server:\n")
Received enable notification request at the server:
enableNotificationReqCfg
enableNotificationReqCfg = 
  bleATTPDUConfig with properties:

             Opcode: 'Write request'
    AttributeHandle: '0004'
     AttributeValue: [2x2 char]

% Transmit the application response data (|enableNotificationRespPDU|) to
% the client through PHY
[bleWaveform, pcapPackets{count}] = helperBLETransmitData(enableNotificationRespPDU);
count = count+1;

Receive Server response at Client

% Decode the received Bluetooth LE waveform and retrieve the application
% data
receivedPDU = helperBLEDecodeData(bleWaveform);

% Decode received ATT PDU and generate response PDU, if applicable
[~, enableNotificationRespCfg] = receiveData(gattClient, receivedPDU);

% Expected response from the server: |'Write response'| or |'Error
% response'|
if strcmp(enableNotificationRespCfg.Opcode, 'Error response')
    fprintf("Received error response at the client:\n")
    enableNotificationRespCfg
    enableNotificRespMsg = ['Error response(''' enableNotificationRespCfg.ErrorMessage ''')'];
else
    fprintf("Received enable notification response at the client:\n")
    enableNotificationRespCfg
    enableNotificRespMsg = 'Notifications enabled(''Heart rate measurement '')';
end
Received enable notification response at the client:
enableNotificationRespCfg = 
  bleATTPDUConfig with properties:

    Opcode: 'Write response'

Notifying the Heart Rate Measurement Value to the Client

When a client enables notifications for a characteristic, the server periodically notifies the value of characteristic (Heart rate measurement) to the client.

The HRP server notifies heart rate measurement to the client after its subscription.

Server sends notifications to Client

The notifyHeartRateMeasurement object function of the helperBLEGATTServer helper object generates notification PDU as specified in the Bluetooth core specification.

% Reset the random number generator seed
rng default

% Measure heart rate value using sensor (generate a random number for heart
% rate measurement value)
heartRateMeasurementValue = randi([65 95]);

% Notify the heart rate measurement
[gattServer, notificationPDU] = notifyHeartRateMeasurement(gattServer, ...
    heartRateMeasurementValue);

% Transmit the application data (|notificationPDU|) to the client through
% PHY
[bleWaveform, pcapPackets{count}] = helperBLETransmitData(notificationPDU);
count = count+1;

Receive Server notifications at Client

% Decode the received Bluetooth LE waveform and retrieve the application
% data
receivedPDU = helperBLEDecodeData(bleWaveform);

% Decode received ATT PDU and generate response PDU, if applicable
[~, notificationCfg] = receiveData(gattClient, receivedPDU);

fprintf("Received notification at the client:\n")
Received notification at the client:
% Decode the received heart rate measurement characteristic value
heartRateCharacteristicValue = helperBLEDecodeAttributeValue(...
    notificationCfg.AttributeValue, 'Heart rate measurement');
heartRateCharacteristicValue
heartRateCharacteristicValue = 
  helperBLEAttributeValueConfig with properties:

              AttributeType: 'Heart rate measurement'
       HeartRateValueFormat: 'UINT8'
        SensorContactStatus: 'Contact detected'
    EnergyExpendedFieldFlag: 'Present, Units: Kilo Joules'
        RRIntervalFieldFlag: 'Present'
             HeartRateValue: 90
             EnergyExpended: 100
                 RRInterval: 10

heartRateMeasurementValue = heartRateCharacteristicValue.HeartRateValue;

% Visualize the Bluetooth LE GATT Client-Server model
helperBLEVisualizeHRPFrame(serviceDiscRespMsg, chrsticDescRespMsg, ...
    chrsticDescDiscRespMsg, enableNotificRespMsg, heartRateMeasurementValue);

Figure Bluetooth LE Heart Rate Profile contains 11 axes objects. Hidden axes object 1 contains 3 objects of type image, text. Hidden axes object 2 contains 2 objects of type image, text. Hidden axes object 3 contains 2 objects of type image, text. Hidden axes object 4 contains 2 objects of type image, text. Hidden axes object 5 contains 2 objects of type image, text. Hidden axes object 6 contains 2 objects of type image, text. Hidden axes object 7 contains 2 objects of type image, text. Hidden axes object 8 contains 2 objects of type image, text. Hidden axes object 9 contains 2 objects of type image, text. Hidden axes object 10 contains 4 objects of type image, text. Hidden axes object 11 contains 2 objects of type image, text.

Exporting to a PCAP File

This example uses blePCAPWriter object to export the generated PDUs to a file with .pcap extension or .pcapng extension. To analyze and visualize this file, use a third part packet analyzer such as Wireshark.

Create an object of type blePCAPWriter and specify the packet capture file name.

% Create the Bluetooth LE PCAP Writer file object
pcapObj = blePCAPWriter("FileName", "bluetoothLEHRP");

Use the write object function to write all the Bluetooth LE LL PDUs to a PCAP file. The constant timestamp specifies the capture time of a PDU. In this example, the capture time is same for all the PDUs.

timestamp = 124800; % timestamp (in microseconds)
 
% Write all the LL PDUs to the PCAP file
for idx = 1:numel(pcapPackets)
    write(pcapObj, pcapPackets{idx}, timestamp, "PacketFormat", "bits");
end
 
% Clear the object
clear pcapObj;

fprintf("Open generated pcap file 'bluetoothLEHRP.pcap' in a protocol analyzer to view the generated frames.\n")
Open generated pcap file 'bluetoothLEHRP.pcap' in a protocol analyzer to view the generated frames.

Visualization of the Generated ATT PDUs

Since the generated heart rate profile packets are compliant with the Bluetooth standard, you can open, analyze and visualize the PCAP file using a third party packet analyzer such as Wireshark [ 4 ]. The data shown in these figures uses the heart rate profile packets generated in this example.

  • Service discovery request

  • Service discovery response

  • Notifying heart rate measurement value

Conclusion

This example demonstrated the modeling of Bluetooth LE devices with Heart Rate Profile using the GATT client-server scenario as specified in the Bluetooth core specification [ 2 ]. You can use a packet analyzer to view the generated frames.

Appendix

The example uses these helpers:

Selected Bibliography

  1. Bluetooth® Technology Website. "Bluetooth Technology Website | The Official Website of Bluetooth Technology." Accessed November 29, 2021. https://www.bluetooth.com

  2. Bluetooth Special Interest Group (SIG). "Bluetooth Core Specification" Version 5.3. https://www.bluetooth.com

  3. Bluetooth Special Interest Group (SIG). "Heart Rate Profile" Version 1.0. https://www.bluetooth.com

  4. “Development/LibpcapFileFormat - The Wireshark Wiki.” Accessed November 29, 2021. https://www.wireshark.org

  5. Group, The Tcpdump. “Tcpdump/Libpcap Public Repository.” Accessed November 29, 2021. https://www.wireshark.org

  6. Tuexen, M. “PCAP Next Generation (Pcapng) Capture File Format.” 2021. https://www.ietf.org

See Also

Functions

Objects

Related Topics