In 10th century Harald Gormsson, the king of Denmark and Norway united most of Jutland and Zealand under his reign. To this day we are celebrating his achievements by using one of the handiest communication technologies ever created, named after his nickname. In this article, I want to focus on a specific aspect of it, important for people building custom hardware products and applications communicating with them. I’ll try to explain when and why a custom Bluetooth Low Energy service is a good idea. This article has been written from the perspective of an iOS developer, but it's generic enough to be useful for any other environment.
Let’s start with a summary of the technology itself. Bluetooth Low Energy was introduced as a part of the Bluetooth 4.0 specification in 2010. The next leap forward happened in 2014 with BLE 4.2, optimized for the Internet of Things. Finally in 2016, when Bluetooth 5 (yes, without .0) allow increasing the speed (at the expense of range), the range (at the cost of data rate), and the data broadcasting capacity of transmissions, by increasing the packet lengths. Low energy consumption and unique protocol stack allow using BLE efficiently in many IoT solutions.
As this article is created mostly for advanced usage of BLE, I will just quickly glance over the basics. Bluetooth Low Energy relies on a master - peripheral architecture, where a master is a device which is interested in some data, and a peripheral is a device providing the data. The first step for a master is to scan the bandwidth for peripherals. They are using advertising, which is just a mechanism for broadcasting device's availability and intentions.
The next step is connecting a master to a peripheral. After that, a master starts to scan the peripheral for services. Services are a basic layer of structuring the communication between devices. We can think of them as separate areas of information. A peripheral can publish on one or more services, depending on the project. After discovering interesting services, a master can subscribe to characteristics which are part of the service.
Characteristics are the second layer, the layer with the actual data. They correspond to the specific information. An example can be an architecture of BLE stack in a sensor. Services could be identified for areas as temperature or air pressure. Then characteristics are corresponding to specific values, for example, one can contain the last temperature reading for a temperature service, the other the lowest temperature from the last 24h. It’s easy to imagine a two-level tree-like structure of services and characteristics.
The thing is, we don’t need to reinvent the wheel for the most cases. Bluetooth specification provides dozens of predefined services and characteristics, to mention only a few:
They are well-thought and described in details to the point of a specific acceptable data format for characteristics. Working with them is relatively easy, and we can be sure that any device operating on these will be compatible with ours.
As we can see, there is an abundance of predefined services, covering most of the everyday use cases for an external Bluetooth periphery. A developer can quickly dive into the technology and prototype the idea. But there are a few scenarios when we would want to create a service and corresponding characteristics ourselves.
First is when your company is creating a brand new product targeted to fill a niche in the market. It can be something doing a thing no other device ever did, and you want to create a whole separate infrastructure around it.
You may also want to avoid misleading developers and users by using a service which does not sufficiently correspond with the primary purpose of the device. That can happen when your device is doing something overlapping two or more existing services' purposes.
The other situation when custom services are handy is when the data you want to send does not fit nicely to any particular service, or if it does, the service for some reason feels unfitting.
The last reason can be the main for some companies - you want to prevent any device from accidental discovering and connecting to your service. Either it’s because you don’t want to create chaos when another device could misinterpret the data being sent, or because you just don’t like the idea of another dirty, filthy device messing around your precious solution. You have a power to control it.
One thing needs to be stated - creating a custom service will not prevent it from being discovered by any other device. Moreover, any other device can subscribe to it, and to its characteristic, thus read the data. Creating custom service is not the way to secure the data. For this, proper encryption must be used, which is possible. However, it’s not a topic of this article.
Creating a custom service is as simple as creating a custom UUID for it. This unique identifier can be randomly created in a terminal with “uuidgen” command-line tool. There are also many websites which can give us a randomized UUID. It can also be created by hand. It just needs to keep a convention XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX where every X is an alphanumeric representation of a hexadecimal value. The other rule is that, obviously, it has to be different than already existing services UUID. It will not be a "custom" service otherwise.
With UUID the scenario looks just like for any other Bluetooth service. If the external device advertises the same UUID, our device can subscribe to it to receive the data on one of the service’s characteristics. The characteristics can have (and for custom services will often do) custom UUIDs as well, generated just like above.
One of the main reason for choosing custom services is a specific structure of the data being sent. For example, the external device is bound to some data size, and we want to use it to its maximum. The other case would be that the device expects much larger payload than the standard value. This exact case needs to be explained.
The Bluetooth Low Energy specification requires devices to support a specific length of the data which can be sent at once in one characteristic. For iOS, this value, which is called ATT_MTU (Attribute Protocol Maximum Transmission Unit), is set to 20 bytes. For a provided services this means that any two devices implementing them can safely assume, that the payload will not exceed this value.
With custom services, there is more flexibility. If only two devices are intentionally programmed to expect a more significant payload, the data sent at once can be as big as 512 bytes (for BLE 4.2). That gives a way to send big data faster (with fewer messages).
The thing which needs to be remembered here is that the data is being sent as a binary payload and all files need to be converted to this format. It’s pretty obvious. There is also a way to utilize the payload to the maximum by setting the bytes by hand. It can be helpful if the payload is a setting object with multiple values as simple as Booleans or small integers. It’s often done by creating an array of 8 (or 16 or whatever) bits long integers, which are being set one by one. Then they are converted into the Data object, so into the binary payload. It gives a full low-level control over the data. But it will only work if the other devices will know how to decode the message byte by byte.
Bluetooth Low Energy is a remarkable technology for many projects aimed to connect multiple devices and pass data efficiently. It’s so popular and well established that you might think that there is nothing which can surprise you anymore. I hope that this article got you as excited as I was when I learned about custom services and their possibilities for the first time.