Skip to main content

How to connect a bluetooth device to your own web application

Written by Eoin Fanning

Introduction

Bluetooth technology has been around now for over 20 years, becoming faster, more efficient and more accessible during that time. Bluetooth Low Energy (BLE) technology was created as a low energy option for a device that could last a long period of time with a single battery.

While most communications with BLE devices are carried out via custom built mobile apps e.g. tile, Apple AirTags, Smart light bulbs etc., more use cases are arising to use this technology within a web application. For example, at Miami Airport, iBeacon devices are located at various points of interest such as terminal gates, cafes, shops and toilets allowing passengers to easily navigate through the airport avoiding any stress in thinking about a missed flight.

But, currently, this is only possible if the passenger downloads the airport app. What if, instead of having to download the app, you could just go to the website on a phone or laptop and track the Beacons through a custom built map?

So, what does it take to communicate with a BLE device through a web application? Answer: Use the Bluetooth Web API. In this post we’ll take a look at what it takes to communicate with a BLE device through a web browser and build out a web app connecting to a BLE device and read its data.

Brief history of Bluetooth

Before we get into building out an awesome example, let's take you through a (very) brief history of Bluetooth.

Named after King Harald ‘Bluetooth’ Gormsson, Bluetooth was developed by Ericsson in the 1990s. It was an ambitious project to unify standards across various technologies i.e. phones, computers and devices.

Previously, the only ‘wireless’ technology to transfer data between devices was infrared. However this used line of sight communication and required the user to ‘aim’ their devices at each other. However, bluetooth did not require line of sight and had a maximum range of 10m (approx. 30ft).

The first bluetooth headset was introduced by Ericsson in 1999. It was the early 2000s that saw the introduction of bluetooth peripherals for computers e.g. mice, keyboards and printers. Then, in 2005, Bluetooth 2.0 was introduced, tripling the transfer rate to 2.1Mbps!!

It was in 2009 when Bluetooth had a significant improvement with 3.0 seeing a boost in transfer speed up to 24Mbps.

Bluetooth Low Energy (BLE) was introduced in 2010, originally developed by Nokia and later integrated into Bluetooth’s next version - 4.0. While BLE was a lot slower, reaching just 1 Mbs, it was a lot more energy efficient, allowing devices to run for up to a year with a single battery.

The current version of Bluetooth, 5.0, was released in 2016 and came with a significant improvement in range with up to 240m (with line of sight) and 40m (without line of sight) .

Today, bluetooth is used across various peripherals and devices including light bulbs, door locks, temperature control units etc.

Web Bluetooth API

The Web Bluetooth API was developed to provide developers with the ability to connect and interact with BLE devices (aka Bluetooth 4.0) via the GATT server (an acronym for the Generic ATTribute Profile). It is currently classed as ‘experimental technology’ so browser support is not great.

There is also currently no World Wide Web Consortium (W3C) Standard for Web Bluetooth API , however, documentation does exist on the W3C Community Group which you can read more about here as well as see some further examples.

However, the lack of browser support (and a few issues around security), shouldn’t stop us from having a play around with the API. In the example below we will connect to a BLE device called ‘Flower Care’. This device measures environmental variables that are important for a plant such as temperature, brightness, and soil moisture.

Eoin and his plant

Flower care device by Xiaomi

As you can see in the images above, this device connects to an accompanying app. The app allows the user to select the type of plant they wish to test so that ranges can be set. The app also provides historical data of the various readings.

So let’s try to rebuild this User Interface (UI) within a web browser!

First thing we need to do is verify that the browser being used supports the Bluetooth API by checking `navigator.bluetooth`. Once we have confirmed that the browser has the appropriate support, we can request the device that we want to connect to. It’s worth noting at this point that the request must be made via a manual interaction by a user e.g. clicking a button.

Connect to device

To request a device, just call `navigator.bluetooth.requestDevice()`. This takes a mandatory object that contains filters to return a list of devices to pair with. In this example we can filter by the specific device name i.e. ‘Flower care’.

