QLowEnergyService Class

QLowEnergyService 类代表蓝牙低功耗设备上的一项单独服务。更多

Header: #include <QLowEnergyService>
qmake: QT += bluetooth
Inherits: QObject

公共类型

(since 6.2) enum DiscoveryMode { FullDiscovery, SkipValueDiscovery }
enum ServiceError { NoError, OperationError, CharacteristicReadError, CharacteristicWriteError, DescriptorReadError, …, UnknownError }
enum ServiceState { InvalidService, RemoteService, RemoteServiceDiscovering, RemoteServiceDiscovered, LocalService, …, ServiceDiscovered }
enum ServiceType { PrimaryService, IncludedService }
flags ServiceTypes
enum WriteMode { WriteWithResponse, WriteWithoutResponse, WriteSigned }

公共函数

virtual ~QLowEnergyService()
QLowEnergyCharacteristic characteristic(const QBluetoothUuid &uuid) const
QList<QLowEnergyCharacteristic> characteristics() const
bool contains(const QLowEnergyCharacteristic &characteristic) const
bool contains(const QLowEnergyDescriptor &descriptor) const
void discoverDetails(QLowEnergyService::DiscoveryMode mode = FullDiscovery)
QLowEnergyService::ServiceError error() const
QList<QBluetoothUuid> includedServices() const
void readCharacteristic(const QLowEnergyCharacteristic &characteristic)
void readDescriptor(const QLowEnergyDescriptor &descriptor)
QString serviceName() const
QBluetoothUuid serviceUuid() const
QLowEnergyService::ServiceState state() const
QLowEnergyService::ServiceTypes type() const
void writeCharacteristic(const QLowEnergyCharacteristic &characteristic, const QByteArray &newValue, QLowEnergyService::WriteMode mode = WriteWithResponse)
void writeDescriptor(const QLowEnergyDescriptor &descriptor, const QByteArray &newValue)

信号

void characteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &newValue)
void characteristicRead(const QLowEnergyCharacteristic &characteristic, const QByteArray &value)
void characteristicWritten(const QLowEnergyCharacteristic &characteristic, const QByteArray &newValue)
void descriptorRead(const QLowEnergyDescriptor &descriptor, const QByteArray &value)
void descriptorWritten(const QLowEnergyDescriptor &descriptor, const QByteArray &newValue)
(since 6.2) void errorOccurred(QLowEnergyService::ServiceError newError)
void stateChanged(QLowEnergyService::ServiceState newState)

详细说明

QLowEnergyService 可访问蓝牙低功耗服务的详细信息。该类便于发现和公布服务详情,允许读写所含数据,并通知数据变更。

服务结构

蓝牙低功耗外围设备可包含多个服务。反过来,每个服务又可包括更多服务。该类代表外围设备的单个服务,通过QLowEnergyController::createServiceObject() 创建。type() 表示该服务是主要(顶级)服务,还是其他服务的一部分。每个服务可包含一个或多个特征,每个特征可包含描述符。由此产生的结构如下图所示:

通用外设的结构

特征是主要的信息载体。它有value() 和properties() 描述值的访问权限。所含描述符的一般目的是进一步定义特性的性质。例如,它可以指定如何解释值,或者是否可以通知值消费者值的变化。

服务交互

服务对象首次创建后,其详细信息尚待发现。其当前state() 为DiscoveryRequired 。只能检索serviceUuid() 和serviceName() 。

在调用discoverDetails() 时会触发对其包含的服务、特性和描述符的发现。在发现过程中,state() 会从DiscoveryRequired 通过DiscoveringService 过渡到最终的ServiceDiscovered 状态。这一转换通过stateChanged() 信号发布。一旦知道了详细信息,就知道了所有包含的特征、描述符和包含的服务,并且可以读取或写入。

