advlib
Library for wireless advertising packet decoding. Currently supports the following protocols:
For a live, interactive version of advlib visit reelyactive.github.io/advlib.
This project was published in a scientific paper entitled Low-Power Wireless Advertising Software Library for Distributed M2M and Contextual IoT presented at the 2nd IEEE World Forum on Internet of Things (WF-IoT) in Milan, Italy on December 16th, 2015.
Installation
npm install advlib
Hello advlib
var advlib = require('advlib');
var rawHexPacket = '421655daba50e1fe0201050c097265656c79416374697665';
var processedPacket = advlib.ble.process(rawHexPacket);
console.log(JSON.stringify(processedPacket, null, " "));
The console output should appear as follows:
{
type: "ADVA-48",
value: "fee150bada55",
advHeader: {
type: "ADV_NONCONNECT_IND",
length: 22,
txAdd: "random",
rxAdd: "public"
},
advData: {
flags: [ "LE Limited Discoverable Mode", "BR/EDR Not Supported" ],
completeLocalName: "reelyActive"
}
}
Bluetooth Smart (BLE) Advertising Packet Library
Process a raw packet (as a hexadecimal string) with the following command:
advlib.ble.process(rawHexPacket);
The library is organised hierarchically so that the separate elements of a packet can be processed individually. Refer to the index below for details on each element:
Complementary to the packet processing hierarchy above is a common folder which contains supporting functions and lookups that are subject to frequent evolution:
Process a 16-bit header (as a hexadecimal string) with the following command:
advlib.ble.header.process(rawHexHeader);
For reference, the 16-bit header is as follows (reading the hexadecimal string from left to right):
Bit(s) | Function |
---|
15 | RxAdd: 0 = public, 1 = random |
14 | TxAdd: 0 = public, 1 = random |
13-12 | Reserved for Future Use (RFU) |
11-8 | Type (see table below) |
7-6 | Reserved for Future Use (RFU) |
5-0 | Payload length in bytes |
And the advertising packet types are as follows:
Type | Packet | Purpose |
---|
0 | ADV_IND | Connectable undirected advertising |
1 | ADV_DIRECT_IND | Connectable directed advertising |
2 | ADV_NONCONN_IND | Non-connectable undirected advertising |
3 | SCAN_REQ | Scan for more info from advertiser |
4 | SCAN_RSP | Response to scan request from scanner |
5 | CONNECT_REQ | Request to connect |
6 | ADV_DISCOVER_IND | Scannable undirected advertising |
For example:
advlib.ble.header.process('4216');
would yield:
{
rxAdd: "public",
txAdd: "random",
type: "ADV_NONCONNECT_IND",
length: 22
}
Address
Process a 48-bit address (as a hexadecimal string) with the following command:
advlib.ble.address.process(rawHexAddress);
For reference, the 48-bit header is as follows (reading the hexadecimal string from left to right):
Bit(s) | Address component |
---|
47-40 | xx:xx:xx:xx:xx:## |
39-32 | xx:xx:xx:xx:##:xx |
31-24 | xx:xx:xx:##:xx:xx |
23-16 | xx:xx:##:xx:xx:xx |
15-8 | xx:##:xx:xx:xx:xx |
7-0 | ##:xx:xx:xx:xx:xx |
This is best illustrated with an example:
advlib.ble.address.process('0123456789ab');
Would yield:
{
type: "ADVA-48",
value: "ab8967452301"
}
which can alternatively be represented as ab:89:67:45:23:01.
Data (Generic Access Profile)
Process GAP data (as a hexadecimal string) with the following command:
advlib.ble.data.process(rawHexData);
For reference, the structure of the data is as follows:
Byte(s) | Data component |
---|
0 | Length of the data in bytes (including type and data) |
1 | GAP Data Type (see table below) |
2 to length | Type-specifc data |
The Generic Access Profile Data Types are listed on the Bluetooth GAP Assigned Numbers website. The following table lists the Data Types, their names and the section in this document in which they are described.
Data Type | Data Type Name | See advlib section |
---|
0x01 | Flags | Flags |
0x02 | Incomplete List of 16-bit UUIDs | UUID |
0x03 | Complete List of 16-bit UUIDs | UUID |
0x04 | Incomplete List of 32-bit UUIDs | UUID |
0x05 | Complete List of 32-bit UUIDs | UUID |
0x06 | Incomplete List of 128-bit UUIDs | UUID |
0x07 | Complete List of 128-bit UUIDs | UUID |
0x08 | Shortened Local Name | Local Name |
0x09 | Complete Local Name | Local Name |
0x0a | Tx Power Level | Tx Power |
0x0d | Class of Device | Generic Data |
0x0e | Simple Pairing Hash C-192 | Generic Data |
0x0f | Simple Pairing Randomizer R-192 | Generic Data |
0x10 | Security Manager TK Value | Generic Data |
0x11 | Security Manager OOB Flags | Generic Data |
0x12 | Slave Connection Interval Range | SCIR |
0x14 | 16-bit Solicitation UUIDs | Solicitation |
0x15 | 128-bit Solicitation UUIDs | Solicitation |
0x16 | Service Data 16-bit UUID | Service Data |
0x17 | Public Target Address | Generic Data |
0x18 | Random Target Address | Generic Data |
0x19 | Public Target Address | Generic Data |
0x1a | Advertising Interval | Generic Data |
0x1b | LE Bluetooth Device Address | Generic Data |
0x1c | LE Bluetooth Role | Generic Data |
0x1d | Simple Pairing Hash C-256 | Generic Data |
0x1e | Simple Pairing Hash Randomizer C-256 | Generic Data |
0x1f | 32-bit Solicitation UUIDs | Solicitation |
0x20 | Service Data 32-bit UUID | Service Data |
0x21 | Service Data 128-bit UUID | Service Data |
0x3d | 3-D Information Data | Generic Data |
0xff | Manufacturer Specific Data | Mfr. Specific Data |
UUID
Process a UUID assigned to the device with any of the following commands:
advlib.ble.data.gap.uuid.nonComplete16BitUUIDs(payload, cursor, advertiserData);
advlib.ble.data.gap.uuid.complete16BitUUIDs(payload, cursor, advertiserData);
advlib.ble.data.gap.uuid.nonComplete128BitUUIDs(payload, cursor, advertiserData);
advlib.ble.data.gap.uuid.complete128BitUUIDs(payload, cursor, advertiserData);
This is best illustrated with an example:
var payload = '16074449555520657669746341796c656572';
advlib.ble.data.gap.uuid.complete128BitUUIDs(payload, 0, {});
For reference, the example payload is interpreted as follows:
Byte(s) | Hex String | Description |
---|
0 | 16 | Length, in bytes, of type and data |
1 | 07 | GAP Data Type for Complete 128-bit UUID |
2 to 16 | 4449555520657669746341796c656572 | reelyActive's 128-bit UUID |
Which would add the following property to advData:
complete128BitUUIDs: "7265656c794163746976652055554944"
Local Name
Process the device's local name, complete or shortened, with the following commands, respectively:
advlib.ble.data.gap.localname.completeLocalName(payload, cursor, advertiserData);
advlib.ble.data.gap.localname.shortenedLocalName(payload, cursor, advertiserData);
This is best illustrated with an example:
advlib.ble.data.gap.localname.completeLocalName('12097265656c79416374697665', 0, {});
For reference, the example payload is interpreted as follows:
Byte(s) | Hex String | Description |
---|
0 | 12 | Length, in bytes, of type and data |
1 | 09 | GAP Data Type for Complete Local Name |
2 to 12 | 7265656c79416374697665 | ASCII representation of 'reelyActive' |
Which would add the following property to advData:
completeLocalName: "reelyActive"
Flags
Process the flags with the following command:
advlib.ble.data.gap.flags.process(payload, cursor, advertiserData);
For reference, the flags are as follows:
Bit | Description |
---|
0 | LE Limited Discoverable Mode |
1 | LE General Discoverable Mode |
2 | BR/EDR Not Supported |
3 | Simultaneous LE and BR/EDR to Same Device Capable (Controller) |
4 | Simultaneous LE and BR/EDR to Same Device Capable (Host) |
5 | Reserved |
This is best illustrated with an example:
advlib.ble.data.gap.flags.process('020104', 0, {});
For reference, the example payload is interpreted as follows:
Byte | Hex String | Description |
---|
0 | 02 | Length, in bytes, of type and data |
1 | 01 | GAP Data Type for flags |
2 | 04 | See table above |
Which would add the following property to advData:
flags: [ "BR/EDR Not Supported" ]
Manufacturer Specific Data
Process manufacturer specific data with the following command:
advlib.ble.data.gap.manufacturerspecificdata.process(payload, cursor, advertiserData);
This is best illustrated with an example:
advlib.ble.data.gap.manufacturerspecificdata.process('03ff8c00', 0, {});
For reference, the example payload is interpreted as follows:
Byte(s) | Hex String | Description |
---|
0 | 03 | Length, in bytes, of type and data |
1 | ff | GAP Data Type for manufacturer specific data |
2-3 | 8c00 | Gimbal company identifier code (bytes reversed) |
Which would add the following property to advData:
manufacturerSpecificData: {
companyIdentifierCode: "008c",
data: ""
}
The proprietary data of some manufacturers can be further processed. The data for those supported will automatically be processed. See the Manufacturers section for the list of all supported manufacturers.
TX Power Level
Process Tx Power Level with the following command:
advlib.ble.data.gap.txpower.process(payload, cursor, advertiserData);
This is best illustrated with an example:
advlib.ble.data.gap.txpower.process('020a7f', 0, {});
For reference, the example payload is interpreted as follows:
Byte(s) | Hex String | Description |
---|
0 | 02 | Length, in bytes, of type and data |
1 | 0a | GAP Data Type for TxPower |
2 | 7f | TxPower (see table below) |
TxPower is a two's complement value which is interpreted as follows:
Hex String | Power dBm |
---|
7f | 127 dBm |
00 | 0 dBm |
ff | -128 dBm |
Which would add the following property to advData:
txPower: "127dBm"
Slave Connection Interval Range
Process the Slave Connection Interval Range with the following command:
advlib.ble.data.gap.slaveconnectionintervalrange.process(payload, cursor, advertiserData);
This is best illustrated with an example:
advlib.ble.data.gap.slaveconnectionintervalrange.process('061200060c80', 0, {});
For reference, the example payload is interpreted as follows:
Byte(s) | Hex String | Description |
---|
0 | 06 | Length, in bytes, of type and data |
1 | 12 | GAP Data Type for Slave Connection Interval Range |
2-6 | 00060c80 | Min and max intervals (see table below) |
And the intervals are intepreted as follows:
Byte(s) | Hex String | Description |
---|
0-1 | 0006 | Min interval = 6 x 1.25 ms = 7.5 ms |
2-3 | 0c80 | Max interval = 12128 x 1.25 ms = 15160 ms |
Which would add the following property to advData:
slaveConnectionIntervalRange: "00060c80"
Service Solicitation
Process a Service Solicitation UUID with any of the following commands:
advlib.ble.data.gap.solicitation.solicitation16BitUUIDs(payload, cursor, advertiserData);
advlib.ble.data.gap.solicitation.solicitation128BitUUIDs(payload, cursor, advertiserData);
This is best illustrated with an example:
advlib.ble.data.gap.uuid.solicitation16BitUUIDs('0314d8fe', 0, {});
For reference, the example payload is interpreted as follows:
Byte(s) | Hex String | Description |
---|
0 | 03 | Length, in bytes, of type and data |
1 | 14 | GAP Data Type for 16-bit Service Solicitation UUID |
2-3 | d8fe | Google's UriBeacon UUID (bytes reversed) |
Which would add a property to advData as follows:
solicitation16BitUUIDs: "fed8"
Service Data
Process service data assigned to the device.
advlib.ble.data.gap.servicedata.process(payload, cursor, advertiserData);
This is best illustrated with an example:
advlib.ble.data.gap.servicedata.process('09160a181204eb150000', 0, {});
For reference, the example payload is interpreted as follows:
Byte(s) | Hex String | Description |
---|
0 | 09 | Length, in bytes, of type and data |
1 | 16 | GAP Data Type for service data |
2-3 | 0a18 | UUID (bytes reversed) |
4-9 | 1204eb150000 | Service Data |
Which would add the following properties to advData:
serviceData: {
uuid : "180a",
data : "1204eb150000",
specificationName: "Device Information"
}
In this case, the service UUID represents one of the GATT Standard Services and is processed as such.
Additional examples for service UUIDs among the GATT Member Services are given in that section below.
Data (Generic Attribute Profile)
Process GATT service data (as a hexadecimal string) with the following command:
advlib.ble.data.gatt.process(advData);
Where advData contains a serviceData object (see Service Data), for instance:
advData: {
serviceData: {
uuid: "fed8",
data: "00f2027265656c7961637469766507"
}
}
Based on the UUID, the serviceData will be parsed as either a member service or a standard service, as applicable. Note that not all services are yet implemented.
Member Services
Based on a pilot program for members which allows the SIG to allocate a 16-bit Universally Unique Identifier (UUID) for use with a custom GATT-based service defined by the member.
UUID | Member | Description |
---|
0xfed8 | Google | UriBeacon (Physical Web) |
0xfeaa | Google | Eddystone |
0xfe9a | Estimote | Estimote Location & Telemetry |
Google
Supports Eddystone (UID, URL, TLM & EID) and the UriBeacon of the Physical Web.
UriBeacon
Process UriBeacon data (UUID = 0xfed8).
advlib.ble.data.gatt.services.members.process(advData);
This is best illustrated with an example using the following input:
advData: {
serviceData: {
uuid: "fed8",
data: "00f2027265656c7961637469766507"
}
}
For reference, the example serviceData.data is interpreted as follows, based on the UriBeacon Advertising Packet Specification:
Byte(s) | Hex String | Description |
---|
0 | 00 | UriBeacon flags |
1 | f2 | UriBeacon TxPower level |
3 | 02 | Uri Scheme Prefix (http://) |
4-15 | 7265656c7961637469766507 | Encoded Uri (reelyactive.com) |
Which would add the following properties to advData:
serviceData: {
uuid: "fed8",
data: "00f2027265656c7961637469766507",
companyName: "Google",
uriBeacon: {
invisibleHint: false,
txPower: "-14dBm",
url: "http://reelyactive.com"
}
}
Eddystone-UID
Process Eddystone-UID data (UUID = 0xfeaa).
advlib.ble.data.gatt.services.members.process(advData);
This is best illustrated with an example using the following input:
advData: {
serviceData: {
uuid: "feaa",
data: "00128b0ca750095477cb3e770011223344550000"
}
}
For reference, the example serviceData.data is interpreted as follows, based on the Eddystone-UID specification:
Byte(s) | Hex String | Description |
---|
0 | 00 | Eddystone-UID Frame Type |
1 | 12 | Calibrated TxPower at 0m |
2-11 | 8b0ca750095477cb3e77 | 10-byte ID Namespace |
12-17 | 001122334455 | 6-byte ID Instance |
18-19 | 0000 | Reserved for Future Use |
Which would add the following properties to advData:
serviceData: {
uuid: "feaa",
data: "00128b0ca750095477cb3e770011223344550000",
eddystone: {
type: "UID",
txPower: "18dBm",
uid: {
namespace: "8b0ca750095477cb3e77",
instance: "001122334455"
}
}
}
Eddystone-URL
Process Eddystone-URL data (UUID = 0xfeaa).
advlib.ble.data.gatt.services.members.process(advData);
This is best illustrated with an example using the following input:
advData: {
serviceData: {
uuid: "feaa",
data: "1012027265656c7961637469766507"
}
}
For reference, the example serviceData.data is interpreted as follows, based on the Eddystone-URL specification:
Byte(s) | Hex String | Description |
---|
0 | 10 | Eddystone-URL Frame Type |
1 | 12 | Calibrated TxPower at 0m |
2 | 02 | URL Scheme (http://) |
3-14 | 7265656c7961637469766507 | Encoded URL (reelyactive.com) |
Which would add the following properties to advData:
serviceData: {
uuid: "feaa",
data: "1012027265656c7961637469766507",
eddystone: {
type: "URL",
txPower: "18dBm",
url: "http://reelyactive.com"
}
}
Eddystone-TLM
Process Eddystone-TLM data (UUID = 0xfeaa).
advlib.ble.data.gatt.services.members.process(advData);
This is best illustrated with an example using the following input:
advData: {
serviceData: {
uuid: "feaa",
data: "20000bb81800000000010000000a"
}
}
For reference, the example serviceData.data is interpreted as follows, based on the Eddystone-TLM specification, in this case Unencrypted TLM:
Byte(s) | Hex String | Description |
---|
0 | 20 | Eddystone-TLM Frame Type |
1 | 00 | TLM Version |
2-3 | 0bb8 | Battery Voltage (mV) |
4-5 | 1800 | Temperature (8:8 fixed point) |
6-9 | 00000001 | Advertising PDU Count |
10-13 | 0000000a | Uptime (0.1s resolution) |
Which would add the following properties to advData:
serviceData: {
uuid: 'feaa',
data: '20000bb81800000000010000000a',
eddystone: {
type: "TLM",
version: "00",
batteryVoltage: "3000mV",
temperature: "24C",
advertisingCount: 1,
uptime: "1s"
}
}
Consider also the example of Encrypted TLM:
Byte(s) | Hex String | Description |
---|
0 | 20 | Eddystone-TLM Frame Type |
1 | 01 | TLM Version |
2-13 | 112233445566778899aabbcc | Encrypted TLM |
14-15 | 0123 | Salt |
16-17 | abcd | Message Integrity Check |
Which would add the following properties to advData:
serviceData: {
uuid: 'feaa',
data: '2001112233445566778899aabbcc0123abcd',
eddystone: {
type: "TLM",
version: "01",
etlm: "112233445566778899aabbcc",
salt: "0123",
mic: "abcd"
}
}
Eddystone-EID
Process Eddystone-EID data (UUID = 0xfeaa).
advlib.ble.data.gatt.services.members.process(advData);
This is best illustrated with an example using the following input:
advData: {
serviceData: {
uuid: "feaa",
data: "30001122334455667788"
}
}
For reference, the example serviceData.data is interpreted as follows, based on the Eddystone-EID specification:
Byte(s) | Hex String | Description |
---|
0 | 30 | Eddystone-EID Frame Type |
1 | 00 | Calibrated TxPower at 0m |
2-9 | 1122334455667788 | 8-byte Ephemeral Identifier |
Which would add the following properties to advData:
serviceData: {
uuid: "feaa",
data: "30001122334455667788",
eddystone: {
type: "EID",
txPower: "0dBm",
eid: "1122334455667788"
}
}
Estimote
Supports Estimote beacons that send additional data within a service. Note that Estimote Nearables use Manufacturer Specific Data instead of a service. The first byte of the service data specifies the type, of which there are two observed: location (0x00) and telemetry (0x12).
Byte(s) | Hex String | Description |
---|
0 | 12 | Estimote Telemetry packet |
1-8 | ceaac4fd251b1e35 | 64-bit static identifier |
9+ | | Type-dependent |
Process Estimote service data (UUID = 0xfe9a).
advlib.ble.data.gatt.services.members.process(advData);
Consider the following case.
Estimote Telemetry
This is best illustrated with an example using the following input:
advData: {
serviceData: {
uuid: "fe9a",
data: "12ceaac4fd251b1e350020c9010196f002"
}
}
Byte(s) | Hex String | Description |
---|
0 | 12 | Estimote Telemetry packet |
1-8 | ceaac4fd251b1e35 | 64-bit static identifier |
9 | 00 | Subtype 0x00 |
10 | 20 | Acceleration in X-axis (two's complement) |
11 | c9 | Acceleration in Y-axis (two's complement) |
12 | 01 | Acceleration in Z-axis (two's complement) |
13-16 | 0196f002 | Motion/state/duration bytes? |
Which would add the following property to advData:
serviceData: {
uuid: "fe9a",
data: "12ceaac4fd251b1e350020c9010196f002",
estimote: {
type: "telemetry",
id: "ceaac4fd251b1e35",
subtype: "00",
accelerationX: 0.5,
accelerationY: -0.859375,
accelerationZ: 0.015625,
statusBytes: [ "01", "96", "f0", "02" ]
}
}
Minew
Supports the Minew S1, E6 and i7 sensor beacons which send data within a service. Note that the service UUID 0xffe1 does not appear to be a valid member service granted by the Bluetooth SIG.
S1 Temperature/Humidity Beacon
This is best illustrated with an example using the following input:
advData: {
serviceData: {
uuid: "ffe1",
data: "a1016416dc27fd0c04a03f23ac"
}
}
Byte(s) | Hex String | Description |
---|
0 | a1 | Frame type |
1 | 01 | Product model |
2 | 64 | Battery level in percent |
3-4 | 16dc | Temperature in celcius (signed 8.8) |
5-6 | 27fd | Humidity percentage (signed 8.8) |
7-12 | 0c04a03f23ac | MAC address |
Which would add the following property to advData:
serviceData: {
uuid: "ffe1",
data: "a1016416dc27fd0c04a03f23ac",
minew: {
frameType: "a1",
productModel: 1,
batteryPercent: 100,
temperature: 22.859375,
humidity: 39.98828125,
macAddress: "ac:23:3f:a0:04:0c"
}
}
E6 Light Sensor Beacon
This is best illustrated with an example using the following input:
advData: {
serviceData: {
uuid: "ffe1",
data: "a1026401ff04a03f23ac"
}
}
Byte(s) | Hex String | Description |
---|
0 | a1 | Frame type |
1 | 02 | Product model |
2 | 64 | Battery level in percent |
3 | 01 | Visible light observed? (Bit 0) |
7-12 | ff04a03f23ac | MAC address |
Which would add the following property to advData:
serviceData: {
uuid: "ffe1",
data: "a1026401ff04a03f23ac",
minew: {
frameType: "a1",
productModel: 2,
batteryPercent: 100,
visibleLight: true,
macAddress: "ac:23:3f:a0:04:ff"
}
}
i7 Accelerometer Beacon
This is best illustrated with an example using the following input:
advData: {
serviceData: {
uuid: "ffe1",
data: "a1036400d70087fffe5705a03f23ac"
}
}
Byte(s) | Hex String | Description |
---|
0 | a1 | Frame type |
1 | 03 | Product model |
2 | 64 | Battery level in percent |
3-4 | 00d7 | Acceleration in X-axis (signed 8.8) |
5-6 | 0087 | Acceleration in Y-axis (signed 8.8) |
7-8 | fffe | Acceleration in Z-axis (signed 8.8) |
9-14 | 5705a03f23ac | MAC address |
Which would add the following property to advData:
serviceData: {
uuid: "ffe1",
data: "a1036400d70087fffe5705a03f23ac",
minew: {
frameType: "a1",
productModel: 3,
batteryPercent: 100,
accelerationX: 0.83984375,
accelerationY: 0.52734375,
accelerationZ: -0.0078125,
macAddress: "ac:23:3f:a0:05:57"
}
}
Standard Services
The following GATT Services, assigned in the GATT Specification are identified but not parsed:
- Alert Notification Service
- Automation IO
- Battery Service
- Blood Pressure
- Body Composition
- Bond Management
- Continous Glucose Monitoring
- Current Time Service
- Cycling Power
- Cycling Speed and Cadence
- Device Information
- Environmental Sensing
- Generic Access
- Generic Attribute
- Glucose
- Health Thermometer
- Heart Rate
- Human Interface Device
- Immediate Alert
- Indoor Positioning
- Internet Protocol Support
- Link Loss
- Location and Navigation
- Next DST Change Service
- Phone Alert Status Service
- Pulse Oximeter
- Reference Time Update Service
- Running Speed and Cadence
- Scan Parameters
- TX Power
- User Data
- Weight Scale
Common
Supporting functions and lookups that are subject to frequent evolution are:
Assigned Numbers
The Bluetooth SIG maintains a list of assigned numbers. The advlib currently implements 16-bit UUIDs for Members and Company Identifiers.
Company Identifiers
The Bluetooth SIG maintains a list of company identifiers. Look up a company name from its 16-bit code.
advlib.ble.common.companyidentifiercodes.companyNames[companyCode];
For example:
advlib.ble.common.companyidentifiercodes.companyNames['000d'];
would yield:
"Texas Instruments Inc."
Member Services
The Bluetooth SIG maintains a list of 16-bit UUIDs for Members, for members. In other words, this list is accessible to members. Look up a company name from its 16-bit service UUID.
advlib.ble.common.memberservices.companyNames[uuid];
For example:
advlib.ble.common.memberservices.companyNames['feed'];
would yield:
"Tile, Inc."
Manufacturers
All functions and lookups which represent manufacturer-proprietary data contained in the Manufacturer Specific Data data type are included here. Each manufacturer is contained in a separate subfolder.
Apple
Process Apple-proprietary data from a packet's contained advertiser data.
advlib.ble.common.manufacturers.apple.process(advData);
The first byte of the proprietary data specifies the type:
iBeacon
A specific case of Apple-proprietary data is the iBeacon. Look up a licensee name from its 128-bit iBeacon UUID.
advlib.ble.common.manufacturers.apple.ibeacon.licenseeNames[uuid];
For example:
advlib.ble.common.manufacturers.apple.ibeacon.licenseeNames['f7826da64fa24e988024bc5b71e0893e'];
would yield:
"Kontakt.io"
Process an iBeacon packet from the contained advertiser data.
advlib.ble.common.manufacturers.apple.ibeacon.process(advData);
This is best illustrated with an example using the following input:
advData: {
manufacturerSpecificData: {
companyIdentifierCode: "004c",
data: "0215b9407f30f5f8466eaff925556b57fe6d294c903974"
}
}
For reference, the iBeacon payload is interpreted as follows:
Byte(s) | Hex String | Description |
---|
0 | 26 | Length, in bytes, of type and data |
1 | ff | GAP Data Type for manufacturer specific data |
2-3 | 4c00 | Apple company identifier code (bytes reversed) |
4-5 | 0215 | Identifier code for iBeacon |
6-21 | b9407f30f5f8466eaff925556b57fe6d | UUID (assigned by Apple) |
21-22 | 294c | Major |
23-24 | 9039 | Minor |
25 | 74 | TxPower (see TxPower section) |
Which would add the following property to advData:
manufacturerSpecificData: {
iBeacon: {
uuid: "b9407f30f5f8466eaff925556b57fe6d",
major: "294c",
minor: "9039",
txPower: "116dBm",
licenseeName: "Estimote"
}
}
AirDrop
A specific case of Apple-proprietary data is AirDrop. Process an AirDrop packet from the contained advertiser data.
advlib.ble.common.manufacturers.apple.airdrop.process(advData);
This is best illustrated with an example using the following input:
advData: {
manufacturerSpecificData: {
companyIdentifierCode: "004c",
data: "05120000000000000000011bc238fa0000000000"
}
}
For reference, the AirDrop payload is interpreted as follows:
Byte(s) | Hex String | Description |
---|
0 | 12 | Length, in bytes, of data |
1-18 | 0000000000000000011bc238fa0000000000 | Data |
Which would add the following property to advData:
manufacturerSpecificData: {
airdrop: {
length: 18,
data: "0000000000000000011bc238fa0000000000"
}
}
AirPods
A specific case of Apple-proprietary data is AirPods. Process an AirPods packet from the contained advertiser data.
advlib.ble.common.manufacturers.apple.airpods.process(advData);
This is best illustrated with an example using the following input:
advData: {
manufacturerSpecificData: {
companyIdentifierCode: "004c",
data: "071901022021880f00000049bcba4477206b0447472c771e53bbeb"
}
}
For reference, the AirDrop payload is interpreted as follows:
Byte(s) | Hex String | Description |
---|
0 | 19 | Length, in bytes, of data |
1-25 | 01022021880f00000049bcba4477206b0447472c771e53bbeb | Data |
Which would add the following property to advData:
manufacturerSpecificData: {
airpods: {
length: 25,
data: "01022021880f00000049bcba4477206b0447472c771e53bbeb"
}
}
AirPlay
A specific case of Apple-proprietary data is AirPlay. Process an AirPlay packet from the contained advertiser data.
advlib.ble.common.manufacturers.apple.airplay.process(advData);
This is best illustrated with an example using the following input:
advData: {
manufacturerSpecificData: {
companyIdentifierCode: "004c",
data: "09060200c0a80030"
}
}
For reference, the AirPlay payload is interpreted as follows:
Byte(s) | Hex String | Description |
---|
0 | 06 | Length, in bytes, of data |
1-6 | 0200c0a80030 | Data |
Which would add the following property to advData:
manufacturerSpecificData: {
airplay: {
length: 6,
role: "destination",
data: "0200c0a80030"
}
}
Handoff
A specific case of Apple-proprietary data is Handoff. Process a Handoff packet from the contained advertiser data.
advlib.ble.common.manufacturers.apple.handoff.process(advData);
This is best illustrated with an example using the following input:
advData: {
manufacturerSpecificData: {
companyIdentifierCode: "004c",
data: "0c0e0000041b59594de21ab6fbbb5cf6"
}
}
For reference, the Handoff payload is interpreted as follows:
Byte(s) | Hex String | Description |
---|
0 | 0e | Length, in bytes, of data |
1-14 | 0000041b59594de21ab6fbbb5cf6 | Data |
Which would add the following property to advData:
manufacturerSpecificData: {
handoff: {
length: 14,
data: "0000041b59594de21ab6fbbb5cf6"
}
}
Nearby
A specific case of Apple-proprietary data is Nearby. Process a Nearby packet from the contained advertiser data.
advlib.ble.common.manufacturers.apple.nearby.process(advData);
This is best illustrated with an example using the following input:
advData: {
manufacturerSpecificData: {
companyIdentifierCode: "004c",
data: "10020100"
}
}
For reference, the Nearby payload is interpreted as follows:
Byte(s) | Hex String | Description |
---|
0 | 02 | Length, in bytes, of data |
1-2 | 0100 | Data |
Which would add the following property to advData:
manufacturerSpecificData: {
nearby: {
length: 2,
data: "0100"
}
}
StickNFind
Process StickNFind-proprietary data from a packet's contained advertiser data.
advlib.ble.common.manufacturers.sticknfind.process(advData);
The first byte of the proprietary data specifies the type:
Type (Hex String) | Description |
---|
01 | StickNFind Single |
42 | StickNSense Motion |
Estimote
Process Estimote-proprietary data from a packet's contained advertiser data.
advlib.ble.common.manufacturers.estimote.process(advData);
Nearables
A specific case of Estimote-proprietary data is the Nearable. In the absence of official documentation, advlib assumes that all Estimote-proprietary data represents a Nearable and is interpreted based on experimentally-observed behaviour. Kindly advise or submit a pull request should official documentation be made available.
This is best illustrated with an example using the following input:
advData: {
manufacturerSpecificData: {
companyIdentifierCode: "015d",
data: "012b9e3834cfbfaa710401acc1b202ff3f000353"
}
Byte(s) | Hex String | Description |
---|
0 | 01 | Unknown (Type?). Status byte 0. |
1-8 | 2b9e3834cfbfaa71 | 64-bit Nearables ID |
9 | 04 | Unknown (Firmware?). Status byte 1. |
10 | 01 | Unknown (Toggling status?). Status byte 2. |
11 | ac | Temperature (LSB) (upper bits in next byte) |
12 | c1 | Bit 6 current state. Temp. Status byte 3. |
13 | b2 | Unknown (Toggling status?). Status byte 4. |
14 | 02 | Acceleration in X-axis (two's complement) |
15 | ff | Acceleration in Y-axis (two's complement) |
16 | 3f | Acceleration in Z-axis (two's complement) |
17 | 00 | Duration of current state (var. resolution) |
18 | 03 | Duration of previous state (var. resolution) |
19 | 53 | Unknown (TX power?). Status byte 5. |
Which would add the following property to advData:
manufacturerSpecificData: {
nearable: {
id: "2b9e3834cfbfaa71",
temperature: 26.75,
currentState: "still",
accelerationX: 0.03125,
accelerationY: -0.015625,
accelerationZ: 0.984375,
currentStateSeconds: 0,
previousStateSeconds: 3,
statusBytes: [ "01", "04", "01", "c1", "b2", "53" ]
}
}
Radius Networks
AltBeacon
Process an AltBeacon packet from the contained advertiser data.
advlib.ble.common.manufacturers.radiusnetworks.altbeacon.process(advData);
This is best illustrated with an example using the following input:
advData: {
manufacturerSpecificData: {
companyIdentifierCode: "0118",
data: "beac00010203040506070809101112131415161718190069"
}
}
For reference, the AltBeacon payload is interpreted as follows:
Byte(s) | Hex String | Description |
---|
0-1 | beac | AltBeacon code |
2-21 | 0001020304050607080910111213141516171819 | ID |
22 | 00 | Reference RSSI |
23 | 69 | Manufacturer reserved |
Which would add the following property to advData:
manufacturerSpecificData: {
altBeacon: {
id: "0001020304050607080910111213141516171819",
refRSSI: "0dBm",
mfgReserved: "69"
}
}
Utilities
PDU
More info to come.
reelyActive RFID Library
Process a raw packet (as a hexadecimal string) with the following command:
advlib.reelyactive.process(rawHexPacket);
What's next?
This is an active work in progress. Expect regular changes and updates, as well as improved documentation! If you'd like to contribute, kindly read our Node.js style guide and contact us or make a pull request.
License
MIT License
Copyright (c) 2015-2017 reelyActive
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.