summaryrefslogtreecommitdiffstats
path: root/examples/bluetooth/heartrate-game/devicehandler.cpp
diff options
context:
space:
mode:
authorAlex Blasche <alexander.blasche@qt.io>2017-02-28 15:30:46 +0100
committerOliver Wolff <oliver.wolff@qt.io>2017-04-03 06:26:30 +0000
commit283870e39be08e89050377cf2e4584b453cdc4e2 (patch)
tree9bdf769ca9cccc29e991cad418fb1f7e79f6d4ea /examples/bluetooth/heartrate-game/devicehandler.cpp
parent382bf8f2c656e5cb52dbad4b5b66b75a2e941f5c (diff)
Add new heartrate example
The example is a redesign by Lasse Räihä based in the heartrate-listener example. Change-Id: Iec0f48603408b37c7054839d520368eb1e436895 Reviewed-by: Oliver Wolff <oliver.wolff@qt.io>
Diffstat (limited to 'examples/bluetooth/heartrate-game/devicehandler.cpp')
-rw-r--r--examples/bluetooth/heartrate-game/devicehandler.cpp319
1 files changed, 319 insertions, 0 deletions
diff --git a/examples/bluetooth/heartrate-game/devicehandler.cpp b/examples/bluetooth/heartrate-game/devicehandler.cpp
new file mode 100644
index 00000000..da643ff5
--- /dev/null
+++ b/examples/bluetooth/heartrate-game/devicehandler.cpp
@@ -0,0 +1,319 @@
+/***************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "devicehandler.h"
+#include "deviceinfo.h"
+#include <QtEndian>
+
+DeviceHandler::DeviceHandler(QObject *parent) :
+ BluetoothBaseClass(parent),
+ m_control(0),
+ m_service(0),
+ m_currentDevice(0),
+ m_foundHeartRateService(false),
+ m_measuring(false),
+ m_currentValue(0),
+ m_min(0), m_max(0), m_sum(0), m_avg(0), m_calories(0)
+{
+#ifdef Q_OS_WIN32
+ m_demoTimer.setSingleShot(false);
+ m_demoTimer.setInterval(2000);
+ connect(&m_demoTimer, &QTimer::timeout, this, &DeviceHandler::updateDemoHR);
+ m_demoTimer.start();
+ updateDemoHR();
+#endif
+}
+
+void DeviceHandler::setDevice(DeviceInfo *device)
+{
+ clearMessages();
+ m_currentDevice = device;
+
+#ifdef Q_OS_WIN32
+ setInfo(tr("Demo device connected."));
+ return;
+#endif
+
+ // Disconnect and delete old connection
+ if (m_control) {
+ m_control->disconnectFromDevice();
+ delete m_control;
+ m_control = 0;
+ }
+
+ // Create new controller and connect it if device available
+ if (m_currentDevice) {
+
+ // Make connections
+ m_control = new QLowEnergyController(m_currentDevice->getDevice(), this);
+ connect(m_control, &QLowEnergyController::serviceDiscovered,
+ this, &DeviceHandler::serviceDiscovered);
+ connect(m_control, &QLowEnergyController::discoveryFinished,
+ this, &DeviceHandler::serviceScanDone);
+
+ connect(m_control, static_cast<void (QLowEnergyController::*)(QLowEnergyController::Error)>(&QLowEnergyController::error),
+ this, [this](QLowEnergyController::Error error) {
+ Q_UNUSED(error);
+ setError("Cannot connect to remote device.");
+ });
+ connect(m_control, &QLowEnergyController::connected, this, [this]() {
+ setInfo("Controller connected. Search services...");
+ m_control->discoverServices();
+ });
+ connect(m_control, &QLowEnergyController::disconnected, this, [this]() {
+ setError("LowEnergy controller disconnected");
+ });
+
+ // Connect
+ m_control->connectToDevice();
+ }
+}
+
+void DeviceHandler::startMeasurement()
+{
+ if (alive()) {
+ m_start = QDateTime::currentDateTime();
+ m_min = 0;
+ m_max = 0;
+ m_avg = 0;
+ m_sum = 0;
+ m_calories = 0;
+ m_measuring = true;
+ m_measurements.clear();
+ emit measuringChanged();
+ }
+}
+
+void DeviceHandler::stopMeasurement()
+{
+ m_measuring = false;
+ emit measuringChanged();
+}
+
+void DeviceHandler::serviceDiscovered(const QBluetoothUuid &gatt)
+{
+ if (gatt == QBluetoothUuid(QBluetoothUuid::HeartRate)) {
+ setInfo("Heart Rate service discovered. Waiting for service scan to be done...");
+ m_foundHeartRateService = true;
+ }
+}
+
+void DeviceHandler::serviceScanDone()
+{
+ setInfo("Service scan done.");
+
+ // Delete old service if available
+ if (m_service) {
+ delete m_service;
+ m_service = 0;
+ }
+
+ // If heartRateService found, create new service
+ if (m_foundHeartRateService)
+ m_service = m_control->createServiceObject(QBluetoothUuid(QBluetoothUuid::HeartRate), this);
+
+ if (m_service) {
+ connect(m_service, &QLowEnergyService::stateChanged, this, &DeviceHandler::serviceStateChanged);
+ connect(m_service, &QLowEnergyService::characteristicChanged, this, &DeviceHandler::updateHeartRateValue);
+ connect(m_service, &QLowEnergyService::descriptorWritten, this, &DeviceHandler::confirmedDescriptorWrite);
+ m_service->discoverDetails();
+ } else {
+ setError("Heart Rate Service not found.");
+ }
+}
+
+// Service functions
+void DeviceHandler::serviceStateChanged(QLowEnergyService::ServiceState s)
+{
+ switch (s) {
+ case QLowEnergyService::DiscoveringServices:
+ setInfo(tr("Discovering services..."));
+ break;
+ case QLowEnergyService::ServiceDiscovered:
+ {
+ setInfo(tr("Service discovered."));
+
+ const QLowEnergyCharacteristic hrChar = m_service->characteristic(QBluetoothUuid(QBluetoothUuid::HeartRateMeasurement));
+ if (!hrChar.isValid()) {
+ setError("HR Data not found.");
+ break;
+ }
+
+ m_notificationDesc = hrChar.descriptor(QBluetoothUuid::ClientCharacteristicConfiguration);
+ if (m_notificationDesc.isValid())
+ m_service->writeDescriptor(m_notificationDesc, QByteArray::fromHex("0100"));
+
+ break;
+ }
+ default:
+ //nothing for now
+ break;
+ }
+
+ emit aliveChanged();
+}
+
+void DeviceHandler::updateHeartRateValue(const QLowEnergyCharacteristic &c, const QByteArray &value)
+{
+ // ignore any other characteristic change -> shouldn't really happen though
+ if (c.uuid() != QBluetoothUuid(QBluetoothUuid::HeartRateMeasurement))
+ return;
+
+ const quint8 *data = reinterpret_cast<const quint8 *>(value.constData());
+ quint8 flags = data[0];
+
+ //Heart Rate
+ int hrvalue = 0;
+ if (flags & 0x1) // HR 16 bit? otherwise 8 bit
+ hrvalue = (int)qFromLittleEndian<quint16>(data[1]);
+ else
+ hrvalue = (int)data[1];
+
+ addMeasurement(hrvalue);
+}
+
+#ifdef Q_OS_WIN32
+void DeviceHandler::updateDemoHR()
+{
+ int randomValue = 0;
+ if (m_currentValue < 30) { // Initial value
+ randomValue = 55 + qrand()%30;
+ } else if (!m_measuring) { // Value when relax
+ randomValue = qBound(55, m_currentValue - 2 + qrand()%5, 75);
+ } else { // Measuring
+ randomValue = m_currentValue + qrand()%10 - 2;
+ }
+
+ addMeasurement(randomValue);
+}
+#endif
+
+void DeviceHandler::confirmedDescriptorWrite(const QLowEnergyDescriptor &d, const QByteArray &value)
+{
+ if (d.isValid() && d == m_notificationDesc && value == QByteArray("0000")) {
+ //disabled notifications -> assume disconnect intent
+ m_control->disconnectFromDevice();
+ delete m_service;
+ m_service = 0;
+ }
+}
+
+void DeviceHandler::disconnectService()
+{
+ m_foundHeartRateService = false;
+
+ //disable notifications
+ if (m_notificationDesc.isValid() && m_service) {
+ m_service->writeDescriptor(m_notificationDesc, QByteArray::fromHex("0000"));
+ } else {
+ if (m_control)
+ m_control->disconnectFromDevice();
+
+ delete m_service;
+ m_service = 0;
+ }
+}
+
+bool DeviceHandler::measuring() const
+{
+ return m_measuring;
+}
+
+bool DeviceHandler::alive() const
+{
+#ifdef Q_OS_WIN32
+ return true;
+#endif
+
+ if (m_service)
+ return m_service->state() == QLowEnergyService::ServiceDiscovered;
+
+ return false;
+}
+
+int DeviceHandler::hr() const
+{
+ return m_currentValue;
+}
+
+int DeviceHandler::time() const
+{
+ return m_start.secsTo(m_stop);
+}
+
+int DeviceHandler::maxHR() const
+{
+ return m_max;
+}
+
+int DeviceHandler::minHR() const
+{
+ return m_min;
+}
+
+float DeviceHandler::average() const
+{
+ return m_avg;
+}
+
+float DeviceHandler::calories() const
+{
+ return m_calories;
+}
+
+void DeviceHandler::addMeasurement(int value)
+{
+ m_currentValue = value;
+
+ // If measuring and value is appropriate
+ if (m_measuring && value > 30 && value < 250) {
+
+ m_stop = QDateTime::currentDateTime();
+ m_measurements << value;
+
+ m_min = m_min == 0 ? value : qMin(value, m_min);
+ m_max = qMax(value, m_max);
+ m_sum += value;
+ m_avg = (double)m_sum / m_measurements.size();
+ m_calories = ((-55.0969 + (0.6309 * m_avg) + (0.1988 * 94) + (0.2017 * 24)) / 4.184) * 60 * time()/3600;
+ }
+
+ emit statsChanged();
+}