Kickstart with Web Bluetooth

Mert Akengin
mert’s blog
3 min readJan 31, 2018

--

Web Bluetooth is the future of web interaction, you can connect any Bluetooth Smart enabled device. Which means by the way any device that supports Bluetooth Specification version 4.0 and up.

Even though it is wonderful thing to add new human-computer interaction between web and physical world when it comes to implemeting stuff, specifications list seem longer and longer. Actually thats why open-source is that popular and everyone uses multi-layered frameworks these days.

Since early 2017 and late 2016 I was excited about Web Bluetooth because I closely worked on Bluetooth Low Energy Beacons such as Eddystone (my favorite) and iBeacon (still did not understand all). The thing is with Web Bluetooth is you can actually access to raw services and their characteristics via browser’s javascript. So virtually any nearby Bluetooth Smart device is controllable via your device without needing to install any application.

You can skip this explanation part and dive into code directly :)

Bluetooth Structure: Services and Characteristics

When I made my first application, I just defined service and characteristic values into some global object and using it. But it did not result clean code and even maintainable one. Basically it was spaghetti code :)

If you are new to these Bluetooth stuff, basically structure looks like this:

  • Service 1
    — Characteristic 1
    — Characteristic 2
  • Service X
    — Characteristic Y

You can have any number of characteristics inside a service, and you can have any number of services inside device.
All services and characteristics have distinct identifier, UUID. It is normally 128-bit but you can also have short 16-bit identifier. Which actually having 16-bit counterpart of 128-bit UUID. It is again same for both service and characteristic.
For instance 0xABCD is equal to 0000ABCD-0000-1000-8000-00805f9b34fb.
Note: There is also 32-bit identifiers for BLE, it also consists leftmost characters of the 128-bit UUID.

Characteristics:

Characteristics have 3 different properties that you can execute on, these are:

  • Read Value
  • Write Value
  • Subscribe (Notify)

As names state, you can read and write to characteristics, but if they allow you to. It is like read write and execute permissions on directories in Unix.
Subscribe is, you sub to some characteristic then when value changes, you get notified and your callback is fired with that characteristic as a parameter to function.
There is more detail under the hood, like ‘indication’ which is a different type of notification and writing with or without reply. I am not covering those as they are already well explained in the BLE specs or device documentations.

Some services and characteristic UUIDs are pre-defined with string keys that are embedded into browsers’ API, so you can use them. All those are called GATT (Generic Attributes) and specified like:
Digital org.bluetooth.characteristic.digital 0x2A56

Let’s code then.

So in order to interact with some BLE device, first you need to scan the device, which is mostly handled by user-agent. Then you select your device and sequentally you scan services, get characteristics and do some operations on those characteristics. While you are working device may disconnect so you also need to cleanup your stuff later.

After couple of projects, I settled in service -> characteristic -> function mapping scheme. Which makes coding and testing easier by just defining some nested javascript objects with corresponding functions to behave in some way. Also incerases bootstrapping time when you start to the project.

I have provided the function used to traverse basic structure I made and the example structure for reading raw accelerometer data from Xiaomi Mi Band. You can use any table you want with corresponding and valid UUID’s of the device.

An example structure looks like this:

{
"service-uuid-1": {
"characteristic-uuid-1": function(char) { ... },
"characteristic-uuid-2": function(char) { ... },
},
"service-uuid-2": {
"characteristic-uuid-3": function(char) { ... },
},
}

Here is the actual structure I use for reading Mi Band’s accelerometer:

In the ble function of webbt.js you pass a table of service/characteristic/function mapping, then a optional callback that cleans up stuff if device gets disconnected. (For example showing a pop-up.)

Basically function scans services for given service/characteristic combination then executes corresponding function in table. In production, you may want to change allowAllDevices to false and optionalServices to filter. Since it gets filtering service list from the keys of passed mapping, everything done automatically.

An error which gets occured in ble will be logged gracefully to the console :)

--

--

Mert Akengin
mert’s blog

Devops & Systems Engineer, #linux power user, #c dev,