Bluetooth LE Link Layer Packet Generation and Decoding
This example shows how to generate and decode Bluetooth® Low Energy (LE) link layer packets.
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 (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. 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).
Packet Formats
Bluetooth Core Specification [ 2 ] defines two kinds of PHYs for Bluetooth LE. 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 Bluetooth® Toolbox generates LL packets that consist of Protocol Data Unit (PDU) and the Cyclic Redundancy Check (CRC) shown in the PHY packet.
Bluetooth LE classifies 40 RF channels into three advertising channels (Channel indices: 37, 38, 39) and thirty-seven data channels (Channel indices: 0 to 36). Bluetooth LE 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 [ 2 ]) 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 of bleLLAdvertisingChannelPDUConfig
object.
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.
Advertising Channel PDUs Generation
You can use the bleLLAdvertisingChannelPDU
function to generate an advertising channel PDU. This function accepts a configuration object 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:
status
: An enumeration of typeblePacketDecodeStatus
, specifying whether the LL decoding was successful.cfgLLAdv
: A LL advertising channel PDU configuration object of typebleLLAdvertisingChannelPDUConfig
, 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 [ 2 ]) 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 of bleLLControlPDUConfig
and bleLLDataChannelPDUConfig
objects, respectively.
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.
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 CRCInitialization: 'ED321C'
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 CRCInitialization: 'ED321C' 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:
status
: An enumeration of typeblePacketDecodeStatus
, specifying whether the LL decoding was successful.cfgLLData
: An LL data channel PDU configuration object of typebleLLDataChannelPDUConfig
, which contains the decoded LL properties.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 CRCInitialization: 'ED321C'
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 CRCInitialization: 'ED321C' 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
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.
Prepend access address
The PCAP format expects access address to be prepended to the LL packet.
% Advertising channel PDUs use the default access address advAccessAddress = '8E89BED6'; advAccessAddressBinary = int2bit(hex2dec(advAccessAddress), 32, false); % Data channel PDUs use the access address obtained from 'Connection % indication' packet. A random access address is used for this example connAccessAddress = 'E213BC42'; connAccessAddressBinary = int2bit(hex2dec(connAccessAddress), 32, false); % Prepend access address llPkts{1} = [advAccessAddressBinary; llAdvPDU]; llPkts{2} = [connAccessAddressBinary; llDataPDU]; llPkts{3} = [connAccessAddressBinary; llControlPDU];
Export to a PCAP file
Create an object of type blePCAPWriter
and specify the packet capture file name.
% Create the Bluetooth LE PCAP Writer file object pcapObj = blePCAPWriter("FileName", "bluetoothLELLPackets");
Use the blePCAPWriter
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(llPkts) write(pcapObj, llPkts{idx}, timestamp, 'PacketFormat', 'bits'); end % Clear the object clear pcapObj;
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 Bluetooth® Toolbox. 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 [ 2 ]. You can use a packet analyzer to view the generated LL packets.
Selected Bibliography
Bluetooth Technology Website. “Bluetooth Technology Website | The Official Website of Bluetooth Technology.” Accessed November 25, 2021. https://www.bluetooth.com.
Bluetooth Special Interest Group (SIG). "Bluetooth Core Specification." Version 5.3. https://www.bluetooth.com/.
"Development/LibpcapFileFormat - The Wireshark Wiki." https://wiki.wireshark.org/Development/LibpcapFileFormat
Group, The Tcpdump. "Tcpdump/Libpcap Public Repository." https://www.tcpdump.org.
See Also
Functions
bleLLAdvertisingChannelPDU
|bleLLAdvertisingChannelPDUDecode
|bleLLDataChannelPDU
|bleLLDataChannelPDUDecode
Objects
bleLLAdvertisingChannelPDUConfig
|bleLLDataChannelPDUConfig
|bleLLControlPDUConfig
|blePCAPWriter