/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtBluetooth module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include static QByteArray deviceName() { return "Qt GATT server"; } static QScopedPointer leController; typedef QSharedPointer ServicePtr; static QHash services; static int descriptorWriteCount = 0; static int disconnectCount = 0; static QBluetoothAddress remoteDevice; void addService(const QLowEnergyServiceData &serviceData) { const ServicePtr service(leController->addService(serviceData)); Q_ASSERT(service); services.insert(service->serviceUuid(), service); } void addRunningSpeedService() { QLowEnergyServiceData serviceData; serviceData.setUuid(QBluetoothUuid::RunningSpeedAndCadence); serviceData.setType(QLowEnergyServiceData::ServiceTypePrimary); QLowEnergyDescriptorData desc; desc.setUuid(QBluetoothUuid::ClientCharacteristicConfiguration); desc.setValue(QByteArray(2, 0)); // Default: No indication, no notification. QLowEnergyCharacteristicData charData; charData.setUuid(QBluetoothUuid::RSCMeasurement); charData.addDescriptor(desc); charData.setProperties(QLowEnergyCharacteristic::Notify); QByteArray value(4, 0); value[0] = 1 << 2; // "Running", no optional fields. charData.setValue(value); serviceData.addCharacteristic(charData); charData = QLowEnergyCharacteristicData(); charData.setUuid(QBluetoothUuid::RSCFeature); charData.setProperties(QLowEnergyCharacteristic::Read); value = QByteArray(2, 0); qToLittleEndian(1 << 2, reinterpret_cast(value.data())); charData.setValue(value); serviceData.addCharacteristic(charData); addService(serviceData); } void addGenericAccessService() { QLowEnergyServiceData serviceData; serviceData.setUuid(QBluetoothUuid::GenericAccess); serviceData.setType(QLowEnergyServiceData::ServiceTypePrimary); QLowEnergyCharacteristicData charData; charData.setUuid(QBluetoothUuid::DeviceName); charData.setProperties(QLowEnergyCharacteristic::Read | QLowEnergyCharacteristic::Write); charData.setValue(deviceName()); serviceData.addCharacteristic(charData); charData = QLowEnergyCharacteristicData(); charData.setUuid(QBluetoothUuid::Appearance); charData.setProperties(QLowEnergyCharacteristic::Read); QByteArray value(2, 0); qToLittleEndian(128, reinterpret_cast(value.data())); // Generic computer. charData.setValue(value); serviceData.addCharacteristic(charData); serviceData.addIncludedService(services.value(QBluetoothUuid::RunningSpeedAndCadence).data()); addService(serviceData); } void addCustomService() { QLowEnergyServiceData serviceData; serviceData.setUuid(QBluetoothUuid(quint16(0x2000))); serviceData.setType(QLowEnergyServiceData::ServiceTypePrimary); QLowEnergyCharacteristicData charData; charData.setUuid(QBluetoothUuid(quint16(0x5000))); // Made up. charData.setProperties(QLowEnergyCharacteristic::Read); charData.setValue(QByteArray(1024, 'x')); // Long value to test "Read Blob". serviceData.addCharacteristic(charData); charData.setUuid(QBluetoothUuid(quint16(0x5001))); charData.setProperties(QLowEnergyCharacteristic::Read | QLowEnergyCharacteristic::WriteSigned); charData.setReadConstraints(QBluetooth::AttAuthorizationRequired); // To test read failure. serviceData.addCharacteristic(charData); charData.setValue("something"); charData.setUuid(QBluetoothUuid(quint16(0x5002))); charData.setProperties(QLowEnergyCharacteristic::Read | QLowEnergyCharacteristic::Indicate); charData.setReadConstraints(QBluetooth::AttAccessConstraints()); const QLowEnergyDescriptorData desc(QBluetoothUuid::ClientCharacteristicConfiguration, QByteArray(2, 0)); charData.addDescriptor(desc); serviceData.addCharacteristic(charData); charData.setUuid(QBluetoothUuid(quint16(0x5003))); charData.setProperties(QLowEnergyCharacteristic::Read | QLowEnergyCharacteristic::Notify); serviceData.addCharacteristic(charData); addService(serviceData); } void startAdvertising() { QLowEnergyAdvertisingParameters params; params.setMode(QLowEnergyAdvertisingParameters::AdvInd); QLowEnergyAdvertisingData data; data.setDiscoverability(QLowEnergyAdvertisingData::DiscoverabilityLimited); data.setServices(services.keys()); data.setIncludePowerLevel(true); data.setLocalName(deviceName()); leController->startAdvertising(params, data); } int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); leController.reset(QLowEnergyController::createPeripheral()); addRunningSpeedService(); addGenericAccessService(); addCustomService(); startAdvertising(); const ServicePtr customService = services.value(QBluetoothUuid(quint16(0x2000))); Q_ASSERT(customService); const auto stateChangedHandler = [customService]() { switch (leController->state()) { case QLowEnergyController::ConnectedState: remoteDevice = leController->remoteAddress(); break; case QLowEnergyController::UnconnectedState: { if (++disconnectCount == 2) { qApp->quit(); break; } Q_ASSERT(disconnectCount == 1); const QLowEnergyCharacteristic indicatableChar = customService->characteristic(QBluetoothUuid(quint16(0x5002))); Q_ASSERT(indicatableChar.isValid()); customService->writeCharacteristic(indicatableChar, "indicated2"); Q_ASSERT(indicatableChar.value() == "indicated2"); const QLowEnergyCharacteristic notifiableChar = customService->characteristic(QBluetoothUuid(quint16(0x5003))); Q_ASSERT(notifiableChar.isValid()); customService->writeCharacteristic(notifiableChar, "notified2"); Q_ASSERT(notifiableChar.value() == "notified2"); startAdvertising(); break; } default: break; } }; QObject::connect(leController.data(), &QLowEnergyController::stateChanged, stateChangedHandler); const auto descriptorWriteHandler = [customService]() { if (++descriptorWriteCount != 2) return; const QLowEnergyCharacteristic indicatableChar = customService->characteristic(QBluetoothUuid(quint16(0x5002))); Q_ASSERT(indicatableChar.isValid()); customService->writeCharacteristic(indicatableChar, "indicated"); Q_ASSERT(indicatableChar.value() == "indicated"); const QLowEnergyCharacteristic notifiableChar = customService->characteristic(QBluetoothUuid(quint16(0x5003))); Q_ASSERT(notifiableChar.isValid()); customService->writeCharacteristic(notifiableChar, "notified"); Q_ASSERT(notifiableChar.value() == "notified"); QLowEnergyConnectionParameters connParams; connParams.setIntervalRange(30, 62.5); connParams.setLatency(5); connParams.setSupervisionTimeout(5500); leController->requestConnectionUpdate(connParams); }; QObject::connect(customService.data(), &QLowEnergyService::descriptorWritten, descriptorWriteHandler); return app.exec(); }