特性和描述符的值可分别通过QLowEnergyCharacteristicQLowEnergyDescriptor 获取。不过,直接读取或写入这些属性需要服务对象。readCharacteristic() 函数试图重新读取特征值。虽然初始服务发现可能已经获得了一个值,但在特性值不断变化而没有提供任何通知的情况下,可能需要调用该函数。例如,提供连续值的时间特性。如果读取尝试成功,就会发出characteristicRead() 信号。如果读取失败,则会触发CharacteristicReadErrorwriteCharacteristic() 函数会尝试向给定特性写入新值。如果写入尝试成功,则发出characteristicWritten() 信号。如果写入失败,则会触发CharacteristicWriteError 信号。描述符的读取和写入遵循相同的模式。

每次尝试都是为了读取或写入硬件上描述符或特性的值。这意味着读写时一般会忽略QLowEnergyCharacteristic::properties() 等元信息。举例来说,根据元数据描述,尽管特性是只读的,但仍有可能调用writeCharacteristic() 。由此产生的写入请求会被转发到所连接的设备,并由设备来响应可能无效的请求。在这种情况下,结果是发送CharacteristicWriteError 以回应返回的设备错误。这种行为简化了与报告错误元信息的设备的交互。如果无法将请求转发到远程设备,则会设置OperationError 。一个潜在的原因可能是待写特性对象甚至不属于当前服务。总之,这两类错误可以快速区分本地和远程错误。

所有请求都根据先进先出原则进行序列化。例如,在前一个写入请求完成之前发出第二个写入请求,会被延迟到第一个写入请求完成之后。

注: 目前无法发送签名写入或可靠写入请求。

