One is and is not in the centre of the maelstrom of it all.
The application of Bluetooth low energy can be broken into two separate disciplines: the design of applications that find and interact with peripherals, and the design of peripherals that can provide information to these applications running on a central device. Both disciplines require knowledge of how all the previous parts fit together to make a functioning whole. This chapter looks at the applications from a central point of view. The next chapter subsequently deals with how a peripheral can be designed.
Central devices are vitally important for Bluetooth low energy to be useful. Typically, these devices are highly functional and have complex user interfaces. This chapter will not go into how to write an application for a particular type of device; that is the job of that platform’s developer program. Instead, important considerations will be given on how to optimize the user experience and the best ways to save power.
The first thing that any central device needs to do is to discover other devices. To do this, it can use either passive or active scanning. For passive scanning, a central device passively listens to any advertisement packets that peripherals are transmitting. Active scanning is when the central device, after hearing a peripheral, asks for more information.
If the central device is only looking for what devices are around, and perhaps any information they may also contain, it should use passive scanning. If the central device is also populating a user interface such as a screen or a window, active scanning should be used because the additional information can be useful to build the list of discoverable devices on a user interface.
The information that can be found by scanning includes the name of the device and a unique number that identifies the device. This identity can be used if the central device later needs to connect to it. It is also possible to find some broadcast data within the scan responses so that information that is being broadcast by a service can be obtained. This could be useful information such as the battery level or the current time.
Some information is not immediately available but can be obtained by making a quick connection to the device and reading it as needed. For example, the complete name of the device might be very long and might not fit into a single advertising or scan response packet. Therefore, it would be necessary to make a connection with the device to read the rest of the device name. When doing this, the application on the central device should be very careful about only reading characteristics within the correct service.
Therefore, first the application should perform a service discovery of the required service, for example, to look for the GAP Service. It can then look within that service for the Device Name characteristic value. This can be read directly using the Read Characteristic Value by UUID procedure. The GAP Service specification requires that only a single GAP service can exist on a device, and it will have only one device name characteristic value. If the device name is longer than what can be read in a single attribute protocol response, additional requests for the rest of the device name value can be made by using the handle that is returned in the first response.
This same procedure can be used for other useful information, such as the Appearance characteristic value in GAP.
It should be noted that when passively or actively scanning, not only can the application obtain the contents of advertising packets but it can also receive the received signal strength (RSSI) of these packets in the controller. This RSSI value can be subtracted from the Tx Power that might be included within the advertising packets to give a very basic estimate of the path loss and therefore an estimate of the distance between this device and the central device, as illustrated in the following equation:
path loss = TxPower – RSSI
If the path loss is very small—between 0 and 20—this indicates that the device is very close. If the path loss is very large, for example, above 70, the device is very far away. Because of the nature of wireless transmissions, these values should be averaged over a number of seconds so that any multipath interference is averaged out. Once this is done, the list of devices presented to the user should place the closest (the smallest path loss) at the top of the list. This way, the user will preferentially see those devices that are very close. When there are lots of devices, the user will need to scroll down to find those devices that are farther away.
Another consideration is that some devices might be advertising but not discoverable. Unless the user interface is displaying the broadcast service data, the devices that are not discoverable should be removed from the list of found devices.
In the preceding section, the list of discoverable devices was created. This list can be presented to the user so that he can select a particular device (or devices) with which he wants to interact. The next task will be connecting to the device.
Connecting to a device selected from a user interface should just be a case of initiating a connection to that device. If, however, the chosen device is using a private address, care should be taken to recover gracefully if the device found has recently changed its private address. If that does happen, the initiation of the connection should timeout and the list of discoverable devices should be refreshed. The device might still be there, but it’s using a different private address because it wants to protect its privacy. This is not a problem once the devices have bonded and exchanged Identify Resolving Keys (IRKs) because the central device can automatically refresh the list, resolve the private addresses to identify which one belongs to the desired device, and then connect to it.
When initiating a connection, a set of connection parameters need to be chosen. The parameters used depend on what the two devices are intending to do. Typically, peripherals have a Client Preferred Connection Parameters characteristic that gives a very strong hint to a central device about the types of connection parameters it prefers. When making the very first connection with a device, this information will not be available, and therefore a compromise between low power consumption and rapid characterization of a device needs to be struck.
The best set of parameters are those that have a fairly rapid connection interval, allowing for a rapid exchange of Attribute Protocol and Link Layer control messages during the initial connection. A reasonably large slave latency should also be offered so that the peripheral device can save power whenever possible. For example, a connection interval within the range of 15 milliseconds and 30 milliseconds, with a slave latency of 150 milliseconds, allows for both rapid collection of data about the peripheral using up to 60Hz connection interval and a 6Hz idle frequency for the slave.
The slave might request different parameters from those that the application on the central device chose. The central device should always try to honor these requests, especially after it has finished reading all the data that it wants at that moment.
After connecting to a peripheral device, the central device will want to know what the device does. To gather this information, it uses four procedures, in a specific order: primary services discovery, relationship discovery, characteristic discovery, and descriptor discovery.
The first procedure is the primary services discovery. These are the services that describe what the device does. For example, if the device has a battery, primary services would expose the Battery Service; if the device has a temperature sensor, it would expose the Temperature Service; if the device had a temperature sensor within the battery, this secondary temperature would not be exposed through a primary service because this might confuse the central device. Primary services only expose the primary functionality of a device.
Next, for each primary service that the central device knows could include another service, these relationships need to be discovered. These relationships could be because of an extending, combining, or reusing relationship.
The set of services that a device has does not necessarily determine the set of profiles that a peripheral device supports. There is no way to quickly determine the set of profile roles that a device supports. Instead, a more complex algorithm has to be used, matching the profile roles that the central device supports with the set of services that are exposed on the peripheral and checking which roles are valid. This could be a nontrivial operation; however, the devices that would be doing these checks have plenty of resources, and this type of complexity is not considered a Bluetooth low energy issue.
The benefit of this approach is that future client profile roles that use a set of services on a peripheral don’t need to be designed into the peripheral when it is manufactured. This becomes a very flexible and extendable system ideally suited to the downloadable application models being deployed in many central devices.
Once the services have been discovered, the set of characteristics and their descriptors can also be discovered. There are no version numbers in Bluetooth low energy services; therefore, the only way to know if a given optional feature exists is to check for the exposure of a given characteristic that is linked to this optional feature. Alternatively, characteristic properties and descriptors such as the notification and Client Characteristic Configuration descriptor can be used to differentiate optional behavior.
It is possible to build entirely generic clients. These are clients that can read and display characteristic values, possibly in a human-readable format. This gives central devices that have no understanding of the meaning of the individual services or characteristics on a peripheral device the ability to make them available to the user. There are two levels of generic clients: those that use the available information on the peripheral device directly, and those that augment this information with characteristic information available via the Internet.
The first level of generic clients finds all the characteristics within a peripheral and filters out any that cannot be read directly. It then also filters out any that do not have a Characteristic Presentation Format descriptor. This descriptor includes most of the information needed to change the binary representation of the data into a human-readable value. It does this by using the format and exponent fields to determine how to convert the value from a fixed-point value into a more intuitive number. The unit field is a UUID that encodes the unit of this value.
There are units for most physical quantities, which are taken from the BIPM1 list of units. All standard SI units and most common units are included. Finally, a namespace:description pair is included in the descriptor to allow for an even finer-grained client display. This could allow not only for the weight of the item being measured to be converted from a 16-bit unsigned value into pounds weight, but also noting that this weight is from a hanging weighing machine as opposed to a vehicle weight bridge or a set of bathroom scales.
The second level of generic clients can display the most complex values in a characteristic. The Characteristic Presentation Format descriptor is limited in that it can only represent a very small subset of all possible data structures. Therefore, the second level of generic clients does not rely on that descriptor. Instead, it uses the knowledge that each and every characteristic type has a unique number, the UUID, that can be looked up on the developer website of the Bluetooth Special Interest Group (SIG)2 to find an XML representation of the data format.
For every characteristic type in any service specification, there must also be an XML file defined for that characteristic. These files are used to help validate devices when they are being tested, but they can also be used to determine the structure of the characteristic value. The XML files can encode every possible data representation required, including enumerated types, bitfields, optional fields based off the value of an enumerated value or a bitfield, and binary and decimal exponential fixed-point formats. These fields can also be concatenated together to make very complex data structures.
A generic client with a connection to the Internet can therefore find a readable characteristic value and perform a simple query of a website to download the characteristic representation’s XML file. This file can be used to display the value to the user.
Once the central device has determined that it is necessary to interact with a service on a peripheral device, it makes a connection to that device and starts to read and write characteristic values and descriptors. The protocol used to do this, Attribute Protocol, is essentially a stateless protocol.
The protocol has no state when connected or when between one connection and the next. There is no “session protocol” either. To get around this limitation, all state is maintained at the application layers, where the applications can make intelligent decisions on how to save energy. The next sections describe how this is done.
The most basic of services simply exposes a set of readable characteristics. For example, the Device Information Service contains one or more characteristics that provide additional information about a device. These basic services are easy to use. For each characteristic in the service that the client understands, it reads the value, either in a single request if the value is short, or by using multiple requests if the value is long.
The next level of complexity is a service that has a characteristic that is both writable and readable. The Link Loss Service is a good example of this type of service. It has a single writable characteristic, Alert Level, that the client can write to configure the behavior when the link between the two devices is lost.
If the client writes “No Alert” into this characteristic, when the devices disconnect, the server does nothing. If the client writes “Mild Alert” into this characteristic, when the devices disconnect for any reason, the server will use a mild alert to the user to notify her of this occurrence. If the client writes “High Alert” into this characteristic, when the devices disconnect, the server will use as many bells, whistles, flashing lights, and other “alert” methods that it has at its disposal.
The key element to understand here is that the state of the service is exposed in the service characteristics. The Alert Level characteristic in the Link Loss Service determines the device behavior. If this holds the value “No Alert”, the server will do nothing. You could consider this the “not-connected” state. If this holds the value “Mild Alert” or “High Alert”, the server will do something when the client disconnects. You could consider that when this service’s characteristic holds one of these values, it is “connected”.
Of course, this implies that if the client wants to gracefully disconnect from the server that has a mild or high alert level, it must change the alert state by writing “No Alert” to this characteristic before it disconnects.
Many people will also ask about what this means if a server can have more than one connection at the same time, and one client writes the value “Mild Alert” to the link loss service’s alert level characteristic, but the other doesn’t and then disconnects. This is not a problem. The value for each characteristic of each service can be different for each client. If client A writes “Mild Alert”, this does not mean that the server will alert when client B disconnects because the value for client B is still “No Alert”.
An important difference between this type of service and those described in the following sections is that these services have characteristics that can be readable and writable. This means that a client can always check the current state of these services without having to remember what it had done previously. This is most useful when the client application unexpectedly terminates, perhaps during debugging of the client application software. When restarting, the client can just refresh its knowledge of the state by reading the appropriate characteristic values.
Another type of service is one that holds no state, but the client can still write values to the service. This might appear strange at first, especially considering the services described in the preceding section were holding state. How can a service have a characteristic that is writable and not hold on to that state? The answer is easy: The service uses the value written immediately, and the server does not have any need to store that value after it has been consumed. This type of characteristic is called a control point.
The previously described Link Loss Service had an Alert Level characteristic that could be written that determined the behavior when the two devices disconnected. But what if the client wants to just make the server alert now? It could write the appropriate value into this service and then disconnect. But this is incredibly disruptive to any other applications that are also using other services on this device that might need the connection. Is there a better way? Of course there is, and it uses a control point characteristic.
The service is called the Immediate Alert Service and the characteristic is called Alert Level. Yes, it is the same alert level characteristic that was used in the Link Loss Service. But characteristics are simply a data format, in this case, an enumeration of three values, “No Alert”, “Mild Alert”, and “High Alert.” The behavior is determined by the service, not the characteristic.
In the Immediate Alert Service, the Alert Level characteristic is only writable, and it causes an alert immediately. Because the alert is immediate, this characteristic cannot have a state. Any value written is immediately consumed, used to make an alert, and not stored. Therefore, there is no point in making this characteristic readable. The characteristic has no state.
There is another type of control point that is discussed in the Notifications and Indications subsection that follows.
The advantage of this type of control point-based service is that it doesn’t matter which slave commanded the control point. Instead, the device will act upon the command written into the control point.
The next type of service exposes a few writable control points along with one or more readable characteristics. These expose the state of a state machine. A state machine in this context is a “machine” that has an exposed state and a way of internally or externally changing states. Essentially, the only difference between a state machine and a control point described earlier is that the state is remembered in a state machine. This state can therefore be read and notified to the client if it changes.
To help illustrate this, let’s consider a state machine for time synchronization. It would have the current states: the machine is doing nothing, and the machine is busy trying to find a more accurate value for the current time. Let’s label these states “Idle” and “Searching.” This is the exposed state of the state machine.
Next, we need a way for the device to control the state machine. This is done by using a control point. This is a control point as described just a moment ago, except that the control point is connected with the state machine that has an external state. For example, with the time synchronization service, this could be enumerated with two commands: Start Synchronization and Cancel Synchronization. This has a number of advantages.
Any device can interrogate the current state of the state machine through the machine’s exposed state characteristic. This means that if three devices all want to synchronize time at the same time, they can all check the state and only command it to start a new synchronization if the machine is in the idle state.
More realistically, each of the devices can just command the state machine to start synchronization. This should not be a problem with a well-defined state machine that can continue to operate even when given potentially invalid commands. For example, if the state machine stays in the searching state when it was commanded to start synchronization, it would be safe to have multiple clients all ask it to start synchronizing any time they want. This is much preferred over the alternative of sending error responses to all clients when any sends a second start synchronization command, and then having to instigate some form of random back-off procedure to start synchronizing again.
The state machines must be sufficiently robust that all commands will have a defined state transition. For example, if the time synchronization service was idle and a client asked it to cancel synchronization, the state machine could take this as an error, or it could accept that the client was unaware that it was already in the idle state and do nothing. This latter approach is typically required in state machines, so all commands should be safe for the client at any time.
However, this scenario points up an interesting side effect: When one device asks for synchronization to start, it has no guarantee that synchronization will complete because it might be cancelled by another device. As an alternative, a service could be defined in which synchronization would always run to completion once started, unless canceled by that client.
Services expose state information. Some of this data can change rapidly or randomly. It would be inefficient to constantly poll the state of a service on a device just in case the value has changed. Take, for example, a battery in a device. Sometimes, when the device is not being used, the battery level might only change once a day. However, if the device is actively being used, the battery level can change once every 15 minutes or more. How often should the client check the battery level? If the client checked the battery level once each day, then at the end of one day the client might think the battery is full, but the battery is actually at 4 percent. If it checked the battery level every 15 minutes, even when the device is idle, the battery level might drop faster than it should due to excessive energy use from reading the battery level.
Instead, it is possible to set up a characteristic to tell the client when the value changes. This way, the client is only notified when the state has actually changed. The client can wait for the changes in state to arrive from its peripheral devices and then efficiently deal with these changes.
To do this, the client can configure a characteristic to send these notifications as required. Most notifications are sent as defined by a service, but some can be configured further by using additional characteristic descriptors.
It should also be noted that there is a second way of receiving data from a service. Notifications are sent at any time and aren’t acknowledged by the application layer. Therefore, they must be considered unreliable. This is fine for many things, such as a status update on a state machine characteristic. But if it is a blood glucose reading, the concept of the server unreliably sending that data is untenable. Therefore, a client can configure characteristics to send indications. These are the same as notifications except that a confirmation message is sent back to the server to inform it that the client has received the data and that the application has received the data.
Indications are configured in the same way by using the Client Configuration Descriptor.
Some devices need a longer relationship than only a single connection. Other devices might want to transmit data confidentially or to only transmit data to a device that has been authenticated to really be the same device as used last time rather than any available device in the area. This is achieved by using a security model that fundamentally results in a “bond.”
A client that wants to establish a long-term relationship with another device will first connect to that device, find some services that it can use, and then initiate a secure connection with the device. These secure connections first authenticate that the device is the correct device. Next, it encrypts the link to ensure confidentiality. Finally, the devices exchange some pairing information. This is the critical bit: If this pairing information is stored on the client, the client has a “bond” with that device.
This is important because when the client reconnects to this device, it does not need to reauthenticate and exchange pairing information again. It just encrypts the link by using information it has stored as part of this bond, and the devices then have an authenticated and confidential data connection.
Bonding also provides other benefits. When devices are bonded, the server will save their configuration data for this client. This model allows a device that reconnects to immediately receive notifications without the need to reconfigure the server.
For example, if the central device configures a characteristic such as a battery level to be notified, the battery level would be notified to the client if it changes when it is connected. But if the client disconnects from the server, the server will not be able to send the notifications. With bonding, when the client reconnects to the server, the notification can be sent immediately.
This has a side effect that must be considered. The server could ignore all state changes for bonded devices and require the client to query the current state after it reconnects. If the data changes infrequently, this can be inefficient for both the client and server. This model also assumes that the client doesn’t disconnect and reconnect frequently because the cost of reconnecting would be significantly higher because each bit of state must be read on each reconnection.
The alternative approach taken in Bluetooth low energy is that the server has to remember not only that a characteristic is configured for notification but also that the value has changed while a client is disconnected. This requires an additional bit to be associated with each notifiable characteristic that would be set when the characteristic value changes. Then, when the client reconnects, these bits can be checked and the characteristic value notified only if it did change.
The consequence of the server saving the configuration information for a bonded connection is that the client doesn’t “connect” with a server. Instead, the client bonds and configures a peripheral to perform a function and then the client disconnects and reconnects to the peripheral, as and when it needs. The peripherals will be connectable when they have data to send, and the central device will reconnect to these bonded devices and quickly receive the data, as needed.
This is even true if the peripheral is doing something that the central device requested. For example, a central might ask a peripheral that has a time synchronization service to start synchronizing time and then disconnect. Some time later, the central device can reconnect to the peripheral and check on the state of the time synchronization, albeit with no guarantee that the synchronization completed.
The other major advantage of this approach is that the client can remember information about the handles of attributes in the peripheral. This means that once the central device has determined the set of services on a peripheral and configured them, the client can remember the attribute handles. When the central device reconnects to the peripheral, it can read and write these attribute handles without doing another service scan. This reduces the time required between reconnecting to a device and being able to use this device.
As stated in the preceding section, a central device’s client can remember or cache the sets of services and characteristics between connections. Some devices will have the capability to change or add services. For example, a computer or a smart phone can add an application that might include a service, or a peripheral might have updated its firmware. When this happens, the client will not be able to read any attributes, and all requests to that server will fail. This protects the client from reading the wrong attributes.
Along with these error messages, the client will also receive a notification of the Service Changed characteristic from the GATT Service. The client should have stored this attribute’s handle, so when the client receives this notification, it can take the appropriate action.
The Service Changed notification includes a range of handles that have been changed. This means that if a device has only added or removed one service, only the range of handles for that service will be included in the notification. If the device has had all of its services changed—for example, if the operating system on the device has been updated—the handle range might include all the handles of the device.
Note that Service Changed is only relevant for bonded devices. If the central device does not bond with a peripheral device, then no attribute handles can be cached by the client and no notification of any service changes will be made. This means that for two devices that are not bonded, the client must refresh the list of all services and characteristics on the server every time they connect.
When designing a central device, the biggest implication is what it supports. Usually, a central device will implement the client roles of one or more profiles. To understand what this means, profiles must be explained.
A profile is a description of how a device functions for a given use case. Within a profile, roles are defined. A profile role defines a device that can act as one part of an ecosystem of devices that enable the profile’s use case.
Each profile role defines the set of services that a device must implement. Some profile roles don’t require any services, others require just one service, and others require many services. Typically, profiles define two roles, for example, a reporter role and a monitor role. The reporter role would require one service and would be implemented in a peripheral device. A monitor role would not require any services and would be implemented in a central device.
For example, the proximity profile defines two profile roles: the proximity monitor and the proximity reporter. The proximity reporter implements the Immediate Alert Service, the Link Loss Service, and the Tx Power Service. These three services act independently on the reporter, behaving as defined in each of their associated service specifications. This independence of services is critical to this profile model being flexible and therefore future proof.
The proximity monitor defines how a client uses these three services to enable the proximity use case. For example, once the services and characteristics have been discovered, the monitor can read the current Tx Power characteristic and compare it with the received signal strength of packets from the reporter. The proximity monitor can then ensure that the implied distance between the two devices is acceptable. If this distance is not acceptable, the proximity monitor can write the alert level characteristic in the Immediate Alert Service.
The proximity monitor can also write the alert level characteristic in the Link Loss Service. In this way, if the signal strength drops so quickly that the connection drops before the signal strength drop is noticed or the alert level characteristic in the Immediate Alert Service can be written, the proximity reporter can still alert in a controlled way.
This combination of services allows the proximity monitoring use case. But the Immediate Alert Service could also be used to enable different use cases, such as allowing the user to see which device is selected on a user interface by writing the alert level characteristic of the Immediate Alert Service. This behavior in the client would be described in a different profile.
The first thing that a profile needs to do is find the services that the peer device supports. It can do this by first discovering primary services. This can be done by either finding primary services by service type or by finding all primary services.
This makes it possible for a profile that only requires the use of a single service to just find the particular service it needs, ignoring all other services on the device. For example, the user interface alerting service would only need to find the single service that it uses.
A more complex profile might require that many services are found. The proximity monitor profile role requires that three services are discovered. It could do this by doing the same single service search, one at a time, or it could do this by finding all primary services and then only storing the services that it needs.
Typically, a complex client only finds all services once and then caches them for later. When an application that implements a given profile role asks for all the services on a device, the cached list of services is used. This is possible because when the set of services changes on a server, a notification is sent to inform the client that the services have changed.
Once the set of services has been found on the peer device, the set of characteristics will be required. Just like services, only one characteristic might be required by the profile, or many characteristics might be required. Some characteristics might be optional, and as such, they must be searched for within a service to check whether they exist.
Typically, all characteristics for each service previously discovered will be found by the client. This is because for the simplest service that has just one characteristic, this is a very fast and efficient operation. Also, if the service contains many optional characteristics, this operation finds all the characteristics the service implements.
Once the characteristics have been found, they can be used. For example, for the Immediate Alert Service, the proximity monitor client can write the required alert level into the Alert Level characteristic value.
It should be noted that for some services that have just one characteristic that is readable, the characteristic discovery and the reading of the characteristic value can be combined into a single operation. For example, once the Tx Power Service has been discovered, the Tx Power characteristic can be discovered and read by using the Read Characteristic By Type GATT procedure. This means that the separate step of finding the characteristics that was just described is not required.
Some characteristics can support being notified or indicated. For a client to be able to use these capabilities of a server, the client must first find the Client Characteristic Configuration Descriptor. The client does this by finding any additional attributes after the characteristic value, within this characteristic group. This will find all additional descriptors for the characteristic. Once the Client Characteristic Configuration Descriptor has been discovered, it can be written with the correct value to enable notifications or indications.
Finally, if the client wants to disconnect and reconnect quickly again in the future, or the characteristics require an encrypted or authenticated link, the client must bond with the server. Typically, this is driven by the client role in the profile.
The client can attempt to read or write any characteristic value in a service. The service only has to respond with the value of the characteristic or with the response that the value was written, if the correct permissions are in place to read or write this characteristic. If the permissions are not correct, the server will respond with an appropriate error code.
If the error code suggested that the client had insufficient security, the client could attempt to pair or bond with the server to enable the correct level of security. If the client is just reading the information once, pairing would be sufficient. If the client will reconnect again and again, bonding would also be required. Once the devices have the appropriate security, the connection will be encrypted, and the client can retry the request that previously failed.