QLowEnergyService Class
The QLowEnergyService class represents an individual service on a Bluetooth Low Energy Device. More...
| Header: | #include <QLowEnergyService> |
| qmake: | QT += bluetooth |
| Since: | Qt 5.4 |
| Inherits: | QObject |
This class was introduced in Qt 5.4.
Public Types
| enum | ServiceError { NoError, OperationError, CharacteristicReadError, CharacteristicWriteError, DescriptorReadError, …, UnknownError } |
| enum | ServiceState { InvalidService, DiscoveryRequired, DiscoveringServices, ServiceDiscovered, LocalService } |
| enum | ServiceType { PrimaryService, IncludedService } |
| enum | WriteMode { WriteWithResponse, WriteWithoutResponse, WriteSigned } |
Detailed Description
QLowEnergyService provides access to the details of Bluetooth Low Energy services. The class facilitates the discovery and publification of service details, permits reading and writing of the contained data and notifies about data changes.
Service Structure
A Bluetooth Low Energy peripheral device can contain multiple services. In turn each service may include further services. This class represents a single service of the peripheral device and is created via QLowEnergyController::createServiceObject(). The type() indicates whether this service is a primary (top-level) service or whether the service is part of another service. Each service may contain one or more characteristics and each characteristic may contain descriptors. The resulting structure may look like the following diagram:

