bluez tutorial BLE Initiating State & Connection State

 

 

bluez tutorial BLE Initiating State & Connection State
Chapter 8 — Active Scanning Wrap-up, Initiating State Rules, Connection Creation vs Establishment & Full CONNECT_REQ Payload
CONNECT_REQ
34 bytes payload
LLData
22 bytes of params
Permission
ADV_IND or ADV_DIRECT
Max LE Links
1 per device pair
Keywords:

BLE initiating state BLE CONNECT_REQ permission ADV_IND BLE connection created vs established BLE CONNECT_REQ LLData BLE access address CRCInit BLE connInterval connSlaveLatency BLE hop increment SCA BlueZ connection initiation

Where We Are — Section 8.11

This file closes the SCAN_RSP payload fields left open from the Scanning state, then covers the last two link layer states: the Initiating state (how a device requests a connection) and the Connection state (what happens once that request is accepted). It ends with a deep dive into every field of the CONNECT_REQ PDU — the most data-rich single packet in BLE.

Active Scanning — SCAN_RSP Payload Wrap-up

The Two Remaining Payload Fields of SCAN_RSP

In active scanning the Advertiser sends a SCAN_RSP after receiving a SCAN_REQ. Its payload carries two fields:

AdvA (6 octets)

The Advertiser’s Bluetooth address. Public or random, flagged by TxAdd in the header. The Scanner uses this to confirm which device replied.

AdvData (0–31 octets)

The extra advertising data the host wanted to share but could not fit in the original ADV_IND — full device name, additional service UUIDs, manufacturer data, etc.

Figure 8.19 — Active Scanning Full Sequence
Advertising
ADV_IND or ADV_NONCONN_IND
Scanning
Advertising
SCAN_REQ
Scanning
Advertising
SCAN_RSP (AdvA + AdvData)
Scanning
Advertising report (with SCAN_RSP data merged) → Host
Sniffer evidence: Frame #428 sent SCAN_REQ to get more info from Advertiser

8.11.1.4 — Initiating State

The Initiator’s Job — Listen, Then Connect

In the Initiating state the link layer listens on advertising channels 37, 38, and 39 — exactly like the Scanning state. The critical difference is intent: the Initiator is not just collecting information. It has a specific target device in mind and is waiting for that device’s advertising packet so it can send a CONNECT_REQ.

The Initiator cannot connect to just any Advertiser. There is a strict permission rule: a CONNECT_REQ may only be sent in response to two specific advertising PDU types:

ALLOWED

ADV_IND

Connectable Undirected. Any Initiator in range may send a CONNECT_REQ to the Advertiser.

ALLOWED

ADV_DIRECT_IND

Connectable Directed. CONNECT_REQ is only allowed if the Initiator’s own address matches the InitA field embedded in the ADV_DIRECT_IND payload.

NOT ALLOWED

ADV_NONCONN_IND and ADV_SCAN_IND — neither accepts a CONNECT_REQ. The Initiator must keep listening.

Figure 8.20 — Initiating State: Sending CONNECT_REQ
Advertising
ADV_IND or ADV_DIRECT_IND
Initiator
Advertising
CONNECT_REQ
Initiator
Connection Indication → Host
Advertiser becomes Slave
Connection Indication → Host
Initiator becomes Master
Sniffer evidence — Figure 8.21 capture:

Frame #670

Advertiser sends ADV_IND on channel 39

Frame #671

Advertiser sends ADV_IND on channel 37

Frame #672

Advertiser sends ADV_IND on channel 38

Frame #673

Initiator sends CONNECT_REQ — connection begins

8.11.2 — Connection State

Two Paths In, Two Roles Out

The Connection state is entered the moment the CONNECT_REQ is sent. Who enters it and how they entered determines the role each device takes for the duration of that connection:

Two Ways to Enter Connection State — Roles Are Fixed at Entry
Was in Initiating State
Sent the CONNECT_REQ
Becomes MASTER
Controls timing & hopping
Was in Advertising State
Received the CONNECT_REQ
Becomes SLAVE
Follows Master’s schedule
Connection Created vs Connection Established

Entering the Connection state does not mean data can flow immediately. There are two stages:

Connection Lifecycle — Created Then Established
Initiating
Listening for ADV
CONNECT_REQ
CREATED
State entered
No data yet
First data PKT
ESTABLISHED
Link confirmed
Data can flow

When the CONNECT_REQ is sent both devices share the same timing, access address, and channel map — so they are synchronised. But neither has yet proven that radio packets are actually reaching the other side in both directions. When the first data channel PDU successfully arrives from the peer device, that proves the link is working. At that point the connection moves from created to established.

One LE link per pair: There can only be one LE connection between any two devices at any time. The stack will reject or reuse an existing connection if a duplicate is attempted.

