蓝牙低功耗心率服务器
一个演示如何设置和广播 GATT 服务的示例。该示例演示了如何使用 Qt Bluetooth 与外设(从机)功能相关的低功耗类。
蓝牙低功耗心率服务器是一个命令行应用程序,演示了如何使用 Qt Bluetooth API开发蓝牙GATT服务器的示例。该应用程序涵盖了服务配置、广播以及向客户端通知特征值变化的操作。
该示例使用了以下 Qt 类:
- QLowEnergyAdvertisingData
- QLowEnergyAdvertisingParameters
- QLowEnergyServiceData
- QLowEnergyCharacteristicData
- QLowEnergyDescriptorData
- QLowEnergyController
- QLowEnergyService
该示例实现了一个服务器应用程序,这意味着它没有图形用户界面。为了直观地了解其运行情况,您可以使用“心率游戏”示例,该示例基本上是本应用程序对应的客户端。
检查蓝牙权限
在应用程序创建服务并开始广播之前,我们必须检查该应用程序是否具有使用蓝牙的权限。
auto permissionStatus = app.checkPermission(QBluetoothPermission{});请求蓝牙权限
如果蓝牙授权状态未确定,我们必须请求使用蓝牙的权限。
if(permissionStatus==Qt::PermissionStatus::Undetermined) {
qInfo("Requesting Bluetooth permission ...");
app.requestPermission(QBluetoothPermission{}, [&permissionStatus](constQPermission&permission){
qApp->exit();
permissionStatus=permission.status();
});
// 现在,等待权限请求处理完成。
app.exec();
}设置广告数据和参数
有两个类用于配置广告流程:
- QLowEnergyAdvertisingData 指定要广播的信息
- QLowEnergyAdvertisingParameters 用于配置具体细节,例如设置广播间隔或控制允许连接的设备。
在本示例中,我们直接使用默认参数。
QLowEnergyAdvertisingData 中包含的信息将对当前正在扫描的其他设备可见。这些设备可以利用这些信息来决定是否建立连接。 在本例中,我们包含所提供服务的类型、一个能向人类充分描述本设备的名称,以及设备的发射功率级别。后者对潜在客户端通常很有用,因为它们可以通过将接收到的信号强度与广播的信号强度进行比较,来判断本设备距离有多远。
注意: 广告数据的存储空间 非常有限(总计仅 31 字节),因此诸如设备名称之类的可变长度数据应尽可能简短。
QLowEnergyAdvertisingData advertisingData;
advertisingData.setDiscoverability(QLowEnergyAdvertisingData::DiscoverabilityGeneral);
advertisingData.setIncludePowerLevel(true);
advertisingData.setLocalName("HeartRateServer");
advertisingData.setServices(QList<QBluetoothUuid>() << QBluetoothUuid::ServiceClassUuid::HeartRate);设置服务数据
接下来,我们配置要提供的服务类型。我们采用蓝牙规范中定义的Heart Rate 服务,并采用其最简形式,即仅包含Heart Rate Measurement 特征。该特征必须支持Notify 属性(且仅支持该属性),并需包含Client Characteristic Configuration 描述符,以便客户端注册以接收特征值变化的通知。 我们将初始心率值设为零,因为该值本就无法被读取(客户端获取该值的唯一途径是通过通知)。
QLowEnergyCharacteristicData charData;
charData.setUuid(QBluetoothUuid::CharacteristicType::HeartRateMeasurement);
charData.setValue(QByteArray(2, 0));
charData.setProperties(QLowEnergyCharacteristic::Notify);
const QLowEnergyDescriptorData clientConfig(QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration,
QByteArray(2, 0));
charData.addDescriptor(clientConfig);
QLowEnergyServiceData serviceData;
serviceData.setType(QLowEnergyServiceData::ServiceTypePrimary);
serviceData.setUuid(QBluetoothUuid::ServiceClassUuid::HeartRate);
serviceData.addCharacteristic(charData);广播和监听传入连接
现在所有数据都已配置完毕,我们可以开始广播了。首先,我们在peripheral role 中创建一个QLowEnergyController 对象,并利用它从我们的(静态)QLowEnergyServiceData 中创建一个(动态)QLowEnergyService 对象。然后,我们调用QLowEnergyController::startAdvertising()。 请注意,我们向QLowEnergyAdvertisingData 传递了两次参数:第一个参数作为实际的广告数据,第二个参数作为扫描响应数据。它们本可以传输不同的信息,但在此处我们没有这个需求。 我们还传递了一个通过默认构造创建的QLowEnergyAdvertisingParameters 实例,因为默认的广播参数已能满足我们的需求。如果客户端对广播的服务感兴趣,它现在可以与我们的设备建立连接。当这种情况发生时,设备将停止广播,并发出QLowEnergyController::connected()信号。
注意:当 客户端断开连接时 ,广播不会自动恢复。若要实现自动恢复,需订阅QLowEnergyController::disconnected()信号,并在相应的槽中调用QLowEnergyController::startAdvertising()。
boolerrorOccurred= false;
conststd::unique_ptr<QLowEnergyController>leController(QLowEnergyController::createPeripheral());
autoerrorHandler= [&leController, &errorOccurred](QLowEnergyController::Error errorCode) {
qWarning().noquote().nospace() << errorCode << " occurred: "
<< leController->errorString();
if(errorCode!=QLowEnergyController::RemoteHostClosedError) {
qWarning("Heartrate-server quitting due to the error.");
errorOccurred= true;
QCoreApplication::quit();
}
};
QObject::connect(leController.get(), &QLowEnergyController::errorOccurred,errorHandler);
std::unique_ptr<QLowEnergyService>service(leController->addService(serviceData));
leController->startAdvertising(QLowEnergyAdvertisingParameters(),advertisingData,
advertisingData);
if(errorOccurred)
return-1;提供心率数据
目前进展顺利。但客户端究竟如何获取心率数据呢?这是通过定期更新QLowEnergyService 对象中相应特征的值来实现的,该对象是我们从上文代码片段中的QLowEnergyController 处接收到的。 心率的来源通常是某种传感器,但在本示例中,我们只是随意生成一些数值,并让它们在 60 到 100 之间波动。 以下代码片段中最关键的部分是调用QLowEnergyService::writeCharacteristic 。如果当前有客户端已连接,并且通过向前述的Client Characteristic Configuration 写入数据启用了通知功能,它将收到新值的通知。
QTimer heartbeatTimer;
quint8 currentHeartRate = 60;
enum ValueChange { ValueUp, ValueDown } valueChange = ValueUp;
const auto heartbeatProvider = [&service, ¤tHeartRate, &valueChange]() {
QByteArray value;
value.append(char(0)); // Flags that specify the format of the value.
value.append(char(currentHeartRate)); // Actual value.
QLowEnergyCharacteristic characteristic
= service->characteristic(QBluetoothUuid::CharacteristicType::HeartRateMeasurement);
Q_ASSERT(characteristic.isValid());
service->writeCharacteristic(characteristic, value); // Potentially causes notification.
if (currentHeartRate == 60)
valueChange = ValueUp;
else if (currentHeartRate == 100)
valueChange = ValueDown;
if (valueChange == ValueUp)
++currentHeartRate;
else
--currentHeartRate;
};
QObject::connect(&heartbeatTimer, &QTimer::timeout, heartbeatProvider);
heartbeatTimer.start(1000);© 2026 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.