blob: 1275aa65567a662beee26d4b564c2bdbfe6ad926 [file] [log] [blame]
/****************************************************************************
**
** 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 <QtBluetooth/qlowenergyadvertisingdata.h>
#include <QtBluetooth/qlowenergyadvertisingparameters.h>
#include <QtBluetooth/qlowenergyconnectionparameters.h>
#include <QtBluetooth/qlowenergycontroller.h>
#include <QtBluetooth/qlowenergycharacteristicdata.h>
#include <QtBluetooth/qlowenergydescriptordata.h>
#include <QtBluetooth/qlowenergyservicedata.h>
#include <QtCore/qcoreapplication.h>
#include <QtCore/qendian.h>
#include <QtCore/qhash.h>
#include <QtCore/qscopedpointer.h>
#include <QtCore/qsharedpointer.h>
#include <QtCore/qvector.h>
static QByteArray deviceName() { return "Qt GATT server"; }
static QScopedPointer<QLowEnergyController> leController;
typedef QSharedPointer<QLowEnergyService> ServicePtr;
static QHash<QBluetoothUuid, ServicePtr> 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<quint16>(1 << 2, reinterpret_cast<uchar *>(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<quint16>(128, reinterpret_cast<uchar *>(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);
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);
charData.setUuid(QBluetoothUuid(quint16(0x5004)));
charData.setProperties(QLowEnergyCharacteristic::Read | QLowEnergyCharacteristic::WriteSigned);
charData.setDescriptors(QList<QLowEnergyDescriptorData>());
charData.setValue("initial");
serviceData.addCharacteristic(charData);
addService(serviceData);
// service with full 128 bit custom uuids
QLowEnergyServiceData serviceData128;
serviceData128.setUuid(QBluetoothUuid(QString("c47774c7-f237-4523-8968-e4ae75431daf")));
serviceData128.setType(QLowEnergyServiceData::ServiceTypePrimary);
QLowEnergyCharacteristicData charData128;
charData128.setUuid(QBluetoothUuid(QString("c0ad61b1-79e7-42f9-ace0-0a9aa0d0a4f8")));
charData128.setProperties(QLowEnergyCharacteristic::Read);
charData128.setValue(QByteArray(15, 'a'));
serviceData128.addCharacteristic(charData128);
addService(serviceData128);
}
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();
}