BLE link layer – bluez development course

 

Chapter 8 — BLE Link Layer
Part 1: The 5-State Machine, Roles (Advertiser/Scanner/Initiator/Master/Slave) & Device Addressing
States
5 total
Roles
Advertiser, Scanner…
Address
48-bit
Privacy
Random Address
Keywords:

BLE link layer 5 states BLE advertiser role BLE scanner role BLE initiator role BLE master slave BLE public device address BLE random address privacy OUI Bluetooth address BLE resolvable private address BlueZ link layer states

8.1 — What the Link Layer Does

The Link Layer sits directly above the Physical Layer in the BLE stack. While the Physical Layer just moves raw bits over the air, the Link Layer gives those bits structure and meaning. It is responsible for:

  • Controlling and establishing links — deciding when to connect, how to connect, and managing the lifecycle of a connection
  • Selecting frequencies — implementing frequency hopping across the 37 data channels during a connection
  • Supporting topologies — managing the Master/Slave relationship in a piconet
  • Supporting data exchange modes — advertising, scanning, and connected data transfer

The Link Layer is one of the layers that was completely redesigned for BLE rather than adapted from classic Bluetooth. Its 5-state design is intentionally minimal — just enough to support all required BLE use cases while keeping the implementation as simple and power-efficient as possible.

Position in the Stack
Link Layer Position in BLE Protocol Stack
GATT Based Profiles
Generic Access Profile (GAP)
Generic Attribute Profile (GATT)
ATT
SM
L2CAP
HCI
🔗 Link Layer ← YOU ARE HERE
Bluetooth Radio (Physical Layer)

The Link Layer provides services upward to L2CAP and receives radio services downward from the Physical Layer. HCI is the interface that separates the host stack (everything from L2CAP upward, typically running on the main application processor) from the controller (Link Layer + Physical Layer, typically on a dedicated Bluetooth chip).

8.2 — The Five Link Layer States

A Simple State Machine That Covers Every BLE Scenario

The entire operation of the BLE Link Layer can be described by a state machine with exactly five states. At any point in time, the Link Layer is in exactly one of these states. The state determines what radio activity is happening and what the device is currently doing on the air.

BLE Link Layer State Machine — All 5 States and Transitions

SCANNING
(Scanner role)
Listens for
advertising packets
start scan

STANDBY
Default state
No TX, no RX
start adv
ADVERTISING
(Advertiser role)
Broadcasts on
ch 37, 38, 39

initiate
connection
accepted
connection

INITIATING
(Initiator role)
Sends CONNECT_REQ
to an Advertiser
CONNECT_REQ sent
CONNECTION
Initiator → Master
Advertiser → Slave
Data transfer
on 37 data channels

Any state can return to STANDBY by stopping the current activity

In practice, most BLE devices only ever need two or three of these states. A simple sensor that just advertises its data might only use Standby and Advertising. A smartphone scanning for peripherals uses Standby, Scanning, Initiating, and Connection. The full five-state machine is the union of all possible device behaviours.

State 1 — Standby: The Starting Point

Standby is the default state. The device is powered on but the radio is completely inactive — no packets are transmitted or received. Every BLE device starts here after power-on reset. A device can transition to Standby from any other state, making it the universal reset point of the state machine.

In terms of power consumption, Standby is the cheapest active state — the device is ready to do something but is not consuming radio power yet. A BLE sensor in deep sleep would power up into Standby, move to Advertising to send its data, then return to Standby and power down the radio until the next sensor reading.

State 2 — Advertising: Broadcasting Presence to the World

In the Advertising state, the Link Layer transmits advertising packets on the three advertising channels (37, 38, 39) in a repeating cycle. A device in this state is called an Advertiser. It may also listen briefly for responses from devices that react to its advertisement.

Advertising serves multiple purposes depending on what the Advertiser puts in its advertising packets:

  • Announcing existence: “I am here, I am a heart rate monitor”
  • Broadcasting data: Sending a small payload (like a temperature reading or beacon data) to any listener within range, with no connection needed
  • Inviting connections: Signalling that the device is ready to accept a connection request from an Initiator
Advertising State — Thermometer Example

🌡️
BLE Thermometer
State: ADVERTISING
(Advertiser role)
ADV packet contents:
• “I am a thermometer”
• Current temp: 37.2°C
• Connectable: YES
• TX Power: -4 dBm

ch 37
ch 38
ch 39
📶
Every 100 ms
(typical interval)

📱
Phone (Scanner)
Picks up ADV packet
→ may connect
📊
Display unit (Scanner)
Receives temp data
no connection needed
State 3 — Scanning: Listening for Advertisers

In the Scanning state, the Link Layer listens on the advertising channels for packets from Advertisers. A device in this state is called a Scanner. The Scanner may also send a Scan Request to an Advertiser to ask for additional information not included in the initial advertising packet (for example, the full device name if it was too long to fit). The Advertiser responds with a Scan Response.

