As shown in the figure above, to implement a BLE application, you first need a chip that supports BLE radio frequency, then you need to provide a BLE protocol stack that matches this chip, and finally, you can develop your own application on top of the protocol stack. It can be seen that the BLE protocol stack is the bridge connecting the chip and the application, and it is the key to implementing the entire BLE application. So what specific functions does the BLE protocol stack include? In simple terms, the BLE protocol stack is mainly used to encapsulate your application data layer by layer to generate an air data packet that meets the BLE protocol, which means wrapping the application data in a series of frame headers and tails. Specifically, the BLE protocol stack mainly consists of the following parts:
-
PHY Layer (Physical Layer). The PHY layer specifies the wireless frequency band used by BLE, modulation methods, and techniques. The performance of the PHY layer directly determines the power consumption, sensitivity, and selectivity of the entire BLE chip. The physical layer specifically uses 1Mbps adaptive frequency hopping GFSK radio frequency, operating in the unlicensed 2.4GHz ISM (Industrial, Scientific, and Medical) band.
-
LL Layer (Link Layer). The LL layer is the core of the entire BLE protocol stack and is also the focus and difficulty of the BLE protocol stack. For example, Nordic’s BLE protocol stack can support 20 links simultaneously, thanks to the LL layer. The LL layer has many responsibilities, such as selecting which radio frequency channel to use for communication, how to identify air data packets, when to send data packets, how to ensure data integrity, how to receive ACKs, how to retransmit, and how to manage and control the link, etc. The LL layer is only responsible for sending or receiving data; how to parse the data is handed over to the upper layers, such as GAP or GATT. The LL layer controls the radio frequency state of the device, which can be in one of five states: waiting, advertising, scanning, initializing, or connecting. Broadcasting devices can send data without establishing a connection, while scanning devices receive data sent by broadcasting devices; the device initiating the connection responds to the broadcasting device by sending a connection request. If the broadcasting device accepts the connection request, the broadcasting device and the initiating device will enter the connection state. The initiating device is called the master, and the device accepting the connection request is called the slave.
-
HCI (Host Controller Interface). HCI is optional (for details, please refer to the article: Three Bluetooth Architecture Implementation Solutions (Bluetooth Protocol Stack Solutions)). HCI is mainly used in scenarios where two chips implement the BLE protocol stack, to standardize the communication protocol and commands between them. This layer can be a software or hardware interface, such as UART, SPI, USB, etc.
-
GAP Layer (Generic Access Profile). GAP is one of the two ways to parse the LL layer payload (valid data packet), and it is the simplest one. GAP simply standardizes and defines the LL payload, so the functions that GAP can achieve are extremely limited. Currently, GAP is mainly used for broadcasting, scanning, and initiating connections.
-
L2CAP Layer (Logical Link Control and Adaptation Protocol). L2CAP provides a simple encapsulation of LL; LL only cares about the data being transmitted, while L2CAP distinguishes between encrypted channels and ordinary channels, and also manages connection intervals.
-
SMP (Security Manager Protocol). SMP is used to manage the encryption and security of BLE connections, ensuring the security of the connection without affecting user experience, which are all considerations for SMP.
-
ATT (Attribute Protocol). In simple terms, the ATT layer is used to define user commands and the data for command operations, such as reading or writing certain data. The most frequently encountered layer by developers in the BLE protocol stack is ATT. BLE introduces the concept of attributes to describe individual pieces of data. Attributes not only define the data but also the ATT commands that can be used with that data, hence this layer is called the ATT layer.
-
GATT (Generic Attribute Profile). GATT standardizes the data content within attributes and uses the concept of groups to classify and manage attributes. GATT defines the structure of profiles. Without GATT, the BLE protocol stack could still operate, but interoperability would be problematic. It is precisely because of GATT and various application profiles that BLE has overcome the compatibility issues faced by wireless protocols like ZigBee, becoming the most widely shipped 2.4G wireless communication product. GATT defines the service framework that uses ATT. In BLE, all data blocks used by profiles or services are called “characteristics,” and all data communication between connected devices is handled through GATT subroutines. The GATT layer is used for data communication between connected Bluetooth devices, and applications and profiles directly use the GATT layer. Once two devices are connected, they will be in one of the following two roles: GATT server: the device that provides data services to the GATT client. GATT client: the device that reads and writes application data from the GATT server.
I believe many people still do not understand the working principle of the BLE protocol stack and what each layer specifically does, and why it is divided into layers after reading the above introduction. Below, I will explain how to send a data packet as an example to illustrate how the various layers of the BLE protocol stack work closely together to complete the sending task.
How to Send a Data Packet Wirelessly
Assuming there are devices A and B, device A wants to send its current battery status of 83% (represented in hexadecimal as 0x53) to device B, how should it do this? As a developer, they hope for simplicity; they want to call a simple API to complete this task, such as send(0x53). In fact, our BLE protocol stack is designed this way, allowing developers to simply call send(0x53) to send the data, while the BLE protocol stack takes care of the rest. Many people might think that the BLE protocol stack directly sends 0x53 at the physical layer, as shown in the figure below:
This method seems appealing at first glance, but many details have not been considered, making it impractical. First, it does not consider which radio frequency channel to use for transmission. Without changing the API, we can only layer the protocol stack, thus introducing the LL layer. The developer still calls send(0x53), which then calls send_LL(0x53,2402M) (note: 2402M is the channel frequency). There is also the question of how device B knows whether this data packet is intended for itself or someone else. To address this, BLE introduces the access address concept to indicate the identity of the receiver, among which, 0x8E89BED6 is a special access address that indicates the data is to be sent to all surrounding devices, i.e., broadcasting. If you want to communicate one-to-one (which BLE refers to as connection), meaning that device A’s data packet can only be received by device B, and vice versa, a unique random access address must be generated to identify the connection between device A and device B.
Broadcasting Method
Let’s first look at a simple broadcasting scenario, where we call device A the advertiser and device B the scanner or observer. In broadcasting mode, device A’s LL layer API will change to send_LL(0x53,2402M, 0x8E89BED6). Since device B can receive broadcasts from many devices simultaneously, the data packet must also include device A’s device address (0xE1022AAB753B) to confirm that this broadcast packet comes from device A. Therefore, the send_LL parameters need to change to (0x53,2402M, 0x8E89BED6, 0xE1022AAB753B). The LL layer also needs to check the integrity of the data, i.e., whether the data has been tampered with during transmission, for which CRC24 is introduced to verify the data packet (assumed to be 0xB2C78E). Additionally, to make the modulation and demodulation circuits work more efficiently, each data packet will have a 1-byte preamble added at the front, which is usually 0x55 or 0xAA. Thus, the entire air packet becomes (note: the air packet is represented in little-endian format!):
This data packet still has the following issues:
-
It does not classify and organize the data packet, making it impossible for device B to find the desired data 0x53. Therefore, we need to add two fields after the access address: the LL header and the length byte. The LL header indicates the type of the LL data packet, and the length byte indicates the length of the payload.
-
When should device B open its radio frequency window to receive the air data packet? As shown in case 1 of the figure above, when device A’s data packet is being transmitted in the air, device B has its receiving window closed, resulting in a communication failure; similarly, in case 2, when device A is not sending a data packet in the air, device B has its receiving window open, leading to another communication failure. Only in case 3, when device A’s data packet is being transmitted in the air while device B has its receiving window open, can communication succeed. In other words, the LL layer must also define the communication timing.
-
After device B receives the data 0x53, how does it parse this data? Does it represent humidity, battery level, or something else? This is the job of the GAP layer, which introduces the LTV (Length-Type-Value) structure to define data, for example, 020105, where 02 is the length, 01 is the type (mandatory field indicating the broadcast flag, which must be included in the broadcast packet), and 05 is the value. Since the maximum size of a broadcast packet is only 31 bytes, the types of data it can define are extremely limited. For the battery level mentioned here, GAP has not defined it, so to send battery data via broadcasting, we can only use a vendor-defined data type 0xFF, i.e., 04FF590053, where 04 indicates the length, FF indicates the data type (custom data), 0x0059 is the vendor ID (mandatory field in custom data), and 0x53 is our data (the two devices agree that 0x53 represents battery level, and not something else).
The final data packet transmitted in the air will become: AA D6BE898E 60 0E 3B75AB2A02E1 02010504FF5900 53 8EC7B2
-
AA – Preamble
-
D6BE898E – Access Address
-
60 – LL Frame Header Field
-
0E – Payload Length
-
3B75AB2A02E1 – Advertiser Device Address
-
02010504FF590053 – Broadcast Data
-
8EC7B2 – CRC24 Value
With PHY, LL, and GAP, we can send broadcast packets, but the information carried by broadcast packets is extremely limited, and there are several major limitations:
-
One-to-one bidirectional communication is not possible (broadcasting is one-to-many communication and is unidirectional).
-
Since it does not support packet assembly and disassembly, large data cannot be transmitted.
-
Communication is unreliable and inefficient. The broadcast channel cannot be too many, or it will lead to low efficiency on the scanning end. Therefore, BLE only uses three channels for broadcasting and scanning: 37 (2402MHz), 38 (2426MHz), and 39 (2480MHz), so broadcasting does not support frequency hopping. Since broadcasting is one-to-many, it also cannot support ACK. All these make broadcast communication unreliable.
-
The power consumption of the scanning end is high. Since the scanning end does not know when the device side broadcasts or which channel the device side uses for broadcasting, the scanning end can only extend the scanning window time and scan all three channels (37/38/39) simultaneously, resulting in higher power consumption.
However, connections can effectively solve the above problems. Now let’s see how a connection sends 0x53.
Connection Method
What exactly is a connection? Like wired UART, it is easy to understand; it is simply connecting devices A and B with wires (Rx, Tx, etc.). Connecting two devices with a “wire” essentially means that the two devices have a common communication medium and synchronize their clocks. Bluetooth connections operate on the same principle; establishing a Bluetooth connection between device A and device B means that device A and device B successfully synchronize one-to-one, which specifically includes the following aspects:
-
Device A and device B agree on the physical channel to be used next.
-
Device A and device B establish a common time anchor, meaning that both parties’ time origins become the same point.
-
Device A and device B successfully synchronize their clocks, meaning both parties know when to send and receive data packets.
-
After a successful connection, the communication process between device A and device B is as follows:
As shown in the figure above, once device A and device B successfully connect (in this case, we call device A the Master or Central and device B the Slave or Peripheral), device A will periodically send data packets to device B at intervals of CI (connection interval), while device B will also periodically open its radio frequency receiving window at intervals of CI to receive data packets from device A. According to Bluetooth specifications, device B must switch to sending state 150us after receiving a data packet from device A, and device A will switch to receiving state to receive the data sent by device B. It can be seen that in the connection state, the radio frequency sending and receiving windows of both device A and device B are periodically opened and closed in a planned manner, and the open time is very short, greatly reducing system power consumption and significantly improving system efficiency.
Now let’s see how the data 0x53 is sent in the connection state, from which everyone can appreciate the cleverness of the layered BLE protocol stack.
-
For the developer, it is very simple; they only need to call send(0x53).
-
The GATT layer defines the type and grouping of the data. For convenience, we use 0x0013 to represent the battery level data type, so the GATT layer packages the data into 130053 (little-endian format!).
-
The ATT layer is used to select specific communication commands, such as read/write/notify/indicate, etc. Here, we choose the notify command 0x1B, so the data packet becomes: 1B130053.
-
L2CAP specifies the connection interval (e.g., synchronizing every 10ms, which is not reflected in the data packet) and specifies the logical channel number 0004 (indicating the ATT command), finally adding the ATT data length 0x0004 to the packet header, so the data becomes: 040004001B130053.
-
The LL layer has many tasks; first, it needs to specify which physical channel to use for transmission (the physical channel is not reflected in the data packet), then allocate an Access Address (0x50655DAB) for this connection to indicate that this connection is exclusively for device A and device B, then add the LL header and payload length fields. The LL header indicates that this packet is a data packet, not a control packet, etc., and the payload length indicates the total length of the L2CAP field. Finally, the CRC24 field is added to ensure the integrity of the entire packet, so the final data packet becomes:
-
AA – Preamble
-
0x50655DAB – Access Address
-
1E – LL Frame Header Field
-
08 – Payload Length
-
04000400 – ATT Data Length and L2CAP Channel Number
-
1B – Notify Command
-
0x0013 – Battery Data Handle
-
0x53 – The actual battery data to be sent
-
0xF650D5 – CRC24 Value
-
Although the developer only called send(0x53), due to the layered packaging of the low-power Bluetooth protocol stack, the actual data transmitted in the air will look like the following:
-
AAAB5D65501E08040004001B130053D550F6
The above is just a simple overview of the implementation principles of the BLE protocol stack. Even so, since these are all about the lower-level aspects of the BLE protocol stack, many developers may still find it dull and obscure. Moreover, for many developers, they are not concerned with how the BLE protocol stack is implemented; they are more interested in how to use the BLE protocol stack, that is, how to develop a BLE application. BLE applications are tangible things; they cannot be discussed in general terms like the protocol stack above; they must be explained in conjunction with specific Bluetooth chips and Bluetooth protocol stacks. Therefore, in the following sections, I will use Nordic chips and protocol stacks as examples to specifically explain how to develop BLE applications and how to understand some concepts and terms defined in the BLE protocol through code.