GAP Characteristics & Discovery Procedures
Chapter 14 · Part 2 of 3 · GAP Service Characteristics · Broadcast Mode · Discovery Modes · Limited, General & Name Discovery
14.5 — GAP Characteristics
The GAP Service — One Instance Per Device
GAP defines its own GATT service (UUID = <<Generic Access Profile>>) that every LE device exposes exactly once. This service holds characteristics that provide basic device information — name, visual appearance, privacy settings, and preferred connection parameters. Any Central can read these after connecting without any special permissions.
A UTF-8 encoded string up to 248 octets long, null-terminated if shorter. This is how any Central learns what to call this device on screen without asking the user. Value: “BT Thermometer”, “SmartPhone”, etc.
The Master first reads the Characteristic Declaration at handle 2 to confirm it contains Device Name, then reads the actual Device Name value at handle 3. Two round trips instead of one — but the result is an unambiguous confirmed name read.
A 2-byte value (from the Bluetooth Assigned Numbers document) that tells the connecting device what category this device belongs to. The Central can then show an appropriate icon — headset, phone, printer, watch, thermometer. This is the LE equivalent of the BR/EDR Class of Device.
A 1-byte flag indicating whether the Peripheral is currently using a random private address. Value 0x00 = privacy off, 0x01 = privacy on. If the Central is allowed to write this characteristic, it can remotely toggle privacy on the Peripheral without physical access to the device.
A 6-byte address the Peripheral stores so the Central knows which address to connect to next session. When a device using privacy rotates its random address, it writes the new address here so its bonded peers can find it again without scanning. Updated at each connection as needed.
An 8-byte structure that tells the Central what connection parameters this Peripheral would prefer. The Central is not obligated to use these values but should consider them when setting up the connection to balance performance and power consumption for the Peripheral.
| Field | Size | Value (ff ff / 00 00) | Meaning |
|---|---|---|---|
| Min Connection Interval | 2 | ff ff | No specific minimum |
| Max Connection Interval | 2 | ff ff | No specific maximum |
| Slave Latency | 2 | 00 00 | 0 events — respond to every event |
| Supervision Timeout Multiplier | 2 | ff ff | No specific value requested |
When all interval fields are 0xFFFF it means the Peripheral has no preference — the Central can choose whatever interval works best for the use case. Peripherals that need specific timing (e.g. a device streaming audio that requires short intervals) would set concrete values here.
/* Read all GAP service characteristics: */
gatttool -b AA:BB:CC:DD:EE:FF --primary --uuid=0x1800
/* attr handle: 0x0001, end grp handle: 0x0007 uuid: 0x1800 */
gatttool -b AA:BB:CC:DD:EE:FF --characteristics \
--start=0x0001 --end=0x0007
/* Read Device Name (handle 3 in most devices): */
gatttool -b AA:BB:CC:DD:EE:FF --char-read --handle=0x0003
/* Read Appearance: */
gatttool -b AA:BB:CC:DD:EE:FF --char-read --handle=0x0005
/* Read PPCP (8 bytes, little-endian fields): */
gatttool -b AA:BB:CC:DD:EE:FF --char-read --handle=0x0007
/* ff ff ff ff 00 00 ff ff → no preference for most params */
/* With bluetoothctl after connecting: */
[AA:BB:CC:DD:EE:FF]# info
/* Name: Battery V1.0 */
/* Appearance: Generic tag */
14.6.1 — Broadcast Mode and Observation Procedure
Simplest LE Data Distribution — No Connection, No Ack
The Broadcast mode is the most stripped-down data delivery mechanism in BLE. A Broadcaster sends data outward in advertising events continuously, with no mechanism to know if anyone received it. An Observer receives those events with no ability to respond or connect. Data is considered unreliable — lost packets are simply gone.
This one-way pattern is extremely power-efficient for the Broadcaster because there is no connection state to maintain. iBeacon transmitters, Eddystone beacons, environmental sensor tags, and retail price displays all operate in broadcast mode.
/* Set up a broadcaster with BlueZ hcitool: */
/* Set non-connectable advertising: */
sudo hcitool -i hci0 cmd 0x08 0x0008 \
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
/* Enable advertising (non-connectable type = 0x03): */
sudo hciconfig hci0 leadv 3
/* With btmgmt (newer BlueZ interface): */
sudo btmgmt add-adv --connectable false --scannable false \
--data "020106 09094d79426561636f6e"
/* Observe advertising with hcitool passive scan: */
sudo hcitool lescan --passive
/* Devices appear with addresses and names (if in AdvData) */
/* Raw advertising reports via btmon: */
sudo btmon 2>&1 | grep -A 8 "ADV_NONCONN_IND"
14.6.2 — Discovery Modes and Procedures
Three Modes, Three Procedures — How Devices Find Each Other
Discovery is the mechanism by which a Central finds Peripherals nearby and decides which ones to connect to. The Peripheral controls whether it is discoverable (via the Flags AD Type in its advertisements). The Central controls which Peripherals it considers found (via the discovery procedure it is running).
Flags not set (both bits = 0)
LE_General_Disc = 0
LE_General_Disc = 1
14.6.2.2 & 14.6.2.3 — Limited Discoverable Mode + Limited Discovery Procedure
Limited discoverability is designed for devices that should only be pairable for a short window — like a sensor that enters pairing mode when the user holds a button. After the window expires, the device stops advertising with the Limited flag, becoming invisible until the user activates it again.
LE_Limited = 1, LE_General = 0
ADV_SCAN_IND with Limited flag
/* Enable Limited Discoverable mode with btmgmt: */
/* Flags 0x01 = LE Limited Discoverable */
sudo btmgmt power on
sudo btmgmt connectable on
sudo btmgmt limited-discov on /* sets Limited flag */
/* After timeout, disable: */
sudo btmgmt limited-discov off
/* With hciconfig (older interface): */
sudo hciconfig hci0 up
sudo hciconfig hci0 leadv 0 /* 0 = connectable + limited */
/* Check Flags value in advertising packet: */
sudo btmon 2>&1 | grep "Flags:"
/* Flags: 0x01 (LE Limited Discoverable Mode) */
14.6.2.4 & 14.6.2.5 — General Discoverable Mode + General Discovery Procedure
General discoverability is the default mode for most BLE devices. The device advertises continuously with the LE General Discoverable flag set and stays visible indefinitely. The General Discovery Procedure on the Central collects both General and Limited Discoverable devices — it is intentionally inclusive. Any device with either discoverable flag set gets added to the Central’s found-devices list.
→ add to found list
LE_Limited = 0, LE_General = 1
ADV_SCAN_IND continuously
/* Enable General Discoverable mode with btmgmt: */
sudo btmgmt power on
sudo btmgmt connectable on
sudo btmgmt discov on /* sets General flag */
/* BlueZ hciconfig: */
sudo hciconfig hci0 piscan /* page + inquiry scan */
/* For LE advertising with General Discoverable flag: */
sudo hciconfig hci0 leadv /* default = general */
/* Flags byte in advertisement: */
/* 0x02 = LE General Discoverable Only */
/* 0x06 = LE General Discoverable + BR/EDR Not Supported */
/* Scan from Central side: */
sudo hcitool lescan
/* [AA:BB:CC:DD:EE:FF] Battery V1.0 (found via General disc.) */
14.6.2.6 — Name Discovery Procedure
Once a Central has discovered a device through General or Limited discovery, it may have only a shortened name (or no name at all) from the advertising packet. Name Discovery fills in the full name by reading the Device Name Characteristic via GATT. The procedure involves connecting, reading the characteristic, then disconnecting if the connection was opened only for this purpose.
/* Name Discovery — BlueZ does this automatically: */
/* When you scan and connect, BlueZ reads Device Name char */
/* Manual: read name characteristic after connecting: */
gatttool -b AA:BB:CC:DD:EE:FF --char-read --uuid=0x2A00
/* This uses Read Using Characteristic UUID sub-procedure */
/* = Read By Type Request with UUID=0x2A00 */
/* In bluetoothctl: */
[AA:BB:CC:DD:EE:FF]# info
/* Name: Battery V1.0 (fetched from GATT if not in adv) */
/* Check if name is in advertising data first: */
sudo btmon 2>&1 | grep "Name:"
/* Complete Local Name: Battery V1.0 ← from AD Type 0x09 */
/* Shortened Local Name: Battery ← from AD Type 0x08 */
