Listen only
SCAN_REQ allowed
Receive only
+ SCAN_REQ
Finishing the Advertising Event Types
The previous file covered Connectable Undirected (ADV_IND) and Connectable Directed (ADV_DIRECT_IND) — both of which allow devices to form connections. This file covers the remaining two event types: Non-Connectable Undirected (ADV_NONCONN_IND) and Scannable Undirected (ADV_SCAN_IND). Neither allows connections. We then move to the Scanning state and look at passive and active scanning in detail.
Event Type 3 — Non-Connectable Undirected (ADV_NONCONN_IND)
This advertising type is used when a device wants to broadcast information to anyone nearby but does not want anything back. The receiving scanner can only listen. It cannot send a SCAN_REQ for more data and it cannot send a CONNECT_REQ to start a connection. The information flows strictly one way.
This is the most restrictive advertising type in terms of what the receiver can do — but also the most power-efficient for the Advertiser because no response handling is needed. The Advertiser fires its packet and moves on.
Payload fields — same as ADV_IND:
AdvA (6 octets)
The Advertiser’s address. TxAdd header bit says if it is public or random.
AdvData (0–31 octets)
The actual broadcast data from the host — the payload the scanner receives.
(AdvA + AdvData)
(listen only)
Event Type 4 — Scannable Undirected (ADV_SCAN_IND)
ADV_SCAN_IND sits between ADV_NONCONN_IND and ADV_IND in terms of what the receiver can do. Like ADV_NONCONN_IND, connections are not allowed — no device can send a CONNECT_REQ. But unlike ADV_NONCONN_IND, a Scanner can send a SCAN_REQ to request additional data from the Advertiser.
Think of it as a notice board: the Advertiser puts up a short message, and anyone can ask for the full document. But nobody can sit down and have a private conversation (connection) with the notice board.
Payload fields — same structure as ADV_NONCONN_IND:
AdvA (6 octets)
Advertiser’s address.
AdvData (0–31 octets)
The initial broadcast data. A Scanner may then ask for more via SCAN_REQ.
| PDU Type | SCAN_REQ? | CONNECT_REQ? | Use When… |
|---|---|---|---|
| ADV_IND | ✓ | ✓ (any device) | Device wants to be discovered by and connected to anyone |
| ADV_DIRECT_IND | ✗ | ✓ (named only) | Device wants to reconnect fast to one specific known peer |
| ADV_NONCONN_IND | ✗ | ✗ | Device just broadcasts data, no interaction needed |
| ADV_SCAN_IND | ✓ | ✗ | Device broadcasts but can provide extra data on request |
8.11.1.3 — Scanning State
In the Scanning state, the link layer listens on the three advertising channels (37, 38, 39) for PDUs broadcast by Advertisers. The device in this state is called a Scanner. The Scanner never initiates a connection (that is the Initiating state’s job) — it only receives advertising packets and optionally asks for more data.
There are two modes of scanning, and which one is used depends on how much interaction the Scanner wants to have with Advertisers:
In passive scanning the Scanner only receives packets. It never sends anything back to the Advertiser — no SCAN_REQ, no response of any kind. The radio transmitter is completely unused in this mode. This makes passive scanning very power-efficient for the Scanner because receiving is cheaper than transmitting.
The Scanner’s job in passive mode:
- Receive advertising PDUs from nearby Advertisers
- Remove duplicate reports (the same Advertiser may be heard on all three channels in rapid succession — the Scanner deduplicates these)
- Pass advertising reports up to the host application, which decides what to do with the information
receives
duplicate
(deduplicated) → Host
/* Passive BLE scan using BlueZ — hcitool lescan (passive flag) */
/* sudo hcitool lescan --passive */
/* In C using BlueZ HCI library: */
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>
int start_passive_scan(int dev_id)
{
int sock = hci_open_dev(dev_id);
/* scan_type 0x00 = passive (never sends SCAN_REQ) */
/* scan_type 0x01 = active (may send SCAN_REQ) */
int err = hci_le_set_scan_parameters(
sock,
0x00, /* passive scan — no SCAN_REQ */
htobs(0x0010), /* scan interval 10ms */
htobs(0x0010), /* scan window 10ms */
0x00, /* own address: public */
0x00, /* accept all advertisers */
1000);
if (err < 0) return err;
/* filter_duplicates = 0x01 removes duplicate advertisers */
return hci_le_set_scan_enable(sock, 0x01, 0x01, 1000);
}
In active scanning the link layer does the same as passive scanning but additionally sends a SCAN_REQ to an Advertiser when it wants more information than the initial advertising packet contained. The Advertiser responds with a SCAN_RSP containing the extra data.
Important rule: The Scanner is only allowed to send a SCAN_REQ if the Advertiser used either an ADV_IND or an ADV_SCAN_IND — the two types that accept SCAN_REQ (marked “Yes” in the SCAN_REQ column of Table 8.4). If the Advertiser used ADV_DIRECT_IND or ADV_NONCONN_IND, the Scanner must stay silent and cannot ask for more data.
(active mode)
(ScanA + AdvA)
(AdvA + ScanRspData)
Both the SCAN_REQ and SCAN_RSP have defined payload structures. Knowing these fields is important when writing code that parses raw BLE advertising captures.
ScanA (6 octets)
The Scanner’s own address. Public or random, indicated by the TxAdd bit in the PDU header. The Advertiser uses this to know who is asking.
AdvA (6 octets)
The Advertiser’s address. The Advertiser checks this field — if it matches its own address, it responds with SCAN_RSP. If not, it ignores the request (it was meant for someone else).
AdvA (6 octets)
The Advertiser’s address. Confirms which device is sending this response.
ScanRspData (0–31 octets)
The supplemental advertising data. This is the extra content the Advertiser sends in response to the SCAN_REQ — things like full device name, additional service UUIDs, or other information that did not fit in the original ADV_IND.
/* Parsing a SCAN_REQ/SCAN_RSP using BlueZ btmon output */
/* sudo btmon | grep -A 20 "LE Advertising Report" */
/* For an active scan, btmon output shows: */
/* > HCI Event: LE Meta Event (0x3e) plen 17 */
/* LE Advertising Report (0x02) */
/* Num reports: 1 */
/* Event type: ADV_IND (connectable undirected) (0x00) */
/* Address type: Public (0x00) */
/* Address: 00:1A:7D:DA:71:13 (Polar H7) */
/* Data length: 7 */
/* Flags: 0x06 (LE General Discoverable, BR/EDR not supp) */
/* RSSI: -62 dBm */
/* Then a SCAN_RSP follows automatically in active mode: */
/* > HCI Event: LE Meta Event (0x3e) plen 27 */
/* LE Advertising Report (0x02) */
/* Event type: Scan Response (0x04) <--- SCAN_RSP */
/* Address: 00:1A:7D:DA:71:13 (Polar H7) */
/* Data length: 17 */
/* Complete name: Polar H7 <--- extra data */
/* Starting an active scan in code: */
hci_le_set_scan_parameters(sock,
0x01, /* 0x01 = ACTIVE scan mode */
htobs(0x0010), /* interval */
htobs(0x0010), /* window */
0x00, 0x00, 1000);
hci_le_set_scan_enable(sock, 0x01, 0x01, 1000);
- No SCAN_REQ sent — TX radio stays off
- Lower power on the Scanner
- Does not disturb the Advertiser
- Good for background monitoring, proximity detection
- Only gets data from the initial ADV packet
- Sends SCAN_REQ to get more data
- Slightly higher power (TX on briefly)
- Gets full device name and extra services
- Good for device discovery UI, pairing screens
- Only works with ADV_IND or ADV_SCAN_IND Advertisers
All Three Files Complete!
You now have complete coverage of BLE Data Packet Extensions, Bit Stream Processing, and all Link Layer non-connected states with every advertising event type and the full scanning state.
