// Copyright (C) 2017 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #include #include #include #include #include #include #include #include #include #if defined(Q_OS_ANDROID) || defined(Q_OS_DARWIN) #include #else #include #endif #include #include #include #if QT_CONFIG(permissions) #include #endif #include int main(int argc, char *argv[]) { // QLoggingCategory::setFilterRules(QStringLiteral("qt.bluetooth* = true")); #if defined(Q_OS_ANDROID) || defined(Q_OS_DARWIN) QGuiApplication app(argc, argv); #else QCoreApplication app(argc, argv); #endif #if QT_CONFIG(permissions) //! [Check Bluetooth Permission] auto permissionStatus = app.checkPermission(QBluetoothPermission{}); //! [Check Bluetooth Permission] //! [Request Bluetooth Permission] 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(); } //! [Request Bluetooth Permission] if (permissionStatus == Qt::PermissionStatus::Denied) { // Either explicitly denied by a user, or Bluetooth is off. qWarning("This application cannot use Bluetooth, the permission was denied"); return -1; } #endif //! [Advertising Data] QLowEnergyAdvertisingData advertisingData; advertisingData.setDiscoverability(QLowEnergyAdvertisingData::DiscoverabilityGeneral); advertisingData.setIncludePowerLevel(true); advertisingData.setLocalName("HeartRateServer"); advertisingData.setServices(QList() << QBluetoothUuid::ServiceClassUuid::HeartRate); //! [Advertising Data] //! [Service Data] 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); //! [Service Data] //! [Start Advertising] bool errorOccurred = false; const std::unique_ptr 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 service(leController->addService(serviceData)); leController->startAdvertising(QLowEnergyAdvertisingParameters(), advertisingData, advertisingData); if (errorOccurred) return -1; //! [Start Advertising] //! [Provide Heartbeat] 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); //! [Provide Heartbeat] auto reconnect = [&leController, advertisingData, &service, serviceData]() { service.reset(leController->addService(serviceData)); if (service) { leController->startAdvertising(QLowEnergyAdvertisingParameters(), advertisingData, advertisingData); } }; QObject::connect(leController.get(), &QLowEnergyController::disconnected, reconnect); const int retval = QCoreApplication::exec(); return errorOccurred ? -1 : retval; }