CONNECT_REQ Payload — Every Field Explained

Three Top-Level Fields in the 34-Byte Payload

The CONNECT_REQ is the most information-dense packet in BLE. Its payload sets up the entire connection before a single data packet is exchanged.

CONNECT_REQ Payload Structure
InitA
6 octets
Initiator’s address
AdvA
6 octets
Advertiser’s address
LLData
22 octets
9 connection parameters
TxAdd in header → Initiator address type | RxAdd in header → Advertiser address type
LLData — All 9 Sub-Fields Decoded
LLData Sub-Fields — Field by Field
AA (4 oct)
Access Address — 32-bit random identifier unique to this connection. Every packet for this link carries this value. Other devices on the same channel will not match it and ignore those packets.
CRCInit (3)
CRC seed — random value chosen by the Master, used to initialise the 24-bit CRC calculation for all packets on this connection. Different from the fixed 0x555555 seed used on advertising channels.
WinSize (1)
Transmit window size — how wide (in 1.25 ms units) the window is during which the Slave should listen for the very first Master packet after connection creation.
WinOffset (2)
Transmit window offset — time (in 1.25 ms units) from when CONNECT_REQ was sent to when the first transmit window opens. Slave uses this to know how long to wait before starting to listen.
Interval (2)
connInterval — time between consecutive connection event anchor points, in 1.25 ms units. Range is 7.5 ms (fast, high power) to 4 s (slow, low power).
Latency (2)
connSlaveLatency — how many consecutive connection events the Slave is allowed to skip. 0 = must respond every event. Higher value = more battery savings for Slave, higher data latency.
Timeout (2)
connSupervisionTimeout — maximum time (in 10 ms units) without receiving a packet before declaring the connection lost. Both devices run this timer independently.
ChM (5)
Channel Map — 37-bit bitmap showing which data channels are currently marked as usable. Bit = 1 means the channel is good. Bit = 0 means it has interference and will be remapped by the hopping algorithm.
Hop+SCA (1)
Packed byte: lower 5 bits = Hop increment (5–16) used in the (fn + hop) mod 37 frequency hopping formula. Upper 3 bits = SCA (Sleep Clock Accuracy) — describes how much the Master’s clock drifts when asleep.
Total LLData = 4+3+1+2+2+2+2+5+1 = 22 octets

The SCA field tells the Slave how much timing drift to expect from the Master while the radio is sleeping between connection events. A higher SCA code (worse clock accuracy) means the Slave must open a wider listening window to catch the Master’s packet, which uses more power. Better hardware oscillators give lower SCA codes and narrower windows.

Real Sniffer Values from Figure 8.21

These are the actual parameter values decoded from a real CONNECT_REQ capture:

Captured CONNECT_REQ — Annotated Values
InitA 0x02:07:02:a1:00:04 (public)
AdvA 0x55:44:33:22:11:00 (public)
Access Address 0xAF9A8AAE (random, unique)
CRC Initialization Random value (chosen by Master)
transmitWindowSize 3.75 ms
transmitWindowOffset 86.25 ms
connInterval 86.25 ms (connection events every 86.25 ms)
connSlaveLatency 0 — Slave must listen at every single event
/* Parsing CONNECT_REQ LLData with BlueZ */
/* Maps directly to the 22 bytes of LLData in the PDU */

typedef struct {
    uint32_t  aa;          /* Access Address — 4 bytes        */
    uint8_t   crc_init[3]; /* CRC seed — 3 bytes              */
    uint8_t   win_size;    /* WinSize in units of 1.25ms      */
    uint16_t  win_offset;  /* WinOffset in units of 1.25ms    */
    uint16_t  interval;    /* connInterval in units of 1.25ms */
    uint16_t  latency;     /* connSlaveLatency (event count)  */
    uint16_t  timeout;     /* supervision timeout ×10ms       */
    uint8_t   chm[5];      /* 37-bit channel map bitmap       */
    uint8_t   hop_sca;     /* hop[4:0] | sca[7:5]            */
} __attribute__((packed)) le_lldata_t;

/* Extract hop increment and SCA from the packed byte */
uint8_t hop = lldata.hop_sca & 0x1F;         /* bits 4:0 */
uint8_t sca = (lldata.hop_sca >> 5) & 0x07;  /* bits 7:5 */

/* Using btmon to watch connection events live:          */
/* sudo btmon | grep -A 20 "LE Connection Complete"      */

Next — Link Layer Control Procedures

You now understand every link layer state and the full CONNECT_REQ payload. The next file covers Section 8.12 — the control procedures that manage an active connection: update, encryption, feature exchange, version exchange, and termination.

Next: Link Layer Control Procedures (PDF 1315) →

Leave a Reply

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