A characteristic is the principal information carrier. It has a value() and properties() describing the access permissions for the value. The general purpose of the contained descriptor is to further define the nature of the characteristic. For example, it might specify how the value is meant to be interpreted or whether it can notify the value consumer about value changes.
Service Interaction
Once a service object was created for the first time, its details are yet to be discovered. This is indicated by its current state() being DiscoveryRequired. It is only possible to retrieve the serviceUuid() and serviceName().
The discovery of its included services, characteristics and descriptors is triggered when calling discoverDetails(). During the discovery the state() transitions from DiscoveryRequired via DiscoveringServices to its final ServiceDiscovered state. This transition is advertised via the stateChanged() signal. Once the details are known, all of the contained characteristics, descriptors and included services are known and can be read or written.
The values of characteristics and descriptors can be retrieved via QLowEnergyCharacteristic and QLowEnergyDescriptor, respectively. However, direct reading or writing of these attributes requires the service object. The readCharacteristic() function attempts to re-read the value of a characteristic. Although the initial service discovery may have obtained a value already this call may be required in cases where the characteristic value constantly changes without any notifications being provided. An example might be a time characteristic that provides a continuous value. If the read attempt is successful, the characteristicRead() signal is emitted. A failure to read the value triggers the CharacteristicReadError. The writeCharacteristic() function attempts to write a new value to the given characteristic. If the write attempt is successful, the characteristicWritten() signal is emitted. A failure to write triggers the CharacteristicWriteError. Reading and writing of descriptors follows the same pattern.
Every attempt is made to read or write the value of a descriptor or characteristic on the hardware. This means that meta information such as QLowEnergyCharacteristic::properties() is generally ignored when reading and writing. As an example, it is possible to call writeCharacteristic() despite the characteristic being read-only based on its meta data description. The resulting write request is forwarded to the connected device and it is up to the device to respond to the potentially invalid request. In this case the result is the emission of the CharacteristicWriteError in response to the returned device error. This behavior simplifies interaction with devices which report wrong meta information. If it was not possible to forward the request to the remote device the OperationError is set. A potential reason could be that the to-be-written characteristic object does not even belong the current service. In summary, the two types of errors permit a quick distinction of local and remote error cases.
All requests are serialised based on First-In First-Out principle. For example, issuing a second write request, before the previous write request has finished, is delayed until the first write request has finished.
Note: Currently, it is not possible to send signed write or reliable write requests.
In some cases the peripheral generates value updates which the central is interested in receiving. In order for a characteristic to support such notifications it must have the QLowEnergyCharacteristic::Notify or QLowEnergyCharacteristic::Indicate property and a descriptor of type QBluetoothUuid::ClientCharacteristicConfiguration. Provided those conditions are fulfilled notifications can be enabled as shown in the following code segment:
//PreCondition: service details already discovered
QLowEnergyCharacteristic batteryLevel = service->characteristic(
QBluetoothUuid::BatteryLevel);
if (!batteryLevel.isValid())
return;
QLowEnergyDescriptor notification = batteryLevel.descriptor(
QBluetoothUuid::ClientCharacteristicConfiguration);
if (!notification.isValid())
return;
// establish hook into notifications
connect(service, SIGNAL(characteristicChanged(QLowEnergyCharacteristic,QByteArray)),
this, SLOT(characteristicChanged(QLowEnergyCharacteristic,QByteArray)));
// enable notification
service->writeDescriptor(notification, QByteArray::fromHex("0100"));
// disable notification
//service->writeDescriptor(notification, QByteArray::fromHex("0000"));
// wait until descriptorWritten() signal is emitted
// to confirm successful write
The example shows a battery level characteristic which updates the central on every value change. The notifications are provided via the characteristicChanged() signal. More details about this mechanism are provided by the Bluetooth Specification.
Service Data Sharing
Each QLowEnergyService instance shares its internal states and information with other QLowEnergyService instance of the same service. If one instance initiates the discovery of the service details, all remaining instances automatically follow. Therefore the following snippet always works:
QLowEnergyService *first, *second;
QLowEnergyController control(remoteDevice);
control.connectToDevice();
// waiting for connection
first = control.createServiceObject(QBluetoothUuid::BatteryService);
second = control.createServiceObject(QBluetoothUuid::BatteryService);
Q_ASSERT(first->state() == QLowEnergyService::DiscoveryRequired);
Q_ASSERT(first->state() == second->state());
first->discoverDetails();
Q_ASSERT(first->state() == QLowEnergyService::DiscoveringServices);
Q_ASSERT(first->state() == second->state());
Other operations such as calls to readCharacteristic(), readDescriptor(), writeCharacteristic(), writeDescriptor() or the invalidation of the service due to the related QLowEnergyController disconnecting from the device are shared the same way.
See also QLowEnergyController, QLowEnergyCharacteristic, and QLowEnergyDescriptor.
Member Type Documentation
enum QLowEnergyService::ServiceError
This enum describes all possible error conditions during the service's existence. The error() function returns the last occurred error.
| Constant | Value | Description |
|---|---|---|
QLowEnergyService::NoError | 0 | No error has occurred. |
QLowEnergyService::OperationError | 1 | An operation was attempted while the service was not ready. An example might be the attempt to write to the service while it was not yet in the ServiceDiscovered state() or the service is invalid due to a loss of connection to the peripheral device. |
QLowEnergyService::CharacteristicReadError | 5 | An attempt to read a characteristic value failed. For example, it might be triggered in response to a call to readCharacteristic(). This value was introduced by Qt 5.5. |
QLowEnergyService::CharacteristicWriteError | 2 | An attempt to write a new value to a characteristic failed. For example, it might be triggered when attempting to write to a read-only characteristic. |
QLowEnergyService::DescriptorReadError | 6 | An attempt to read a descriptor value failed. For example, it might be triggered in response to a call to readDescriptor(). This value was introduced by Qt 5.5. |
QLowEnergyService::DescriptorWriteError | 3 | An attempt to write a new value to a descriptor failed. For example, it might be triggered when attempting to write to a read-only descriptor. |
QLowEnergyService::UnknownError | 4 | An unknown error occurred when interacting with the service. This value was introduced by Qt 5.5. |
enum QLowEnergyService::ServiceState
This enum describes the state() of the service object.
| Constant | Value | Description |
|---|---|---|
QLowEnergyService::InvalidService | 0 | A service can become invalid when it looses the connection to the underlying device. Even though the connection may be lost it retains its last information. An invalid service cannot become valid anymore even if the connection to the device is re-established. |
QLowEnergyService::DiscoveryRequired | 1 | The service details are yet to be discovered by calling discoverDetails(). The only reliable pieces of information are its serviceUuid() and serviceName(). |
QLowEnergyService::DiscoveringServices | 2 | The service details are being discovered. |
QLowEnergyService::ServiceDiscovered | 3 | The service details have been discovered. |
QLowEnergyService::LocalService | 4 | The service is associated with a controller object in the peripheral role. Such service objects do not change their state. This value was introduced by Qt 5.7. |
enum QLowEnergyService::ServiceType
This enum describes the type of the service.
| Constant | Value | Description |
|---|---|---|
QLowEnergyService::PrimaryService | 0x0001 | The service is a top-level/primary service. If this type flag is not set, the service is considered to be a secondary service. Each service may be included by another service which is indicated by IncludedService. |
QLowEnergyService::IncludedService | 0x0002 | The service is included by another service. On some platforms, this flag cannot be determined until the service that includes the current service was discovered. |
enum QLowEnergyService::WriteMode
This enum describes the mode to be used when writing a characteristic value. The characteristic advertises its supported write modes via its properties.
| Constant | Value | Description |
|---|---|---|
QLowEnergyService::WriteWithResponse | 0 | If a characteristic is written using this mode, the peripheral shall send a write confirmation. If the operation is successful, the confirmation is emitted via the characteristicWritten() signal. Otherwise the CharacteristicWriteError is emitted. A characteristic must have set the QLowEnergyCharacteristic::Write property to support this write mode. |
QLowEnergyService::WriteWithoutResponse | 1 | If a characteristic is written using this mode, the remote peripheral shall not send a write confirmation. The operation's success cannot be determined and the payload must not be longer than 20 bytes. A characteristic must have set the QLowEnergyCharacteristic::WriteNoResponse property to support this write mode. Its adavantage is a quicker write operation as it may happen in between other device interactions. |
QLowEnergyService::WriteSigned | 2 | If a characteristic is written using this mode, the remote peripheral shall not send a write confirmation. The operation's success cannot be determined and the payload must not be longer than 8 bytes. A bond must exist between the two devices and the link must not be encrypted. A characteristic must have set the QLowEnergyCharacteristic::WriteSigned property to support this write mode. This value was introduced in Qt 5.7 and is currently only supported on Android and on Linux with BlueZ 5 and a kernel version 3.7 or newer. |