Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More

advlib

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

advlib

Library for Decoding Wireless Advertising Packets. We believe in an open Internet of Things.


Version published
Weekly downloads
147
increased by234.09%
Maintainers
1
Weekly downloads
 
Created

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:

Header

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
15RxAdd: 0 = public, 1 = random
14TxAdd: 0 = public, 1 = random
13-12Reserved for Future Use (RFU)
11-8Type (see table below)
7-6Reserved for Future Use (RFU)
5-0Payload length in bytes

And the advertising packet types are as follows:

TypePacketPurpose
0ADV_INDConnectable undirected advertising
1ADV_DIRECT_INDConnectable directed advertising
2ADV_NONCONN_INDNon-connectable undirected advertising
3SCAN_REQScan for more info from advertiser
4SCAN_RSPResponse to scan request from scanner
5CONNECT_REQRequest to connect
6ADV_DISCOVER_INDScannable 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-40xx:xx:xx:xx:xx:##
39-32xx:xx:xx:xx:##:xx
31-24xx:xx:xx:##:xx:xx
23-16xx:xx:##:xx:xx:xx
15-8xx:##: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
0Length of the data in bytes (including type and data)
1GAP Data Type (see table below)
2 to lengthType-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 TypeData Type NameSee advlib section
0x01FlagsFlags
0x02Incomplete List of 16-bit UUIDsUUID
0x03Complete List of 16-bit UUIDsUUID
0x04Incomplete List of 32-bit UUIDsUUID
0x05Complete List of 32-bit UUIDsUUID
0x06Incomplete List of 128-bit UUIDsUUID
0x07Complete List of 128-bit UUIDsUUID
0x08Shortened Local NameLocal Name
0x09Complete Local NameLocal Name
0x0aTx Power LevelTx Power
0x0dClass of DeviceGeneric Data
0x0eSimple Pairing Hash C-192Generic Data
0x0fSimple Pairing Randomizer R-192Generic Data
0x10Security Manager TK ValueGeneric Data
0x11Security Manager OOB FlagsGeneric Data
0x12Slave Connection Interval RangeSCIR
0x1416-bit Solicitation UUIDsSolicitation
0x15128-bit Solicitation UUIDsSolicitation
0x16Service Data 16-bit UUIDService Data
0x17Public Target AddressGeneric Data
0x18Random Target AddressGeneric Data
0x19Public Target AddressGeneric Data
0x1aAdvertising IntervalGeneric Data
0x1bLE Bluetooth Device AddressGeneric Data
0x1cLE Bluetooth RoleGeneric Data
0x1dSimple Pairing Hash C-256Generic Data
0x1eSimple Pairing Hash Randomizer C-256Generic Data
0x1f32-bit Solicitation UUIDsSolicitation
0x20Service Data 32-bit UUIDService Data
0x21Service Data 128-bit UUIDService Data
0x3d3-D Information DataGeneric Data
0xffManufacturer Specific DataMfr. 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 StringDescription
016Length, in bytes, of type and data
107GAP Data Type for Complete 128-bit UUID
2 to 164449555520657669746341796c656572reelyActive'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 StringDescription
012Length, in bytes, of type and data
109GAP Data Type for Complete Local Name
2 to 127265656c79416374697665ASCII 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:

BitDescription
0LE Limited Discoverable Mode
1LE General Discoverable Mode
2BR/EDR Not Supported
3Simultaneous LE and BR/EDR to Same Device Capable (Controller)
4Simultaneous LE and BR/EDR to Same Device Capable (Host)
5Reserved

This is best illustrated with an example:

advlib.ble.data.gap.flags.process('020104', 0, {});

For reference, the example payload is interpreted as follows:

ByteHex StringDescription
002Length, in bytes, of type and data
101GAP Data Type for flags
204See 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 StringDescription
003Length, in bytes, of type and data
1ffGAP Data Type for manufacturer specific data
2-38c00Gimbal 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 StringDescription
002Length, in bytes, of type and data
10aGAP Data Type for TxPower
27fTxPower (see table below)

TxPower is a two's complement value which is interpreted as follows:

Hex StringPower dBm
7f127 dBm
000 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 StringDescription
006Length, in bytes, of type and data
112GAP Data Type for Slave Connection Interval Range
2-600060c80Min and max intervals (see table below)

And the intervals are intepreted as follows:

Byte(s)Hex StringDescription
0-10006Min interval = 6 x 1.25 ms = 7.5 ms
2-30c80Max 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 StringDescription
003Length, in bytes, of type and data
114GAP Data Type for 16-bit Service Solicitation UUID
2-3d8feGoogle'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 StringDescription
009Length, in bytes, of type and data
116GAP Data Type for service data
2-30a18UUID (bytes reversed)
4-91204eb150000Service 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.

UUIDMemberDescription
0xfed8GoogleUriBeacon (Physical Web)
0xfeaaGoogleEddystone
Google

Supports Eddystone (UID, URL & TLM) 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 StringDescription
000UriBeacon flags
1f2UriBeacon TxPower level
302Uri Scheme Prefix (http://)
4-157265656c7961637469766507Encoded 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 StringDescription
000Eddystone-UID Frame Type
112Calibrated TxPower at 0m
2-118b0ca750095477cb3e7710-byte ID Namespace
12-170011223344556-byte ID Instance
18-190000Reserved 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 StringDescription
010Eddystone-URL Frame Type
112Calibrated TxPower at 0m
202URL Scheme (http://)
3-147265656c7961637469766507Encoded 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:

Byte(s)Hex StringDescription
020Eddystone-TLM Frame Type
100TLM Version
2-30bb8Battery Voltage (mV)
4-51800Temperature (8:8 fixed point)
6-900000001Advertising PDU Count
10-130000000aUptime (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"
  }
}
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:

Type (Hex String)Description
02iBeacon
09AppleTV & ???
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: "008c",
    data: "0215b9407f30f5f8466eaff925556b57fe6d294c903974"
  }
}

For reference, the iBeacon payload is interpreted as follows:

Byte(s)Hex StringDescription
026Length, in bytes, of type and data
1ffGAP Data Type for manufacturer specific data
2-34c00Apple company identifier code (bytes reversed)
4-50215Identifier code for iBeacon
6-21b9407f30f5f8466eaff925556b57fe6dUUID (assigned by Apple)
21-22294cMajor
23-249039Minor
2574TxPower (see TxPower section)

Which would add the following property to advData:

manufacturerSpecificData: {
  iBeacon: {
    uuid: "b9407f30f5f8466eaff925556b57fe6d",
    major: "294c",
    minor: "9039",
    txPower: "116dBm",
    licenseeName: "Estimote"
  }
}
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
01StickNFind Single
42StickNSense Motion
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-2016 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.

FAQs

Package last updated on 10 Apr 2016

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts