BLE Link Layer Packet Generation and Decoding

This example shows how to generate and decode Bluetooth® Low Energy (BLE) link layer packets 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. 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).

Packet Formats

Bluetooth core specification [ 1 ] defines two kinds of PHYs for BLE. Each PHY has its own packet format.

(i) Uncoded PHYs (1 Mbps and 2 Mbps)

(ii) Coded PHYs (125 Kbps and 500 Kbps)

Coded PHYs use Forward Error Correction (FEC) encoding (with coding scheme S = 8 or S = 2) for the packets. The figures show the uncoded and coded PHY packet formats.

Format of LE Air Interface Packet for Uncoded PHY

Format of LE Air Interface Packet for Coded PHY

The Communications Toolbox™ Library for the Bluetooth Protocol generates LL packets that consist of Protocol Data Unit (PDU) and the Cyclic Redundancy Check (CRC) shown in the PHY packet.

BLE classifies 40 RF channels into three advertising channels (Channel indices: 37, 38, 39) and thirty-seven data channels (Channel indices: 0 to 36). BLE link layer defines two categories of PDUs, advertising channel PDUs and data channel PDUs. There are different PDU types within these two categories of PDUs. The access address field in the air interface packet format differentiates between a data channel PDU and an advertising channel PDU. Each category of PDU has its own format.

Advertising Channel PDUs

The advertising channel PDUs (see Section 2.3, Part-B, Vol-6 in [ 1 ]) are used before a LL connection is created. These PDUs are transmitted only on the advertising channels and used in establishing the LL connection. The advertising channel PDU has a 16-bit header and a variable size payload.

The advertising channel PDU has the following packet format:

This example illustrates generation and decoding of advertising indication PDU. For a list of other advertising channel PDUs supported, see the PDUType property.

Advertising indication: The advertising indication PDU is used when a device wants to advertise itself. This PDU contains the advertising data related to the application profile of the device.

Check for Support Package Installation

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

Advertising Channel PDUs Generation

You can use the bleLLAdvertisingChannelPDU bleLLAdvertisingChannelPDU> function to generate an advertising channel PDU. This function accepts a configuration object bleLLAdvertisingChannelPDUConfig bleLLAdvertisingChannelPDUConfig>. This object configures the fields required for generating an advertising channel PDU.

Advertising Indication Generation

To generate an 'Advertising indication' PDU, create a bleLLAdvertisingChannelPDUConfig object with PDUType set to 'Advertising indication'.

cfgLLAdv = bleLLAdvertisingChannelPDUConfig('PDUType', ...
    'Advertising indication');

Configure the fields:

% Advertiser address
cfgLLAdv.AdvertiserAddress = '012345ABCDEF';
% Advertising data
cfgLLAdv.AdvertisingData = '0201060D09426174746572792056312E30'
cfgLLAdv = 

  bleLLAdvertisingChannelPDUConfig with properties:

                  PDUType: 'Advertising indication'
         ChannelSelection: 'Algorithm1'
    AdvertiserAddressType: 'Random'
        AdvertiserAddress: '012345ABCDEF'
          AdvertisingData: [17x2 char]

Generate an 'Advertising indication' PDU.

llAdvPDU = bleLLAdvertisingChannelPDU(cfgLLAdv);

Decoding Advertising Channel PDUs

You can use the bleLLAdvertisingChannelPDUDecode function to decode an advertising channel PDU. This function outputs the following information:

  1. status: An enumeration of type blePacketDecodeStatus, which indicates whether the LL decoding was successful.

  2. cfgLLAdv: A LL advertising channel PDU configuration object of type bleLLAdvertisingChannelPDUConfig, which contains the decoded LL properties.

Provide the advertising channel PDU and an optional name-value pair specifying the format of the input data PDU to the bleLLAdvertisingChannelPDUDecode function. Default input format is 'bits'.

Decoding Advertising Indication

[llAdvDecodeStatus, cfgLLAdv] = bleLLAdvertisingChannelPDUDecode(llAdvPDU);

% Observe the outputs

% Decoding is successful
if strcmp(llAdvDecodeStatus, 'Success')
    fprintf('Link layer decoding status is: %s\n\n', llAdvDecodeStatus);
    fprintf('Received Advertising channel PDU configuration is:\n');
    cfgLLAdv
% Decoding failed
else
    fprintf('Link layer decoding status is: %s\n', llAdvDecodeStatus);
end
Link layer decoding status is: Success

Received Advertising channel PDU configuration is:

