How BLE advertising works: decoding an advertisement byte by byte
Bluetooth Low Energy devices announce themselves by shouting into the air on a fixed schedule. Those broadcasts are advertisements, and they're the layer your phone reads when you scan. Understanding their format is what turns a scanner from a list of mystery names into a tool you can actually reason with.
This guide covers how advertising works on the air, then decodes a real payload byte by byte.
The mechanics: channels, intervals, and PDU types
Three channels. Legacy advertising uses just three of BLE's 40 channels — 37, 38, and 39, at 2402, 2426, and 2480 MHz. They're deliberately spaced to sit in the gaps between Wi-Fi channels 1, 6, and 11. An advertiser transmits the same packet on all three in quick succession, which is why a scanner hopping between them still catches nearly everything.
Advertising interval. Devices repeat their advertisement anywhere from every 20 ms to every 10.24 seconds, plus a random delay of 0–10 ms so two devices on the same interval don't collide forever. Fast intervals mean quick discovery and short battery life; a beacon meant to run for years might advertise once a second or slower. If a device takes a while to show up in your scan, a long interval is usually why.
PDU types. The advertising packet header says what kind of advertiser this is:
- ADV_IND — connectable and scannable, undirected. The default for most peripherals.
- ADV_NONCONN_IND — non-connectable, non-scannable. Pure broadcast; this is what most beacons use.
- ADV_SCAN_IND — scannable but not connectable. Broadcasts, but will answer a scan request.
- ADV_DIRECT_IND — connectable, aimed at one specific peer address.
Scan requests double your space. A legacy advertisement payload is capped at 31 bytes. When a scanner is doing an active scan, it can send a SCAN_REQ to a scannable advertiser, which replies with a SCAN_RSP carrying another 31 bytes. That's why a device's full name sometimes only appears after a moment — the name was in the scan response, not the advertisement.
The payload format: length-type-value, repeated
The payload isn't a fixed struct. It's a sequence of self-describing AD structures, packed one after another until the data runs out:
[length][AD type][data...][length][AD type][data...]
The length byte counts the AD type byte plus the data — not itself. So a structure with a 1-byte value has length 0x02. Parsing is a simple loop: read a length, read that many bytes, repeat.
Common AD types you'll see:
| Type | Meaning |
|---|---|
0x01 |
Flags — discoverability and BR/EDR support |
0x02 / 0x03 |
Incomplete / complete list of 16-bit service UUIDs |
0x06 / 0x07 |
Incomplete / complete list of 128-bit service UUIDs |
0x08 / 0x09 |
Shortened / complete local name |
0x0A |
TX power level, in dBm |
0x16 |
Service data, 16-bit UUID |
0x19 |
Appearance (watch, thermometer, keyboard, …) |
0xFF |
Manufacturer-specific data |
Decoding a real advertisement
If you'd rather not do this by hand, our advertisement decoder takes a pasted hex payload and breaks it into named structures with the byte order already handled. Working through one manually first is still the fastest way to understand what it's showing you.
Here's a heart-rate monitor's payload as raw hex:
02 01 06 03 03 0D 18 09 09 48 52 4D 20 31 32 33 34
Seventeen bytes, three AD structures. Walking it:
02 01 06 — Flags. Length 0x02, type 0x01. The value 0x06 is a bitfield: bit 1 set means LE General Discoverable Mode, bit 2 set means BR/EDR Not Supported. Translation: a Bluetooth-LE-only device that wants to be found.
03 03 0D 18 — Complete list of 16-bit service UUIDs. Length 0x03, type 0x03, two bytes of data. UUIDs go out little-endian, so 0D 18 on the wire is UUID 0x180D — the SIG-assigned Heart Rate service. This single field tells you what the device is for before you ever connect to it.
09 09 48 52 4D 20 31 32 33 34 — Complete local name. Length 0x09, type 0x09, eight bytes of ASCII: HRM 1234.
That's the whole packet. Name, purpose, and capability, in 17 bytes.
Manufacturer-specific data: where the interesting bytes live
Type 0xFF is the escape hatch — a vendor can put whatever it wants there, and most of the genuinely interesting BLE payloads do. The only rule is that the first two bytes are the SIG-assigned company identifier, little-endian.
An iBeacon frame is the classic example:
1A FF 4C 00 02 15 <16-byte UUID> <major:2> <minor:2> <tx:1>
1A— length 26 (the type byte plus 25 bytes of data).FF— manufacturer-specific data.4C 00— little-endian0x004C, which is Apple.02 15— Apple's own sub-type:0x02means iBeacon,0x15is the 21-byte length that follows.- Then the beacon's 16-byte UUID, a 2-byte major, a 2-byte minor, and a calibrated TX power byte used for distance estimation.
Everything past the company ID is that vendor's private layout. Sensor readings, battery levels, button states, and rolling identifiers all tend to hide here, which makes 0xFF the field to watch when you're reverse-engineering a gadget: change something physical, watch which bytes move.
Addresses and why they change
Every advertisement carries a 6-byte address, but it may not be stable. Addresses are either public (a permanent, globally registered identifier) or random. Random breaks down further into static — fixed until reboot — and resolvable private addresses, which rotate every 15 minutes or so specifically to defeat passive tracking. Phones, headphones, and trackers all use rotating addresses.
Only a device that holds the shared identity resolving key can tie those addresses back to one device. For everyone else, one physical gadget looks like a stream of new devices over the course of an afternoon. This is by design, and it's the single most common source of "why do I see the same thing under three addresses?" confusion. Why Bluetooth addresses change goes through all four address types and how to identify each from its top bits.
Bluetooth 5 extended advertising
Bluetooth 5 lifted the 31-byte ceiling. With extended advertising, the three primary channels carry a small ADV_EXT_IND pointer that redirects scanners to an AUX_ADV_IND packet on one of the 37 data channels, where the real payload lives — up to 254 bytes per PDU, and chainable to roughly 1650 bytes total. It can also use the 2 Mbps or long-range coded PHY.
The practical catch: capturing extended advertising exhaustively is harder, because a sniffer has to follow the pointer to the right data channel at the right time. Phones handle it fine as scanners, but if you're doing capture work, this is a place where dedicated hardware earns its keep.
Reading advertisements yourself
You don't need a dongle to see any of this. Your phone's radio receives every advertisement in range, and a scanner app parses the AD structures for you. BLE Sniffer for Android shows each device's name, address, RSSI, manufacturer, and service UUIDs, logs sessions on-device, and exports to CSV so you can diff payloads across time in your own tools. It's free, has no ads, and sends nothing off your phone.
For decoding what happens inside a connection — ATT and GATT operations, rather than the broadcast layer — you'll want Wireshark plus capture hardware, covered in the protocol analyzer guide.
The takeaway
BLE advertising is a small, strict format: repeated length-type-value structures inside a 31-byte budget, broadcast on three channels at an interval the device picks. Learn the handful of common AD types and you can read most packets on sight — and once you can, manufacturer-specific data becomes the interesting part, because that's where every vendor puts the bytes that actually change.