在某些情况下,外设会生成中心有兴趣接收的值更新。为使特性支持此类通知,它必须具有QLowEnergyCharacteristic::NotifyQLowEnergyCharacteristic::Indicate 属性和QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration 类型的描述符。只要满足这些条件,就可以启用通知功能,如以下代码段所示:

    //PreCondition: service details already discovered
    QLowEnergyCharacteristic batteryLevel = service->characteristic(
                QBluetoothUuid::CharacteristicType::BatteryLevel);
    if (!batteryLevel.isValid())
        return;

    QLowEnergyDescriptor notification = batteryLevel.descriptor(
                QBluetoothUuid::DescriptorType::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

该示例显示了一个电池电量特性,每次数值变化都会更新中心。通知通过characteristicChanged() 信号提供。有关该机制的更多详情,请参阅蓝牙规范

服务数据共享

每个 QLowEnergyService 实例都与同一服务的其他 QLowEnergyService 实例共享其内部状态和信息。如果一个实例启动了服务详细信息的发现,其余所有实例都会自动跟进。因此,以下代码段始终有效:

    QLowEnergyService *first, *second;
    QLowEnergyController control(remoteDevice);
    control.connectToDevice();

    // waiting for connection

    first = control.createServiceObject(QBluetoothUuid::ServiceClassUuid::BatteryService);
    second = control.createServiceObject(QBluetoothUuid::ServiceClassUuid::BatteryService);
    Q_ASSERT(first->state() == QLowEnergyService::RemoteService);
    Q_ASSERT(first->state() == second->state());

    first->discoverDetails();

    Q_ASSERT(first->state() == QLowEnergyService::RemoteServiceDiscovering);
    Q_ASSERT(first->state() == second->state());

其他操作,如调用readCharacteristic(),readDescriptor(),writeCharacteristic(),writeDescriptor() 或因相关QLowEnergyController 与设备断开连接而导致服务失效,也是以同样方式共享的。

另请参阅 QLowEnergyController,QLowEnergyCharacteristic, 和QLowEnergyDescriptor

成员类型文档

[since 6.2] enum QLowEnergyService::DiscoveryMode

该枚举列出了服务发现模式。所有模式都能发现服务的特征和特征描述符。这些模式的区别在于是否读取特征值和描述符。

常量描述
QLowEnergyService::FullDiscovery0在完全发现过程中,会发现所有特征。读取所有特征值和描述符。
QLowEnergyService::SkipValueDiscovery1在最小发现期间,会发现所有特征。不读取特征值和描述符。

此枚举在 Qt 6.2 中引入。

另请参阅 discoverDetails()。

enum QLowEnergyService::ServiceError

该枚举描述了服务存在期间所有可能的错误条件。error() 函数会返回上次发生的错误。

常量说明
QLowEnergyService::NoError0未发生错误。
QLowEnergyService::OperationError1在服务尚未准备就绪时尝试了操作。例如,在服务尚未进入ServiceDiscovered state () 时尝试写入服务,或者由于与外围设备失去连接导致服务无效。
QLowEnergyService::CharacteristicReadError (since Qt 5.5)5读取特征值的尝试失败。例如,可能是在调用readCharacteristic() 时触发的。
QLowEnergyService::CharacteristicWriteError2向特性写入新值的尝试失败。例如,在尝试向只读特性写入时可能会触发。
QLowEnergyService::DescriptorReadError (since Qt 5.5)6尝试读取描述符值失败。例如,可能是在调用readDescriptor() 时触发的。
QLowEnergyService::DescriptorWriteError3向描述符写入新值的尝试失败。例如,可能是在尝试向只读描述符写入时触发的。
QLowEnergyService::UnknownError (since Qt 5.5)4与服务交互时发生未知错误。

enum QLowEnergyService::ServiceState

该枚举描述了服务对象的state() 。

常量说明
QLowEnergyService::InvalidService0当服务失去与底层设备的连接时就会失效。尽管连接可能会丢失,但它仍会保留最后的信息。即使重新建立了与设备的连接,无效服务也不会再变为有效。
QLowEnergyService::RemoteService1服务的详细信息需要调用discoverDetails() 才能发现。唯一可靠的信息是serviceUuid() 和serviceName()。
QLowEnergyService::RemoteServiceDiscovering2正在发现服务详细信息。
QLowEnergyService::RemoteServiceDiscovered3服务详细信息已被发现。
QLowEnergyService::LocalService (since Qt 5.7)4服务与peripheral role 中的控制器对象相关联。此类服务对象不会改变其状态。
QLowEnergyService::DiscoveryRequiredRemoteService已废弃。已更名为 RemoteService。
QLowEnergyService::DiscoveringServiceRemoteServiceDiscovering已废弃。已更名为 RemoteServiceDiscovering。
QLowEnergyService::ServiceDiscoveredRemoteServiceDiscovered已废弃。已更名为 RemoteServiceDiscovered。

枚举 QLowEnergyService::ServiceType
flags QLowEnergyService::ServiceTypes

此枚举描述服务类型。

常量描述
QLowEnergyService::PrimaryService0x0001服务是顶级/主要服务。如果未设置此类型标志,则认为该服务是次要服务。每个服务都可能被另一个服务包含,后者由 IncludedService 表示。
QLowEnergyService::IncludedService0x0002该服务被其他服务包含。在某些平台上,在发现包含当前服务的服务之前,无法确定此标记。

ServiceTypes 类型是QFlags<ServiceType> 的类型定义。它存储服务类型值的 OR 组合。

enum QLowEnergyService::WriteMode

该枚举描述了写入特征值时使用的模式。特性通过properties 公开其支持的写入模式。

常量描述
QLowEnergyService::WriteWithResponse0如果使用该模式写入特征值,外设将发送写入确认。如果操作成功,则通过characteristicWritten() 信号发送确认信息。否则,将发送CharacteristicWriteError 。特性必须设置了QLowEnergyCharacteristic::Write 属性才能支持这种写入模式。
QLowEnergyService::WriteWithoutResponse1如果使用这种模式写入特性,远程外设将不会发送写入确认。无法确定操作是否成功,有效载荷不得超过 20 字节。特性必须设置了QLowEnergyCharacteristic::WriteNoResponse 属性才能支持这种写入模式。它的优点是写入操作速度更快,因为它可以在其他设备交互之间进行。
QLowEnergyService::WriteSigned (since Qt 5.7)2如果使用该模式写入特性,远程外设将不会发送写入确认。无法确定操作是否成功,有效载荷不得超过 8 字节。两个设备之间必须存在绑定,链接不得加密。特性必须设置了QLowEnergyCharacteristic::WriteSigned 属性才能支持这种写入模式。目前只有安卓系统和使用 BlueZ 5 及 3.7 或更新版本内核的 Linux 系统支持此值。

成员函数文档

[virtual noexcept] QLowEnergyService::~QLowEnergyService()

销毁QLowEnergyService 实例。

QLowEnergyCharacteristic QLowEnergyService::characteristic(const QBluetoothUuid &uuid) const

返回与uuid 匹配的特征;否则返回无效特征。

如果尚未调用该服务实例的discoverDetails() 或没有与uuid 匹配的特征,则返回的特征无效。

另请参阅 characteristics()。

[signal] void QLowEnergyService::characteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &newValue)

