Command line explorer for Bluetooth Low Energy (BLE) devices

Usage no npm install needed!

<script type="module">
  import bluetoothleExplorer from '';



latest version published to npm

bluetoothle-explorer is a Node.js package for logging information about available Bluetooth Low Energy (BLE) devices, and the data they generate, at the command line.

It is a tool for debugging / exploration, building on top of the noble library, and not intended for consumption from other applications.


git clone
cd BluetoothLE-Explorer
npm install
node server.js

By default, that will limit discovery to devices advertising recognized service UUIDs (those listed in services.js). To specify a different filter, set the BLE_SERVICES environment variable to a :-separated list of UUIDs. E.g., BLE_SERVICES=180a:1821 node server.js will only connect to devices that advertise the "Device Information" or "Indoor Positioning" services. Use an empty value, e.g., BLE_SERVICES= node server.js, to discover all BLE devices.

Notes on Bluetooth LE / the interface provided by noble

Root level noble object

const noble = require('noble')

That's the noble controller you start with.

You can't do anything with it until it's "powered on", which happens automatically, so you have to listen for its stateChange event, check that noble.state is poweredOn, and then do stuff.

noble.startScanning(serviceUUIDs, allowDuplicates) is the main entry point.

  • This triggers the noble object's discover event, once (assuming allowDuplicates is set to false) for each new device
  • When you provide an non-empty list of serviceUUIDs (a list of hexadecimal strings), it will only trigger discover events for devices ("peripherals") that advertise having that serviceUUID.

peripheral objects

Peripherals are Bluetooth LE devices acting as servers that your machine, the client, is connecting to.

  • A peripheral always has an id attribute (duplicated as uuid), and sometimes an address and addressType, as identifying features.
  • The advertisement is a descriptive object with more information about the peripheral. It has some handy attributes, like:
    • localName: a high-level name for the device
    • serviceUuids: which lists the the service UUIDs it will match
  • The rssi indicates the strength of signal from the peripheral; it's negative, and maxes out at 0. So, the closer to 0 it is, the stronger the signal is.

Besides that chunk of information, you can't do much with the peripheral until you "connect" to it. Assuming the connectable attribute is true, and the current state is "disconnected", you "connect" to the peripheral with peripheral.connect(error => { ... }).

At this point you can call peripheral.discoverServices(serviceUUIDs, (error, services) => { ... }), which as with the noble global's startScanning method, can supply an array of serviceUUIDs to filter, or [] to discover all services.

These are all returned in a single batch, via the callback's services argument.

The discoverServices method will likely return many more services than the device directly advertises (though it includes those), but these are probably metadata, like battery level -- not as integral to the function of the device as what it advertises.

service objects

A service object represents a service as conceptualized by the Bluetooth LE standard.

These are mostly just data; using them is negotiated via the peripheral, though the noble package's interface has the relevant methods attached to the service objects.

Calling service.discoverCharacteristics(characteristicUUIDs, (error, characteristics) => { ... }) triggers discovery of the peripherals characteristics for that service, filtered by characteristicUUIDs, or returning all characteristics if characteristicUUIDs is [].

These are all returned in a single batch, via the callback's characteristics argument.

characteristic objects

Characteristic objects represent various sources of data that the device can produce, which are specified in the Bluetooth LE standard.

Again, these are mostly data, but the noble interface provides helper methods on the characteristic objects.

Most of the information you'll need to parse/interpret characteristics comes from the corresponding Bluetooth LE specification, but it provides one handy attribute that determines what you can do with it via noble: properties, which is an array of strings.

  • If properties contains the string "notify", you can "subscribe" to the characteristic, which will start triggering data events on the characteristic object, as sent by the device.

  • If properties contains the string "read", you can "read" from the characteristic to get the current value.

    You cannot "subscribe" to characteristics with a "read" property hoping to get continual updates whenever it changes; instead, you'd have to set up an interval of your own and poll the characteristic as often as you suspect it might change.

The data structures returned by or emitted as the characteristic's data events are Buffer instances.

  • To parse these, you must consult the Bluetooth LE specification for that characteristic.
  • Several parsers for characteristics of interest (to me) are provided in characteristics.js; when a characteristic defined in that file matches a characteristic received from a device, the command line tool will use that definition's parse(data: Buffer) function to process the data structure. Otherwise, the command line tool will display the raw Buffer.


Copyright 2018 Christopher Brown. MIT Licensed.