An Introduction To WebBluetooth By Niels February 13, 2019
Here omit a paragraph of opening introduction, too long, not what dry goods, directly skip not translated, want to see the reader can go to the original view
WebBluetooth is a new specification that has been implemented in Chrome and Samsung Internet that allows us to communicate directly to Bluetooth Low Energy devices from the browser. Progressive Web Apps in combination with WebBluetooth offer the security and convenience of a web application with the power to directly talk to devices.
WebBlutetooth is a new specification already implemented in Chrome and Samsung Internet that allows us to communicate with low-power Bluetooth devices directly from the browser. Progressive Web applications in combination with WebBluetooth provide security and convenience for web applications that can communicate directly with devices.
Bluetooth has a pretty bad name due to limited range, bad audio quality, and pairing problems. But, pretty much all those problems are a thing of the past. Bluetooth Low Energy is a modern specification that has little to do with the old Bluetooth specifications, apart from using the same frequency spectrum. More than 10 million devices ship with Bluetooth support every single day. That includes computers and phones, but also a variety of devices like heart rate and glucose monitors, IoT devices like light bulbs and toys like remote controllable cars and drones.
Bluetooth has a bad reputation due to its limited input range, poor audio quality, and pairing problems. But all those problems are in the past. Bluetooth Low Energy is a modern specification that bears little relation to previous Bluetooth specifications, except that they all use the same frequency band. Every day, more than 10 million devices are equipped with Bluetooth. These include not only phones and computers, but also all kinds of devices such as heart rate and blood sugar monitors, as well as Internet of things devices such as light bulbs, toys such as remote control cars and aircraft.
The boring theoretical part
Since Bluetooth itself is not a web technology, it uses some vocabulary that may seem unfamiliar to us. So let’s go over how Bluetooth works and some of the terminology.
Since Bluetooth itself is not a Web technology, it uses some vocabulary that may not be familiar to us. So let’s take a look at how it works and some of the terminology.
Every Bluetooth device is either a ‘Central device’ or a ‘Peripheral’. Only central devices can initiate communication and can only talk to peripherals. An example of a central device would be a computer or a mobile phone.
Each Bluetooth device is either a “central device” or a “peripheral device”. Only the central device can initiate communication, and only with peripheral devices. Computers and mobile phones are one example of central equipment.
A peripheral cannot initiate communication and can only talk to a central device. Furthermore, a peripheral can only talk to one central device at the same time. A peripheral cannot talk to another peripheral.
Peripheral devices cannot initiate communication and can only communicate with central devices. Also, peripheral devices can only communicate with one central device at a time. A peripheral cannot communicate with another peripheral.
A central device can talk to multiple peripherals at the same time and could relay messages if it wanted to. So a heart rate monitor could not talk to your lightbulbs, however, you could write a program that runs on a central device that receives your heart rate and turns the lights red if the heart rate gets above a certain threshold.
A central device can communicate with multiple peripherals simultaneously and relay messages. So while the heart rate monitor doesn’t talk to your light bulb, you could write a program that runs on a central device that picks up your heart rate and turns the light red when it reaches a certain threshold.
When we talk about WebBluetooth, we are talking about a specific part of the Bluetooth specification called Generic Attribute Profile, which has the very obvious abbreviation GATT. (Apparently, GAP was already taken.)
When we talk about WebBluetooth, we are talking about a specific part of the Bluetooth specification called the Generic Attribute Profile, or GATT (apparently because GAP has been taken over).
In the context of GATT, we are no longer talking about central devices and peripherals, but clients and servers. Your light bulbs are servers. That may seem counter-intuitive, but it actually makes sense if you think about it. The light bulb offers a service, i.e. light. Just like when the browser connects to a server on the Internet, your phone or computer is a client that connects to the GATT server in the light bulb.
In the context of GATT, we no longer refer to central devices and peripherals, but to clients and servers. Your light bulb is the server, which may seem counterintuitive, but it actually makes sense if you think about it. The bulb provides a service, “light,” and just as a browser connects to a server, your phone or computer is a client connected to the GATT server inside the bulb.
Each server offers one or more services. Some of those services are officially part of the standard, but you can also define your own. In the case of the heart rate monitor, there is an official service defined in the specification. In case of the light bulb, there is not, and pretty much every manufacturer tries to re-invent the wheel. Every service has one or more characteristics. Each characteristic has a value that can be read or written. For now, it would be best to think of it as an array of objects, with each object having properties that have values.
Each server can provide one or more services. Some of these services are part of the official standards, but you can also define your own. For heart rate monitors, an official service already exists in the specification; In the case of light bulbs, there is no such thing, so almost all manufacturers try to “reinvent the wheel”. Each service has one or more characteristic. Each feature has a value that can be read or written to. For now, it’s best thought of as an array of objects, each with its own attributes and values.
Unlike properties of objects, the services and characteristics are not identified by a string. Each service and characteristic has a unique UUID which can be 16 or 128 bits long. Officially, the 16 bit UUID is reserved for official standards, but pretty much nobody follows that rule. Finally, every value is an array of bytes. There are no fancy data types in Bluetooth.
Unlike properties of objects, services and properties are not identified by strings. Each service and each feature has a unique UUID 16 or 128 bits long. Officially, the 16-bit UUID is used to remain on various official standards, but few follow this rule. Finally, each attribute value is an array of bytes — there is no such thing as a data type in Bluetooth.
Take a closer look at a Bluetooth bulb
So let’s look at an actual Bluetooth device: a Mipow Playbulb Sphere. You can use an app like BLE Scanner, or nRF Connect to connect to the device and see all the services and characteristics. In this case, I am using the BLE Scanner app for iOS.
Let’s take a look at a real Bluetooth device: a Mipow light ball. You can use an APP like BLE Scanner or nRF Connect to Connect to the device and see all of its services and features. I’m using the BLE Scanner app for iOS.
Video presentation address (over the wall) : vimeo.com/303046505
The first thing you see when you connect to the light bulb is a list of services. There are some standardized ones like the device information service and the battery service. But there are also some custom services. I am particularly interested in the service with the 16 bit UUID of 0xff0f
. If you open this service, you can see a long list of characteristics. I have no idea what most of these characteristics do, as they are only identified by a UUID and because they are unfortunately a part of a custom service; they are not standardized, and the manufacturer did not provide any documentation.
When you connect to the light bulb, the first thing you see is a list of services. Some of them are standardized services, such as device Information and battery Information. Some, however, are custom services. Of particular interest to me is the 16-bit service with a UUID of 0xFF0f. If you click on the service, you’ll see a long list of features; I don’t know what most of these features are because they only have UUID, and unfortunately they belong to custom services; They are not standardized and there is no documentation provided by the vendor.
The first characteristic with the UUID of 0xfffc
seems particularly interesting. It has a value of four bytes. If we change the value of these bytes from 0x00000000
to 0x00ff0000
, the light bulb turns red. Changing it to 0x0000ff00
turns the light bulb green, and 0x000000ff
blue. These are RGB colors and correspond exactly to the hex colors we use in HTML and CSS.
The first feature, whose UUID is 0xFFFC, looks particularly interesting. It has a value of 4 bytes. If we change these bytes from 0x00000000 to 0x00FF0000, the bulb turns red. Changing to 0x0000FF00 turns green and 0x000000FF turns blue. These are RGB colors, which correspond to the hexadecimal colors we use in HTML and CSS.
What does that first byte do? Well, if we change the value to 0xff000000
, the lightbulb turns white. The lightbulb contains four different LEDs, and by changing the value of each of the four bytes, we can create every single color we want.
So what is the first byte used for? Well, if we change the value to 0xFF000000, the bulb will turn white. There are four different leds in the bulb, and by changing the value of each of these four bytes, we can make all the colors we want.
WebBluetooth API
It is fantastic that we can use a native app to change the color of a light bulb, but how do we do this from the browser? It turns out that with the knowledge about Bluetooth and GATT we just learned, this is relatively simple thanks to the WebBluetooth API. It only takes a couple of lines of JavaScript to change the color of a light bulb.
It’s perfectly possible to use a native app to change the color of a light bulb, but how do you do it in a browser? We’ve just learned about Bluetooth and GATT, with the help of the WebBluetooth API. It only takes a few lines of JS code to change the color of the bulb.
Let’s go over the WebBluetooth API.
Let’s take a look at the WebBluetooth API.
Connect to a device
The first thing we need to do is to connect from the browser to the device. We call the function navigator.bluetooth.requestDevice()
and provide the function with a configuration object. That object contains information about which device we want to use and which services should be available to our API.
The first thing we need to do is connect to that device in the browser. Call a function the navigator. Bluetooth. RequestDevice (), and into a configuration object, this object contains about the information we want to use the equipment and services.
In the following example, we are filtering on the name of the device, as we only want to see devices that contain the prefix PLAYBULB
in the name. We are also specifying 0xff0f
as a service we want to use. Since the requestDevice()
function returns a promise, we can await the result.
In the following example, we filter based on the name of the device, because we only want to see devices whose names contain the prefix PLAYBULB; We also specify the service we want to use with 0xFF0f. Since the requestDevice() function returns a promise, we can await the result.
let device = await navigator.bluetooth.requestDevice({
filters: [{namePrefix: 'PLAYBULB'}].optionalServices: [ 0xff0f]});Copy the code
When we call this function, a window pops up with the list of devices that conform to the filters we’ve specified. Now we have to select the device we want to connect to manually. That is an essential step for security and privacy and gives control to the user. The user decides whether the web app is allowed to connect, and of course, to which device it is allowed to connect. The web app cannot get a list of devices or connect without the user manually selecting a device.
When we call this function, a window pops up with a list of devices that meet the filter criteria we specify. Then, we have to select the device we want to connect to. This step, which is essential for security and privacy, puts control in the hands of the user. The user decides whether the web application can connect and, of course, which devices it is allowed to connect to. Web apps can’t get a list of devices, nor can they connect, without the user’s manual selection.
After we get access to the device, we can connect to the GATT server by calling the connect()
function on the gatt
property of the device and await the result.
After we obtain the device, we can connect to the GATT server by calling the connect() function on the gATT property of the device and await its results.
let server = await device.gatt.connect();
Copy the code
Once we have the server, we can call getPrimaryService()
on the server with the UUID of the service we want to use as a parameter and await the result.
Once we have the server, we can call getPrimaryService() with the UUID of the service we want to use as a parameter and await the result.
let service = await server.getPrimaryService(0xff0f);
Copy the code
Then call getCharacteristic()
on the service with the UUID of the characteristic as a parameter and again await the result.
Then call getCharacteristic() on the service item with the UUID of the characteristic as an argument, and continue to await the result.
We now have our characteristics which we can use to write and read data:
Now that we have our feature, we can use it to read and write data:
let characteristic = await service.getCharacteristic(0xfffc);
Copy the code
Write data
To write data, we can call the function writeValue()
on the characteristic with the value we want to write as an ArrayBuffer, which is a storage method for binary data. The reason we cannot use a regular array is that regular arrays can contain data of various types and can even have empty holes.
To write data, we can call the writeValue() function on the property of the value we want to write as an ArrayBuffer — an ArrayBuffer is a way of storing binary data. The reason we don’t use regular arrays is that they can contain any type of data, and they can even have “holes.”
Since we cannot create or modify an ArrayBuffer directly, Every element of a typed array is always the same type, and it does not have any holes. In our case, we are going to use a Uint8Array, which is unsigned so it cannot contain any negative numbers; an integer, so it cannot contain fractions; and it is 8 bits and can contain only values from 0 to 255. In other words: an array of bytes.
Since we cannot create and modify an ArrayBuffer directly, we need to implement a ‘typed array’ instead — all elements of a typed array are the same type and there are no ‘holes’. In our example, we’ll use Unit8Array, which is an unsigned integer, so it won’t contain any negative numbers or fractional parts; It is also 8 bits long, so it can only contain 0 to 255 bits. In other words: it’s an array of bytes.
characteristic.writeValue(
new Uint8Array([ 0, r, g, b ])
);
Copy the code
We already know how this particular light bulb works. We have to provide four bytes, one for each LED. Each byte has a value between 0 and 255, and in this case, we only want to use the red, green and blue LEDs, so we leave the white LED off, by using the value 0.
We already know how this bulb works. We need to provide four bytes corresponding to each LED. The values for each byte range from 0 to 255. In this case, we want to use only red, green, and blue leds, so we keep the white LEDS off by using 0.
Read the data
To read the current color of the light bulb, we can use the readValue()
function and await the result.
We can use the readValue() function to read the current color of the bulb and await its result.
let value = await characteristic.readValue();
let r = value.getUint8(1);
let g = value.getUint8(2);
let b = value.getUint8(3);
Copy the code
The value we get back is a DataView of an ArrayBuffer, and it offers a way to get the data out of the ArrayBuffer. In our case, we can use the getUint8()
function with an index as a parameter to pull out the individual bytes from the array.
The value we get back is the DataView of an ArrayBuffer, which provides a way to get data out of the ArrayBuffer. In our example, we can use the getUint8() function to pull a single byte by taking a subscript as an argument.
To monitor changes
Finally, there is also a way to get notified when the value of a device changes. That isn’t really useful for a lightbulb, but for our heart rate monitor we have constantly changing values, and we don’t want to poll the current value manually every single second.
Finally, there is another way to get notified when the value of the device changes. For a light bulb, this really doesn’t work, but for our heart rate monitor, it’s constantly changing, and we don’t want to manually get the current value every second.
characteristic.addEventListener(
'characteristicvaluechanged', e => {
let r = e.target.value.getUint8(1);
let g = e.target.value.getUint8(2);
let b = e.target.value.getUint8(3); }); characteristic.startNotifications();Copy the code
To get a callback whenever a value changes, we have to call the addEventListener()
function on the characteristic with the parameter characteristicvaluechanged
and a callback function. Whenever the value changes, the callback function will be called with an event object as a parameter, and we can get the data from the value property of the target of the event. And, finally extract the individual bytes again from the DataView of the ArrayBuffer.
If you want to get a callback when values are changed, we need to call on feature addEventListener () function, using characteristicvaluechanged and a callback function as a parameter. Thus, when the value changes, the callback will be called and receive an Event object, which we can retrieve from the value property of the event’s target property, and then extract the bytes from the DataView of the ArrayBuffer.
Because the bandwidth on the Bluetooth network is limited, we have to manually start this notification mechanism by calling startNotifications()
on the characteristic. Otherwise, the network is going to be flooded by unnecessary data. Furthermore, because these devices typically use a battery, every single byte that we do not have to send will definitively improve the battery life of the device because the internal radio does not need to be turned on as often.
Due to the limited bandwidth of the Bluetooth network, startNotifications() must be called manually to activate the notification mechanism; Otherwise, the network will be flooded with unnecessary data. However, since these devices typically use a single battery, every byte saved that is not sent unnecessarily can improve the battery life of the device because there is no need to constantly turn on the internal RF signal.
conclusion
We’ve now gone over 90% of the WebBluetooth API. With just a few function calls and sending 4 bytes, you can create a web app that controls the colors of your light bulbs. If you add a few more lines, you can even control a toy car or fly a drone. With more and more Bluetooth devices making their way on to the market, the possibilities are endless.
We’ve covered 90% of the WebBluetooth API. By calling a few functions and sending four bytes, you can create a web application that controls the color of your light bulbs. With a few more lines of code, you could even control a toy car or fly a flying machine. As more and more Bluetooth devices continue to enter the market, the possibilities are endless.
Video presentation address (over the wall) : vimeo.com/303045191
(This video demonstrates how to control lights, LED panels, toy cars, aircraft, etc.)
Extend resources
-
Bluetooth.rocks! Demos (the above project example program) | (Source code on the lot) (and its Source code)
-
“Web Bluetooth Specification,” The Web Bluetooth Community Group Bluetooth Specification document
-
Unofficial collection of documentation for Generic Attribute services for Bluetooth Low Energy devices.
An unofficial collection of Generic Attribute service documents for tiny Bluetooth devices.