Main Content

Modeling of BLE Devices with Heart Rate Profile

This example shows the modeling of Bluetooth® Low Energy devices with Heart Rate Profile using the Communications Toolbox™ Library for the Bluetooth® Protocol.

Background

The Bluetooth core specification [ 1 ] includes a Low Energy version for low-rate wireless personal area networks, that is referred to as Bluetooth Low Energy (BLE) or Bluetooth Smart. The BLE 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). BLE 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 BLE. 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.

Heart Rate Profile

Heart Rate Profile (HRP) [ 2 ] 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.

Check for Support Package Installation

% Check if the 'Communications Toolbox Library for the Bluetooth Protocol'
% support package is installed or not.
commSupportPackageCheck('BLUETOOTH');

BLE 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 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 function decodes the incoming PDU as a GATT-server and returns the corresponding ATT PDU configuration object and the appropriate response PDU.

% Decode the received BLE 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")
serviceDiscReqCfg

% Transmit the application response data (|attServerRespPDU|) to the client
% through PHY.
[bleWaveform, pcapPackets{count}] = helperBLETransmitData(attServerRespPDU);
count = count+1;
Received service discovery request at the server:

serviceDiscReqCfg = 

  bleATTPDUConfig with properties:

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

Receive Server response at Client

The receiveData 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 BLE 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 BLE 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")
chrsticDiscReqCfg

% Transmit the application response data (|chrsticDiscRespPDU|) to the
% client through PHY.
[bleWaveform, pcapPackets{count}] = helperBLETransmitData(chrsticDiscRespPDU);
count = count+1;
Received characteristic discovery request at the server:

chrsticDiscReqCfg = 

  bleATTPDUConfig with properties:

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

Receive Server response at Client

% Decode the received BLE 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 BLE 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")
chrsticDescDiscReqCfg

% Transmit the application response data (|chrsticDescDiscRespPDU|) to the
% client through PHY.
[bleWaveform, pcapPackets{count}] = helperBLETransmitData(chrsticDescDiscRespPDU);
count = count+1;
Received characteristic descriptor discovery request at the server:

chrsticDescDiscReqCfg = 

  bleATTPDUConfig with properties:

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

Receive Server response at Client

% Decode the received BLE 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 BLE 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")
enableNotificationReqCfg

% Transmit the application response data (|enableNotificationRespPDU|) to
% the client through PHY.
[bleWaveform, pcapPackets{count}] = helperBLETransmitData(enableNotificationRespPDU);
count = count+1;
Received enable notification request at the server:

enableNotificationReqCfg = 

  bleATTPDUConfig with properties:

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

Receive Server response at Client

% Decode the received BLE 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 function 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 BLE 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")

% Decode the received heart rate measurement characteristic value.
heartRateCharacteristicValue = helperBLEDecodeAttributeValue(...
    notificationCfg.AttributeValue, 'Heart rate measurement');
heartRateCharacteristicValue

heartRateMeasurementValue = heartRateCharacteristicValue.HeartRateValue;

% Visualize the BLE GATT Client-Server model.
helperBLEVisualizeHRPFrame(serviceDiscRespMsg, chrsticDescRespMsg, ...
    chrsticDescDiscRespMsg, enableNotificRespMsg, heartRateMeasurementValue);
Received notification at the client:

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

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 BLE PCAP Writer file object
pcapObj = blePCAPWriter("FileName", "bleHRP");

Use the write function to write all the BLE 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 'bleHRP.pcap' in a protocol analyzer to view the generated frames.\n")
Open generated pcap file 'bleHRP.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 [ 3 ]. 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 BLE devices with Heart Rate Profile using the GATT client-server scenario as specified in the Bluetooth core specification [ 1 ]. You can use a packet analyzer to view the generated frames.

Appendix

The example uses these features:

The example uses these helpers:

Selected Bibliography

  1. Bluetooth® Technology Website. "Bluetooth Technology Website | The Official Website of Bluetooth Technology." Accessed July 8, 2020. https://www.bluetooth.com/.

  2. "Development/LibpcapFileFormat - The Wireshark Wiki." Accessed July 8, 2020. https://wiki.wireshark.org/Development/LibpcapFileFormat.

  3. Group, The Tcpdump. "Tcpdump/Libpcap Public Repository." Accessed July 8, 2020. https://www.tcpdump.org.

Related Topics