diff options
Diffstat (limited to 'core')
-rw-r--r-- | core/CMakeLists.txt | 5 | ||||
-rw-r--r-- | core/qt-ble.cpp | 228 | ||||
-rw-r--r-- | core/qt-ble.h | 38 | ||||
-rw-r--r-- | core/qtserialbluetooth.cpp | 14 |
4 files changed, 284 insertions, 1 deletions
diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 77521d6a1..5622f5e65 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -24,6 +24,11 @@ if(BTSUPPORT) set(BT_CORE_SRC_FILES qtserialbluetooth.cpp) endif() +if(BLESUPPORT) + add_definitions(-DBLE_SUPPORT) + set(BT_CORE_SRC_FILES qt-ble.cpp) +endif() + # compile the core library, in C. set(SUBSURFACE_CORE_LIB_SRCS btdiscovery.cpp diff --git a/core/qt-ble.cpp b/core/qt-ble.cpp new file mode 100644 index 000000000..3fe7e3eea --- /dev/null +++ b/core/qt-ble.cpp @@ -0,0 +1,228 @@ +#include <errno.h> + +#include <QtBluetooth/QBluetoothAddress> +#include <QLowEnergyController> +#include <QEventLoop> +#include <QTimer> +#include <QDebug> + +#include <libdivecomputer/version.h> + +#include "libdivecomputer.h" +#include "core/qt-ble.h" + +#if defined(SSRF_CUSTOM_IO) + +#include <libdivecomputer/custom_io.h> + +extern "C" { + +void BLEObject::serviceStateChanged(QLowEnergyService::ServiceState s) +{ + QList<QLowEnergyCharacteristic> list; + + list = service->characteristics(); + + Q_FOREACH(QLowEnergyCharacteristic c, list) { + fprintf(stderr, " %s\n", c.uuid().toString().toUtf8().data()); + } +} + +void BLEObject::characteristcStateChanged(const QLowEnergyCharacteristic &c, const QByteArray &value) +{ + receivedPackets.append(value); + waitForPacket.exit(); +} + +void BLEObject::writeCompleted(const QLowEnergyDescriptor &d, const QByteArray &value) +{ + fprintf(stderr, "Write completed\n"); +} + +void BLEObject::addService(const QBluetoothUuid &newService) +{ + const char *uuid = newService.toString().toUtf8().data(); + + fprintf(stderr, "Found service %s\n", uuid); + if (uuid[1] == '0') { + fprintf(stderr, " .. ignoring\n"); + return; + } + service = controller->createServiceObject(newService, this); + fprintf(stderr, " .. created service object %p\n", service); + if (service) { + connect(service, &QLowEnergyService::stateChanged, this, &BLEObject::serviceStateChanged); + connect(service, &QLowEnergyService::characteristicChanged, this, &BLEObject::characteristcStateChanged); + connect(service, &QLowEnergyService::descriptorWritten, this, &BLEObject::writeCompleted); + service->discoverDetails(); + } +} + +BLEObject::BLEObject(QLowEnergyController *c) +{ + controller = c; +} + +BLEObject::~BLEObject() +{ +fprintf(stderr, "Deleting BLE object\n"); +} + +dc_status_t BLEObject::write(const void* data, size_t size, size_t *actual) +{ + QList<QLowEnergyCharacteristic> list = service->characteristics(); + QByteArray bytes((const char *)data, (int) size); + + if (!list.isEmpty()) { + const QLowEnergyCharacteristic &c = list.constFirst(); + QLowEnergyService::WriteMode mode; + + mode = (c.properties() & QLowEnergyCharacteristic::WriteNoResponse) ? + QLowEnergyService::WriteWithoutResponse : + QLowEnergyService::WriteWithResponse; + + service->writeCharacteristic(c, bytes, mode); + return DC_STATUS_SUCCESS; + } + + return DC_STATUS_IO; +} + +dc_status_t BLEObject::read(void* data, size_t size, size_t *actual) +{ + if (receivedPackets.isEmpty()) { + QList<QLowEnergyCharacteristic> list = service->characteristics(); + if (list.isEmpty()) + return DC_STATUS_IO; + + const QLowEnergyCharacteristic &c = list.constLast(); + + QTimer timer; + int msec = 5000; + timer.setSingleShot(true); + + waitForPacket.connect(&timer, SIGNAL(timeout()), SLOT(quit())); + timer.start(msec); + waitForPacket.exec(); + } + + // Still no packet? + if (receivedPackets.isEmpty()) + return DC_STATUS_IO; + + QByteArray packet = receivedPackets.takeFirst(); + if (size > packet.size()) + size = packet.size(); + memcpy(data, packet.data(), size); + *actual = size; + return DC_STATUS_SUCCESS; +} + +dc_status_t qt_ble_open(dc_custom_io_t *io, dc_context_t *context, const char *devaddr) +{ + QBluetoothAddress remoteDeviceAddress(devaddr); + + // HACK ALERT! Qt 5.9 needs this for proper Bluez operation + qputenv("QT_DEFAULT_CENTRAL_SERVICES", "1"); + + QLowEnergyController *controller = new QLowEnergyController(remoteDeviceAddress); + +fprintf(stderr, "qt_ble_open(%s)\n", devaddr); + + // Wait until the connection succeeds or until an error occurs + QEventLoop loop; + loop.connect(controller, SIGNAL(connected()), SLOT(quit())); + loop.connect(controller, SIGNAL(error(QLowEnergyController::Error)), SLOT(quit())); + + // Create a timer. If the connection doesn't succeed after five seconds or no error occurs then stop the opening step + QTimer timer; + int msec = 5000; + timer.setSingleShot(true); + loop.connect(&timer, SIGNAL(timeout()), SLOT(quit())); + + // Try to connect to the device + controller->connectToDevice(); + timer.start(msec); + loop.exec(); + + switch (controller->state()) { + case QLowEnergyController::ConnectedState: + break; + default: + report_error("Failed to connect to %s: '%s'", devaddr, controller->errorString().toUtf8().data()); + controller->disconnectFromDevice(); + delete controller; + return DC_STATUS_IO; + } + +fprintf(stderr, "Connected to device %s\n", devaddr); + + /* We need to discover services etc here! */ + BLEObject *ble = new BLEObject(controller); + loop.connect(controller, SIGNAL(discoveryFinished()), SLOT(quit())); + ble->connect(controller, SIGNAL(serviceDiscovered(QBluetoothUuid)), SLOT(addService(QBluetoothUuid))); + +fprintf(stderr, " .. discovering services\n"); + + controller->discoverServices(); + timer.start(msec); + loop.exec(); + +fprintf(stderr, " .. done discovering services\n"); + +fprintf(stderr, " .. discovering details\n"); + + timer.start(msec); + loop.exec(); + +fprintf(stderr, " .. done waiting\n"); + +fprintf(stderr, " .. enabling notifications\n"); + + /* Enable notifications */ + QList<QLowEnergyCharacteristic> list = ble->service->characteristics(); + + if (!list.isEmpty()) { + const QLowEnergyCharacteristic &c = list.constLast(); + QList<QLowEnergyDescriptor> l = c.descriptors(); + +fprintf(stderr, "Descriptor list (%p, %d)\n", l, l.length()); + + if (!l.isEmpty()) { + QLowEnergyDescriptor d = l.first(); + +fprintf(stderr, "Descriptor: %s uuid: %s\n", d.name().toUtf8().data(), d.uuid().toString().toUtf8().data()); + + ble->service->writeDescriptor(d, QByteArray::fromHex("0100")); + } + } + + // Fill in info + io->userdata = (void *)ble; + return DC_STATUS_SUCCESS; +} + +dc_status_t qt_ble_close(dc_custom_io_t *io) +{ + BLEObject *ble = (BLEObject *) io->userdata; + + io->userdata = NULL; + delete ble; + + return DC_STATUS_SUCCESS; +} + +dc_status_t qt_ble_read(dc_custom_io_t *io, void* data, size_t size, size_t *actual) +{ + BLEObject *ble = (BLEObject *) io->userdata; + return ble->read(data, size, actual); +} + +dc_status_t qt_ble_write(dc_custom_io_t *io, const void* data, size_t size, size_t *actual) +{ + BLEObject *ble = (BLEObject *) io->userdata; + return ble->write(data, size, actual); +} + +} /* extern "C" */ +#endif diff --git a/core/qt-ble.h b/core/qt-ble.h new file mode 100644 index 000000000..b819b0123 --- /dev/null +++ b/core/qt-ble.h @@ -0,0 +1,38 @@ +#ifndef QT_BLE_H +#define QT_BLE_H + +#include <QLowEnergyController> +#include <QEventLoop> + +class BLEObject : public QObject +{ + Q_OBJECT + +public: + BLEObject(QLowEnergyController *c); + ~BLEObject(); + dc_status_t write(const void* data, size_t size, size_t *actual); + dc_status_t read(void* data, size_t size, size_t *actual); + QLowEnergyService *service; + +public slots: + void addService(const QBluetoothUuid &newService); + void serviceStateChanged(QLowEnergyService::ServiceState s); + void characteristcStateChanged(const QLowEnergyCharacteristic &c, const QByteArray &value); + void writeCompleted(const QLowEnergyDescriptor &d, const QByteArray &value); + +private: + QLowEnergyController *controller; + QList<QByteArray> receivedPackets; + QEventLoop waitForPacket; +}; + + +extern "C" { +dc_status_t qt_ble_open(dc_custom_io_t *io, dc_context_t *context, const char *name); +dc_status_t qt_ble_read(dc_custom_io_t *io, void* data, size_t size, size_t *actual); +dc_status_t qt_ble_write(dc_custom_io_t *io, const void* data, size_t size, size_t *actual); +dc_status_t qt_ble_close(dc_custom_io_t *io); +} + +#endif diff --git a/core/qtserialbluetooth.cpp b/core/qtserialbluetooth.cpp index d23265d42..ab05a1c73 100644 --- a/core/qtserialbluetooth.cpp +++ b/core/qtserialbluetooth.cpp @@ -19,6 +19,10 @@ #include <libdivecomputer/custom_io.h> +#ifdef BLE_SUPPORT +# include "qt-ble.h" +#endif + QList<QBluetoothUuid> registeredUuids; static QBluetoothUuid getBtUuid() @@ -414,7 +418,15 @@ dc_custom_io_t qt_serial_ops = { .serial_set_dtr = NULL, .serial_set_rts = NULL, .serial_set_halfduplex = NULL, - .serial_set_break = NULL + .serial_set_break = NULL, + +#ifdef BLE_SUPPORT + .packet_size = 20, + .packet_open = qt_ble_open, + .packet_close = qt_ble_close, + .packet_read = qt_ble_read, + .packet_write = qt_ble_write, +#endif }; dc_custom_io_t* get_qt_serial_ops() { |