qtbluetoothbluezgattqt5.7

Qt 5.7 QBluetooth LE GATT Server Example with Raspberry Pi 3 and BlueZ 5.39


I am trying to run the Qt 5.7 beta QBluetooth GATT Server example ( https://doc-snapshots.qt.io/qt5-dev/qtbluetooth-heartrate-server-example.html ) on a Raspberry Pi 3 with it's integrated Broadcom Bluetooth chip.

Bluetooth works great on my Pi 3 and the hci0 interface is "UP RUNNING" after a fresh boot:

root@raspberrypi:~/bluez-5.39# hciconfig -a
hci0:   Type: BR/EDR  Bus: UART
    BD Address: B8:27:EB:6F:71:A7  ACL MTU: 1021:8  SCO MTU: 64:1
    UP RUNNING PSCAN 
    RX bytes:2316 acl:0 sco:0 events:99 errors:0
    TX bytes:2676 acl:0 sco:0 commands:99 errors:0
    Features: 0xbf 0xfe 0xcf 0xfe 0xdb 0xff 0x7b 0x87
    Packet type: DM1 DM3 DM5 DH1 DH3 DH5 HV1 HV2 HV3 
    Link policy: RSWITCH SNIFF 
    Link mode: SLAVE ACCEPT 
    Name: 'raspberrypi'
    Class: 0x000000
    Service Classes: Unspecified
    Device Class: Miscellaneous, 
    HCI Version: 4.1 (0x7)  Revision: 0xb6
    LMP Version: 4.1 (0x7)  Subversion: 0x2209
    Manufacturer: Broadcom Corporation (15)

I successfully downloaded, compiled, and installed the latest BlueZ 5.39, which contains a complete DBus interface to the BlueZ BLE functionality:

http://www.kernel.org/pub/linux/bluetooth/bluez-5.39.tar.xz

I've confirmed that the versions picked up in the path are the right version:

[bluetooth]# version
Version 5.39

I used buildroot to cross compile Qt 5.7 and deployed it's libraries to the Pi. I can successfully build on my host machine and run the resulting executable on the Pi.

The GATT example from Qt is very simple and I only added 2 "qDebug" lines to see some console ouput. I'll paste it here for completeness:

#include <QtBluetooth/qlowenergyadvertisingdata.h>
#include <QtBluetooth/qlowenergyadvertisingparameters.h>
#include <QtBluetooth/qlowenergycharacteristic.h>
#include <QtBluetooth/qlowenergycharacteristicdata.h>
#include <QtBluetooth/qlowenergydescriptordata.h>
#include <QtBluetooth/qlowenergycontroller.h>
#include <QtBluetooth/qlowenergyservice.h>
#include <QtBluetooth/qlowenergyservicedata.h>
#include <QtCore/qbytearray.h>
#include <QtCore/qcoreapplication.h>
#include <QtCore/qlist.h>
#include <QtCore/qscopedpointer.h>
#include <QtCore/qtimer.h>

#include <QDebug>

int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);

    QLowEnergyAdvertisingData advertisingData;
    advertisingData.setDiscoverability(QLowEnergyAdvertisingData::DiscoverabilityGeneral);
    advertisingData.setIncludePowerLevel(true);
    advertisingData.setLocalName("BlueZ 5 GATT Server");
    advertisingData.setServices(QList<QBluetoothUuid>() << QBluetoothUuid::HeartRate);

    QLowEnergyCharacteristicData charData;
    charData.setUuid(QBluetoothUuid::HeartRateMeasurement);
    charData.setValue(QByteArray(2, 0));
    charData.setProperties(QLowEnergyCharacteristic::Notify);
    const QLowEnergyDescriptorData clientConfig(QBluetoothUuid::ClientCharacteristicConfiguration,
                                                QByteArray(2, 0));
    charData.addDescriptor(clientConfig);

    QLowEnergyServiceData serviceData;
    serviceData.setType(QLowEnergyServiceData::ServiceTypePrimary);
    serviceData.setUuid(QBluetoothUuid::HeartRate);
    serviceData.addCharacteristic(charData);

    const QScopedPointer<QLowEnergyController> leController(QLowEnergyController::createPeripheral());
    const QScopedPointer<QLowEnergyService> service(leController->addService(serviceData));
    qDebug() << "Beginning to advertise...";
    leController->startAdvertising(QLowEnergyAdvertisingParameters(), advertisingData,
                                   advertisingData);

    QTimer heartbeatTimer;
    quint8 currentHeartRate = 60;
    enum ValueChange { ValueUp, ValueDown } valueChange = ValueUp;
    const auto heartbeatProvider = [&service, &currentHeartRate, &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::HeartRateMeasurement);
        Q_ASSERT(characteristic.isValid());
        qDebug() << "Changing characteristic to: " << value;
        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);

    auto reconnect = [&leController, advertisingData]() {
        leController->startAdvertising(QLowEnergyAdvertisingParameters(), advertisingData,
                                       advertisingData);
    };
    QObject::connect(leController.data(), &QLowEnergyController::disconnected, reconnect);

    return app.exec();
}

Pro file:

TEMPLATE = app
TARGET = qt-gatt

QT += bluetooth
CONFIG += c++11

SOURCES += main.cpp

## Install directory
target.path = /home/pi
INSTALLS += target

When I run the app on the Pi as root after a fresh boot - it runs and outputs my print messages:

root@raspberrypi:/home/pi# ./qt-gatt 
Beginning to advertise...
Changing characteristic to:  "\x00<"
Changing characteristic to:  "\x00="
Changing characteristic to:  "\x00>"
Changing characteristic to:  "\x00?"

There are no errors or warnings displayed - but none of my devices can see it. My iPhone and Mac can see other BLE devices, but not the Pi. The code sets the Pi as "discoverable".

How can I make this work / What am I doing wrong?


Solution

  • You may already know this but I think bluetoothd still needs to be started with -E for experimental mode.

    Also you can add the following line at the top of main() to see logging from QBluetooth. Let us know what you see or figure out. I'm trying to do a very similar thing and having now luck...and the logging doesn't seem to work on my platform.

    QLoggingCategory::setFilterRules(QStringLiteral("qt.bluetooth* = true"));