Structuring Bluetooth Low Energy (BLE) advertising data for interoperability and interpretability
Learn how we at reelyActive structure BLE advertising packets based on best practices we've established.
This tutorial is organised into four parts as follows:
Links to related tutorials are provided at the end.
Tutorials to consider instead
Just enough structure with just enough space for a modest amount of data.
The diagram below illustrates the structure of a BLE advertising packet with the Packet Data Unit (PDU) expanded and identifier content highlighted. The preamble, access address and CRC are typically generated/processed automatically, and are irrelevant to this discussion.
Every BLE advertising packet includes a 48-bit Advertiser Address which uniquely identifies the device that is advertising data. See our best practices guide on identifiers to learn more about this, and other options for interoperable device identification:
A BLE advertising packet may include an optional payload of up to 31 bytes for additional data. The Bluetooth Generic Access Protocol (GAP), documented here, affords many payload options. Below are the most common for data.
Bluetooth GAP and the advertising packet payload capacity allow for the inclusion of up to 20 bytes of data for a specific 16-bit service class UUID.
| Common Data Type | Name | Max. Data |
|---|---|---|
| 0x16 | Service Data (16-bit UUID) | 20 bytes |
| 0x20 | Service Data (32-bit UUID) | 18 bytes |
| 0x21 | Service Data (128-bit UUID) | 6 bytes |
The Bluetooth SIG maintains assigned numbers for GATT Services and Characteristics. It is possible to include service/characteristic data provided it does not exceed the payload capacity. An example is battery, where a device advertises the percentage as a Battery Level characteristic (0x2A19).
An entity may purchase a 16-bit member service from the Bluetooth SIG and define the service data structure as the wish, either as an open or closed standard. An example is Google Eddystone which has the reserved UUID 0xFEAA and supports several different frame types defined as open standards (). Eddystone-TLM, which includes battery voltage, temperature, tx count and uptime, is presented in detail in Part 3. Another example is Tile which has the reserved UUID 0xFEED but whose specification is proprietary and not published.
Service data offers strictly interpreted representations of many common data types.
Bluetooth GAP and the advertising packet payload capacity allow for the inclusion of up to 22 bytes of use-as-you-please data for manufacturers who register (at no cost) with the Bluetooth SIG and receive a 16-bit company identifier.
| Common Data Type | Name | Max. Data |
|---|---|---|
| 0xff | Manufacturer Specific Data | 22 bytes |
For instance, Espruino device users may use the manufacturer's company identifier (0x0590) to send any data as a JSON string, for convenience. This is presented in detail in Part 3.
Manufacturer data affords full freedom for the representation of data, constrained only by the payload capacity.
Bluetooth GAP specifies other common data types which may be relevant, a subset of which are listed below.
| Common Data Type | Name | Max. Data |
|---|---|---|
| 0x08 | Shortened Local Name | 22 bytes |
| 0x09 | Complete Local Name | 22 bytes |
| 0x19 | Appearance | 2 bytes |
| 0x24 | URI | 22 bytes |
| 0x31 | Encrypted Advertising Data | 22 bytes |
For instance, a Local Name type could be used to represent the name of the device (or an associated person, product or place) as a short string of characters.
Observe the Bluetooth Specification whenever possible to maximise interoperability.
How to use GATT services and characteristics in an advertising packet.
The following table was compiled using the Assigned Numbers Document and the GATT Specification Supplement.
| UUID | Name | PDU Bytes | Data Type |
|---|---|---|---|
| 0x2A19 | Battery Level | 04 16 19 2a XX | uint8 (1% resolution) |
| 0x2A6C | Elevation | 06 16 6c 2a XX XX XX | sint24 (0.01m resolution) |
| 0x2A6D | Pressure | 07 16 6d 2a XX XX XX XX | uint32 (0.1Pa resolution) |
| 0x2A6E | Temperature | 05 16 6e 2a XX XX | sint16 (0.01°C resolution) |
| 0x2A6F | Humidity | 05 16 6f 2a XX XX | uint16 (0.01% resolution) |
| 0x2AA1 | Magnetic Flux Density 3D | 09 16 a1 2a XX XX YY YY ZZ ZZ | 3 x sint16 (10-7T resolution) |
| 0x2AA2 | Language | ** 16 a2 2a XX .. XX | utf8s (variable-length) |
| 0x2AF9 | Generic Level | 05 16 f9 2a XX XX | uint16 (unitless) |
| 0x2AFB | Illuminance | 06 16 fb 2a XX XX XX | uint24 (0.01lux resolution) |
| 0x2B8C | CO2 Concentration | 05 16 8c 2b XX XX | uint16 (ppm resolution) |
Many GATT characteristics and services are processed by our /advlib-ble-services open source library module which is included in our Pareto Anywhere open source IoT middleware.
The following section presents how open standards outside the Bluetooth Specification can be used in the absence of an appropriate Service/Characteristic to represent the data.
Reinforce existing open standards before creating yet another standard.
Member services that are offered as open specifications for general use include BTHome and Eddystone.
0xFCD2) " BTHome is an open standard for broadcasting sensor data and button presses over Bluetooth LE"
The BTHome v2 format specifies an efficient means of encapsulating a variety of measurements (and other data types) in a single advertising packet.*
* The data is interpreted by BTHome BLE and the advlib-ble-services module of Pareto Anywhere.
The BTHome v2 frame is illustrated below. It consists of a Device Info byte followed by one or more measurements. Each measurement begins with an Object ID byte which implicitly defines the type/length of the subsequent data bytes.
The table below lists a subset of the supported measurement types and their corresponding interpretation. For the complete, latest specification visit bthome.io/format.
| Name | Measurement Bytes | Data Type | Interpretation* |
|---|---|---|---|
| count | 09 XX | uint8 | count = 0xXX |
| gas | 1c BB1 | uint8 | isGasDetected = [ true/false ] |
| count | 3d YY XX2 | uint16 | count = 0xXXYY |
| duration | 42 ZZ YY XX2 | uint24 | duration = 0xXXYYZZ |
| text | 53 LL XX YY ZZ ... | utf-8 | text = "xyz..." |
| raw | 54 LL XX YY ZZ ... | — | raw = "XXYYZZ..." |
BTHome is the ideal choice, provided it supports all the required data types.
0xFEAA) " Eddystone is a protocol specification that defines a BLE message format for proximity beacon messages."
The Eddystone-TLM frame includes the following telemetry (TLM) measurements:
The telemetry data can be encrypted, when interwoven with Eddystone-TLM frames.
Eddystone-TLM is arguably the most commonly supported telemetry format.
Manufacturer-specific data offered as open specifications for general use include Espruino and DirAct.
0x0590) "Send up to 24 bytes of JSON on Espruino's manufacturer ID."
See the NRF.setAdvertising() documentation for details on advertising JSON data.*
* The data is interpreted as JSON by EspruinoHub and the advlib-ble-manufacturers module of Pareto Anywhere.
Espruino JSON payloads are ideal for dev/test when using official Espruino devices.
0x0583) " DirAct is open source embedded software for real-time proximity interaction detection."
The DirAct specification details how to populate an advertising packet* with the deviceId and rssi of up to three nearby peer devices, using InteroperaBLE Identifiers (see Part 4 below), as well as:
Open source code is provided for Espruino devices: /reelyactive/diract
* The data is interpreted by the advlib-ble-manufacturers module of Pareto Anywhere.
DirAct is ideal for non-commercial proximity identification applications.
The following section presents the InteroperaBLE Identifier as an if-nothing-else-fits option for representing data in a BLE advertising packet.
Leverage existing standards to represent data in an interoperable way.
See our Best Practices for BLE Identifiers tutorial for an introduction to the InteroperaBLE Identifier with regards to its primary purpose: identification.
In addition to interoperable identification, the InteroperaBLE Identifier can also encapsulate data in its Entity UUID and Instance ID, which is presented next.
Moreover, all defined InteroperaBLE Identifiers are interpreted by our /advlib-interoperable open source interpreter module which is included in our Pareto Anywhere open source IoT middleware.
The InteroperaBLE Identifier can be implemented in a standard iBeacon or Eddystone-UID frame, as illustrated below.
In the case of encapsulated data, the Elided UUID identifies the data type, as listed in the table below, with the identifier interpreted as data in certain cases.
| Elided UUID | Name | Identifier | Interpretation* |
|---|---|---|---|
| 496f49445554462d3332 | Unicode Code Point | UTF-32 | ex: 0x001f989 = 🦉 |
| 496f49444d6f74696f63 | Motion | User-defined | isMotionDetected = [ true ] |
| 496f4944427574746f6e | Button | User-defined | isButtonPressed = [ true ] |
Continue exploring our open architecture and all its applications.