如果相关控制器对象处于central 角色,则当外设/设备侧的事件改变characteristic 的值时会发出该信号。在这种情况下,信号的发射意味着在外设发生变化事件之前,必须通过特征ClientCharacteristicConfiguration 描述符激活变化通知。有关如何做到这一点的更多详细信息,请进一步访问above

如果控制器的角色是peripheral ,即服务对象是通过QLowEnergyController::addService 创建的,那么当 GATT 客户端使用写入请求或命令写入特性值时,就会发出信号。

newValue 参数包含characteristic 的更新值。

[signal] void QLowEnergyService::characteristicRead(const QLowEnergyCharacteristic &characteristic, const QByteArray &value)

characteristic 的读取请求成功返回其value 时,就会发出该信号。调用 characteristicRead() 可能会触发该信号。如果读取操作不成功,则使用CharacteristicReadError 标志发出errorOccurred() 信号。

注意: 该信号仅在与中心角色相关的用例中发出。

另请参阅 readCharacteristic()。

[signal] void QLowEnergyService::characteristicWritten(const QLowEnergyCharacteristic &characteristic, const QByteArray &newValue)

characteristic 的值被成功修改为newValue 时,就会发出该信号。修改必须是通过调用writeCharacteristic() 触发的。如果写入操作不成功,则使用CharacteristicWriteError 标志发出errorOccurred() 信号。

接收到写入信号可视为目标设备接收到待写入值并报告写入请求状态的标志。

注: 如果使用WriteWithoutResponse 模式调用writeCharacteristic() ,则不会发出该信号和errorOccurred() 信号。

注: 该信号仅在与中心角色相关的用例中发出。

另请参阅 writeCharacteristic()。

QList<QLowEnergyCharacteristic> QLowEnergyService::characteristics() const

返回与此QLowEnergyService 实例相关的所有特征。

如果尚未调用此服务实例的discoverDetails() 或没有已知特征,则返回的列表为空。

另请参阅 characteristic()、state() 和discoverDetails()。

bool QLowEnergyService::contains(const QLowEnergyCharacteristic &characteristic) const

如果characteristic 属于此服务,则返回true ;否则返回false

如果characteristics() 包含characteristic ,则该特性属于某项服务。

bool QLowEnergyService::contains(const QLowEnergyDescriptor &descriptor) const

如果descriptor 属于此服务,则返回true ;否则false

[signal] void QLowEnergyService::descriptorRead(const QLowEnergyDescriptor &descriptor, const QByteArray &value)

descriptor 的读取请求成功返回其value 时,就会发出该信号。调用 descriptorRead() 可能会触发该信号。如果读取操作不成功,则会使用DescriptorReadError 标志发出errorOccurred() 信号。

注意: 该信号仅在与中心角色相关的用例中发出。

另请参阅 readDescriptor().

[signal] void QLowEnergyService::descriptorWritten(const QLowEnergyDescriptor &descriptor, const QByteArray &newValue)

descriptor 的值成功更改为newValue 时,就会发出该信号。如果相关控制器对象处于central 角色,则必须通过调用writeDescriptor() 才能完成更改。否则,该信号是 GATT 客户端向相应描述符发出写入请求或命令的结果。

另请参阅 writeDescriptor()。

void QLowEnergyService::discoverDetails(QLowEnergyService::DiscoveryMode mode = FullDiscovery)

启动对服务所含服务、特征及其相关描述符的发现。