Scanning uses more power than advertising because the receiver must stay active and sensitive to detect incoming signals. This is by design — in the BLE power model, the central device (typically a phone or hub with a large battery) does the scanning while the peripheral (the small sensor) does the advertising.

/* Starting BLE scan from BlueZ — two ways */

/* Method 1: Command line */
/* sudo hcitool lescan */
/* sudo hcitool lescan --duplicates   (show repeated advertisers) */
/* sudo hcitool lescan --passive      (don't send scan requests)  */

/* Method 2: C code using BlueZ HCI library */
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>

int start_le_scan(int dev_id)
{
    int sock = hci_open_dev(dev_id);

    /* scan_type: 0x01 = active (sends scan requests)
                  0x00 = passive (only listens)       */
    int err = hci_le_set_scan_parameters(
        sock,
        0x01,          /* active scan */
        htobs(0x0010), /* interval: 10ms */
        htobs(0x0010), /* window:   10ms  */
        0x00,          /* own addr type: public */
        0x00,          /* filter policy: accept all */
        1000);

    if (err < 0) { perror("Set scan params"); return err; }

    err = hci_le_set_scan_enable(sock, 0x01, 0x00, 1000);
    if (err < 0) { perror("Enable scan"); return err; }

    return sock; /* caller must read HCI events from this socket */
}
State 4 — Initiating: Requesting a Connection

The Initiating state is entered when the Scanner has found an Advertiser it wants to connect to and decides to establish a connection. A device in this state is called an Initiator. The Initiator sends a CONNECT_REQ packet to the specific Advertiser. This packet contains all the parameters needed for the connection — timing, channel map, hop increment, and so on.

When the Advertiser receives the CONNECT_REQ:

  • The Initiator transitions to the Connection state and takes the Master role
  • The Advertiser also transitions to the Connection state and takes the Slave role

This is the critical moment where the roles become fixed for the duration of the connection. The device that initiated (sent CONNECT_REQ) is always the Master. The device that advertised and accepted is always the Slave.

State 5 — Connection: Two Roles — Master and Slave

In the Connection state, two devices are communicating directly over the 37 data channels using adaptive frequency hopping. This state has two distinct roles:

Master Role

The Master controls the connection timing. It decides when connection events happen, sets the connection interval, and manages the channel hopping sequence. The Master is always the device that was the Initiator — the one that sent the CONNECT_REQ to start the connection. Typically a smartphone, hub, or computer.

Slave Role

The Slave follows the timing set by the Master. It wakes up at the scheduled connection events, exchanges data, and then sleeps until the next event. The Slave is always the device that was the Advertiser. Typically a sensor, peripheral device, or IoT node.

Scatternet support — Bluetooth 4.0 vs 4.1: In version 4.0 of the specification, a Slave could only be connected to one Master at a time. There was no scatternet support. Specification 4.1 extended this: a device can now be the Master of one piconet and the Slave of another simultaneously, or a Slave in multiple different piconets. This flexibility enables more complex IoT topologies but adds implementation complexity — which is why it remained optional.

/* Connecting to a BLE peripheral using bluetoothctl */
/* bluetoothctl is the standard BlueZ management tool */

/* Step 1: Start scanning to find the device */
/* $ bluetoothctl */
/* [bluetooth]# scan on */
/* Discovery started */
/* [NEW] Device AA:BB:CC:DD:EE:FF Polar H7 */

/* Step 2: Connect — our device becomes Master, Polar H7 = Slave */
/* [bluetooth]# connect AA:BB:CC:DD:EE:FF */
/* Attempting to connect to AA:BB:CC:DD:EE:FF */
/* [CHG] Device AA:BB:CC:DD:EE:FF Connected: yes */

/* Step 3: Verify connection state via hcitool */
/* $ hcitool con */
/* Connections: */
/*   LE AA:BB:CC:DD:EE:FF handle 64 state 1 lm MASTER */

/* The "MASTER" label confirms our device is in Master role */

8.3 — Device Address

BLE Devices Can Have Two Types of Address — Unlike Classic Bluetooth

In classic Bluetooth, every device has exactly one permanent 48-bit Bluetooth Device Address (BD_ADDR) burned into the hardware at the factory. You cannot change it. BLE is more flexible — a BLE device may have a Public Address, a Random Address, or both. At least one must be present so the device can be identified on the air.

BLE Device Address Types — Complete Hierarchy
BLE Device Address (48-bit)
Public Address
• Globally unique, permanent
• Same as BD_ADDR (dual mode)
• Administered by IEEE
• Cannot change
Random Address
Static Address
Changes only at power cycle. Same device, same address until rebooted.
Private Address
Non-resolvable: Random, unresolvable. Nobody can find the real address.
Resolvable: Bonded devices can resolve it using the link key. Others cannot.
8.3.1 — Public Device Address

The Public Address is BLE’s equivalent of the classic Bluetooth BD_ADDR. It is a 48-bit globally unique identifier that is permanently assigned to the device by the manufacturer. It never changes. For dual-mode devices (those supporting both BR/EDR and BLE), the public device address is the same value as the BR/EDR BD_ADDR — the single address identifies the device regardless of which radio it is using.

