summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorJuha Vuolle <juha.vuolle@insta.fi>2022-11-21 11:44:43 +0200
committerJuha Vuolle <juha.vuolle@insta.fi>2022-12-07 11:47:43 +0200
commita79a8de70853f5acc9c8d9a9b10760c2418b31ac (patch)
treee79f1d5b1f8a7d5405fbc9a957904a9c5024222d /tests
parent4317cc1aca2097444f5ab139da4a0b4ae1b0d43f (diff)
Improve bluetooth manual test support
This is a preparatory commit for adding the Bluez DBus peripheral role support. Aim is to improve the manual test coverage: add large descriptor writes, ability to set advertising data multiple times, add more services, characteristics etc. Also adjust the large attribute size so that it works with bluez dbus. Task-number: QTBUG-107510 Change-Id: I5f22880349606feb4f61032b78c62b990329eb69 Reviewed-by: Ivan Solovev <ivan.solovev@qt.io> Reviewed-by: Alex Blasche <alexander.blasche@qt.io>
Diffstat (limited to 'tests')
-rw-r--r--tests/bluetoothtestdevice/bluetoothtestdevice.cpp34
-rw-r--r--tests/bttestui/btlocaldevice.cpp345
-rw-r--r--tests/bttestui/btlocaldevice.h19
-rw-r--r--tests/bttestui/main.qml40
-rw-r--r--tests/manual/qlowenergycontroller/tst_qlowenergycontroller_device.cpp85
5 files changed, 403 insertions, 120 deletions
diff --git a/tests/bluetoothtestdevice/bluetoothtestdevice.cpp b/tests/bluetoothtestdevice/bluetoothtestdevice.cpp
index 62daa942..98da879d 100644
--- a/tests/bluetoothtestdevice/bluetoothtestdevice.cpp
+++ b/tests/bluetoothtestdevice/bluetoothtestdevice.cpp
@@ -21,8 +21,10 @@
#include <thread>
-static const QLatin1String largeCharacteristicServiceUuid("1f85e37c-ac16-11eb-ae5c-93d3a763feed");
-static const QLatin1String largeCharacteristicCharUuid("40e4f68e-ac16-11eb-9956-cfe55a8c370c");
+static const QLatin1String largeAttributeServiceUuid("1f85e37c-ac16-11eb-ae5c-93d3a763feed");
+static const QLatin1String largeAttributeCharUuid("40e4f68e-ac16-11eb-9956-cfe55a8c370c");
+static const QLatin1String largeAttributeDescUuid("44e4f68e-ac16-11eb-9956-cfe55a8c370c");
+static constexpr qsizetype largeAttributeSize{508}; // Size for char and desc values
static const QLatin1String platformIdentifierServiceUuid("4a92cb7f-5031-4a09-8304-3e89413f458d");
static const QLatin1String platformIdentifierCharUuid("6b0ecf7c-5f09-4c87-aaab-bb49d5d383aa");
@@ -119,26 +121,36 @@ int main(int argc, char *argv[])
serviceDefinitions << serviceData;
}
{
- // large characteristic service
+ // large attribute service
//
- // This is just a service offering a 512 bytes large characteristic which can
- // be read and written. It is used to test reading and writing at MTU sizes smaller
- // than the size of the characteristic.
+ // This is a service offering a large characteristic and descriptor which can
+ // be read and written to
QLowEnergyServiceData serviceData;
serviceData.setType(QLowEnergyServiceData::ServiceTypePrimary);
- serviceData.setUuid(QBluetoothUuid(largeCharacteristicServiceUuid));
+ serviceData.setUuid(QBluetoothUuid(largeAttributeServiceUuid));
QLowEnergyCharacteristicData charData;
- charData.setUuid(QBluetoothUuid(largeCharacteristicCharUuid));
- QByteArray initialValue(512, 0);
+ charData.setUuid(QBluetoothUuid(largeAttributeCharUuid));
+ QByteArray initialValue(largeAttributeSize, 0);
initialValue[0] = 0x0b;
charData.setValue(initialValue);
- charData.setValueLength(512, 512);
+ charData.setValueLength(largeAttributeSize, largeAttributeSize);
charData.setProperties(QLowEnergyCharacteristic::PropertyType::Read
| QLowEnergyCharacteristic::PropertyType::Write);
- serviceData.addCharacteristic(charData);
+ QByteArray descInitialValue(largeAttributeSize, 0);
+ descInitialValue[0] = 0xdd;
+ QLowEnergyDescriptorData descData(
+ QBluetoothUuid{largeAttributeDescUuid},
+ descInitialValue
+ );
+ descData.setWritePermissions(true);
+ descData.setReadPermissions(true);
+
+ charData.addDescriptor(descData);
+
+ serviceData.addCharacteristic(charData);
serviceDefinitions << serviceData;
}
diff --git a/tests/bttestui/btlocaldevice.cpp b/tests/bttestui/btlocaldevice.cpp
index 5f684b16..9306c482 100644
--- a/tests/bttestui/btlocaldevice.cpp
+++ b/tests/bttestui/btlocaldevice.cpp
@@ -22,16 +22,28 @@
//#define SOCKET_PROTOCOL QBluetoothServiceInfo::L2capProtocol
using namespace Qt::Literals::StringLiterals;
-static const auto leServiceUuid = "1ff5e37c-ac16-11eb-ae5c-93d3a763feed"_L1;
-static const auto leCharacteristicUuid = "2ff4f68e-ac16-11eb-9956-cfe55a8ccafe"_L1;
+// Main service used for testing read/write/notify
+static constexpr auto leServiceUuid{"10f5e37c-ac16-11eb-ae5c-93d3a763feed"_L1};
+static constexpr auto leCharUuid1{"11f4f68e-ac16-11eb-9956-cfe55a8ccafe"_L1};
+static constexpr auto leCharUuid2{"12f4f68e-ac16-11eb-9956-cfe55a8ccafe"_L1};
+// Service for testing the included services
+static constexpr auto leSecondServiceUuid{"20f5e37c-ac16-11eb-ae5c-93d3a763feed"_L1};
+static constexpr auto leSecondServiceCharUuid1{"21f4f68e-ac16-11eb-9956-cfe55a8ccafe"_L1};
+// Service for testing the secondary service and other miscellaneous
+static constexpr auto leThirdServiceUuid{"30f5e37c-ac16-11eb-ae5c-93d3a763feed"_L1};
+static constexpr auto leThirdServiceCharUuid1{"31f4f68e-ac16-11eb-9956-cfe55a8ccafe"_L1};
+
// Used for finding a matching LE peripheral device. Typically the default BtTestUi is ok
// when running against macOS/iOS/Linux peripheral, but with Android this needs to be adjusted
// to device's name. We can't use bluetooth address for matching as the public address of the
// peripheral may change
static const auto leRemotePeriphreralDeviceName = "BtTestUi"_L1;
static const qsizetype leCharacteristicSize = 4; // Set to 1...512 bytes
-static auto leCharacteristicValue = QByteArray{leCharacteristicSize, 0};
-static quint8 leValueUpdate = 1;
+static QByteArray leCharacteristicValue = QByteArray{leCharacteristicSize, 1};
+static QByteArray leDescriptorValue = "a descriptor value"_ba;
+static auto leSecondCharacteristicValue = QByteArray{leCharacteristicSize, 2};
+static quint8 leCharacteristicValueUpdate = 1;
+static char leDescriptorValueUpdate = 'b';
// String tables to shorten the enum strings to fit the screen estate.
// The values in the tables must be in same order as the corresponding enums
@@ -810,7 +822,8 @@ void BtLocalDevice::dumpErrors()
printError("LE Central"_L1, leCentralController.get());
printError("LE Central Service"_L1, leCentralService.get());
printError("LE Peripheral"_L1, lePeripheralController.get());
- printError("LE Peripheral Service"_L1, lePeripheralService.get());
+ if (!lePeripheralServices.isEmpty())
+ printError("LE Peripheral Service"_L1, lePeripheralServices[0].get());
printError("Socket"_L1, socket);
printError("Server"_L1, server);
}
@@ -924,85 +937,155 @@ void BtLocalDevice::peripheralCreate()
});
}
-void BtLocalDevice::peripheralStartAdvertising()
+void BtLocalDevice::peripheralAddServices()
{
- qDebug() << "######" << "LE start advertising";
+ qDebug() << "######" << "LE add services";
if (!lePeripheralController) {
qDebug() << "Create peripheral first";
return;
}
+ if (lePeripheralServiceData.isEmpty()) {
+ // Create service data
+ {
+ QLowEnergyServiceData sd;
+ sd.setType(QLowEnergyServiceData::ServiceTypePrimary);
+ sd.setUuid(QBluetoothUuid(leServiceUuid));
+
+ QLowEnergyCharacteristicData charData;
+ charData.setUuid(QBluetoothUuid(leCharUuid1));
+ charData.setValue(leCharacteristicValue);
+ charData.setValueLength(leCharacteristicSize, leCharacteristicSize);
+ charData.setProperties(QLowEnergyCharacteristic::PropertyType::Read
+ | QLowEnergyCharacteristic::PropertyType::Write
+ | QLowEnergyCharacteristic::PropertyType::Notify);
+
+ const QLowEnergyDescriptorData clientConfig(
+ QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration,
+ QLowEnergyCharacteristic::CCCDDisable);
+ charData.addDescriptor(clientConfig);
+
+ const QLowEnergyDescriptorData userDescription(
+ QBluetoothUuid::DescriptorType::CharacteristicUserDescription,
+ leDescriptorValue);
+ charData.addDescriptor(userDescription);
+ sd.addCharacteristic(charData);
+
+ // Set another characteristic without notifications
+ QLowEnergyCharacteristicData secondCharData;
+ secondCharData.setUuid(QBluetoothUuid(leCharUuid2));
+ secondCharData.setValue(leSecondCharacteristicValue);
+ secondCharData.setValueLength(leCharacteristicSize, leCharacteristicSize);
+ secondCharData.setProperties(QLowEnergyCharacteristic::PropertyType::Read
+ | QLowEnergyCharacteristic::PropertyType::Write);
+ sd.addCharacteristic(secondCharData);
+ lePeripheralServiceData << sd;
+ }
+ {
+ QLowEnergyServiceData sd;
+ sd.setType(QLowEnergyServiceData::ServiceTypePrimary);
+ sd.setUuid(QBluetoothUuid(leSecondServiceUuid));
+
+ QLowEnergyCharacteristicData charData;
+ charData.setUuid(QBluetoothUuid(leSecondServiceCharUuid1));
+ charData.setValue("second service char"_ba);
+ charData.setProperties(QLowEnergyCharacteristic::PropertyType::Read);
+ sd.addCharacteristic(charData);
+ lePeripheralServiceData << sd;
+ }
+ {
+ QLowEnergyServiceData sd;
+ sd.setType(QLowEnergyServiceData::ServiceTypeSecondary);
+ sd.setUuid(QBluetoothUuid(leThirdServiceUuid));
+
+ QLowEnergyCharacteristicData charData;
+ charData.setUuid(QBluetoothUuid(leThirdServiceCharUuid1));
+ charData.setValue("third service char"_ba);
+ charData.setProperties(QLowEnergyCharacteristic::PropertyType::Read);
+ sd.addCharacteristic(charData);
+ lePeripheralServiceData << sd;
+ }
+ }
- if (!leServiceData.isValid()) {
- // Create service data if this is the first advertisement
- leServiceData.setType(QLowEnergyServiceData::ServiceTypePrimary);
- leServiceData.setUuid(QBluetoothUuid(leServiceUuid));
-
- QLowEnergyCharacteristicData charData;
- charData.setUuid(QBluetoothUuid(leCharacteristicUuid));
- charData.setValue(leCharacteristicValue);
- charData.setValueLength(leCharacteristicSize, leCharacteristicSize);
- charData.setProperties(QLowEnergyCharacteristic::PropertyType::Read
- | QLowEnergyCharacteristic::PropertyType::Write
- | QLowEnergyCharacteristic::PropertyType::Notify);
-
- const QLowEnergyDescriptorData clientConfig(
- QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration,
- QLowEnergyCharacteristic::CCCDDisable);
- charData.addDescriptor(clientConfig);
- leServiceData.addCharacteristic(charData);
-
- // Create advertisement data
- leAdvertisingData.setDiscoverability(QLowEnergyAdvertisingData::DiscoverabilityGeneral);
- leAdvertisingData.setIncludePowerLevel(true);
- leAdvertisingData.setLocalName(leRemotePeriphreralDeviceName);
-
- leAdvertisingData.setManufacturerData(0xCAFE, "maker");
- // Here we use short unrelated UUID so we can fit both service UUID and manufacturer data
- // into the advertisement. This is for testing purposes
- leAdvertisingData.setServices(
- {QBluetoothUuid(QBluetoothUuid::ServiceClassUuid::AlertNotificationService)});
- // Results in too big advertisement data, can be useful for testing such scenario
- // leAdvertisingData.setServices(QList{leServiceData.uuid()});
+ Q_ASSERT(lePeripheralServiceData.size() == 3);
+ // Free previous services if any
+ lePeripheralServices.clear();
+ // Add first service, and then set the first service as the included service for the second
+ auto service = lePeripheralController->addService(lePeripheralServiceData[0]);
+ if (service) {
+ lePeripheralServiceData[1].setIncludedServices({service});
+ // Then add the services to controller
+ lePeripheralServices.emplaceBack(service);
+ lePeripheralServices.emplaceBack(
+ lePeripheralController->addService(lePeripheralServiceData[1]));
+ lePeripheralServices.emplaceBack(
+ lePeripheralController->addService(lePeripheralServiceData[2]));
}
- // Add/create the service
- lePeripheralService.reset(lePeripheralController->addService(leServiceData));
emit leChanged();
- if (!lePeripheralService) {
+
+ if (lePeripheralServices.isEmpty()) {
qDebug() << "Peripheral service creation failed";
return;
}
- QObject::connect(lePeripheralService.get(), &QLowEnergyService::characteristicWritten,
+
+ QObject::connect(lePeripheralServices[0].get(), &QLowEnergyService::characteristicWritten,
[](const QLowEnergyCharacteristic&, const QByteArray& value){
qDebug() << "LE peripheral service characteristic value written" << value;
});
- QObject::connect(lePeripheralService.get(), &QLowEnergyService::characteristicRead,
+ QObject::connect(lePeripheralServices[0].get(), &QLowEnergyService::characteristicRead,
[](const QLowEnergyCharacteristic&, const QByteArray& value){
qDebug() << "LE peripheral service characteristic value read" << value;
});
- QObject::connect(lePeripheralService.get(), &QLowEnergyService::characteristicChanged,
+ QObject::connect(lePeripheralServices[0].get(), &QLowEnergyService::characteristicChanged,
[](const QLowEnergyCharacteristic&, const QByteArray& value){
qDebug() << "LE peripheral service characteristic value changed" << value;
});
- QObject::connect(lePeripheralService.get(), &QLowEnergyService::errorOccurred,
+ QObject::connect(lePeripheralServices[0].get(), &QLowEnergyService::descriptorRead,
+ [](const QLowEnergyDescriptor&, const QByteArray& value){
+ qDebug() << "LE peripheral service descriptor value read" << value;
+ });
+ QObject::connect(lePeripheralServices[0].get(), &QLowEnergyService::descriptorWritten,
+ [](const QLowEnergyDescriptor&, const QByteArray& value){
+ qDebug() << "LE peripheral service descriptor value written" << value;
+ });
+ QObject::connect(lePeripheralServices[0].get(), &QLowEnergyService::errorOccurred,
[this](QLowEnergyService::ServiceError error){
qDebug() << "LE peripheral service errorOccurred:" << error;
emit leChanged();
});
- QObject::connect(lePeripheralService.get(), &QLowEnergyService::stateChanged,
+ QObject::connect(lePeripheralServices[0].get(), &QLowEnergyService::stateChanged,
[this](QLowEnergyService::ServiceState state){
qDebug() << "LE peripheral service state changed:" << state;
emit leChanged();
});
+}
+
+void BtLocalDevice::peripheralStartAdvertising()
+{
+ qDebug() << "######" << "LE start advertising";
+ if (!lePeripheralController) {
+ qDebug() << "Create peripheral first";
+ return;
+ }
- // Start advertising
- QLowEnergyAdvertisingParameters advertisingParameters;
- advertisingParameters.setInterval(30, 60);
+ if (leAdvertisingData.localName().isEmpty()) {
+ // Create advertisement data
+ leAdvertisingData.setDiscoverability(QLowEnergyAdvertisingData::DiscoverabilityGeneral);
+ leAdvertisingData.setIncludePowerLevel(true);
+ leAdvertisingData.setLocalName(leRemotePeriphreralDeviceName);
- lePeripheralController->startAdvertising(advertisingParameters,
+ leAdvertisingData.setManufacturerData(0xCAFE, "maker");
+ // Here we use short unrelated UUID so we can fit both service UUID and manufacturer data
+ // into the advertisement. This is for testing purposes
+ leAdvertisingData.setServices(
+ {QBluetoothUuid(QBluetoothUuid::ServiceClassUuid::AlertNotificationService)});
+ // May result in too big advertisement data, can be useful for testing such scenario
+ // leAdvertisingData.setServices({QBluetoothUuid(leServiceUuid)});
+ }
+ // Start advertising. For testing the advertising can be started without valid services
+ qDebug() << "Starting advertising, services are valid:" << !lePeripheralServiceData.isEmpty();
+ lePeripheralController->startAdvertising(QLowEnergyAdvertisingParameters{},
leAdvertisingData, leAdvertisingData);
- //lePeripheralController->startAdvertising(QLowEnergyAdvertisingParameters{},
- // leAdvertisingData, leAdvertisingData);
}
void BtLocalDevice::peripheralStopAdvertising()
@@ -1015,20 +1098,20 @@ void BtLocalDevice::peripheralStopAdvertising()
lePeripheralController->stopAdvertising();
}
-void BtLocalDevice::centralWrite()
+void BtLocalDevice::centralCharacteristicWrite()
{
- qDebug() << "######" << "LE central write";
+ qDebug() << "######" << "LE central write characteristic";
if (!leCentralController || !leCentralService) {
qDebug() << "Central or central service does not exist";
return;
}
- // Update value at the beginning and end so we can check whole data is sent in large writes.
- // Value is offset'd with 100 to easily see which end did the write when testing
- leValueUpdate += 1;
- leCharacteristicValue[0] = leValueUpdate + 100;
- leCharacteristicValue[leCharacteristicSize - 1] = leValueUpdate + 100;
- auto characteristic = leCentralService->characteristic(QBluetoothUuid(leCharacteristicUuid));
+ auto characteristic = leCentralService->characteristic(QBluetoothUuid(leCharUuid1));
if (characteristic.isValid()) {
+ // Update value at the beginning and end so we can check whole data is sent in large writes
+ // Value is offset'd with 100 to easily see which end did the write when testing
+ leCharacteristicValueUpdate += 1;
+ leCharacteristicValue[0] = leCharacteristicValueUpdate + 100;
+ leCharacteristicValue[leCharacteristicSize - 1] = leCharacteristicValueUpdate + 100;
qDebug() << " Central writes value:" << leCharacteristicValue;
leCentralService->writeCharacteristic(characteristic, leCharacteristicValue);
} else {
@@ -1036,14 +1119,14 @@ void BtLocalDevice::centralWrite()
}
}
-void BtLocalDevice::centralRead()
+void BtLocalDevice::centralCharacteristicRead()
{
- qDebug() << "######" << "LE central read";
+ qDebug() << "######" << "LE central read characteristic";
if (!leCentralController || !leCentralService) {
qDebug() << "Central or central service does not exist";
return;
}
- auto characteristic = leCentralService->characteristic(QBluetoothUuid(leCharacteristicUuid));
+ auto characteristic = leCentralService->characteristic(QBluetoothUuid(leCharUuid1));
if (characteristic.isValid()) {
qDebug() << " Value before issuing read():" << characteristic.value();
leCentralService->readCharacteristic(characteristic);
@@ -1052,39 +1135,111 @@ void BtLocalDevice::centralRead()
}
}
-void BtLocalDevice::peripheralWrite()
+void BtLocalDevice::centralDescriptorWrite()
{
- qDebug() << "######" << "LE peripheral write";
- if (!lePeripheralController || !lePeripheralService) {
+ qDebug() << "######" << "LE central write descriptor";
+ if (!leCentralController || !leCentralService) {
+ qDebug() << "Central or central service does not exist";
+ return;
+ }
+ auto descriptor = leCentralService->characteristic(QBluetoothUuid(leCharUuid1))
+ .descriptor(QBluetoothUuid::DescriptorType::CharacteristicUserDescription);
+
+ if (descriptor.isValid()) {
+ leDescriptorValue[0] = leDescriptorValueUpdate++;
+ qDebug() << " Central writes value: " << leDescriptorValue;
+ leCentralService->writeDescriptor(descriptor, leDescriptorValue);
+ } else {
+ qDebug() << "Descriptor was invalid";
+ }
+}
+
+void BtLocalDevice::centralDescriptorRead()
+{
+ qDebug() << "######" << "LE central read descriptor";
+ if (!leCentralController || !leCentralService) {
+ qDebug() << "Central or central service does not exist";
+ return;
+ }
+ auto descriptor = leCentralService->characteristic(QBluetoothUuid(leCharUuid1))
+ .descriptor(QBluetoothUuid::DescriptorType::CharacteristicUserDescription);
+ if (descriptor.isValid()) {
+ qDebug() << " Value before issuing read():" << descriptor.value();
+ leCentralService->readDescriptor(descriptor);
+ } else {
+ qDebug() << "Descriptor was invalid";
+ }
+}
+
+
+void BtLocalDevice::peripheralCharacteristicWrite()
+{
+ qDebug() << "######" << "LE peripheral write characteristic";
+ if (!lePeripheralController || lePeripheralServices.isEmpty()) {
qDebug() << "Peripheral or peripheral service does not exist";
return;
}
- // Update value at the beginning and end so we can check whole data is sent in large writes
- leCharacteristicValue[0] = ++leValueUpdate;
- leCharacteristicValue[leCharacteristicSize - 1] = leValueUpdate;
- auto characteristic = lePeripheralService->characteristic(QBluetoothUuid(leCharacteristicUuid));
+
+ auto characteristic = lePeripheralServices[0]->characteristic(QBluetoothUuid(leCharUuid1));
if (characteristic.isValid()) {
+ // Update value at the beginning and end so we can check whole data is sent in large writes
+ leCharacteristicValue[0] = ++leCharacteristicValueUpdate;
+ leCharacteristicValue[leCharacteristicSize - 1] = leCharacteristicValueUpdate;
qDebug() << " Peripheral writes value:" << leCharacteristicValue;
- lePeripheralService->writeCharacteristic(characteristic, leCharacteristicValue);
+ lePeripheralServices[0]->writeCharacteristic(characteristic, leCharacteristicValue);
} else {
qDebug() << "Characteristic was invalid";
}
}
-void BtLocalDevice::peripheralRead()
+void BtLocalDevice::peripheralCharacteristicRead()
{
- qDebug() << "######" << "LE peripheral read";
- if (!lePeripheralController || !lePeripheralService) {
+ qDebug() << "######" << "LE peripheral read characteristic";
+ if (!lePeripheralController || lePeripheralServices.isEmpty()) {
qDebug() << "Peripheral or peripheral service does not exist";
return;
}
- auto characteristic = lePeripheralService->characteristic(QBluetoothUuid(leCharacteristicUuid));
+ auto characteristic = lePeripheralServices[0]->characteristic(QBluetoothUuid(leCharUuid1));
if (characteristic.isValid())
qDebug() << " Value:" << characteristic.value();
else
qDebug() << "Characteristic was invalid";
}
+void BtLocalDevice::peripheralDescriptorWrite()
+{
+ qDebug() << "######" << "LE peripheral write descriptor";
+ if (!lePeripheralController || lePeripheralServices.isEmpty()) {
+ qDebug() << "Peripheral or peripheral service does not exist";
+ return;
+ }
+ auto descriptor = lePeripheralServices[0]->characteristic(QBluetoothUuid(leCharUuid1))
+ .descriptor(QBluetoothUuid::DescriptorType::CharacteristicUserDescription);
+
+ if (descriptor.isValid()) {
+ leDescriptorValue[0] = leDescriptorValueUpdate++;
+ qDebug() << " Peripheral writes value: " << leDescriptorValue;
+ lePeripheralServices[0]->writeDescriptor(descriptor, leDescriptorValue);
+ } else {
+ qDebug() << "Descriptor was invalid";
+ }
+}
+
+void BtLocalDevice::peripheralDescriptorRead()
+{
+ qDebug() << "######" << "LE peripheral read descriptor";
+ if (!lePeripheralController || lePeripheralServices.isEmpty()) {
+ qDebug() << "Peripheral or peripheral service does not exist";
+ return;
+ }
+ auto descriptor = lePeripheralServices[0]->characteristic(QBluetoothUuid(leCharUuid1))
+ .descriptor(QBluetoothUuid::DescriptorType::CharacteristicUserDescription);
+ if (descriptor.isValid())
+ qDebug() << " Value:" << descriptor.value();
+ else
+ qDebug() << "Descriptor was invalid";
+}
+
void BtLocalDevice::startLeDeviceDiscovery()
{
qDebug() << "######" << "LE device discovery start for:" << leRemotePeriphreralDeviceName;
@@ -1185,6 +1340,10 @@ void BtLocalDevice::centralDiscoverServiceDetails()
[](const QLowEnergyCharacteristic&, const QByteArray& value){
qDebug() << "LE central service characteristic value changed" << value;
});
+ QObject::connect(leCentralService.get(), &QLowEnergyService::descriptorRead,
+ [](const QLowEnergyDescriptor&, const QByteArray& value){
+ qDebug() << "LE central service descriptor value read" << value;
+ });
QObject::connect(leCentralService.get(), &QLowEnergyService::descriptorWritten,
[this](const QLowEnergyDescriptor&, const QByteArray& value){
qDebug() << "LE central service descriptor value written" << value;
@@ -1253,8 +1412,8 @@ void BtLocalDevice::dumpLeInfo()
serviceDump(leCentralService.get());
qDebug() << "######" << "LE Peripheral-side service";
- if (lePeripheralService)
- serviceDump(lePeripheralService.get());
+ if (!lePeripheralServices.isEmpty())
+ serviceDump(lePeripheralServices[0].get());
}
void BtLocalDevice::centralSubscribeUnsubscribe()
@@ -1264,7 +1423,7 @@ void BtLocalDevice::centralSubscribeUnsubscribe()
qDebug() << "Service object does not exist";
return;
}
- auto characteristic = leCentralService->characteristic(QBluetoothUuid(leCharacteristicUuid));
+ auto characteristic = leCentralService->characteristic(QBluetoothUuid(leCharUuid1));
if (!characteristic.isValid()) {
qDebug() << "Characteristic is not valid";
return;
@@ -1294,6 +1453,16 @@ void BtLocalDevice::centralDelete()
emit leChanged();
}
+void BtLocalDevice::centralDisconnect()
+{
+ qDebug() << "######" << "LE central disconnect";
+ if (!leCentralController) {
+ qDebug() << "Create central first";
+ return;
+ }
+ leCentralController->disconnectFromDevice();
+}
+
void BtLocalDevice::peripheralDelete()
{
qDebug() << "######" << "Delete peripheral" << lePeripheralController.get();
@@ -1301,6 +1470,16 @@ void BtLocalDevice::peripheralDelete()
emit leChanged();
}
+void BtLocalDevice::peripheralDisconnect()
+{
+ qDebug() << "######" << "LE peripheral disconnect";
+ if (!lePeripheralController) {
+ qDebug() << "Create peripheral first";
+ return;
+ }
+ lePeripheralController->disconnectFromDevice();
+}
+
bool BtLocalDevice::centralExists() const
{
return leCentralController.get();
@@ -1311,7 +1490,7 @@ bool BtLocalDevice::centralSubscribed() const
if (!leCentralService)
return false;
- auto characteristic = leCentralService->characteristic(QBluetoothUuid(leCharacteristicUuid));
+ auto characteristic = leCentralService->characteristic(QBluetoothUuid(leCharUuid1));
if (!characteristic.isValid())
return false;
@@ -1378,10 +1557,10 @@ QByteArray BtLocalDevice::peripheralState() const
QByteArray BtLocalDevice::peripheralServiceState() const
{
- if (!lePeripheralService)
+ if (lePeripheralServices.isEmpty())
return "(N/A)"_ba;
- return serviceStateString[lePeripheralService->state()];
+ return serviceStateString[lePeripheralServices[0]->state()];
}
QByteArray BtLocalDevice::peripheralError() const
@@ -1394,10 +1573,10 @@ QByteArray BtLocalDevice::peripheralError() const
QByteArray BtLocalDevice::peripheralServiceError() const
{
- if (!lePeripheralService)
+ if (lePeripheralServices.isEmpty())
return "(N/A)"_ba;
- return serviceErrorString[lePeripheralService->error()];
+ return serviceErrorString[lePeripheralServices[0]->error()];
}
bool BtLocalDevice::peripheralExists() const
diff --git a/tests/bttestui/btlocaldevice.h b/tests/bttestui/btlocaldevice.h
index 4c093bdb..9ac0e6ec 100644
--- a/tests/bttestui/btlocaldevice.h
+++ b/tests/bttestui/btlocaldevice.h
@@ -109,10 +109,13 @@ public slots:
void centralConnect();
void centralStartServiceDiscovery();
void centralDiscoverServiceDetails();
- void centralWrite();
- void centralRead();
+ void centralCharacteristicWrite();
+ void centralCharacteristicRead();
+ void centralDescriptorWrite();
+ void centralDescriptorRead();
void centralSubscribeUnsubscribe();
void centralDelete();
+ void centralDisconnect();
bool centralExists() const;
bool centralSubscribed() const;
QByteArray centralState() const;
@@ -124,11 +127,15 @@ public slots:
//QLowEnergyController peripheral
void peripheralCreate();
+ void peripheralAddServices();
void peripheralStartAdvertising();
void peripheralStopAdvertising();
- void peripheralWrite();
- void peripheralRead();
+ void peripheralCharacteristicRead();
+ void peripheralCharacteristicWrite();
+ void peripheralDescriptorRead();
+ void peripheralDescriptorWrite();
void peripheralDelete();
+ void peripheralDisconnect();
bool peripheralExists() const;
QByteArray peripheralState() const;
QByteArray peripheralServiceState() const;
@@ -154,10 +161,10 @@ private:
std::unique_ptr<QLowEnergyController> leCentralController;
std::unique_ptr<QLowEnergyController> lePeripheralController;
- std::unique_ptr<QLowEnergyService> lePeripheralService;
std::unique_ptr<QLowEnergyService> leCentralService;
QLowEnergyAdvertisingData leAdvertisingData;
- QLowEnergyServiceData leServiceData;
+ QList<QLowEnergyServiceData> lePeripheralServiceData;
+ QList<QSharedPointer<QLowEnergyService>> lePeripheralServices;
QBluetoothDeviceInfo leRemotePeripheralDevice;
QByteArray latestRSSI = "N/A";
};
diff --git a/tests/bttestui/main.qml b/tests/bttestui/main.qml
index 38823199..4105c561 100644
--- a/tests/bttestui/main.qml
+++ b/tests/bttestui/main.qml
@@ -262,11 +262,19 @@ Flickable {
}
Button {
buttonText: "CharacteristicRead"
- onClicked: device.centralRead()
+ onClicked: device.centralCharacteristicRead()
}
Button {
buttonText: "CharacteristicWrite"
- onClicked: device.centralWrite()
+ onClicked: device.centralCharacteristicWrite()
+ }
+ Button {
+ buttonText: "DescriptorRead"
+ onClicked: device.centralDescriptorRead()
+ }
+ Button {
+ buttonText: "DescriptorWrite"
+ onClicked: device.centralDescriptorWrite()
}
Button {
buttonText: "Sub/Unsubscribe"
@@ -277,6 +285,10 @@ Flickable {
onClicked: device.centralDelete()
}
Button {
+ buttonText: "Disconnect"
+ onClicked: device.centralDisconnect()
+ }
+ Button {
buttonText: "ReadRSSI"
onClicked: device.centralReadRSSI()
}
@@ -306,13 +318,15 @@ Flickable {
}
}
Button {
- id: peripheralBtn
buttonText: "1 CreatePeripheral"
onClicked: device.peripheralCreate()
}
Button {
- id: advertiseBtn
- buttonText: "2 StartAdvertise"
+ buttonText: "2 AddServices"
+ onClicked: device.peripheralAddServices()
+ }
+ Button {
+ buttonText: "3 StartAdvertise"
onClicked: device.peripheralStartAdvertising()
}
Button {
@@ -321,16 +335,28 @@ Flickable {
}
Button {
buttonText: "CharacteristicRead"
- onClicked: device.peripheralRead()
+ onClicked: device.peripheralCharacteristicRead()
}
Button {
buttonText: "CharacteristicWrite"
- onClicked: device.peripheralWrite()
+ onClicked: device.peripheralCharacteristicWrite()
+ }
+ Button {
+ buttonText: "DescriptorRead"
+ onClicked: device.peripheralDescriptorRead()
+ }
+ Button {
+ buttonText: "DescriptorWrite"
+ onClicked: device.peripheralDescriptorWrite()
}
Button {
buttonText: "DeleteController"
onClicked: device.peripheralDelete()
}
+ Button {
+ buttonText: "Disconnect"
+ onClicked: device.peripheralDisconnect()
+ }
}
Column {
spacing: 8
diff --git a/tests/manual/qlowenergycontroller/tst_qlowenergycontroller_device.cpp b/tests/manual/qlowenergycontroller/tst_qlowenergycontroller_device.cpp
index 87dab2f1..50054a92 100644
--- a/tests/manual/qlowenergycontroller/tst_qlowenergycontroller_device.cpp
+++ b/tests/manual/qlowenergycontroller/tst_qlowenergycontroller_device.cpp
@@ -12,8 +12,10 @@
#include <QScopeGuard>
#include <QBluetoothLocalDevice>
-static const QLatin1String largeCharacteristicServiceUuid("1f85e37c-ac16-11eb-ae5c-93d3a763feed");
-static const QLatin1String largeCharacteristicCharUuid("40e4f68e-ac16-11eb-9956-cfe55a8c370c");
+static const QLatin1String largeAttributeServiceUuid("1f85e37c-ac16-11eb-ae5c-93d3a763feed");
+static const QLatin1String largeAttributeCharUuid("40e4f68e-ac16-11eb-9956-cfe55a8c370c");
+static const QLatin1String largeAttributeDescUuid("44e4f68e-ac16-11eb-9956-cfe55a8c370c");
+static constexpr qsizetype largeAttributeSize{508}; // Size for char and desc values
static const QLatin1String platformIdentifierServiceUuid("4a92cb7f-5031-4a09-8304-3e89413f458d");
static const QLatin1String platformIdentifierCharUuid("6b0ecf7c-5f09-4c87-aaab-bb49d5d383aa");
@@ -89,6 +91,7 @@ private slots:
void rssiRead();
#endif
void readWriteLargeCharacteristic();
+ void readWriteLargeDescriptor();
void readDuringServiceDiscovery();
void readNotificationAndIndicationProperty();
void testNotificationAndIndication();
@@ -420,6 +423,15 @@ void tst_qlowenergycontroller_device::checkconnectionCounter(
void tst_qlowenergycontroller_device::readWriteLargeCharacteristic()
{
+ // This tests reading and writing a large characteristic value
+ //
+ // Most modern platforms cope with up-to 512 bytes. One exception is Bluez DBus peripheral,
+ // which may reject a write from client when going above 508 due to the way it internally
+ // checks the payload size (tested with Bluez 5.64).
+ //
+ // This test can also be used for testing long (aka prepared) writes & reads by setting
+ // the MTU to for example 50 bytes. Asking a specific MTU is platform-specific and
+ // there is no Qt API for it
QVERIFY(mController->services().isEmpty());
mController->discoverServices();
QTRY_COMPARE(mController->state(), QLowEnergyController::DiscoveredState);
@@ -427,18 +439,17 @@ void tst_qlowenergycontroller_device::readWriteLargeCharacteristic()
checkconnectionCounter(mController);
QSharedPointer<QLowEnergyService> service(
- mController->createServiceObject(QBluetoothUuid(largeCharacteristicServiceUuid)));
+ mController->createServiceObject(QBluetoothUuid(largeAttributeServiceUuid)));
QVERIFY(service != nullptr);
service->discoverDetails(QLowEnergyService::SkipValueDiscovery);
QTRY_COMPARE(service->state(), QLowEnergyService::ServiceState::RemoteServiceDiscovered);
QSignalSpy readSpy(service.get(), &QLowEnergyService::characteristicRead);
QSignalSpy writtenSpy(service.get(), &QLowEnergyService::characteristicWritten);
- QCOMPARE(readSpy.size(), 0);
- QCOMPARE(writtenSpy.size(), 0);
- // The service discovery skipped the values => check that the default value is all zeroes
- auto characteristic = service->characteristic(QBluetoothUuid(largeCharacteristicCharUuid));
+ // The service discovery skipped the values => check that the default values are all zeroes
+ auto characteristic = service->characteristic(QBluetoothUuid(largeAttributeCharUuid));
+ QVERIFY(characteristic.isValid());
QByteArray testArray(0);
qDebug() << "Initial large characteristic value:" << characteristic.value();
QCOMPARE(characteristic.value(), testArray);
@@ -447,12 +458,12 @@ void tst_qlowenergycontroller_device::readWriteLargeCharacteristic()
service->readCharacteristic(characteristic);
QTRY_COMPARE(readSpy.size(), 1);
qDebug() << "Large characteristic value after read:" << characteristic.value();
- testArray = QByteArray(512, 0);
+ testArray = QByteArray(largeAttributeSize, 0);
testArray[0] = 0x0b;
QCOMPARE(characteristic.value(), testArray);
// Write a new value to characteristic and read it back
- for (int i = 0; i < 512; ++i) {
+ for (int i = 0; i < largeAttributeSize; ++i) {
testArray[i] = i % 5;
}
service->writeCharacteristic(characteristic, testArray);
@@ -465,6 +476,54 @@ void tst_qlowenergycontroller_device::readWriteLargeCharacteristic()
QCOMPARE(characteristic.value(), testArray);
}
+void tst_qlowenergycontroller_device::readWriteLargeDescriptor()
+{
+ // This tests reading and writing a large descriptor value
+ QVERIFY(mController->services().isEmpty());
+ mController->discoverServices();
+ QTRY_COMPARE(mController->state(), QLowEnergyController::DiscoveredState);
+
+ checkconnectionCounter(mController);
+
+ QSharedPointer<QLowEnergyService> service(
+ mController->createServiceObject(QBluetoothUuid(largeAttributeServiceUuid)));
+ QVERIFY(service != nullptr);
+ service->discoverDetails(QLowEnergyService::FullDiscovery);
+ QTRY_COMPARE(service->state(), QLowEnergyService::ServiceState::RemoteServiceDiscovered);
+
+ QSignalSpy readSpy(service.get(), &QLowEnergyService::descriptorRead);
+ QSignalSpy writtenSpy(service.get(), &QLowEnergyService::descriptorWritten);
+
+ auto characteristic = service->characteristic(QBluetoothUuid(largeAttributeCharUuid));
+ QVERIFY(characteristic.isValid());
+ auto descriptor = characteristic.descriptor(QBluetoothUuid(largeAttributeDescUuid));
+ QVERIFY(descriptor.isValid());
+
+ QByteArray testArray = QByteArray(largeAttributeSize, 0);
+ testArray[0] = 0xdd;
+
+ // Read descriptor value and verify it is what the server set (0xdd 0x00 ..)
+ QVERIFY(readSpy.isEmpty());
+ QVERIFY(writtenSpy.isEmpty());
+ service->readDescriptor(descriptor);
+ QTRY_COMPARE(readSpy.size(), 1);
+ qDebug() << "Large descriptor value after read:" << descriptor.value();
+ QCOMPARE(descriptor.value(), testArray);
+
+ // Write a new value to descriptor and read it back
+ for (int i = 0; i < largeAttributeSize; ++i) {
+ testArray[i] = i % 5;
+ }
+ service->writeDescriptor(descriptor, testArray);
+ QCOMPARE(service->error(), QLowEnergyService::ServiceError::NoError);
+ QTRY_COMPARE(writtenSpy.size(), 1);
+
+ service->readDescriptor(descriptor);
+ QTRY_COMPARE(readSpy.size(), 2);
+ qDebug() << "Large descriptor value after write/read:" << descriptor.value();
+ QCOMPARE(descriptor.value(), testArray);
+}
+
void tst_qlowenergycontroller_device::readDuringServiceDiscovery()
{
QVERIFY(mController->services().isEmpty());
@@ -474,7 +533,7 @@ void tst_qlowenergycontroller_device::readDuringServiceDiscovery()
checkconnectionCounter(mController);
QSharedPointer<QLowEnergyService> service(
- mController->createServiceObject(QBluetoothUuid(largeCharacteristicServiceUuid)));
+ mController->createServiceObject(QBluetoothUuid(largeAttributeServiceUuid)));
QVERIFY(service != nullptr);
service->discoverDetails(QLowEnergyService::FullDiscovery);
QTRY_COMPARE(service->state(), QLowEnergyService::ServiceState::RemoteServiceDiscovered);
@@ -485,11 +544,11 @@ void tst_qlowenergycontroller_device::readDuringServiceDiscovery()
QCOMPARE(writtenSpy.size(), 0);
// Value that is initially set on the characteristic at the server-side (0x0b 0x00 ..)
- QByteArray testArray(512, 0);
+ QByteArray testArray(largeAttributeSize, 0);
testArray[0] = 0x0b;
// We did a full service discovery and should have an initial value to compare
- auto characteristic = service->characteristic(QBluetoothUuid(largeCharacteristicCharUuid));
+ auto characteristic = service->characteristic(QBluetoothUuid(largeAttributeCharUuid));
QByteArray valueFromServiceDiscovery = characteristic.value();
qDebug() << "Large characteristic value from service discovery:" << valueFromServiceDiscovery;
// On darwin the server does not restart (disconnect) in-between the case runs
@@ -504,7 +563,7 @@ void tst_qlowenergycontroller_device::readDuringServiceDiscovery()
QCOMPARE(characteristic.value(), valueFromServiceDiscovery);
// Write a new value to the characteristic and read it back
- for (int i = 0; i < 512; ++i) {
+ for (int i = 0; i < largeAttributeSize; ++i) {
testArray[i] = i % 5;
}
service->writeCharacteristic(characteristic, testArray);