summaryrefslogtreecommitdiffstats
path: root/core/qt-ble.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'core/qt-ble.cpp')
-rw-r--r--core/qt-ble.cpp228
1 files changed, 228 insertions, 0 deletions
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