发现过程通过stateChanged() 信号显示。创建后,服务处于DiscoveryRequired 状态。调用 discoverDetails() 时,服务会过渡到DiscoveringService 。完成细节发现后,服务会过渡到ServiceDiscovered 状态。每次转换时,都会发出stateChanged() 信号。根据参数mode ,会执行FullDiscoverySkipValueDiscovery 。在任何情况下,所有服务和特性都会被发现。FullDiscovery 会读取所有特征值和描述符。SkipValueDiscovery 不读取特征值和描述符。SkipValueDiscovery 有两个优点。第一,速度更快。其次,它可以规避某些设备的错误,这些设备会错误地将特征值或描述符宣传为可读,但却不允许对其进行读取。这会引发不可预测的行为。在SkipValueDiscovery 之后,有必要调用readCharacteristic() /readDescriptor() 并等待它们成功完成后再访问特性或描述符的值。

参数mode 在 Qt XML 6.2 中引入。

另请参阅 state()。

QLowEnergyService::ServiceError QLowEnergyService::error() const

返回上次发生的错误或NoError

[signal, since 6.2] void QLowEnergyService::errorOccurred(QLowEnergyService::ServiceError newError)

该信号在发生错误时发出。newError 参数描述了发生的错误。

该函数在 Qt 6.2 中引入。

QList<QBluetoothUuid> QLowEnergyService::includedServices() const

返回当前服务包含的所有服务的 UUID。

如果尚未调用该服务实例的discoverDetails() 或没有已知特征,则返回的列表为空。

一个包含的服务可能还包含另一个服务。这种二级包含必须通过相关的一级QLowEnergyService 实例获得。从技术上讲,这可能会产生循环依赖关系。

QLowEnergyController::createServiceObject应使用 () 为每个 UUID 获取服务实例。

另请参阅 createServiceObject()。

void QLowEnergyService::readCharacteristic(const QLowEnergyCharacteristic &characteristic)

读取characteristic 的值。如果操作成功,则发出characteristicRead() 信号;否则设置CharacteristicReadError 。一般来说,如果characteristicQLowEnergyCharacteristic::Read 属性被设置,则该 可被读取。

对同一远程设备的所有描述符和特征请求都是序列化的。同时发出多个请求时,会使用队列。队列不能消除对同一特性的重复读取请求。

只有当服务处于ServiceDiscovered 状态并属于服务时,才能读取特征。如果其中一个条件不成立,则会设置QLowEnergyService::OperationError

注意: 尽管QLowEnergyCharacteristic::properties() 报告了不可读属性,但调用此函数总是试图在硬件上读取特性值。如果硬件返回错误,则会设置CharacteristicReadError

另请参阅 characteristicRead() 和writeCharacteristic()。

void QLowEnergyService::readDescriptor(const QLowEnergyDescriptor &descriptor)

读取descriptor 的值。如果操作成功,则发出descriptorRead() 信号;否则设置DescriptorReadError

针对同一远程设备的所有描述符和特征请求都会被序列化。同时发出多个请求时,会使用队列。队列不能消除对同一描述符的重复读取请求。

只有当服务处于ServiceDiscovered 状态且描述符属于服务时,才能读取描述符。如果其中一个条件不成立,则会设置QLowEnergyService::OperationError

另请参阅 descriptorRead() 和writeDescriptor()。

QString QLowEnergyService::serviceName() const

返回服务名称,否则返回空字符串。

只有当serviceUuid() 是众所周知的 UUID 时,才能检索到返回的名称。

QBluetoothUuid QLowEnergyService::serviceUuid() const

返回服务的 UUID;否则返回空 UUID。

QLowEnergyService::ServiceState QLowEnergyService::state() const

返回服务的当前状态。

如果设备的服务是首次实例化,则对象的状态为DiscoveryRequired 。指向外围设备上同一服务的所有服务对象的状态总是相同的。这是由于内部对象数据的共享性质造成的。因此,在第一个服务对象实例之后创建的任何服务对象实例的状态都与已存在的实例相同。

如果QLowEnergyController 与远程设备断开连接,服务就会失效。无效服务在断开事件发生时仍保留其内部状态。这意味着,一旦发现了服务细节,甚至可以从无效服务中检索到这些细节。这就允许在建立设备连接、检索服务详细信息并立即断开设备连接的情况下,允许下一个设备连接到外围设备。