cfgLLAdv = 

  bleLLAdvertisingChannelPDUConfig with properties:

                  PDUType: 'Advertising indication'
         ChannelSelection: 'Algorithm1'
    AdvertiserAddressType: 'Random'
        AdvertiserAddress: '012345ABCDEF'
          AdvertisingData: [17x2 char]

Data Channel PDUs

The data channel PDUs (see Section 2.4, Part-B, Vol-6 in [ 1 ]) are used after a LL connection is created. The data channel PDUs consist of two sub-categories: LL data PDUs and LL control PDUs. The LL control PDUs are used for managing the LL connection and the LL data PDUs are used to carry the upper-layer data. The data channel PDU has a 16-bit header and a variable size payload.

The data channel PDUs have the following packet format:

This example illustrates generation and decoding of the following PDUs. For a list of other control PDU types and data PDU types supported see Opcode and LLID properties respectively.

  1. Channel map indication: This LL control PDU is used to update the channel map at the peer device. This PDU contains the updated channel map indicating good and bad channels.

  1. Data (start fragment/complete): This LL data PDU is used to carry L2CAP data to the peer device.

Data Channel PDUs Generation

You can use the bleLLDataChannelPDU function to generate a data channel PDU. This function accepts a configuration object bleLLDataChannelPDUConfig, which configures the fields required for generating a data channel PDU. The bleLLControlPDUConfig is a sub-configuration object within bleLLDataChannelPDUConfig, control PDU payload fields are populated using the settings of this configuration object.

Data channel PDUs use the CRC initialization value obtained in the 'Connection indication' packet. The CRC initialization value used in the generation and decoding of packets.

% CRC initialization value
crcInit = 'ED321C';

LL Data PDU Generation

To generate a data PDU, create a bleLLDataChannelPDUConfig object with LLID set to 'Data (start fragment/complete)'.

cfgLLData = bleLLDataChannelPDUConfig('LLID', ...
    'Data (start fragment/complete)');

Configure the fields:

% CRC initialization value
cfgLLData.CRCInitialization = crcInit;
% Sequence number
cfgLLData.SequenceNumber = 0;
% Next expected sequence number
cfgLLData.NESN = 1
cfgLLData = 

  bleLLDataChannelPDUConfig with properties:

              LLID: 'Data (start fragment/complete)'
              NESN: 1
    SequenceNumber: 0
          MoreData: 0

A data PDU is used to transmit a payload from upper-layer. A 18-byte payload is used in this example.

% Payload
payload = '0E00050014010A001F004000170017000000';

Generate a data PDU using payload and configuration.

llDataPDU = bleLLDataChannelPDU(cfgLLData, payload);

LL Control PDU Generation

To generate a control PDU, create a bleLLDataChannelPDUConfig object with LLID set to 'Control'.

cfgLLData = bleLLDataChannelPDUConfig('LLID', 'Control');

Configure the fields:

% CRC initialization value
cfgLLData.CRCInitialization = crcInit
cfgLLData = 

  bleLLDataChannelPDUConfig with properties:

              LLID: 'Control'
              NESN: 0
    SequenceNumber: 0
          MoreData: 0
     ControlConfig: [1x1 bleLLControlPDUConfig]

You can configure the contents of an LL control PDU using bleLLControlPDUConfig.

Create a control PDU configuration object with Opcode set to 'Channel map indication'.

cfgControl = bleLLControlPDUConfig('Opcode', 'Channel map indication');

Configure the fields:

% Used channels
cfgControl.UsedChannels = [9, 10, 12, 24, 28, 32];
% Connection event instant
cfgControl.Instant = 245
cfgControl = 

  bleLLControlPDUConfig with properties:

          Opcode: 'Channel map indication'
         Instant: 245
    UsedChannels: [9 10 12 24 28 32]

Assign the updated control PDU configuration object to the ControlConfig property in the data channel PDU configuration object.

% Update the data channel PDU configuration
cfgLLData.ControlConfig = cfgControl;

Generate a control PDU with the updated configuration.

llControlPDU = bleLLDataChannelPDU(cfgLLData);

Decoding Data Channel PDUs

You can use the bleLLDataChannelPDUDecode function to decode a data channel PDU. This function outputs the following information:

  1. status: An enumeration of type blePacketDecodeStatus, which indicates whether the LL decoding was successful.

  2. cfgLLData: An LL data channel PDU configuration object of type bleLLDataChannelPDUConfig, which contains the decoded LL properties.

  3. payload: An n-by-2 character array representing the upper-layer payload carried by LL data PDUs.