The 48-bit address is structured in two halves:

48-bit Public Device Address Structure
Bits 47–24 (MSB)
OUI
Organizationally Unique Identifier
24 bits assigned by IEEE
to each company.
All devices from the
same company share this prefix.
e.g., 00:1A:7D = Texas Instruments
B8:27:EB = Raspberry Pi Foundation
:
Bits 23–0 (LSB)
Device ID
Company-assigned serial
24 bits assigned by the
manufacturer to each
individual controller unit.
Unique within that company.
e.g., DA:71:13 = specific device
No two devices share this
Full address example: 00:1A:7D:DA:71:13
OUI (Texas Instruments) : Device serial

The OUI is administered by IEEE — the same organisation that manages Ethernet MAC addresses. This is not a coincidence: the 48-bit format and the OUI concept are borrowed directly from the IEEE 802 address space. The Bluetooth SIG is registered with IEEE to allocate OUIs for Bluetooth device manufacturers.

/* Reading the local Bluetooth device address with BlueZ */

#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>

void print_device_address(int dev_id)
{
    struct hci_dev_info di;

    if (hci_devinfo(dev_id, &di) < 0) {
        perror("hci_devinfo");
        return;
    }

    char addr_str[18];
    ba2str(&di.bdaddr, addr_str);
    /* ba2str converts binary bdaddr_t to "XX:XX:XX:XX:XX:XX" string */

    printf("Device: %s\n", di.name);
    printf("BD_ADDR: %s\n", addr_str);
    /* Output example:
       Device: hci0
       BD_ADDR: 00:1A:7D:DA:71:13   */

    /* Command line equivalent: */
    /* hciconfig hci0 | grep "BD Address" */
}
8.3.2 — Random Address: Privacy Through Address Rotation

A fixed address is a tracking problem. If a BLE device always broadcasts from the same address, any device within radio range can build a log of when and where that address appeared. Over time, this creates a detailed movement and behaviour profile of the device owner — without their knowledge or consent.

The shoe example from the spec: Imagine someone wearing BLE-enabled running shoes that constantly broadcast step count data. A stationary listener (a phone, a laptop, anything with a BLE radio) can passively record every packet from those shoes. Since the address is fixed, the listener can track the person’s entire route through a building, a mall, or a city — just by following the known address.

Fixed Address vs Random Address — Privacy Difference

❌ Fixed Address — Easily Tracked
9:00am
AA:BB:CC:11:22:33
loc: lobby
9:15am
AA:BB:CC:11:22:33
loc: corridor
9:30am
AA:BB:CC:11:22:33
loc: office
Same address every time = complete movement trail for anyone listening

✅ Random Address — Cannot Be Tracked
9:00am
AA:BB:CC:11:22:33
9:15am
FF:01:C3:A7:B2:44
9:30am
20:DE:9F:55:C1:08
Different address every time = no way to link transmissions to same device

BLE solves this with three types of random address, each with different privacy levels and use cases. The intended receiver of the data (a paired device) is given a way to resolve the random address back to the real device using a shared key — so only the authorised recipient can link the transmissions together.

Static Address

The device may generate a new static address each time it powers up, but the address cannot change while the device is running. This provides some privacy (the address changes between usage sessions) while maintaining consistency within a single session. The downside: any peer device that stored the old address cannot reconnect after a reboot.

Non-Resolvable Private Address

A purely random address that changes periodically (typically every 15 minutes). Nobody — not even a bonded device — can determine the real identity of the device from this address. Maximum anonymity. Used when the device genuinely needs to be untrackable by anyone.

Resolvable Private Address (RPA)

The address changes periodically, but bonded devices possess an Identity Resolving Key (IRK) that lets them cryptographically verify that the random address belongs to the same device they bonded with. Strangers cannot resolve the address. Only trusted bonded peers can. This is the most practical approach for devices like fitness trackers — the owner’s phone can always recognise the device, but nobody else can track it.

/* BlueZ handles random/resolvable addresses automatically */
/* You can see the address type when scanning: */

/* btmon output for an advertising device using RPA: */
/* > HCI Event: LE Meta Event (0x3e)                 */
/*   LE Advertising Report (0x02)                    */
/*   Address type: Random (0x01)   <--- random!      */
/*   Address: 7D:A3:F1:22:C8:B0                      */
/*   (This is a resolvable private address —          */
/*    top 2 bits are 11 = RPA format)                 */

/* After bonding, BlueZ resolves RPAs automatically  */
/* using stored IRKs. You see the real name/address  */
/* in bluetoothctl even though the RPA changes.      */

/* Check stored bonding keys: */
/* ls /var/lib/bluetooth/<your-adapter>/             */
/* Each subdirectory = one bonded device             */

Chapter 8 Part 1 Complete

You understand the five Link Layer states, the roles in each state, and all three types of device addressing. Part 2 covers physical channels, the channel map, and adaptive frequency hopping.

Leave a Reply

Your email address will not be published. Required fields are marked *