低功耗心率蓝牙服务器

本例演示了如何设置和广播GATT服务。该示例演示了有关外围(从属)功能的相关Qt蓝牙低功耗类的使用。

蓝牙低功耗心率服务器是一个命令行应用程序,展示了如何使用Qt蓝牙API开发蓝牙GATT服务器。应用程序包括设置服务、广播它以及通知客户端关于特征值更改的功能。

该示例使用了以下Qt类

该示例实现了一个服务器应用程序,这意味着它没有图形用户界面。为了可视化它正在做什么,您可以使用心率游戏示例,该示例基本上是这个应用程序的客户端对应程序。

检查蓝牙权限

在应用程序可以创建服务和开始广播之前,我们需要检查应用程序是否有使用蓝牙的权限。

auto permissionStatus = app.checkPermission(QBluetoothPermission{});

请求蓝牙权限

如果蓝牙授权状态为未知,我们必须请求使用蓝牙的权限。

if (permissionStatus == Qt::PermissionStatus::Undetermined) {
    qInfo("Requesting Bluetooth permission ...");
    app.requestPermission(QBluetoothPermission{}, [&permissionStatus](const QPermission &permission){
        qApp->exit();
        permissionStatus = permission.status();
    });
    // Now, wait for permission request to resolve.
    app.exec();
}

设置广告数据和参数

使用以下两个类来配置广告过程

在我们的示例中,我们简单地使用了默认参数。

QLowEnergyAdvertisingData中包含的信息将对当前正在扫描的其他设备可见。它们可以使用它来决定是否要建立连接。在我们的示例中,我们包括我们提供的服务类型、一个对人类来说充分描述我们的设备名称,以及设备的发射功率。后者对于潜在客户通常很有用,因为他们可以通过比较接收信号的强度与广告的信号强度来判断我们的设备有多远。

注意:广告数据的空间非常有限(总共只有31字节),因此变量长度数据,例如设备名称,应该尽可能简短。

QLowEnergyAdvertisingData advertisingData;
advertisingData.setDiscoverability(QLowEnergyAdvertisingData::DiscoverabilityGeneral);
advertisingData.setIncludePowerLevel(true);
advertisingData.setLocalName("HeartRateServer");
advertisingData.setServices(QList<QBluetoothUuid>() << QBluetoothUuid::ServiceClassUuid::HeartRate);

设置服务数据

接下来,我们配置我们想要提供的服务类型。我们使用在蓝牙规范中定义的心率服务,以最简形式使用,即只包含心率测量特性。这个特性必须支持通知属性(以及其他属性),它还需要有一个客户端特征配置描述符,这使得客户端能够注册以接收特性值变化的通告。我们将初始心率值设置为零,因为无论如何都读取不到(客户端获取值的唯一方法是通知)。

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);

广播和监听传入连接

所有数据都设置好了,我们可以开始广播。首先,在外围角色中创建QLowEnergyController对象,并使用它从我们的(静态)QLowEnergyServiceData创建一个(动态)QLowEnergyService对象。然后调用QLowEnergyController::startAdvertising()。注意,我们交送了两次我们的QLowEnergyAdvertisingData:第一个参数作为实际广播数据,第二个参数作为扫描响应数据。它们可以传输不同的信息,但在这里我们没有这个需求。我们还传递了一个默认构造的QLowEnergyAdvertisingParameters实例,因为默认的广播参数对我们来说已经很好了。如果客户端对广播的服务感兴趣,现在它可以连接到我们的设备。当这发生时,设备停止广播,并发出QLowEnergyController::connected()信号。

注意:当客户端断开连接时,不会自动恢复广播。如果您想让它发生,您需要连接到QLowEnergyController::disconnected()信号,并在各自的槽中调用QLowEnergyController::startAdvertising()。

bool errorOccurred = false;
const std::unique_ptr<QLowEnergyController> leController(QLowEnergyController::createPeripheral());
auto errorHandler = [&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;

提供心率数据

到目前为止,一切都按预期进行。但是客户端如何实际获取心率数据呢?这通过定期更新我们在代码片段中从QLowEnergyController收到的QLowEnergyService对象中相应特性的值来实现。心率的数据源通常是某种类型的传感器,但在我们的例子中,我们只是让值在60和100之间振荡。接下来的代码片段中最重要的部分是调用QLowEnergyService::writeCharacteristic。如果客户端当前已连接并且通过写入上述Client Characteristic Configuration已启用通知,它将接收到新的值。

QTimer heartbeatTimer;
quint8 currentHeartRate = 60;
enum ValueChange { ValueUp, ValueDown } valueChange = ValueUp;
const auto heartbeatProvider = [&service, &currentHeartRate, &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);

例项目 @ code.qt.io

© 2024 Qt公司有限公司。此处包含的文档贡献是各自所有者的版权。此处提供的文档是根据由自由软件基金会发布的GNU自由文档许可证版本1.3的条款许可的。Qt和相应的标志是芬兰以及其他国家的Qt公司有限公司的商标。所有其他商标是它们各自所有者的财产。