不过,在正常情况下,应保持连接以避免重复发现服务及其详细信息。发现服务可能需要一段时间,客户端可以订阅持续的变更通知。

另请参阅 stateChanged().

[signal] void QLowEnergyService::stateChanged(QLowEnergyService::ServiceState newState)

该信号在服务状态发生变化时发出。newState 也可通过state() 获取。

另请参阅 state() 。

QLowEnergyService::ServiceTypes QLowEnergyService::type() const

返回服务的类型。

注意: 在服务进入ServiceDiscovered 状态之前,不能依赖 type 属性。该字段的初始化值为PrimaryService

注: 在 Android 上,无法确定服务是主服务还是辅助服务。因此,所有服务都设置了PrimaryService 标志。

void QLowEnergyService::writeCharacteristic(const QLowEnergyCharacteristic &characteristic, const QByteArray &newValue, QLowEnergyService::WriteMode mode = WriteWithResponse)

写入newValue 作为characteristic 的值。具体语义取决于相关控制器对象所处的角色。

中心角色

调用的结果是向远程外设发出写入请求或命令。如果操作成功,则发出characteristicWritten() 信号;否则设置CharacteristicWriteError 。除非外设本身在当前写入请求后再次更改值,否则调用该函数不会触发characteristicChanged() 信号。

mode 参数决定远程设备是否应发送写确认。要写入的characteristic 必须支持相关的写模式。特征支持的写模式由QLowEnergyCharacteristic::WriteQLowEnergyCharacteristic::WriteNoResponse 属性指示。

针对同一远程设备的所有描述符和特性写入请求都是序列化的。同时发出多个写入请求时会使用队列。队列不能消除对同一特性的重复写入请求。例如,如果同一个描述符被设置为 A 值,紧接着又被设置为 B 值,那么这两个写入请求将按照给定的顺序执行。

注: 目前还无法使用蓝牙规范定义的签名或可靠写入。

只有当该服务处于ServiceDiscovered 状态并属于该服务时,才能写入一个特性。如果其中一个条件不为真,QLowEnergyService::OperationError

注意: 尽管QLowEnergyCharacteristic::properties() 报告了不可写属性,但调用此函数总是试图写入硬件。同样,WriteWithoutResponse 也会发送到硬件,尽管该特性可能只支持WriteWithResponse 。如果硬件返回错误,则会设置CharacteristicWriteError

外设角色

调用的结果是在本地数据库中更新特性值。

如果客户端当前已连接,且已启用该特性的通知或指示,则将发送相应信息。如果设备已启用了该特性的通知或指示,且该设备当前未连接,但与本地设备之间存在绑定,则将在下一次重新连接时发送通知或指示。

如果特性值的长度有限制,而newValue 没有遵守该限制,则行为未指定。

注意: 在外设模式下,mode 参数将被忽略。

另请参阅 QLowEnergyService::characteristicWritten() 和QLowEnergyService::readCharacteristic()。

void QLowEnergyService::writeDescriptor(const QLowEnergyDescriptor &descriptor, const QByteArray &newValue)

写入newValue 作为descriptor 的值。具体语义取决于相关控制器对象所处的角色。

中心角色

调用此函数将导致向远程设备发出写入请求。如果操作成功,则发出descriptorWritten() 信号;否则发出DescriptorWriteError 信号。

针对同一远程设备的所有描述符和特征请求都会被序列化。同时发出多个写入请求时,会使用队列。队列不能消除对同一描述符的重复写入请求。例如,如果同一个描述符被设置为 A 值,紧接着又被设置为 B 值,那么这两个写入请求将按照给定的顺序执行。

只有当该服务处于ServiceDiscovered 状态并属于该服务时,描述符才能被写入。如果其中一个条件不成立,QLowEnergyService::OperationError

外设角色

该值将被写入本地服务数据库。如果newValue 的内容对descriptor 无效,则行为未指定。

另请参阅 descriptorWritten() 和readDescriptor()。

© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.