navigator.bluetooth.requestDevice({
 filters: [{name: 'Flower care'}]
})

If the device name is unknown, other filtering options are available, such as `namePrefix’ or a list of `services`. Note, that if you filter by ‘name’ or or ‘namePrefix` you must include an array of `optionalServices`. So in the example above it will need to be amended to…

navigator.bluetooth.requestDevice({
 filters: [{ name: 'Flower care' }],
 optionalServices: ['00001204-0000-1000-8000-00805f9b34fb'] // Required to access service later.
})

`optionalServices` can be a list of commonly named services such as `battery_service` or a unique service Universally Unique Identifier (UUID) as is the case above.

Universally Unique Identifier - Bluetooth assigned 16-bit UUID’s are documented here. 128-bit UUIDs, used in this example for the Flower Care device, are chosen by manufacturers to identify their specific devices, services and characteristics. These were found within Flower Care’s API Documentation.

This will prompt the browser device list to appear with the device available to be paired.

Flower care device available to be paired with

Flower care device available to be paired with

Once the device is selected and paired, connect via the GATT server to retrieve its services and characteristics.

let server = await device.gatt?.connect();

GATT Service - a service provided by a GATT server, including a device, a list of referenced services, and a list of the characteristics of this service.

Now that we have connected to the server, we must get the primary services to retrieve the characteristics. While there are standard GATT Services that can be called (see the bluetooth specs here), in this example, we will be using a unique service UUID for this device which is required to retrieve the GATT Characteristics…

// Retrieving the device's primary service with a unique UUID
let service = await server.getPrimaryService('00001204-0000-1000-8000-00805f9b34fb');

GATT Characteristic - a basic data element that provides further information about a peripheral’s service.

GATT Characteristics encapsulates a single datapoint from which we read its value and, for this particular device, we will be retrieving its battery level and sensor’s data i.e. temperature, brightness and (soil) moisture. From the characteristics we then read their values.

const retrieveData = async (server) => {
 console.log('retrieving data...');
 let service = await server.getPrimaryService('00001204-0000-1000-8000-00805f9b34fb');
 // Battery characteristic using its UUID
 let battery = await service.getCharacteristic('00001a02-0000-1000-8000-00805f9b34fb');
 // Change device mode before retrieving sensor data
 let mode = await service.getCharacteristic('00001a00-0000-1000-8000-00805f9b34fb');
 let commandValue = new Uint8Array([0xa0, 0x1f]);
 await mode.writeValue(commandValue);
  // Retrieve sensor data
 let data = await service.getCharacteristic('00001a01-0000-1000-8000-00805f9b34fb');
  // Retrieve battery data
 let batteryValue = await battery.readValue();
 const batteryLevel = batteryValue.getUint8(0);
 // Sensor data
 let dataValue = await data.readValue();
 // Retrieving sensors data values
 const temperature = dataValue.getUint8(0);
 const moisture = dataValue.getUint8(7);
 const brightness = dataValue.getUint8(3);
 console.log('Battery level', batteryLevel);
 console.log('Temperature', temperature);
 console.log('Moisture', moisture);
 console.log('Brightness', brightness);
}

Adding Event Listeners

Finally, we need to add event listeners to our characteristics. These will be triggered when values of the characteristics change. In this example, this device will continuously send the continuously updated sensor data. To track the event, we must first ‘start notifications’ for that characteristic. In the following example, we are listening to the ‘battery’ characteristic to detect any change in the battery level. Each time the characteristics value change the `handleNotifications` function is called passing through the new value:

battery.startNotifications().then(_ => {
 log('> Notifications started');
 battery.addEventListener('characteristicvaluechanged',
     handleNotifications);
});

Result

And that’s it, all that is left to do is to display the results…

React web app built to display device data

React web app built to display device data

Bluetooth search tool

A Bluetooth Internals page is available in Chrome at `about://bluetooth-internals` so that you can inspect everything about nearby Bluetooth devices: status, services, characteristics, and descriptors.

Resources