Provide the data channel PDU, CRC initialization value and an optional name-value pair specifying the format of the input data PDU to the bleLLDataChannelPDUDecode function. Default input format is 'bits'.

Decoding LL Data PDU

[llDataDecodeStatus, cfgLLData, payload] = ...
    bleLLDataChannelPDUDecode(llDataPDU, crcInit);

% Observe the outputs

% Decoding is successful
if strcmp(llDataDecodeStatus, 'Success')
    fprintf('Link layer decoding status is: %s\n\n', llDataDecodeStatus);
    fprintf('Received Data channel PDU configuration is:\n');
    cfgLLData
    fprintf('Size of the received upper-layer payload is: %d\n', ...
        numel(payload)/2);
% Decoding failed
else
    fprintf('Link layer decoding status is: %s\n', llDataDecodeStatus);
end
Link layer decoding status is: Success

Received Data channel PDU configuration is:

cfgLLData = 

  bleLLDataChannelPDUConfig with properties:

              LLID: 'Data (start fragment/complete)'
              NESN: 1
    SequenceNumber: 0
          MoreData: 0

Size of the received upper-layer payload is: 18

Decoding LL Control PDU

[llControlDecodeStatus, cfgLLData] = ...
    bleLLDataChannelPDUDecode(llControlPDU, crcInit);

% Observe the outputs

% Decoding is successful
if strcmp(llControlDecodeStatus, 'Success')
    fprintf('Link layer decoding status is: %s\n\n', llControlDecodeStatus);
    fprintf('Received Data channel PDU configuration is:\n');
    cfgLLData
    fprintf('Received control PDU configuration is:\n');
    cfgControl = cfgLLData.ControlConfig
% Decoding failed
else
    fprintf('Link layer decoding status is: %s\n', llControlDecodeStatus);
end
Link layer decoding status is: Success

Received Data channel PDU configuration is:

cfgLLData = 

  bleLLDataChannelPDUConfig with properties:

              LLID: 'Control'
              NESN: 0
    SequenceNumber: 0
          MoreData: 0
     ControlConfig: [1x1 bleLLControlPDUConfig]

Received control PDU configuration is:

cfgControl = 

  bleLLControlPDUConfig with properties:

          Opcode: 'Channel map indication'
         Instant: 245
    UsedChannels: [9 10 12 24 28 32]

Exporting to a PCAP File

The generated PDUs are exported to a PCAP file, which can be analyzed and visualized with a third party packet analyzer such as Wireshark [ 2 ]. The helper function helperBLEExportToPCAP generates a PCAP file containing the BLE Link Layer packets.

The following commands generate a PCAP file that can be opened in a packet analyzer. The PCAP file contains all the LL packets generated in this example.

Prepend access address

The PCAP format expects access address to be prepended to the LL packet. The helper function helperBLEPrependAccessAddress prepends access address to the generated LL packet.

% Advertising channel PDUs use the default access address
advAccessAddress = '8E89BED6';
% Data channel PDUs use the access address obtained from 'Connection
% indication' packet. A random access address is used for this example
connAccessAddress = 'E213BC42';
% Prepend access address
llPkts{1} = helperBLEPrependAccessAddress(llAdvPDU, advAccessAddress);
llPkts{2} = helperBLEPrependAccessAddress(llDataPDU, connAccessAddress);
llPkts{3} = helperBLEPrependAccessAddress(llControlPDU, connAccessAddress);

Export to a PCAP file

% Export the generated LL packets (llPkts) to a PCAP file
helperBLEExportToPCAP(llPkts, 'bleLLPackets.pcap');

Visualization of the Generated Link Layer Packets

You can open the PCAP file containing the generated LL packets in a packet analyzer. The packets decoded by the packet analyzer match the standard compliant LL packets generated by the Communications Toolbox™ Library for the Bluetooth Protocol. The captured analysis of the packets is shown below.

  • Advertising indication

  • LL data PDU (carrying L2CAP payload)

  • LL control PDU (channel map indication)

Conclusion

This example demonstrated generation and decoding of LL packets specified in the Bluetooth standard [ 1 ]. You can use a packet analyzer to view the generated LL packets.

Appendix

This example uses the following helper functions:

Selected Bibliography

  1. Bluetooth SIG, Bluetooth core specification v5.0: https://www.bluetooth.com/

  2. Wireshark software: https://www.wireshark.org/