diff options
-rw-r--r-- | src/bluetooth/qlowenergycharacteristic.cpp | 13 | ||||
-rw-r--r-- | src/bluetooth/qlowenergycharacteristic.h | 2 | ||||
-rw-r--r-- | src/bluetooth/qlowenergycontrollernew.cpp | 18 | ||||
-rw-r--r-- | src/bluetooth/qlowenergycontrollernew_bluez.cpp | 109 | ||||
-rw-r--r-- | src/bluetooth/qlowenergycontrollernew_p.h | 11 | ||||
-rw-r--r-- | src/bluetooth/qlowenergyservice.cpp | 14 | ||||
-rw-r--r-- | src/bluetooth/qlowenergyservice.h | 7 | ||||
-rw-r--r-- | tests/auto/qlowenergycontroller/tst_qlowenergycontroller.cpp | 474 |
8 files changed, 607 insertions, 41 deletions
diff --git a/src/bluetooth/qlowenergycharacteristic.cpp b/src/bluetooth/qlowenergycharacteristic.cpp index 0d0ad60c..10136c10 100644 --- a/src/bluetooth/qlowenergycharacteristic.cpp +++ b/src/bluetooth/qlowenergycharacteristic.cpp @@ -100,17 +100,10 @@ QLowEnergyCharacteristic::QLowEnergyCharacteristic(): upon write. */ QLowEnergyCharacteristic::QLowEnergyCharacteristic(const QLowEnergyCharacteristic &other): - d_ptr(other.d_ptr) + d_ptr(other.d_ptr), data(0) { - if (!other.data) { - if (data) { - delete data; - data = 0; - } - } else { - if (!data) - data = new QLowEnergyCharacteristicPrivate(); - + if (other.data) { + data = new QLowEnergyCharacteristicPrivate(); data->handle = other.data->handle; } } diff --git a/src/bluetooth/qlowenergycharacteristic.h b/src/bluetooth/qlowenergycharacteristic.h index 7dc91f43..6454ef67 100644 --- a/src/bluetooth/qlowenergycharacteristic.h +++ b/src/bluetooth/qlowenergycharacteristic.h @@ -92,6 +92,8 @@ public: protected: QSharedPointer<QLowEnergyServicePrivate> d_ptr; + + friend class QLowEnergyService; QLowEnergyCharacteristicPrivate *data; QLowEnergyCharacteristic(QSharedPointer<QLowEnergyServicePrivate> p, QLowEnergyHandle handle); diff --git a/src/bluetooth/qlowenergycontrollernew.cpp b/src/bluetooth/qlowenergycontrollernew.cpp index 8b98c6b9..75fcce86 100644 --- a/src/bluetooth/qlowenergycontrollernew.cpp +++ b/src/bluetooth/qlowenergycontrollernew.cpp @@ -108,6 +108,24 @@ void QLowEnergyControllerNewPrivate::invalidateServices() serviceList.clear(); } +QSharedPointer<QLowEnergyServicePrivate> QLowEnergyControllerNewPrivate::serviceForHandle( + QLowEnergyHandle handle) +{ + foreach (QSharedPointer<QLowEnergyServicePrivate> service, serviceList.values()) + if (service->startHandle <= handle && handle <= service->endHandle) + return service; + + return QSharedPointer<QLowEnergyServicePrivate>(); +} + +void QLowEnergyControllerNewPrivate::updateValueOfCharacteristic( + QLowEnergyHandle charHandle, const QByteArray &value) +{ + QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(charHandle); + if (!service.isNull() && service->characteristicList.contains(charHandle)) + service->characteristicList[charHandle].value = value; +} + QLowEnergyControllerNew::QLowEnergyControllerNew( const QBluetoothAddress &remoteDevice, QObject *parent) diff --git a/src/bluetooth/qlowenergycontrollernew_bluez.cpp b/src/bluetooth/qlowenergycontrollernew_bluez.cpp index 8249f1fc..1b5ea1db 100644 --- a/src/bluetooth/qlowenergycontrollernew_bluez.cpp +++ b/src/bluetooth/qlowenergycontrollernew_bluez.cpp @@ -56,9 +56,11 @@ // GATT commands #define ATT_OP_ERROR_RESPONSE 0x1 -#define ATT_OP_READ_BY_TYPE_REQUEST 0x8 +#define ATT_OP_READ_BY_TYPE_REQUEST 0x8 //discover characteristics #define ATT_OP_READ_BY_TYPE_RESPONSE 0x9 -#define ATT_OP_READ_BY_GROUP_REQUEST 0x10 +#define ATT_OP_READ_REQUEST 0xA //read characteristic value +#define ATT_OP_READ_RESPONSE 0xB +#define ATT_OP_READ_BY_GROUP_REQUEST 0x10 //discover services #define ATT_OP_READ_BY_GROUP_RESPONSE 0x11 // GATT error codes @@ -80,9 +82,6 @@ #define ATT_ERROR_APPLICATION 0x10 #define ATT_ERROR_INSUF_RESOURCES 0x11 - - - QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(QT_BT_BLUEZ) @@ -231,7 +230,7 @@ void QLowEnergyControllerNewPrivate::l2cpReadyRead() { requestPending = false; const QByteArray reply = l2cpSocket->readAll(); - //qDebug() << response.size() << "data:" << response.toHex(); + qDebug() << reply.size() << "data:" << reply.toHex(); if (reply.isEmpty()) return; @@ -248,8 +247,8 @@ void QLowEnergyControllerNewPrivate::sendNextPendingRequest() return; const Request &request = openRequests.head(); - qCDebug(QT_BT_BLUEZ) << "Sending request, type:" << hex << request.command - << request.payload.toHex(); +// qCDebug(QT_BT_BLUEZ) << "Sending request, type:" << hex << request.command +// << request.payload.toHex(); requestPending = true; qint64 result = l2cpSocket->write(request.payload.constData(), @@ -285,7 +284,7 @@ void QLowEnergyControllerNewPrivate::processReply( if (isErrorResponse) { q->discoveryFinished(); - return; + break; } QLowEnergyHandle start = 0, end = 0; @@ -308,8 +307,8 @@ void QLowEnergyControllerNewPrivate::processReply( offset += elementLength; - //qDebug() << "Found uuid:" << uuid << "start handle:" << hex - // << start << "end handle:" << end; +// qDebug() << "Found uuid:" << uuid << "start handle:" << hex +// << start << "end handle:" << end; QLowEnergyServicePrivate *priv = new QLowEnergyServicePrivate(); priv->uuid = uuid; @@ -327,7 +326,6 @@ void QLowEnergyControllerNewPrivate::processReply( sendReadByGroupRequest(end+1, 0xFFFF); else emit q->discoveryFinished(); - } break; case ATT_OP_READ_BY_TYPE_REQUEST: //in case of error @@ -341,8 +339,8 @@ void QLowEnergyControllerNewPrivate::processReply( if (isErrorResponse) { //we reached end of service handle //just finish up characteristic discovery - p->setState(QLowEnergyService::ServiceDiscovered); - return; + readServiceCharacteristicValues(p->uuid); + break; } /* packet format: @@ -378,23 +376,42 @@ void QLowEnergyControllerNewPrivate::processReply( p->characteristicList[startHandle] = characteristic; - qDebug() << "Found handle:" << hex << startHandle - << "properties:" << flags - << "value handle:" << valueHandle - << "uuid:" << uuid.toString(); +// qDebug() << "Found handle:" << hex << startHandle +// << "properties:" << flags +// << "value handle:" << valueHandle +// << "uuid:" << uuid.toString(); } if (startHandle + 1 < p->endHandle) // more chars to discover sendReadByTypeRequest(p, startHandle + 1); else - p->setState(QLowEnergyService::ServiceDiscovered); + readServiceCharacteristicValues(p->uuid); + } + break; + case ATT_OP_READ_REQUEST: //error case + case ATT_OP_READ_RESPONSE: + { + Q_ASSERT(request.command == ATT_OP_READ_REQUEST); + + if (isErrorResponse) { + // we ignore any error and go over to next message + break; + } + + QLowEnergyHandle charHandle = (QLowEnergyHandle) request.reference.toUInt(); + QByteArray a = response.mid(1); + updateValueOfCharacteristic(charHandle, response.mid(1).toHex()); + if (request.reference2.toBool()) { + QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(charHandle); + Q_ASSERT(!service.isNull()); + service->setState(QLowEnergyService::ServiceDiscovered); + } } break; default: qCDebug(QT_BT_BLUEZ) << "Unknown packet: " << response.toHex(); + break; } - - } void QLowEnergyControllerNewPrivate::discoverServices() @@ -419,6 +436,8 @@ void QLowEnergyControllerNewPrivate::sendReadByGroupRequest( QByteArray data(GRP_TYPE_REQ_SIZE, Qt::Uninitialized); memcpy(data.data(), packet, GRP_TYPE_REQ_SIZE); +// qDebug() << "Sending read_by_group_type request, startHandle:" << hex +// << start << "endHandle:" << end; Request request; request.payload = data; @@ -458,9 +477,9 @@ void QLowEnergyControllerNewPrivate::sendReadByTypeRequest( QByteArray data(READ_BY_TYPE_REQ_SIZE, Qt::Uninitialized); memcpy(data.data(), packet, READ_BY_TYPE_REQ_SIZE); - qDebug() << "Sending read_by_type request, startHandle:" << hex - << nextHandle << "endHandle:" << serviceData->endHandle - << "packet:" << data.toHex(); +// qDebug() << "Sending read_by_type request, startHandle:" << hex +// << nextHandle << "endHandle:" << serviceData->endHandle +// << "packet:" << data.toHex(); Request request; request.payload = data; @@ -471,5 +490,47 @@ void QLowEnergyControllerNewPrivate::sendReadByTypeRequest( sendNextPendingRequest(); } +void QLowEnergyControllerNewPrivate::readServiceCharacteristicValues( + const QBluetoothUuid &serviceUuid) +{ +#define READ_REQUEST_SIZE 3 + quint8 packet[READ_REQUEST_SIZE]; + qCDebug(QT_BT_BLUEZ) << "Reading characteristic values for" + << serviceUuid.toString(); + + bool oneReadRequested = false; + QSharedPointer<QLowEnergyServicePrivate> service = serviceList.value(serviceUuid); + const QList<QLowEnergyHandle> &keys = service->characteristicList.keys(); + for (int i = 0; i < keys.count(); i++) { + QLowEnergyHandle charHandle = keys[i]; + const QLowEnergyServicePrivate::CharData &charDetails = + service->characteristicList[charHandle]; + + //Don't try to read readOnly property + if (!(charDetails.properties & QLowEnergyCharacteristic::Read)) + continue; + + packet[0] = ATT_OP_READ_REQUEST; + bt_put_unaligned(htobs(charDetails.valueHandle), (quint16 *) &packet[1]); + + QByteArray data(READ_REQUEST_SIZE, Qt::Uninitialized); + memcpy(data.data(), packet, READ_REQUEST_SIZE); + + Request request; + request.payload = data; + request.command = ATT_OP_READ_REQUEST; + request.reference = charHandle; + //last entry? + request.reference2 = QVariant((bool)(i + 1 == keys.count())); + openRequests.enqueue(request); + oneReadRequested = true; + } + + if (!oneReadRequested) + service->setState(QLowEnergyService::ServiceDiscovered); + + sendNextPendingRequest(); +} + QT_END_NAMESPACE diff --git a/src/bluetooth/qlowenergycontrollernew_p.h b/src/bluetooth/qlowenergycontrollernew_p.h index 3f1862dc..f6bc06e5 100644 --- a/src/bluetooth/qlowenergycontrollernew_p.h +++ b/src/bluetooth/qlowenergycontrollernew_p.h @@ -83,6 +83,13 @@ public: void discoverServiceDetails(const QBluetoothUuid &service); + // misc lookup helpers + QSharedPointer<QLowEnergyServicePrivate> serviceForHandle( + QLowEnergyHandle handle); + void updateValueOfCharacteristic(QLowEnergyHandle charHandle, + const QByteArray &value); + + QBluetoothAddress remoteDevice; QBluetoothAddress localAdapter; @@ -100,7 +107,10 @@ private: struct Request { quint8 command; QByteArray payload; + // TODO reference below is ugly but until we know all commands and their + // requirements this is WIP QVariant reference; + QVariant reference2; }; QQueue<Request> openRequests; bool requestPending; @@ -111,6 +121,7 @@ private: void sendReadByGroupRequest(QLowEnergyHandle start, QLowEnergyHandle end); void sendReadByTypeRequest(QSharedPointer<QLowEnergyServicePrivate> serviceData, QLowEnergyHandle nextHandle); + void readServiceCharacteristicValues(const QBluetoothUuid &service); private slots: void l2cpConnected(); diff --git a/src/bluetooth/qlowenergyservice.cpp b/src/bluetooth/qlowenergyservice.cpp index 0080dee9..d8f80397 100644 --- a/src/bluetooth/qlowenergyservice.cpp +++ b/src/bluetooth/qlowenergyservice.cpp @@ -44,6 +44,8 @@ #include <QtBluetooth/QLowEnergyService> #include <QtBluetooth/QLowEnergyCharacteristicInfo> +#include <algorithm> + #include "qlowenergycontrollernew_p.h" #include "qlowenergyserviceprivate_p.h" @@ -94,10 +96,16 @@ QLowEnergyService::ServiceType QLowEnergyService::type() const } -QList<QLowEnergyCharacteristicInfo> QLowEnergyService::characteristics() const +QList<QLowEnergyCharacteristic> QLowEnergyService::characteristics() const { - //TODO implement - QList<QLowEnergyCharacteristicInfo> results; + QList<QLowEnergyCharacteristic> results; + QList<QLowEnergyHandle> handles = d_ptr->characteristicList.keys(); + std::sort(handles.begin(), handles.end()); + + foreach (const QLowEnergyHandle &handle, handles) { + QLowEnergyCharacteristic characteristic(d_ptr, handle); + results.append(characteristic); + } return results; } diff --git a/src/bluetooth/qlowenergyservice.h b/src/bluetooth/qlowenergyservice.h index af4b657d..c2ecc6b0 100644 --- a/src/bluetooth/qlowenergyservice.h +++ b/src/bluetooth/qlowenergyservice.h @@ -42,7 +42,8 @@ #define QLOWENERGYSERVICE_H #include <QtBluetooth/QBluetoothAddress> -#include <QtBluetooth/QLowEnergyCharacteristicInfo> +#include <QtBluetooth/QBluetoothUuid> +#include <QtBluetooth/QLowEnergyCharacteristic> QT_BEGIN_NAMESPACE @@ -76,7 +77,7 @@ public: QLowEnergyService::ServiceState state() const; - QList<QLowEnergyCharacteristicInfo> characteristics() const; + QList<QLowEnergyCharacteristic> characteristics() const; QBluetoothUuid serviceUuid() const; QString serviceName() const; @@ -87,7 +88,7 @@ public: Q_SIGNALS: void stateChanged(QLowEnergyService::ServiceState newState); - void characteristicChanged(const QLowEnergyCharacteristicInfo &info, + void characteristicChanged(const QLowEnergyCharacteristic &info, const QByteArray &value); void descriptorChanged(const QLowEnergyDescriptorInfo &info, const QByteArray &value); diff --git a/tests/auto/qlowenergycontroller/tst_qlowenergycontroller.cpp b/tests/auto/qlowenergycontroller/tst_qlowenergycontroller.cpp index 0c9e051c..f37d5d80 100644 --- a/tests/auto/qlowenergycontroller/tst_qlowenergycontroller.cpp +++ b/tests/auto/qlowenergycontroller/tst_qlowenergycontroller.cpp @@ -42,9 +42,11 @@ #include <QtTest/QtTest> #include <QBluetoothLocalDevice> #include <QBluetoothServiceDiscoveryAgent> +#include <QBluetoothUuid> #include <QLowEnergyController> #include <QLowEnergyControllerNew> #include <QLowEnergyServiceInfo> +#include <QLowEnergyCharacteristic> #include <QDebug> @@ -74,6 +76,7 @@ private slots: private: void verifyServiceProperties(const QLowEnergyServiceInfo &info); + void verifyServiceProperties(const QLowEnergyService *info); QBluetoothServiceDiscoveryAgent *agent; QBluetoothAddress remoteDevice; @@ -823,7 +826,7 @@ void tst_QLowEnergyController::tst_connectNew() // initiate characteristic discovery foreach (QLowEnergyService *service, savedReferences) { - //qDebug() << "Discoverying" << service->serviceUuid(); + qDebug() << "Discoverying" << service->serviceUuid(); QSignalSpy stateSpy(service, SIGNAL(stateChanged(QLowEnergyService::ServiceState))); QSignalSpy errorSpy(service, SIGNAL(error(QLowEnergyService::ServiceError))); @@ -835,6 +838,8 @@ void tst_QLowEnergyController::tst_connectNew() QCOMPARE(errorSpy.count(), 0); //no error QCOMPARE(stateSpy.count(), 2); // + verifyServiceProperties(service); + // for (int i = 0; i < stateSpy.count(); i++) { // const QVariant v = stateSpy[i].at(0); // //qDebug() << v; @@ -860,6 +865,473 @@ void tst_QLowEnergyController::tst_connectNew() uuid.toString().toLatin1()); } } + + qDeleteAll(savedReferences); + savedReferences.clear(); +} + +void tst_QLowEnergyController::verifyServiceProperties( + const QLowEnergyService *info) +{ + if (info->serviceUuid() == + QBluetoothUuid(QString("00001800-0000-1000-8000-00805f9b34fb"))) { + qDebug() << "Verifying GAP Service"; + QList<QLowEnergyCharacteristic> chars = info->characteristics(); + QCOMPARE(chars.count(), 5); + + // Device Name + QString temp("00002a00-0000-1000-8000-00805f9b34fb"); + qDebug() << chars[0].uuid().toString() << temp; + QCOMPARE(chars[0].uuid(), QBluetoothUuid(temp)); + QCOMPARE(chars[0].handle(), 0x3u); + QCOMPARE(chars[0].properties(), QLowEnergyCharacteristic::Read); + QCOMPARE(chars[0].value(), QByteArray("544920424c452053656e736f7220546167")); + QVERIFY(chars[0].isValid()); + QCOMPARE(chars[0].descriptors().count(), 0); + + // Appearance + temp = QString("00002a01-0000-1000-8000-00805f9b34fb"); + QCOMPARE(chars[1].uuid(), QBluetoothUuid(temp)); + QCOMPARE(chars[1].handle(), 0x5u); + QCOMPARE(chars[1].properties(), QLowEnergyCharacteristic::Read); + QCOMPARE(chars[1].value(), QByteArray("0000")); + QVERIFY(chars[1].isValid()); + QCOMPARE(chars[1].descriptors().count(), 0); + + // Peripheral Privacy Flag + temp = QString("00002a02-0000-1000-8000-00805f9b34fb"); + QCOMPARE(chars[2].uuid(), QBluetoothUuid(temp)); + QCOMPARE(chars[2].handle(), 0x7u); + QCOMPARE(chars[2].properties(), + (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Write)); + QCOMPARE(chars[2].value(), QByteArray("00")); + QVERIFY(chars[2].isValid()); + QCOMPARE(chars[2].descriptors().count(), 0); + + // Reconnection Address + temp = QString("00002a03-0000-1000-8000-00805f9b34fb"); + QCOMPARE(chars[3].uuid(), QBluetoothUuid(temp)); + QCOMPARE(chars[3].handle(), 0x9u); + QCOMPARE(chars[3].properties(), + (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Write)); + QCOMPARE(chars[3].value(), QByteArray("000000000000")); + QVERIFY(chars[3].isValid()); + QCOMPARE(chars[3].descriptors().count(), 0); + + // Peripheral Preferred Connection Parameters + temp = QString("00002a04-0000-1000-8000-00805f9b34fb"); + QCOMPARE(chars[4].uuid(), QBluetoothUuid(temp)); + QCOMPARE(chars[4].handle(), 0xbu); + QCOMPARE(chars[4].properties(), QLowEnergyCharacteristic::Read); + QCOMPARE(chars[4].value(), QByteArray("5000a0000000e803")); + QVERIFY(chars[4].isValid()); + QCOMPARE(chars[4].descriptors().count(), 0); + } else if (info->serviceUuid() == + QBluetoothUuid(QString("00001801-0000-1000-8000-00805f9b34fb"))) { + qDebug() << "Verifying GATT Service"; + QList<QLowEnergyCharacteristic> chars = info->characteristics(); + QCOMPARE(chars.count(), 1); + + // Service Changed + QString temp("00002a05-0000-1000-8000-00805f9b34fb"); + //this should really be readable according to GATT Service spec + QCOMPARE(chars[0].uuid(), QBluetoothUuid(temp)); + QCOMPARE(chars[0].handle(), 0xeu); + QCOMPARE(chars[0].properties(), QLowEnergyCharacteristic::Indicate); + QCOMPARE(chars[0].value(), QByteArray("")); + QVERIFY(chars[0].isValid()); + QCOMPARE(chars[0].descriptors().count(), 0); + } else if (info->serviceUuid() == + QBluetoothUuid(QString("0000180a-0000-1000-8000-00805f9b34fb"))) { + qDebug() << "Verifying Device Information"; + QList<QLowEnergyCharacteristic> chars = info->characteristics(); + QCOMPARE(chars.count(), 9); + + // System ID + QString temp("00002a23-0000-1000-8000-00805f9b34fb"); + //this should really be readable according to GATT Service spec + QCOMPARE(chars[0].uuid(), QBluetoothUuid(temp)); + QCOMPARE(chars[0].handle(), 0x12u); + QCOMPARE(chars[0].properties(), QLowEnergyCharacteristic::Read); + QCOMPARE(chars[0].value(), QByteArray("6e41ab0000296abc")); + QVERIFY(chars[0].isValid()); + QCOMPARE(chars[0].descriptors().count(), 0); + + // Model Number + temp = QString("00002a24-0000-1000-8000-00805f9b34fb"); + QCOMPARE(chars[1].uuid(), QBluetoothUuid(temp)); + QCOMPARE(chars[1].handle(), 0x14u); + QCOMPARE(chars[1].properties(), QLowEnergyCharacteristic::Read); + QCOMPARE(chars[1].value(), QByteArray("4e2e412e00")); + QVERIFY(chars[1].isValid()); + QCOMPARE(chars[1].descriptors().count(), 0); + + // Serial Number + temp = QString("00002a25-0000-1000-8000-00805f9b34fb"); + QCOMPARE(chars[2].uuid(), QBluetoothUuid(temp)); + QCOMPARE(chars[2].handle(), 0x16u); + QCOMPARE(chars[2].properties(), + (QLowEnergyCharacteristic::Read)); + QCOMPARE(chars[2].value(), QByteArray("4e2e412e00")); + QVERIFY(chars[2].isValid()); + QCOMPARE(chars[2].descriptors().count(), 0); + + // Firmware Revision + temp = QString("00002a26-0000-1000-8000-00805f9b34fb"); + QCOMPARE(chars[3].uuid(), QBluetoothUuid(temp)); + QCOMPARE(chars[3].handle(), 0x18u); + QCOMPARE(chars[3].properties(), + (QLowEnergyCharacteristic::Read)); + QCOMPARE(chars[3].value(), QByteArray("312e3031202846656220203720323031332900")); + QVERIFY(chars[3].isValid()); + QCOMPARE(chars[3].descriptors().count(), 0); + + // Hardware Revision + temp = QString("00002a27-0000-1000-8000-00805f9b34fb"); + QCOMPARE(chars[4].uuid(), QBluetoothUuid(temp)); + QCOMPARE(chars[4].handle(), 0x1au); + QCOMPARE(chars[4].properties(), + (QLowEnergyCharacteristic::Read)); + QCOMPARE(chars[4].value(), QByteArray("4e2e412e00")); + QVERIFY(chars[4].isValid()); + QCOMPARE(chars[4].descriptors().count(), 0); + + // Software Revision + temp = QString("00002a28-0000-1000-8000-00805f9b34fb"); + QCOMPARE(chars[5].uuid(), QBluetoothUuid(temp)); + QCOMPARE(chars[5].handle(), 0x1cu); + QCOMPARE(chars[5].properties(), + (QLowEnergyCharacteristic::Read)); + QCOMPARE(chars[5].value(), QByteArray("4e2e412e00")); + QVERIFY(chars[5].isValid()); + QCOMPARE(chars[5].descriptors().count(), 0); + + // Manufacturer Name + temp = QString("00002a29-0000-1000-8000-00805f9b34fb"); + QCOMPARE(chars[6].uuid(), QBluetoothUuid(temp)); + QCOMPARE(chars[6].handle(), 0x1eu); + QCOMPARE(chars[6].properties(), + (QLowEnergyCharacteristic::Read)); + QCOMPARE(chars[6].value(), QByteArray("546578617320496e737472756d656e747300")); + QVERIFY(chars[6].isValid()); + QCOMPARE(chars[6].descriptors().count(), 0); + + // IEEE + temp = QString("00002a2a-0000-1000-8000-00805f9b34fb"); + QCOMPARE(chars[7].uuid(), QBluetoothUuid(temp)); + QCOMPARE(chars[7].handle(), 0x20u); + QCOMPARE(chars[7].properties(), + (QLowEnergyCharacteristic::Read)); + QCOMPARE(chars[7].value(), QByteArray("fe006578706572696d656e74616c")); + QVERIFY(chars[7].isValid()); + QCOMPARE(chars[7].descriptors().count(), 0); + + // PnP ID + temp = QString("00002a50-0000-1000-8000-00805f9b34fb"); + QCOMPARE(chars[8].uuid(), QBluetoothUuid(temp)); + QCOMPARE(chars[8].handle(), 0x22u); + QCOMPARE(chars[8].properties(), + (QLowEnergyCharacteristic::Read)); + QCOMPARE(chars[8].value(), QByteArray("010d0000001001")); + QVERIFY(chars[8].isValid()); + QCOMPARE(chars[8].descriptors().count(), 0); + } else if (info->serviceUuid() == + QBluetoothUuid(QString("f000aa00-0451-4000-b000-000000000000"))) { + qDebug() << "Verifying Temperature"; + QList<QLowEnergyCharacteristic> chars = info->characteristics(); + QCOMPARE(chars.count(), 2); + + // Temp Data + QString temp("f000aa01-0451-4000-b000-000000000000"); + QCOMPARE(chars[0].uuid(), QBluetoothUuid(temp)); + QCOMPARE(chars[0].handle(), 0x25u); + QCOMPARE(chars[0].properties(), + (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Notify)); + //QCOMPARE(chars[0].value(), QByteArray("30303030")); + QVERIFY(chars[0].isValid()); +// QCOMPARE(chars[0].descriptors().count(), 1); +// +// QCOMPARE(chars[0].descriptors().at(0).handle(), 0x26u); +// QCOMPARE(chars[0].descriptors().at(0).uuid(), QBluetoothUuid(temp)); +// QCOMPARE(chars[0].descriptors().at(0).value(), QByteArray("0000")); +// temp = QString("00002902-0000-1000-8000-00805f9b34fb"); +// QCOMPARE(QBluetoothUuid(chars[0].descriptors().at(0).type()), QBluetoothUuid(temp)); + + // Temp Config + temp = QString("f000aa02-0451-4000-b000-000000000000"); + QCOMPARE(chars[1].uuid(), QBluetoothUuid(temp)); + QCOMPARE(chars[1].handle(), 0x29u); + QCOMPARE(chars[1].properties(), + (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Write)); + QCOMPARE(chars[1].value(), QByteArray("00")); + QVERIFY(chars[1].isValid()); + QCOMPARE(chars[1].descriptors().count(), 0); + } else if (info->serviceUuid() == + QBluetoothUuid(QString("0000ffe0-0000-1000-8000-00805f9b34fb"))) { + qDebug() << "Verifying Simple Keys"; + QList<QLowEnergyCharacteristic> chars = info->characteristics(); + QCOMPARE(chars.count(), 1); + + // Temp Data + QString temp("0000ffe1-0000-1000-8000-00805f9b34fb"); + QCOMPARE(chars[0].uuid(), QBluetoothUuid(temp)); + QCOMPARE(chars[0].handle(), 0x5fu); + QCOMPARE(chars[0].properties(), + (QLowEnergyCharacteristic::Notify)); + QCOMPARE(chars[0].value(), QByteArray("")); + QVERIFY(chars[0].isValid()); + QCOMPARE(chars[0].descriptors().count(), 0); //TODO should be 1? + } else if (info->serviceUuid() == + QBluetoothUuid(QString("f000aa10-0451-4000-b000-000000000000"))) { + qDebug() << "Verifying Accelerometer"; + QList<QLowEnergyCharacteristic> chars = info->characteristics(); + QCOMPARE(chars.count(), 3); + + // Accel Data + QString temp("f000aa11-0451-4000-b000-000000000000"); + QCOMPARE(chars[0].uuid(), QBluetoothUuid(temp)); + QCOMPARE(chars[0].handle(), 0x2du); + QCOMPARE(chars[0].properties(), + (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Notify)); + QCOMPARE(chars[0].value(), QByteArray("000000")); + QVERIFY(chars[0].isValid()); +// QCOMPARE(chars[0].descriptors().count(), 1); + +// QCOMPARE(chars[0].descriptors().at(0).handle(), 0x2eu); +// QCOMPARE(chars[0].descriptors().at(0).uuid(), QBluetoothUuid(temp)); +// QCOMPARE(chars[0].descriptors().at(0).value(), QByteArray("0000")); +// temp = QString("00002902-0000-1000-8000-00805f9b34fb"); +// QCOMPARE(QBluetoothUuid(chars[0].descriptors().at(0).type()), QBluetoothUuid(temp)); + + // Accel Config + temp = QString("f000aa12-0451-4000-b000-000000000000"); + QCOMPARE(chars[1].uuid(), QBluetoothUuid(temp)); + QCOMPARE(chars[1].handle(), 0x31u); + QCOMPARE(chars[1].properties(), + (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Write)); + QCOMPARE(chars[1].value(), QByteArray("00")); + QVERIFY(chars[1].isValid()); + QCOMPARE(chars[1].descriptors().count(), 0); + + // Accel Period + temp = QString("f000aa13-0451-4000-b000-000000000000"); + QCOMPARE(chars[2].uuid(), QBluetoothUuid(temp)); + QCOMPARE(chars[2].handle(), 0x34u); + QCOMPARE(chars[2].properties(), + (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Write)); + QCOMPARE(chars[2].value(), QByteArray("64")); // don't change it or set it to 0x64 + QVERIFY(chars[2].isValid()); + QCOMPARE(chars[2].descriptors().count(), 0); + } else if (info->serviceUuid() == + QBluetoothUuid(QString("f000aa20-0451-4000-b000-000000000000"))) { + qDebug() << "Verifying Humidity"; + QList<QLowEnergyCharacteristic> chars = info->characteristics(); + QCOMPARE(chars.count(), 2); + + // Humidity Data + QString temp("f000aa21-0451-4000-b000-000000000000"); + QCOMPARE(chars[0].uuid(), QBluetoothUuid(temp)); + QCOMPARE(chars[0].handle(), 0x38u); + QCOMPARE(chars[0].properties(), + (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Notify)); + QCOMPARE(chars[0].value(), QByteArray("00000000")); + QVERIFY(chars[0].isValid()); +// QCOMPARE(chars[0].descriptors().count(), 1); + +// QCOMPARE(chars[0].descriptors().at(0).handle(), 0x39u); +// QCOMPARE(chars[0].descriptors().at(0).uuid(), QBluetoothUuid(temp)); +// QCOMPARE(chars[0].descriptors().at(0).value(), QByteArray("0000")); +// temp = QString("00002902-0000-1000-8000-00805f9b34fb"); +// QCOMPARE(QBluetoothUuid(chars[0].descriptors().at(0).type()), QBluetoothUuid(temp)); + + // Humidity Config + temp = QString("f000aa22-0451-4000-b000-000000000000"); + QCOMPARE(chars[1].uuid(), QBluetoothUuid(temp)); + QCOMPARE(chars[1].handle(), 0x3cu); + QCOMPARE(chars[1].properties(), + (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Write)); + QCOMPARE(chars[1].value(), QByteArray("00")); + QVERIFY(chars[1].isValid()); + QCOMPARE(chars[1].descriptors().count(), 0); + } else if (info->serviceUuid() == + QBluetoothUuid(QString("f000aa30-0451-4000-b000-000000000000"))) { + qDebug() << "Verifying Magnetometer"; + QList<QLowEnergyCharacteristic> chars = info->characteristics(); + QCOMPARE(chars.count(), 3); + + // Magnetometer Data + QString temp("f000aa31-0451-4000-b000-000000000000"); + QCOMPARE(chars[0].uuid(), QBluetoothUuid(temp)); + QCOMPARE(chars[0].handle(), 0x40u); + QCOMPARE(chars[0].properties(), + (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Notify)); + QCOMPARE(chars[0].value(), QByteArray("000000000000")); + QVERIFY(chars[0].isValid()); +// QCOMPARE(chars[0].descriptors().count(), 1); + +// QCOMPARE(chars[0].descriptors().at(0).handle(), 0x41u); +// QCOMPARE(chars[0].descriptors().at(0).uuid(), QBluetoothUuid(temp)); +// QCOMPARE(chars[0].descriptors().at(0).value(), QByteArray("0000")); +// temp = QString("00002902-0000-1000-8000-00805f9b34fb"); +// QCOMPARE(QBluetoothUuid(chars[0].descriptors().at(0).type()), QBluetoothUuid(temp)); + + // Magnetometer Config + temp = QString("f000aa32-0451-4000-b000-000000000000"); + QCOMPARE(chars[1].uuid(), QBluetoothUuid(temp)); + QCOMPARE(chars[1].handle(), 0x44u); + QCOMPARE(chars[1].properties(), + (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Write)); + QCOMPARE(chars[1].value(), QByteArray("00")); + QVERIFY(chars[1].isValid()); + QCOMPARE(chars[1].descriptors().count(), 0); + + // Magnetometer Period + temp = QString("f000aa33-0451-4000-b000-000000000000"); + QCOMPARE(chars[2].uuid(), QBluetoothUuid(temp)); + QCOMPARE(chars[2].handle(), 0x47u); + QCOMPARE(chars[2].properties(), + (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Write)); + QCOMPARE(chars[2].value(), QByteArray("c8")); // don't change it or set it to 0xc8 + QVERIFY(chars[2].isValid()); + QCOMPARE(chars[2].descriptors().count(), 0); + } else if (info->serviceUuid() == + QBluetoothUuid(QString("f000aa40-0451-4000-b000-000000000000"))) { + qDebug() << "Verifying Pressure"; + QList<QLowEnergyCharacteristic> chars = info->characteristics(); + QCOMPARE(chars.count(), 3); + + // Pressure Data + QString temp("f000aa41-0451-4000-b000-000000000000"); + QCOMPARE(chars[0].uuid(), QBluetoothUuid(temp)); + QCOMPARE(chars[0].handle(), 0x4bu); + QCOMPARE(chars[0].properties(), + (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Notify)); + QCOMPARE(chars[0].value(), QByteArray("00000000")); + QVERIFY(chars[0].isValid()); + //QEXPECT_FAIL("", "0x4b has at least a notification", Continue); +// QCOMPARE(chars[0].descriptors().count(), 1); + +// if (!chars[0].descriptors().isEmpty()) { +// temp = QString("00002902-0000-1000-8000-00805f9b34fb"); +// QCOMPARE(chars[0].descriptors().at(0).handle(), 0x41u); +// QCOMPARE(chars[0].descriptors().at(0).uuid(), QBluetoothUuid(temp)); +// QCOMPARE(chars[0].descriptors().at(0).value(), QByteArray("0000")); +// } + + // Pressure Config + temp = QString("f000aa42-0451-4000-b000-000000000000"); + QCOMPARE(chars[1].uuid(), QBluetoothUuid(temp)); + QCOMPARE(chars[1].handle(), 0x4fu); + QCOMPARE(chars[1].properties(), + (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Write)); + QCOMPARE(chars[1].value(), QByteArray("00")); + QVERIFY(chars[1].isValid()); + QCOMPARE(chars[1].descriptors().count(), 0); + + // Pressure Calibration + temp = QString("f000aa43-0451-4000-b000-000000000000"); + QCOMPARE(chars[2].uuid(), QBluetoothUuid(temp)); + QCOMPARE(chars[2].handle(), 0x52u); + QCOMPARE(chars[2].properties(), + (QLowEnergyCharacteristic::Read)); + QCOMPARE(chars[2].value(), QByteArray("00000000000000000000000000000000")); // don't change it + QVERIFY(chars[2].isValid()); + QCOMPARE(chars[2].descriptors().count(), 0); + } else if (info->serviceUuid() == + QBluetoothUuid(QString("f000aa50-0451-4000-b000-000000000000"))) { + qDebug() << "Verifying Gyroscope"; + QList<QLowEnergyCharacteristic> chars = info->characteristics(); + QCOMPARE(chars.count(), 2); + + // Gyroscope Data + QString temp("f000aa51-0451-4000-b000-000000000000"); + QCOMPARE(chars[0].uuid(), QBluetoothUuid(temp)); + QCOMPARE(chars[0].handle(), 0x57u); + QCOMPARE(chars[0].properties(), + (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Notify)); + QCOMPARE(chars[0].value(), QByteArray("000000000000")); + QVERIFY(chars[0].isValid()); + //QEXPECT_FAIL("", "there is a notification under 0x0057", Continue); +// QCOMPARE(chars[0].descriptors().count(), 1); + +// if (!chars[0].descriptors().isEmpty()) { +// temp = QString("00002902-0000-1000-8000-00805f9b34fb"); +// QCOMPARE(chars[0].descriptors().at(0).handle(), 0x39u); +// QCOMPARE(chars[0].descriptors().at(0).uuid(), QBluetoothUuid(temp)); +// QCOMPARE(chars[0].descriptors().at(0).value(), QByteArray("0000")); +// } + + // Gyroscope Config + temp = QString("f000aa52-0451-4000-b000-000000000000"); + QCOMPARE(chars[1].uuid(), QBluetoothUuid(temp)); + QCOMPARE(chars[1].handle(), 0x5bu); + QCOMPARE(chars[1].properties(), + (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Write)); + QCOMPARE(chars[1].value(), QByteArray("00")); + QVERIFY(chars[1].isValid()); + QCOMPARE(chars[1].descriptors().count(), 0); + } else if (info->serviceUuid() == + QBluetoothUuid(QString("f000aa60-0451-4000-b000-000000000000"))) { + qDebug() << "Verifying Test Service"; + QList<QLowEnergyCharacteristic> chars = info->characteristics(); + QCOMPARE(chars.count(), 2); + + // Test Data + QString temp("f000aa61-0451-4000-b000-000000000000"); + QCOMPARE(chars[0].uuid(), QBluetoothUuid(temp)); + QCOMPARE(chars[0].handle(), 0x64u); + QCOMPARE(chars[0].properties(), + (QLowEnergyCharacteristic::Read)); + QCOMPARE(chars[0].value(), QByteArray("3f00")); + QVERIFY(chars[0].isValid()); + QCOMPARE(chars[0].descriptors().count(), 0); + + // Test Config + temp = QString("f000aa62-0451-4000-b000-000000000000"); + QCOMPARE(chars[1].uuid(), QBluetoothUuid(temp)); + QCOMPARE(chars[1].handle(), 0x67u); + QCOMPARE(chars[1].properties(), + (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Write)); + QCOMPARE(chars[1].value(), QByteArray("00")); + QVERIFY(chars[1].isValid()); + QCOMPARE(chars[1].descriptors().count(), 0); + } else if (info->serviceUuid() == + QBluetoothUuid(QString("f000ffc0-0451-4000-b000-000000000000"))) { + qDebug() << "Verifying Unknown Service"; + QList<QLowEnergyCharacteristic> chars = info->characteristics(); + QCOMPARE(chars.count(), 2); + + // first characteristic + QString temp("f000ffc1-0451-4000-b000-000000000000"); + QCOMPARE(chars[0].uuid(), QBluetoothUuid(temp)); + QCOMPARE(chars[0].handle(), 0x6bu); + QCOMPARE(chars[0].properties(), + (QLowEnergyCharacteristic::Notify|QLowEnergyCharacteristic::Write|QLowEnergyCharacteristicInfo::WriteNoResponse)); + QCOMPARE(chars[0].value(), QByteArray("")); + QVERIFY(chars[0].isValid()); +// QEXPECT_FAIL("", "there is a notification under 0x006b", Continue); +// QCOMPARE(chars[0].descriptors().count(), 1); + +// if (!chars[0].descriptors().isEmpty()) { +// temp = QString("00002902-0000-1000-8000-00805f9b34fb"); +// QCOMPARE(chars[0].descriptors().at(0).handle(), 0x39u); +// QCOMPARE(chars[0].descriptors().at(0).uuid(), QBluetoothUuid(temp)); +// QCOMPARE(chars[0].descriptors().at(0).value(), QByteArray("0000")); +// } + + // second characteristic + temp = QString("f000ffc2-0451-4000-b000-000000000000"); + QCOMPARE(chars[1].uuid(), QBluetoothUuid(temp)); + QCOMPARE(chars[1].handle(), 0x6fu); + QCOMPARE(chars[1].properties(), + (QLowEnergyCharacteristic::Notify|QLowEnergyCharacteristic::Write|QLowEnergyCharacteristicInfo::WriteNoResponse)); + QCOMPARE(chars[1].value(), QByteArray("")); + QVERIFY(chars[1].isValid()); +// QEXPECT_FAIL("", "there is a notification under 0x006f", Continue); +// QCOMPARE(chars[1].descriptors().count(), 1); + } else { + QFAIL(QString("Service not found" + info->serviceUuid().toString()).toUtf8().constData()); + } } void tst_QLowEnergyController::tst_defaultBehavior() |