Merge PR #99: New Plugin: TexasInstruments, replaces MultiSensor plugin

This commit is contained in:
Jenkins 2019-04-12 13:42:43 +02:00
commit c7a948d851
14 changed files with 2445 additions and 0 deletions

15
debian/control vendored
View File

@ -497,6 +497,20 @@ Description: nymea.io plugin for Sonoff-Tasmota devices
.
This package will install the nymea.io plugin for Tasmota-Sonoff devices
Package: nymea-plugin-texasinstruments
Architecture: any
Depends: ${shlibs:Depends},
${misc:Depends},
nymea-plugins-translations,
Description: nymea.io plugin for Texas Instruments devices
The nymea daemon is a plugin based IoT (Internet of Things) server. The
server works like a translator for devices, things and services and
allows them to interact.
With the powerful rule engine you are able to connect any device available
in the system and create individual scenes and behaviors for your environment.
.
This package will install the nymea.io plugin for Texas Instruments devices
Package: nymea-plugin-udpcommander
Architecture: any
@ -761,6 +775,7 @@ Depends: nymea-plugin-awattar,
nymea-plugin-kodi,
nymea-plugin-lgsmarttv,
nymea-plugin-mailnotification,
nymea-plugin-multisensor,
nymea-plugin-netatmo,
nymea-plugin-networkdetector,
nymea-plugin-openweathermap,

View File

@ -0,0 +1 @@
usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_deviceplugintexasinstruments.so

View File

@ -40,6 +40,7 @@ PLUGIN_DIRS = \
snapd \
tasmota \
tcpcommander \
texasinstruments \
udpcommander \
unipi \
unitec \

View File

@ -0,0 +1,255 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2019 Michael Zanetti <michael.zanetti@nymea.io> *
* *
* This file is part of nymea. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Lesser General Public *
* License as published by the Free Software Foundation; either *
* version 2.1 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; If not, see *
* <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*!
\page texasinstruments.html
\title Texas Instruments
\brief Plugin for Texas Instruments Devices.
\ingroup plugins
\ingroup nymea-plugins
This plugin supports devices from Texas Instruments.
Currently supported devices:
* TI SensorTag (CC2650).
\chapter TI SensorTag (CC2650)
Using Bluetooth-LE, the TI SensorTag device class allows nymea to interact with those sensors. All supported
sensors of the device are supported:
* Temperature
* IR Temperature
* Humidity
* Pressure
* Light intensity
* Motion
* Magnetic Objects
Besides reading the sensor values, the buttons, buzzer and LEDs can be read and/or controlled.
\chapter Plugin defintion
\quotefile plugins/deviceplugins/multisensor/deviceplugintexasinstruments.json
\sa {The plugin JSON File}
*/
#include "deviceplugintexasinstruments.h"
#include "plugininfo.h"
#include "sensortag.h"
#include "hardware/bluetoothlowenergy/bluetoothlowenergymanager.h"
#include "plugintimer.h"
#include <QBluetoothDeviceInfo>
DevicePluginTexasInstruments::DevicePluginTexasInstruments(QObject *parent) : DevicePlugin(parent)
{
}
DevicePluginTexasInstruments::~DevicePluginTexasInstruments()
{
}
DeviceManager::DeviceError DevicePluginTexasInstruments::discoverDevices(const DeviceClassId &deviceClassId, const ParamList &params)
{
Q_UNUSED(params)
Q_ASSERT_X(deviceClassId == sensorTagDeviceClassId, "DevicePluginTexasInstruments", "Unhandled DeviceClassId!");
if (!hardwareManager()->bluetoothLowEnergyManager()->available() || !hardwareManager()->bluetoothLowEnergyManager()->enabled()) {
return DeviceManager::DeviceErrorHardwareNotAvailable;
}
BluetoothDiscoveryReply *reply = hardwareManager()->bluetoothLowEnergyManager()->discoverDevices();
connect(reply, &BluetoothDiscoveryReply::finished, this, [this, reply](){
reply->deleteLater();
if (reply->error() != BluetoothDiscoveryReply::BluetoothDiscoveryReplyErrorNoError) {
qCWarning(dcTexasInstruments()) << "Bluetooth discovery error:" << reply->error();
emit devicesDiscovered(sensorTagDeviceClassId, QList<DeviceDescriptor>());
return;
}
QList<DeviceDescriptor> deviceDescriptors;
foreach (const QBluetoothDeviceInfo &deviceInfo, reply->discoveredDevices()) {
if (deviceInfo.name().contains("SensorTag")) {
DeviceDescriptor descriptor(sensorTagDeviceClassId, "Sensor Tag", deviceInfo.address().toString());
Devices existingDevice = myDevices().filterByParam(sensorTagDeviceMacParamTypeId, deviceInfo.address().toString());
if (!existingDevice.isEmpty()) {
descriptor.setDeviceId(existingDevice.first()->id());
}
ParamList params;
params.append(Param(sensorTagDeviceMacParamTypeId, deviceInfo.address().toString()));
foreach (Device *existingDevice, myDevices()) {
if (existingDevice->paramValue(sensorTagDeviceMacParamTypeId).toString() == deviceInfo.address().toString()) {
descriptor.setDeviceId(existingDevice->id());
break;
}
}
descriptor.setParams(params);
deviceDescriptors.append(descriptor);
}
}
emit devicesDiscovered(sensorTagDeviceClassId, deviceDescriptors);
});
return DeviceManager::DeviceErrorAsync;
}
DeviceManager::DeviceSetupStatus DevicePluginTexasInstruments::setupDevice(Device *device)
{
qCDebug(dcTexasInstruments()) << "Setting up Multi Sensor" << device->name() << device->params();
QBluetoothAddress address = QBluetoothAddress(device->paramValue(sensorTagDeviceMacParamTypeId).toString());
QBluetoothDeviceInfo deviceInfo = QBluetoothDeviceInfo(address, device->name(), 0);
BluetoothLowEnergyDevice *bluetoothDevice = hardwareManager()->bluetoothLowEnergyManager()->registerDevice(deviceInfo, QLowEnergyController::PublicAddress);
SensorTag *sensorTag = new SensorTag(device, bluetoothDevice, this);
m_sensorTags.insert(device, sensorTag);
if (!m_reconnectTimer) {
m_reconnectTimer = hardwareManager()->pluginTimerManager()->registerTimer(10);
connect(m_reconnectTimer, &PluginTimer::timeout, this, [this](){
foreach (SensorTag *sensorTag, m_sensorTags) {
if (!sensorTag->bluetoothDevice()->connected()) {
sensorTag->bluetoothDevice()->connectDevice();
}
}
});
}
return DeviceManager::DeviceSetupStatusSuccess;
}
void DevicePluginTexasInstruments::postSetupDevice(Device *device)
{
// Try to connect right after setup
SensorTag *sensorTag = m_sensorTags.value(device);
// Configure sensor with state configurations
sensorTag->setTemperatureSensorEnabled(device->stateValue(sensorTagTemperatureSensorEnabledStateTypeId).toBool());
sensorTag->setHumiditySensorEnabled(device->stateValue(sensorTagHumiditySensorEnabledStateTypeId).toBool());
sensorTag->setPressureSensorEnabled(device->stateValue(sensorTagPressureSensorEnabledStateTypeId).toBool());
sensorTag->setOpticalSensorEnabled(device->stateValue(sensorTagOpticalSensorEnabledStateTypeId).toBool());
sensorTag->setAccelerometerEnabled(device->stateValue(sensorTagAccelerometerEnabledStateTypeId).toBool());
sensorTag->setGyroscopeEnabled(device->stateValue(sensorTagGyroscopeEnabledStateTypeId).toBool());
sensorTag->setMagnetometerEnabled(device->stateValue(sensorTagMagnetometerEnabledStateTypeId).toBool());
sensorTag->setMeasurementPeriod(device->stateValue(sensorTagMeasurementPeriodStateTypeId).toInt());
sensorTag->setMeasurementPeriodMovement(device->stateValue(sensorTagMeasurementPeriodMovementStateTypeId).toInt());
// Connect to the sensor
sensorTag->bluetoothDevice()->connectDevice();
}
void DevicePluginTexasInstruments::deviceRemoved(Device *device)
{
if (!m_sensorTags.contains(device)) {
return;
}
SensorTag *sensorTag = m_sensorTags.take(device);
hardwareManager()->bluetoothLowEnergyManager()->unregisterDevice(sensorTag->bluetoothDevice());
sensorTag->deleteLater();
if (myDevices().isEmpty()) {
hardwareManager()->pluginTimerManager()->unregisterTimer(m_reconnectTimer);
m_reconnectTimer = nullptr;
}
}
DeviceManager::DeviceError DevicePluginTexasInstruments::executeAction(Device *device, const Action &action)
{
SensorTag *sensorTag = m_sensorTags.value(device);
if (action.actionTypeId() == sensorTagBuzzerActionTypeId) {
sensorTag->setBuzzerPower(action.param(sensorTagBuzzerActionBuzzerParamTypeId).value().toBool());
return DeviceManager::DeviceErrorNoError;
} else if (action.actionTypeId() == sensorTagGreenLedActionTypeId) {
sensorTag->setGreenLedPower(action.param(sensorTagGreenLedActionGreenLedParamTypeId).value().toBool());
return DeviceManager::DeviceErrorNoError;
} else if (action.actionTypeId() == sensorTagRedLedActionTypeId) {
sensorTag->setRedLedPower(action.param(sensorTagRedLedActionRedLedParamTypeId).value().toBool());
return DeviceManager::DeviceErrorNoError;
} else if (action.actionTypeId() == sensorTagBuzzerImpulseActionTypeId) {
sensorTag->buzzerImpulse();
return DeviceManager::DeviceErrorNoError;
} else if (action.actionTypeId() == sensorTagTemperatureSensorEnabledActionTypeId) {
bool enabled = action.param(sensorTagTemperatureSensorEnabledActionTemperatureSensorEnabledParamTypeId).value().toBool();
device->setStateValue(sensorTagTemperatureSensorEnabledStateTypeId, enabled);
sensorTag->setTemperatureSensorEnabled(enabled);
return DeviceManager::DeviceErrorNoError;
} else if (action.actionTypeId() == sensorTagHumiditySensorEnabledActionTypeId) {
bool enabled = action.param(sensorTagHumiditySensorEnabledActionHumiditySensorEnabledParamTypeId).value().toBool();
device->setStateValue(sensorTagHumiditySensorEnabledStateTypeId, enabled);
sensorTag->setHumiditySensorEnabled(enabled);
return DeviceManager::DeviceErrorNoError;
} else if (action.actionTypeId() == sensorTagPressureSensorEnabledActionTypeId) {
bool enabled = action.param(sensorTagPressureSensorEnabledActionPressureSensorEnabledParamTypeId).value().toBool();
device->setStateValue(sensorTagPressureSensorEnabledStateTypeId, enabled);
sensorTag->setPressureSensorEnabled(enabled);
return DeviceManager::DeviceErrorNoError;
} else if (action.actionTypeId() == sensorTagOpticalSensorEnabledActionTypeId) {
bool enabled = action.param(sensorTagOpticalSensorEnabledActionOpticalSensorEnabledParamTypeId).value().toBool();
device->setStateValue(sensorTagOpticalSensorEnabledStateTypeId, enabled);
sensorTag->setOpticalSensorEnabled(enabled);
return DeviceManager::DeviceErrorNoError;
} else if (action.actionTypeId() == sensorTagAccelerometerEnabledActionTypeId) {
bool enabled = action.param(sensorTagAccelerometerEnabledActionAccelerometerEnabledParamTypeId).value().toBool();
device->setStateValue(sensorTagAccelerometerEnabledStateTypeId, enabled);
sensorTag->setAccelerometerEnabled(enabled);
return DeviceManager::DeviceErrorNoError;
} else if (action.actionTypeId() == sensorTagGyroscopeEnabledActionTypeId) {
bool enabled = action.param(sensorTagGyroscopeEnabledActionGyroscopeEnabledParamTypeId).value().toBool();
device->setStateValue(sensorTagGyroscopeEnabledStateTypeId, enabled);
sensorTag->setGyroscopeEnabled(enabled);
return DeviceManager::DeviceErrorNoError;
} else if (action.actionTypeId() == sensorTagMagnetometerEnabledActionTypeId) {
bool enabled = action.param(sensorTagMagnetometerEnabledActionMagnetometerEnabledParamTypeId).value().toBool();
device->setStateValue(sensorTagMagnetometerEnabledStateTypeId, enabled);
sensorTag->setMagnetometerEnabled(enabled);
return DeviceManager::DeviceErrorNoError;
} else if (action.actionTypeId() == sensorTagMeasurementPeriodActionTypeId) {
int period = action.param(sensorTagMeasurementPeriodActionMeasurementPeriodParamTypeId).value().toInt();
device->setStateValue(sensorTagMeasurementPeriodStateTypeId, period);
sensorTag->setMeasurementPeriod(period);
return DeviceManager::DeviceErrorNoError;
} else if (action.actionTypeId() == sensorTagMeasurementPeriodMovementActionTypeId) {
int period = action.param(sensorTagMeasurementPeriodMovementActionMeasurementPeriodMovementParamTypeId).value().toInt();
device->setStateValue(sensorTagMeasurementPeriodMovementStateTypeId, period);
sensorTag->setMeasurementPeriodMovement(period);
return DeviceManager::DeviceErrorNoError;
} else if (action.actionTypeId() == sensorTagMovementSensitivityActionTypeId) {
int sensitivity = action.param(sensorTagMovementSensitivityActionMovementSensitivityParamTypeId).value().toInt();
device->setStateValue(sensorTagMovementSensitivityStateTypeId, sensitivity);
sensorTag->setMovementSensitivity(sensitivity);
return DeviceManager::DeviceErrorNoError;
}
return DeviceManager::DeviceErrorActionTypeNotFound;
}

View File

@ -0,0 +1,56 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2019 Michael Zanetti <michael.zanetti@nymea.io> *
* *
* This file is part of nymea. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Lesser General Public *
* License as published by the Free Software Foundation; either *
* version 2.1 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; If not, see *
* <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef DEVICEPLUGINTEXASINSTRUMENTS_H
#define DEVICEPLUGINTEXASINSTRUMENTS_H
#include <QObject>
#include "plugin/deviceplugin.h"
class SensorTag;
class PluginTimer;
class DevicePluginTexasInstruments : public DevicePlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "io.nymea.DevicePlugin" FILE "deviceplugintexasinstruments.json")
Q_INTERFACES(DevicePlugin)
public:
explicit DevicePluginTexasInstruments(QObject *parent = nullptr);
~DevicePluginTexasInstruments() override;
DeviceManager::DeviceError discoverDevices(const DeviceClassId &deviceClassId, const ParamList &params) override;
DeviceManager::DeviceSetupStatus setupDevice(Device *device) override;
void postSetupDevice(Device *device) override;
void deviceRemoved(Device *device) override;
DeviceManager::DeviceError executeAction(Device *device, const Action &action) override;
private:
QHash<Device*, SensorTag*> m_sensorTags;
PluginTimer *m_reconnectTimer = nullptr;
};
#endif // DEVICEPLUGINTEXASINSTRUMENTS_H

View File

@ -0,0 +1,273 @@
{
"displayName": "Texas Instruments",
"name": "TexasInstruments",
"id": "ae550a91-e734-4331-9d71-9f37df0b0fa6",
"vendors": [
{
"id": "2edf543e-dc2c-4693-bb0c-e76c0d305fad",
"name": "texasInstruments",
"displayName": "Texas Instruments",
"deviceClasses": [
{
"id": "158a06b6-b27f-4951-957e-6f1e3b44f604",
"name": "sensorTag",
"displayName": "Sensor Tag (CC2650)",
"createMethods": ["discovery"],
"interfaces": ["temperaturesensor", "humiditysensor", "pressuresensor", "lightsensor", "connectable"],
"paramTypes": [
{
"id": "d51ed68e-c84a-4136-a5b5-be2f95fd5a0f",
"name": "mac",
"displayName": "MAC address",
"type": "QString",
"inputType": "MacAddress"
}
],
"stateTypes": [
{
"id": "a9629b11-0f34-47f0-a0f0-f758a6aec2b4",
"name": "connected",
"displayName": "Connected",
"displayNameEvent": "Connected changed",
"type": "bool",
"cached": false,
"defaultValue": false
},
{
"id": "8359ada9-df1c-4e60-bb87-9e21d05ee2e2",
"name": "temperature",
"displayName": "temperature",
"displayNameEvent": "temperature changed",
"type": "double",
"unit": "DegreeCelsius",
"defaultValue": 0
},
{
"id": "c664e9ec-a045-49ba-add1-1642ceba7c4f",
"name": "objectTemperature",
"displayName": "Object temperature",
"displayNameEvent": "Object temperature changed",
"type": "double",
"unit": "DegreeCelsius",
"defaultValue": 0
},
{
"id": "e83a50ff-96c9-4b6d-889f-f4238353e794",
"name": "humidity",
"displayName": "Humidity",
"displayNameEvent": "Humidity changed",
"type": "double",
"minValue": 0,
"maxValue": 100,
"unit": "Percentage",
"defaultValue": 0
},
{
"id": "645633ad-77d4-45b2-8be8-d6ca7a12eb7a",
"name": "pressure",
"displayName": "Barometric pressure",
"displayNameEvent": "Barometric pressure changed",
"type": "double",
"unit": "HectoPascal",
"defaultValue": 0
},
{
"id": "6635dce4-2d8d-4608-a836-768c3014f356",
"name": "lightIntensity",
"displayName": "Light intensity",
"displayNameEvent": "Light intensity changed",
"type": "double",
"unit": "Lux",
"defaultValue": 0
},
{
"id": "4be5ca26-0565-419d-b18b-2a5a385d2a3d",
"name": "moving",
"displayName": "Moving",
"displayNameEvent": "Moving changed",
"type": "bool",
"cached": false,
"defaultValue": false
},
{
"id": "758d9b39-7390-40f5-8e19-d8b0f4a0a0c6",
"name": "magnetDetected",
"displayName": "Magnet detected",
"displayNameEvent": "Magnet detected changed",
"type": "bool",
"cached": false,
"defaultValue": false
},
{
"id": "8995e49e-ca2d-4dd9-a22f-de6c566c2115",
"name": "leftButtonPressed",
"displayName": "Left button pressed",
"displayNameEvent": "Left button pressed changed",
"type": "bool",
"cached": false,
"defaultValue": false
},
{
"id": "ef8eedc5-6a45-4dfb-bb55-ada1a931b20b",
"name": "rightButtonPressed",
"displayName": "Right button pressed",
"displayNameEvent": "Right button pressed changed",
"type": "bool",
"cached": false,
"defaultValue": false
},
{
"id": "8d692132-f950-486f-b3dd-fe1ebc574e1d",
"name": "greenLed",
"displayName": "Green LED",
"displayNameEvent": "Green LED power changed",
"displayNameAction": "Set green LED power",
"type": "bool",
"cached": false,
"defaultValue": false,
"writable": true
},
{
"id": "bb585725-6393-42de-aea2-ea4f525ad348",
"name": "redLed",
"displayName": "Red LED",
"displayNameEvent": "Red LED power changed",
"displayNameAction": "Set red LED power",
"type": "bool",
"cached": false,
"defaultValue": false,
"writable": true
},
{
"id": "d522c536-4427-4c53-9980-7820b2649aad",
"name": "buzzer",
"displayName": "Buzzer",
"displayNameEvent": "Buzzer power changed",
"displayNameAction": "Set buzzer power",
"type": "bool",
"cached": false,
"defaultValue": false,
"writable": true
},
{
"id": "6f0e40a2-0f97-4c1e-8229-757e3c18b345",
"name": "temperatureSensorEnabled",
"displayName": "Temperature sensor enabled",
"displayNameEvent": "Temperature sensor enabled changed",
"displayNameAction": "Set temperature sensor enabled",
"type": "bool",
"defaultValue": true,
"writable": true
},
{
"id": "29881172-626a-42d5-83b8-3e2e5ca533be",
"name": "humiditySensorEnabled",
"displayName": "Humidity sensor enabled",
"displayNameEvent": "Humidity sensor enabled changed",
"displayNameAction": "Set humidity sensor enabled",
"type": "bool",
"defaultValue": true,
"writable": true
},
{
"id": "50e5e282-9707-4b31-bb3f-a6ca30a7e1ff",
"name": "pressureSensorEnabled",
"displayName": "Pressure sensor enabled",
"displayNameEvent": "Pressure sensor enabled changed",
"displayNameAction": "Set pressure sensor enabled",
"type": "bool",
"defaultValue": true,
"writable": true
},
{
"id": "1460a6d6-9fb4-4385-b27b-ee4b7594e454",
"name": "opticalSensorEnabled",
"displayName": "Optical sensor enabled",
"displayNameEvent": "Optical sensor enabled changed",
"displayNameAction": "Set optical sensor enabled",
"type": "bool",
"defaultValue": true,
"writable": true
},
{
"id": "5786c91a-d94d-461a-8d22-f978dd1438ab",
"name": "accelerometerEnabled",
"displayName": "Accelerometer enabled",
"displayNameEvent": "Accelerometer enabled changed",
"displayNameAction": "Set accelerometer enabled",
"type": "bool",
"defaultValue": true,
"writable": true
},
{
"id": "94517544-cb97-4816-8993-cb4cf2651a1e",
"name": "gyroscopeEnabled",
"displayName": "Gyroscope enabled",
"displayNameEvent": "Gyroscope enabled changed",
"displayNameAction": "Set gyroscope enabled",
"type": "bool",
"defaultValue": true,
"writable": true
},
{
"id": "96aae111-b1c1-48a1-9b1f-b56efa546d0d",
"name": "magnetometerEnabled",
"displayName": "Magnetometer enabled",
"displayNameEvent": "Magnetometer enabled changed",
"displayNameAction": "Set magnetometer enabled",
"type": "bool",
"defaultValue": true,
"writable": true
},
{
"id": "c5308565-5dc9-409e-ae99-577c212c7a92",
"name": "measurementPeriod",
"displayName": "Measurement period for enviromental sensors",
"displayNameEvent": "Measurement period for enviromental sensors changed",
"displayNameAction": "Set measurement period for enviromental sensors",
"type": "int",
"minValue": 10,
"maxValue": 2500,
"defaultValue": 2000,
"writable": true
},
{
"id": "5237c89c-c21d-4d78-ac99-8be661b40da7",
"name": "measurementPeriodMovement",
"displayName": "Measurement period movement sensor",
"displayNameEvent": "Measurement period movement sensor changed",
"displayNameAction": "Set measurement period movement sensor",
"type": "int",
"minValue": 10,
"maxValue": 2500,
"defaultValue": 300,
"writable": true
},
{
"id": "a3298d16-eea6-4474-9061-90466e92d476",
"name": "movementSensitivity",
"displayName": "Movement sensitivity",
"displayNameEvent": "Movement sensitivity changed",
"displayNameAction": "Set movement sensitivity",
"type": "int",
"unit": "Percentage",
"minValue": 0,
"maxValue": 100,
"defaultValue": 40,
"writable": true
}
],
"actionTypes": [
{
"id": "a048ab2e-4f17-4467-a166-a7572156c07e",
"name": "buzzerImpulse",
"displayName": "Buzzer impulse"
}
]
}
]
}
]
}

View File

@ -0,0 +1,329 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015-2018 Simon Stuerz <simon.stuerz@guh.io> *
* *
* This file is part of nymea. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Lesser General Public *
* License as published by the Free Software Foundation; either *
* version 2.1 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; If not, see *
* <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "sensordataprocessor.h"
#include "extern-plugininfo.h"
#include "math.h"
#include <QVector3D>
#include <QByteArray>
#include <QDataStream>
#include <QDateTime>
SensorDataProcessor::SensorDataProcessor(Device *device, QObject *parent) :
QObject(parent),
m_device(device)
{
// Create data filters
m_temperatureFilter = new SensorFilter(SensorFilter::TypeLowPass, this);
m_temperatureFilter->setLowPassAlpha(0.1);
m_temperatureFilter->setFilterWindowSize(30);
m_objectTemperatureFilter = new SensorFilter(SensorFilter::TypeLowPass, this);
m_objectTemperatureFilter->setLowPassAlpha(0.4);
m_objectTemperatureFilter->setFilterWindowSize(20);
m_humidityFilter = new SensorFilter(SensorFilter::TypeLowPass, this);
m_humidityFilter->setLowPassAlpha(0.1);
m_humidityFilter->setFilterWindowSize(30);
m_pressureFilter = new SensorFilter(SensorFilter::TypeLowPass, this);
m_pressureFilter->setLowPassAlpha(0.1);
m_pressureFilter->setFilterWindowSize(30);
m_opticalFilter = new SensorFilter(SensorFilter::TypeLowPass, this);
m_opticalFilter->setLowPassAlpha(0.01);
m_opticalFilter->setFilterWindowSize(10);
m_accelerometerFilter = new SensorFilter(SensorFilter::TypeLowPass, this);
m_accelerometerFilter->setLowPassAlpha(0.6);
m_accelerometerFilter->setFilterWindowSize(40);
// Check if the data should be logged
if (m_filterDebug) {
m_logFile = new QFile("/tmp/multisensor.log", this);
if (!m_logFile->open(QIODevice::Append | QIODevice::Text)) {
qCWarning(dcTexasInstruments()) << "Could not open log file" << m_logFile->fileName();
delete m_logFile;
m_logFile = nullptr;
}
}
}
SensorDataProcessor::~SensorDataProcessor()
{
if (m_logFile) {
m_logFile->close();
}
}
void SensorDataProcessor::setAccelerometerRange(int accelerometerRange)
{
m_accelerometerRange = accelerometerRange;
}
void SensorDataProcessor::setMovementSensitivity(int movementSensitivity)
{
m_movementSensitivity = movementSensitivity;
}
double SensorDataProcessor::roundValue(float value)
{
int tmpValue = static_cast<int>(value * 10);
return static_cast<double>(tmpValue) / 10.0;
}
bool SensorDataProcessor::testBitUint8(quint8 value, int bitPosition)
{
return (((value)>> (bitPosition)) & 1);
}
void SensorDataProcessor::processTemperatureData(const QByteArray &data)
{
Q_ASSERT(data.count() == 4);
quint16 rawObjectTemperature = 0;
quint16 rawAmbientTemperature = 0;
QByteArray payload(data);
QDataStream stream(&payload, QIODevice::ReadOnly);
stream.setByteOrder(QDataStream::LittleEndian);
stream >> rawObjectTemperature >> rawAmbientTemperature ;
float scaleFactor = 0.03125;
float objectTemperature = static_cast<float>(rawObjectTemperature) / 4 * scaleFactor;
float ambientTemperature = static_cast<float>(rawAmbientTemperature) / 4 * scaleFactor;
//qCDebug(dcTexasInstruments()) << "Temperature value" << data.toHex();
//qCDebug(dcTexasInstruments()) << "Object temperature" << roundValue(objectTemperature) << "°C";
//qCDebug(dcTexasInstruments()) << "Ambient temperature" << roundValue(ambientTemperature) << "°C";
float objectTemperatureFiltered = m_objectTemperatureFilter->filterValue(objectTemperature);
float ambientTemperatureFiltered = m_temperatureFilter->filterValue(ambientTemperature);
if (m_objectTemperatureFilter->isReady()) {
m_device->setStateValue(sensorTagObjectTemperatureStateTypeId, roundValue(objectTemperatureFiltered));
}
// Note: only change the state once the filter has collected enough data
if (m_temperatureFilter->isReady()) {
m_device->setStateValue(sensorTagTemperatureStateTypeId, roundValue(ambientTemperatureFiltered));
}
}
void SensorDataProcessor::processKeyData(const QByteArray &data)
{
Q_ASSERT(data.count() == 1);
quint8 flags = static_cast<quint8>(data.at(0));
setLeftButtonPressed(testBitUint8(flags, 0));
setRightButtonPressed(testBitUint8(flags, 1));
setMagnetDetected(testBitUint8(flags, 2));
}
void SensorDataProcessor::processHumidityData(const QByteArray &data)
{
Q_ASSERT(data.count() == 4);
quint16 rawHumidityTemperature = 0;
quint16 rawHumidity = 0;
QByteArray payload(data);
QDataStream stream(&payload, QIODevice::ReadOnly);
stream.setByteOrder(QDataStream::LittleEndian);
stream >> rawHumidityTemperature >> rawHumidity ;
// Note: we don't need the temperature measurement from the humidity sensor
//double humidityTemperature = (rawHumidityTemperature / 65536.0 * 165.0) - 40;
double humidity = rawHumidity / 65536.0 * 100.0;
double humidityFiltered = m_humidityFilter->filterValue(humidity);
if (m_humidityFilter->isReady()) {
m_device->setStateValue(sensorTagHumidityStateTypeId, roundValue(humidityFiltered));
}
}
void SensorDataProcessor::processPressureData(const QByteArray &data)
{
Q_ASSERT(data.count() == 6);
QByteArray temperatureData(data.left(3));
quint32 rawTemperature = static_cast<quint8>(temperatureData.at(2));
rawTemperature <<= 8;
rawTemperature |= static_cast<quint8>(temperatureData.at(1));
rawTemperature <<= 8;
rawTemperature |= static_cast<quint8>(temperatureData.at(0));
QByteArray pressureData(data.right(3));
quint32 rawPressure = static_cast<quint8>(pressureData.at(2));
rawPressure <<= 8;
rawPressure |= static_cast<quint8>(pressureData.at(1));
rawPressure <<= 8;
rawPressure |= static_cast<quint8>(pressureData.at(0));
// Note: we don't need the temperature measurement from the barometic pressure sensor
//qCDebug(dcTexasInstruments()) << "Pressure temperature:" << roundValue(rawTemperature / 100.0) << "°C";
//qCDebug(dcTexasInstruments()) << "Pressure:" << roundValue(rawPressure / 100.0) << "mBar";
double pressureFiltered = m_pressureFilter->filterValue(rawPressure / 100.0);
if (m_pressureFilter->isReady()) {
m_device->setStateValue(sensorTagPressureStateTypeId, roundValue(pressureFiltered));
}
}
void SensorDataProcessor::processOpticalData(const QByteArray &data)
{
Q_ASSERT(data.count() == 2);
quint16 rawOptical = 0;
QByteArray payload(data);
QDataStream stream(&payload, QIODevice::ReadOnly);
stream.setByteOrder(QDataStream::LittleEndian);
stream >> rawOptical;
quint16 lumm = rawOptical & 0x0FFF;
quint16 lume = (rawOptical & 0xF000) >> 12;
double lux = lumm * (0.01 * pow(2,lume));
//qCDebug(dcTexasInstruments()) << "Lux:" << lux;
double luxFiltered = m_opticalFilter->filterValue(lux);
if (m_opticalFilter->isReady()) {
m_device->setStateValue(sensorTagLightIntensityStateTypeId, qRound(luxFiltered));
}
logSensorValue(lux, qRound(luxFiltered));
}
void SensorDataProcessor::processMovementData(const QByteArray &data)
{
//qCDebug(dcTexasInstruments()) << "--> Movement value" << data.toHex();
QByteArray payload(data);
QDataStream stream(&payload, QIODevice::ReadOnly);
stream.setByteOrder(QDataStream::LittleEndian);
qint16 gyroXRaw = 0; qint16 gyroYRaw = 0; qint16 gyroZRaw = 0;
stream >> gyroXRaw >> gyroYRaw >> gyroZRaw;
qint16 accXRaw = 0; qint16 accYRaw = 0; qint16 accZRaw = 0;
stream >> accXRaw >> accYRaw >> accZRaw;
qint16 magXRaw = 0; qint16 magYRaw = 0; qint16 magZRaw = 0;
stream >> magXRaw >> magYRaw >> magZRaw;
// Calculate rotation [deg/s], Range +- 250
float gyroX = static_cast<float>(gyroXRaw) / (65536 / 500);
float gyroY = static_cast<float>(gyroYRaw) / (65536 / 500);
float gyroZ = static_cast<float>(gyroZRaw) / (65536 / 500);
// Calculate acceleration [G], Range +- m_accelerometerRange
float accX = static_cast<float>(accXRaw) / (32768 / static_cast<int>(m_accelerometerRange));
float accY = static_cast<float>(accYRaw) / (32768 / static_cast<int>(m_accelerometerRange));
float accZ = static_cast<float>(accZRaw) / (32768 / static_cast<int>(m_accelerometerRange));
// Calculate magnetism [uT], Range +- 4900
float magX = static_cast<float>(magXRaw);
float magY = static_cast<float>(magYRaw);
float magZ = static_cast<float>(magZRaw);
//qCDebug(dcTexasInstruments()) << "Accelerometer x:" << accX << " y:" << accY << " z:" << accZ;
//qCDebug(dcTexasInstruments()) << "Gyroscope x:" << gyroX << " y:" << gyroY << " z:" << gyroZ;
//qCDebug(dcTexasInstruments()) << "Magnetometer x:" << magX << " y:" << magY << " z:" << magZ;
QVector3D accelerometerVector(accX, accY, accZ);
QVector3D gyroscopeVector(gyroX, gyroY, gyroZ);
QVector3D magnetometerVector(magX, magY, magZ);
Q_UNUSED(gyroscopeVector)
Q_UNUSED(magnetometerVector)
double filteredVectorLength = m_accelerometerFilter->filterValue(accelerometerVector.length());
// Initialize the accelerometer value if no data known yet
if (m_lastAccelerometerVectorLenght == -99999) {
m_lastAccelerometerVectorLenght = filteredVectorLength;
return;
}
double delta = qAbs(qAbs(m_lastAccelerometerVectorLenght) - qAbs(filteredVectorLength));
bool motionDetected = (delta >= m_movementSensitivity);
m_device->setStateValue(sensorTagMovingStateTypeId, motionDetected);
m_lastAccelerometerVectorLenght = filteredVectorLength;
}
void SensorDataProcessor::reset()
{
m_lastAccelerometerVectorLenght = -99999;
// Reset data filters
m_temperatureFilter->reset();
m_objectTemperatureFilter->reset();
m_humidityFilter->reset();
m_pressureFilter->reset();
m_opticalFilter->reset();
m_accelerometerFilter->reset();
}
void SensorDataProcessor::setLeftButtonPressed(bool pressed)
{
if (m_leftButtonPressed == pressed)
return;
qCDebug(dcTexasInstruments()) << "Left button" << (pressed ? "pressed" : "released");
m_leftButtonPressed = pressed;
m_device->setStateValue(sensorTagLeftButtonPressedStateTypeId, m_leftButtonPressed);
}
void SensorDataProcessor::setRightButtonPressed(bool pressed)
{
if (m_rightButtonPressed == pressed)
return;
qCDebug(dcTexasInstruments()) << "Right button" << (pressed ? "pressed" : "released");
m_rightButtonPressed = pressed;
m_device->setStateValue(sensorTagRightButtonPressedStateTypeId, m_rightButtonPressed);
}
void SensorDataProcessor::setMagnetDetected(bool detected)
{
if (m_magnetDetected == detected)
return;
qCDebug(dcTexasInstruments()) << "Magnet detector" << (detected ? "active" : "inactive");
m_magnetDetected = detected;
m_device->setStateValue(sensorTagMagnetDetectedStateTypeId, m_magnetDetected);
}
void SensorDataProcessor::logSensorValue(double originalValue, double filteredValue)
{
if (!m_filterDebug || !m_logFile)
return;
QString logLine = QString("%1 %2 %3\n").arg(QDateTime::currentDateTime().toTime_t()).arg(originalValue).arg(filteredValue);
QTextStream logStream(m_logFile);
logStream.setCodec("UTF-8");
logStream << logLine;
}

View File

@ -0,0 +1,93 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015-2018 Simon Stuerz <simon.stuerz@guh.io> *
* *
* This file is part of nymea. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Lesser General Public *
* License as published by the Free Software Foundation; either *
* version 2.1 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; If not, see *
* <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef SENSORDATAPROCESSOR_H
#define SENSORDATAPROCESSOR_H
#include <QFile>
#include <QObject>
#include "plugin/device.h"
#include "extern-plugininfo.h"
#include "sensorfilter.h"
class SensorDataProcessor : public QObject
{
Q_OBJECT
public:
explicit SensorDataProcessor(Device *device, QObject *parent = nullptr);
~SensorDataProcessor();
void setAccelerometerRange(int accelerometerRange);
void setMovementSensitivity(int movementSensitivity);
static double roundValue(float value);
static bool testBitUint8(quint8 value, int bitPosition);
void processTemperatureData(const QByteArray &data);
void processKeyData(const QByteArray &data);
void processHumidityData(const QByteArray &data);
void processPressureData(const QByteArray &data);
void processOpticalData(const QByteArray &data);
void processMovementData(const QByteArray &data);
void reset();
private:
Device *m_device = nullptr;
double m_lastAccelerometerVectorLenght = -99999;
int m_accelerometerRange = 16;
double m_movementSensitivity = 0.5;
bool m_leftButtonPressed = false;
bool m_rightButtonPressed = false;
bool m_magnetDetected = false;
// Log sensor data for debugging filters
// Note: set this to true for enable sensor filter logging
bool m_filterDebug = true;
QFile *m_logFile = nullptr;
SensorFilter *m_temperatureFilter = nullptr;
SensorFilter *m_objectTemperatureFilter = nullptr;
SensorFilter *m_humidityFilter = nullptr;
SensorFilter *m_pressureFilter = nullptr;
SensorFilter *m_opticalFilter = nullptr;
SensorFilter *m_accelerometerFilter = nullptr;
// Set methods
void setLeftButtonPressed(bool pressed);
void setRightButtonPressed(bool pressed);
void setMagnetDetected(bool detected);
void logSensorValue(double originalValue, double filteredValue);
signals:
private slots:
};
#endif // SENSORDATAPROCESSOR_H

View File

@ -0,0 +1,178 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015-2018 Simon Stuerz <simon.stuerz@guh.io> *
* *
* This file is part of nymea. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Lesser General Public *
* License as published by the Free Software Foundation; either *
* version 2.1 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; If not, see *
* <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "sensorfilter.h"
#include <QDebug>
SensorFilter::SensorFilter(Type filterType, QObject *parent) :
QObject(parent),
m_filterType(filterType)
{
}
float SensorFilter::filterValue(float value)
{
float resultValue = value;
switch (m_filterType) {
case TypeLowPass:
resultValue = lowPassFilterValue(value);
break;
case TypeHighPass:
resultValue = highPassFilterValue(value);
break;
case TypeAverage:
resultValue = averageFilterValue(value);
break;
default:
break;
}
return resultValue;
}
bool SensorFilter::isReady() const
{
// Note: filter is ready once 10% of window filled
return m_inputData.size() >= m_filterWindowSize * 0.1;
}
void SensorFilter::reset()
{
m_averageSum = 0;
m_inputData.clear();
}
SensorFilter::Type SensorFilter::filterType() const
{
return m_filterType;
}
QVector<float> SensorFilter::inputData() const
{
return m_inputData;
}
QVector<float> SensorFilter::outputData() const
{
return m_outputData;
}
uint SensorFilter::windowSize() const
{
return m_filterWindowSize;
}
void SensorFilter::setFilterWindowSize(uint windowSize)
{
Q_ASSERT_X(windowSize > 0, "value out of range", "The filter window size must be bigger than 0");
m_filterWindowSize = windowSize;
}
float SensorFilter::lowPassAlpha() const
{
return m_lowPassAlpha;
}
void SensorFilter::setLowPassAlpha(float alpha)
{
Q_ASSERT_X(alpha > 0 && alpha <= 1, "value out of range", "The alpha low pass filter value must be [ 0 < alpha <= 1 ]");
m_lowPassAlpha = alpha;
}
float SensorFilter::highPassAlpha() const
{
return m_highPassAlpha;
}
void SensorFilter::setHighPassAlpha(float alpha)
{
Q_ASSERT_X(alpha > 0 && alpha <= 1, "value out of range", "The alpha high pass filter value must be [ 0 < alpha <= 1 ]");
m_highPassAlpha = alpha;
}
void SensorFilter::addInputValue(float value)
{
m_inputData.append(value);
if (static_cast<uint>(m_inputData.size()) > m_filterWindowSize) {
m_inputData.removeFirst();
}
}
float SensorFilter::lowPassFilterValue(float value)
{
addInputValue(value);
// Check if we have enough data for filtering
if (m_inputData.size() < 2) {
return value;
}
QVector<float> outputData;
outputData.append(m_inputData.at(0));
for (int i = 1; i < m_inputData.size(); i++) {
// y[i] := y[i-1] + α * (x[i] - y[i-1])
outputData.append(outputData.at(i - 1) + m_lowPassAlpha * (m_inputData.at(i) - outputData.at(i - 1)));
}
m_outputData = outputData;
return m_outputData.last();
}
float SensorFilter::highPassFilterValue(float value)
{
addInputValue(value);
// Check if we have enough data for filtering
if (m_inputData.size() < 2) {
return value;
}
QVector<float> outputData;
outputData.append(m_inputData.at(0));
for (int i = 1; i < m_inputData.size(); i++) {
// y[i] := α * y[i-1] + α * (x[i] - x[i-1])
outputData.append(m_highPassAlpha * outputData.at(i - 1) + m_highPassAlpha * (m_inputData.at(i) - m_inputData.at(i - 1)));
}
m_outputData = outputData;
return m_outputData.last();
}
float SensorFilter::averageFilterValue(float value)
{
if (m_inputData.isEmpty()) {
addInputValue(value);
m_averageSum = value;
return value;
}
if (static_cast<uint>(m_inputData.size()) >= m_filterWindowSize) {
m_averageSum -= m_inputData.takeFirst();
}
addInputValue(value);
m_averageSum += value;
return m_averageSum / m_inputData.size();
}

View File

@ -0,0 +1,81 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015-2018 Simon Stuerz <simon.stuerz@guh.io> *
* *
* This file is part of nymea. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Lesser General Public *
* License as published by the Free Software Foundation; either *
* version 2.1 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; If not, see *
* <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef SENSORFILTER_H
#define SENSORFILTER_H
#include <QObject>
#include <QVector>
class SensorFilter : public QObject
{
Q_OBJECT
public:
enum Type {
TypeLowPass,
TypeHighPass,
TypeAverage
};
Q_ENUM(Type)
explicit SensorFilter(Type filterType, QObject *parent = nullptr);
float filterValue(float value);
bool isReady() const;
void reset();
Type filterType() const;
QVector<float> inputData() const;
QVector<float> outputData() const;
// Filter configuration
uint windowSize() const;
void setFilterWindowSize(uint windowSize = 20);
float lowPassAlpha() const;
void setLowPassAlpha(float alpha = 0.2f);
float highPassAlpha() const;
void setHighPassAlpha(float alpha = 0.2f);
private:
Type m_filterType = TypeLowPass;
uint m_filterWindowSize = 20;
float m_lowPassAlpha = 0.2f;
float m_highPassAlpha = 0.2f;
float m_averageSum = 0;
QVector<float> m_inputData;
QVector<float> m_outputData;
void addInputValue(float value);
// Filter methods
float lowPassFilterValue(float value);
float highPassFilterValue(float value);
float averageFilterValue(float value);
};
#endif // SENSORFILTER_H

View File

@ -0,0 +1,912 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015-2018 Simon Stuerz <simon.stuerz@guh.io> *
* *
* This file is part of guh. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Lesser General Public *
* License as published by the Free Software Foundation; either *
* version 2.1 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; If not, see *
* <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "sensortag.h"
#include "extern-plugininfo.h"
#include "math.h"
#include <QTimer>
#include <QVector3D>
#include <QDataStream>
SensorTag::SensorTag(Device *device, BluetoothLowEnergyDevice *bluetoothDevice, QObject *parent) :
QObject(parent),
m_device(device),
m_bluetoothDevice(bluetoothDevice),
m_dataProcessor(new SensorDataProcessor(m_device, this))
{
connect(m_bluetoothDevice, &BluetoothLowEnergyDevice::connectedChanged, this, &SensorTag::onConnectedChanged);
connect(m_bluetoothDevice, &BluetoothLowEnergyDevice::servicesDiscoveryFinished, this, &SensorTag::onServiceDiscoveryFinished);
}
Device *SensorTag::device()
{
return m_device;
}
BluetoothLowEnergyDevice *SensorTag::bluetoothDevice()
{
return m_bluetoothDevice;
}
void SensorTag::setTemperatureSensorEnabled(bool enabled)
{
qCDebug(dcTexasInstruments()) << "Temperature sensor" << (enabled ? "enabled" : "disabled");
if (m_temperatureEnabled == enabled)
return;
m_temperatureEnabled = enabled;
setTemperatureSensorPower(m_temperatureEnabled);
}
void SensorTag::setHumiditySensorEnabled(bool enabled)
{
qCDebug(dcTexasInstruments()) << "Humidity sensor" << (enabled ? "enabled" : "disabled");
if (m_humidityEnabled == enabled)
return;
m_humidityEnabled = enabled;
setHumiditySensorPower(m_humidityEnabled);
}
void SensorTag::setPressureSensorEnabled(bool enabled)
{
qCDebug(dcTexasInstruments()) << "Pressure sensor" << (enabled ? "enabled" : "disabled");
if (m_pressureEnabled == enabled)
return;
m_pressureEnabled = enabled;
setPressureSensorPower(m_pressureEnabled);
}
void SensorTag::setOpticalSensorEnabled(bool enabled)
{
qCDebug(dcTexasInstruments()) << "Optical sensor" << (enabled ? "enabled" : "disabled");
if (m_opticalEnabled == enabled)
return;
m_opticalEnabled = enabled;
setOpticalSensorPower(m_opticalEnabled);
}
void SensorTag::setAccelerometerEnabled(bool enabled)
{
qCDebug(dcTexasInstruments()) << "Accelerometer" << (enabled ? "enabled" : "disabled");
if (m_accelerometerEnabled == enabled)
return;
m_accelerometerEnabled = enabled;
configureMovement();
}
void SensorTag::setAccelerometerRange(const SensorTag::SensorAccelerometerRange &range)
{
qCDebug(dcTexasInstruments()) << "Accelerometer" << range;
if (m_accelerometerRange == range)
return;
m_accelerometerRange = range;
configureMovement();
}
void SensorTag::setGyroscopeEnabled(bool enabled)
{
qCDebug(dcTexasInstruments()) << "Gyroscope" << (enabled ? "enabled" : "disabled");
if (m_gyroscopeEnabled == enabled)
return;
m_gyroscopeEnabled = enabled;
configureMovement();
}
void SensorTag::setMagnetometerEnabled(bool enabled)
{
qCDebug(dcTexasInstruments()) << "Magnetometer" << (enabled ? "enabled" : "disabled");
if (m_magnetometerEnabled == enabled)
return;
m_magnetometerEnabled = enabled;
configureMovement();
}
void SensorTag::setMeasurementPeriod(int period)
{
qCDebug(dcTexasInstruments()) << "Set sensor measurement period to" << period << "ms";
if (period % 10 != 0) {
int adjustedValue = qRound(static_cast<float>(period) / 10.0) * 10;
qCWarning(dcTexasInstruments()) << "Measurement period of sensors" << period << "must be a multiple of 10ms. Adjusting it to" << adjustedValue;
period = adjustedValue;
}
m_temperaturePeriod = period;
if (m_temperatureService && m_temperaturePeriodCharacteristic.isValid())
configurePeriod(m_temperatureService, m_temperaturePeriodCharacteristic, m_temperaturePeriod);
m_humidityPeriod = period;
if (m_humidityService && m_humidityPeriodCharacteristic.isValid())
configurePeriod(m_humidityService, m_humidityPeriodCharacteristic, m_humidityPeriod);
m_pressurePeriod = period;
if (m_pressureService && m_pressurePeriodCharacteristic.isValid())
configurePeriod(m_pressureService, m_pressurePeriodCharacteristic, m_pressurePeriod);
m_opticalPeriod = period;
if (m_opticalService && m_opticalPeriodCharacteristic.isValid())
configurePeriod(m_opticalService, m_opticalPeriodCharacteristic, m_opticalPeriod);
}
void SensorTag::setMeasurementPeriodMovement(int period)
{
qCDebug(dcTexasInstruments()) << "Set movement sensor measurement period to" << period << "ms";
if (period % 10 != 0) {
int adjustedValue = qRound(static_cast<float>(period) / 10.0) * 10;
qCWarning(dcTexasInstruments()) << "Measurement period of movement sensor" << period << "must be a multiple of 10ms. Adjusting it to" << adjustedValue;
period = adjustedValue;
}
m_movementPeriod = period;
if (m_movementService && m_movementPeriodCharacteristic.isValid())
configurePeriod(m_movementService, m_movementPeriodCharacteristic, m_movementPeriod);
}
void SensorTag::setMovementSensitivity(int percentage)
{
m_movementSensitivity = static_cast<double>(percentage) / 100.0;
}
void SensorTag::setGreenLedPower(bool power)
{
m_greenLedEnabled = power;
qCDebug(dcTexasInstruments()) << "Green LED" << (power ? "enabled" : "disabled");
configureIo();
m_device->setStateValue(sensorTagGreenLedStateTypeId, m_greenLedEnabled);
}
void SensorTag::setRedLedPower(bool power)
{
m_redLedEnabled = power;
qCDebug(dcTexasInstruments()) << "Red LED" << (power ? "enabled" : "disabled");
configureIo();
m_device->setStateValue(sensorTagRedLedStateTypeId, m_redLedEnabled);
}
void SensorTag::setBuzzerPower(bool power)
{
m_buzzerEnabled = power;
qCDebug(dcTexasInstruments()) << "Buzzer" << (power ? "enabled" : "disabled");
configureIo();
m_device->setStateValue(sensorTagBuzzerStateTypeId, m_buzzerEnabled);
}
void SensorTag::buzzerImpulse()
{
qCDebug(dcTexasInstruments()) << "Buzzer impulse";
setBuzzerPower(true);
QTimer::singleShot(1000, this, &SensorTag::onBuzzerImpulseTimeout);
}
void SensorTag::configurePeriod(QLowEnergyService *serice, const QLowEnergyCharacteristic &characteristic, int measurementPeriod)
{
Q_ASSERT(measurementPeriod % 10 == 0);
QByteArray payload;
QDataStream stream(&payload, QIODevice::WriteOnly);
stream << static_cast<quint8>(measurementPeriod / 10);
qCDebug(dcTexasInstruments()) << "Configure period to" << measurementPeriod << payload.toHex();
serice->writeCharacteristic(characteristic, payload);
}
void SensorTag::configureMovement()
{
if (!m_movementService || !m_movementConfigurationCharacteristic.isValid())
return;
quint16 configuration = 0;
if (m_gyroscopeEnabled) {
configuration |= (1 << 0); // enable x-axis
configuration |= (1 << 1); // enable y-axis
configuration |= (1 << 2); // enable z-axis
}
if (m_accelerometerEnabled) {
configuration |= (1 << 3); // enable x-axis
configuration |= (1 << 4); // enable y-axis
configuration |= (1 << 5); // enable z-axis
}
if (m_magnetometerEnabled) {
configuration |= (1 << 6); // enable all axis
}
// Always enable wake on movement in order to save energy
configuration |= (1 << 8); // enable
// Accelerometer range 2 Bit ( 0 = 2G, 1 = 4G, 2 = 8G, 3 = 16G)
switch (m_accelerometerRange) {
case SensorAccelerometerRange2G:
// Bit 9 = 0
// Bit 10 = 0
break;
case SensorAccelerometerRange4G:
configuration |= (1 << 11);
// Bit 12 = 0
break;
case SensorAccelerometerRange8G:
// Bit 13 = 0
configuration |= (1 << 14);
break;
case SensorAccelerometerRange16G:
configuration |= (1 << 15);
configuration |= (1 << 16);
break;
default:
break;
}
QByteArray data;
QDataStream stream(&data, QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
stream << configuration;
qCDebug(dcTexasInstruments()) << "Configure movement sensor" << data.toHex();
m_movementService->writeCharacteristic(m_movementConfigurationCharacteristic, data);
}
void SensorTag::configureSensorMode(const SensorTag::SensorMode &mode)
{
if (!m_ioService || !m_ioDataCharacteristic.isValid())
return;
qCDebug(dcTexasInstruments()) << "Setting" << mode;
QByteArray data;
QDataStream stream(&data, QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
stream << static_cast<quint8>(mode);
m_ioService->writeCharacteristic(m_ioConfigurationCharacteristic, data);
}
void SensorTag::configureIo()
{
if (!m_ioService || !m_ioDataCharacteristic.isValid())
return;
// Write value to IO
quint8 configuration = 0;
if (m_redLedEnabled)
configuration |= (1 << 0); // Red LED
if (m_greenLedEnabled)
configuration |= (1 << 1); // Green LED
if (m_buzzerEnabled)
configuration |= (1 << 2); // Buzzer
QByteArray payload;
QDataStream stream(&payload, QIODevice::WriteOnly);
stream << configuration;
m_ioService->writeCharacteristic(m_ioDataCharacteristic, payload);
}
void SensorTag::setTemperatureSensorPower(bool power)
{
if (!m_temperatureService || !m_temperatureConfigurationCharacteristic.isValid())
return;
QByteArray payload = (power ? QByteArray::fromHex("01") : QByteArray::fromHex("00"));
m_temperatureService->writeCharacteristic(m_temperatureConfigurationCharacteristic, payload);
}
void SensorTag::setHumiditySensorPower(bool power)
{
if (!m_humidityService || !m_humidityConfigurationCharacteristic.isValid())
return;
QByteArray payload = (power ? QByteArray::fromHex("01") : QByteArray::fromHex("00"));
m_humidityService->writeCharacteristic(m_humidityConfigurationCharacteristic, payload);
}
void SensorTag::setPressureSensorPower(bool power)
{
if (!m_pressureService || !m_pressureConfigurationCharacteristic.isValid())
return;
QByteArray payload = (power ? QByteArray::fromHex("01") : QByteArray::fromHex("00"));
m_pressureService->writeCharacteristic(m_pressureConfigurationCharacteristic, payload);
}
void SensorTag::setOpticalSensorPower(bool power)
{
if (!m_opticalService || !m_opticalConfigurationCharacteristic.isValid())
return;
QByteArray payload = (power ? QByteArray::fromHex("01") : QByteArray::fromHex("00"));
m_opticalService->writeCharacteristic(m_opticalConfigurationCharacteristic, payload);
}
void SensorTag::onConnectedChanged(const bool &connected)
{
qCDebug(dcTexasInstruments()) << "Sensor" << m_bluetoothDevice->name() << m_bluetoothDevice->address().toString() << (connected ? "connected" : "disconnected");
m_device->setStateValue(sensorTagConnectedStateTypeId, connected);
if (!connected) {
// Clean up services
m_temperatureService->deleteLater();
m_humidityService->deleteLater();
m_pressureService->deleteLater();
m_opticalService->deleteLater();
m_keyService->deleteLater();
m_movementService->deleteLater();
m_ioService->deleteLater();
m_temperatureService = nullptr;
m_humidityService = nullptr;
m_pressureService = nullptr;
m_opticalService = nullptr;
m_keyService = nullptr;
m_movementService = nullptr;
m_ioService = nullptr;
m_dataProcessor->reset();
}
}
void SensorTag::onServiceDiscoveryFinished()
{
foreach (const QBluetoothUuid serviceUuid, m_bluetoothDevice->serviceUuids()) {
qCDebug(dcTexasInstruments()) << "-->" << serviceUuid;
}
if (!m_bluetoothDevice->serviceUuids().contains(temperatureServiceUuid)) {
qCWarning(dcTexasInstruments()) << "Could not find temperature service";
m_bluetoothDevice->disconnectDevice();
return;
}
if (!m_bluetoothDevice->serviceUuids().contains(humidityServiceUuid)) {
qCWarning(dcTexasInstruments()) << "Could not find humidity service";
m_bluetoothDevice->disconnectDevice();
return;
}
if (!m_bluetoothDevice->serviceUuids().contains(pressureServiceUuid)) {
qCWarning(dcTexasInstruments()) << "Could not find pressure service";
m_bluetoothDevice->disconnectDevice();
return;
}
if (!m_bluetoothDevice->serviceUuids().contains(opticalServiceUuid)) {
qCWarning(dcTexasInstruments()) << "Could not find optical service";
m_bluetoothDevice->disconnectDevice();
return;
}
if (!m_bluetoothDevice->serviceUuids().contains(keyServiceUuid)) {
qCWarning(dcTexasInstruments()) << "Could not find key service";
m_bluetoothDevice->disconnectDevice();
return;
}
if (!m_bluetoothDevice->serviceUuids().contains(movementServiceUuid)) {
qCWarning(dcTexasInstruments()) << "Could not find movement service";
m_bluetoothDevice->disconnectDevice();
return;
}
if (!m_bluetoothDevice->serviceUuids().contains(ioServiceUuid)) {
qCWarning(dcTexasInstruments()) << "Could not find IO service";
m_bluetoothDevice->disconnectDevice();
return;
}
// IR Temperature
if (!m_temperatureService) {
m_temperatureService = m_bluetoothDevice->controller()->createServiceObject(temperatureServiceUuid, this);
if (!m_temperatureService) {
qCWarning(dcTexasInstruments()) << "Could not create temperature service.";
m_bluetoothDevice->disconnectDevice();
return;
}
connect(m_temperatureService, &QLowEnergyService::stateChanged, this, &SensorTag::onTemperatureServiceStateChanged);
connect(m_temperatureService, &QLowEnergyService::characteristicChanged, this, &SensorTag::onTemperatureServiceCharacteristicChanged);
if (m_temperatureService->state() == QLowEnergyService::DiscoveryRequired) {
m_temperatureService->discoverDetails();
}
}
// Humidity
if (!m_humidityService) {
m_humidityService = m_bluetoothDevice->controller()->createServiceObject(humidityServiceUuid, this);
if (!m_humidityService) {
qCWarning(dcTexasInstruments()) << "Could not create humidity service.";
m_bluetoothDevice->disconnectDevice();
return;
}
connect(m_humidityService, &QLowEnergyService::stateChanged, this, &SensorTag::onHumidityServiceStateChanged);
connect(m_humidityService, &QLowEnergyService::characteristicChanged, this, &SensorTag::onHumidityServiceCharacteristicChanged);
if (m_humidityService->state() == QLowEnergyService::DiscoveryRequired) {
m_humidityService->discoverDetails();
}
}
// Pressure
if (!m_pressureService) {
m_pressureService = m_bluetoothDevice->controller()->createServiceObject(pressureServiceUuid, this);
if (!m_pressureService) {
qCWarning(dcTexasInstruments()) << "Could not create pressure service.";
m_bluetoothDevice->disconnectDevice();
return;
}
connect(m_pressureService, &QLowEnergyService::stateChanged, this, &SensorTag::onPressureServiceStateChanged);
connect(m_pressureService, &QLowEnergyService::characteristicChanged, this, &SensorTag::onPressureServiceCharacteristicChanged);
if (m_pressureService->state() == QLowEnergyService::DiscoveryRequired) {
m_pressureService->discoverDetails();
}
}
// Optical
if (!m_opticalService) {
m_opticalService = m_bluetoothDevice->controller()->createServiceObject(opticalServiceUuid, this);
if (!m_opticalService) {
qCWarning(dcTexasInstruments()) << "Could not create optical service.";
m_bluetoothDevice->disconnectDevice();
return;
}
connect(m_opticalService, &QLowEnergyService::stateChanged, this, &SensorTag::onOpticalServiceStateChanged);
connect(m_opticalService, &QLowEnergyService::characteristicChanged, this, &SensorTag::onOpticalServiceCharacteristicChanged);
if (m_opticalService->state() == QLowEnergyService::DiscoveryRequired) {
m_opticalService->discoverDetails();
}
}
// Key
if (!m_keyService) {
m_keyService = m_bluetoothDevice->controller()->createServiceObject(keyServiceUuid, this);
if (!m_keyService) {
qCWarning(dcTexasInstruments()) << "Could not create key service.";
m_bluetoothDevice->disconnectDevice();
return;
}
connect(m_keyService, &QLowEnergyService::stateChanged, this, &SensorTag::onKeyServiceStateChanged);
connect(m_keyService, &QLowEnergyService::characteristicChanged, this, &SensorTag::onKeyServiceCharacteristicChanged);
if (m_keyService->state() == QLowEnergyService::DiscoveryRequired) {
m_keyService->discoverDetails();
}
}
// Movement
if (!m_movementService) {
m_movementService = m_bluetoothDevice->controller()->createServiceObject(movementServiceUuid, this);
if (!m_movementService) {
qCWarning(dcTexasInstruments()) << "Could not create movement service.";
m_bluetoothDevice->disconnectDevice();
return;
}
connect(m_movementService, &QLowEnergyService::stateChanged, this, &SensorTag::onMovementServiceStateChanged);
connect(m_movementService, &QLowEnergyService::characteristicChanged, this, &SensorTag::onMovementServiceCharacteristicChanged);
if (m_movementService->state() == QLowEnergyService::DiscoveryRequired) {
m_movementService->discoverDetails();
}
}
// IO
if (!m_ioService) {
m_ioService = m_bluetoothDevice->controller()->createServiceObject(ioServiceUuid, this);
if (!m_ioService) {
qCWarning(dcTexasInstruments()) << "Could not create IO service.";
m_bluetoothDevice->disconnectDevice();
return;
}
connect(m_ioService, &QLowEnergyService::stateChanged, this, &SensorTag::onIoServiceStateChanged);
connect(m_ioService, &QLowEnergyService::characteristicChanged, this, &SensorTag::onIoServiceCharacteristicChanged);
if (m_ioService->state() == QLowEnergyService::DiscoveryRequired) {
m_ioService->discoverDetails();
}
}
}
void SensorTag::onBuzzerImpulseTimeout()
{
setBuzzerPower(false);
}
void SensorTag::onTemperatureServiceStateChanged(const QLowEnergyService::ServiceState &state)
{
// Only continue if discovered
if (state != QLowEnergyService::ServiceDiscovered)
return;
qCDebug(dcTexasInstruments()) << "Temperature sensor service discovered.";
foreach (const QLowEnergyCharacteristic &characteristic, m_temperatureService->characteristics()) {
qCDebug(dcTexasInstruments()) << " -->" << characteristic.name() << characteristic.uuid().toString() << characteristic.value();
foreach (const QLowEnergyDescriptor &descriptor, characteristic.descriptors()) {
qCDebug(dcTexasInstruments()) << " -->" << descriptor.name() << descriptor.uuid().toString() << descriptor.value();
}
}
// Data characteristic
m_temperatureDataCharacteristic = m_temperatureService->characteristic(temperatureDataCharacteristicUuid);
if (!m_temperatureDataCharacteristic.isValid()) {
qCWarning(dcTexasInstruments()) << "Invalid temperature data characteristic.";
m_bluetoothDevice->disconnectDevice();
return;
}
// Enable notifications
QLowEnergyDescriptor notificationDescriptor = m_temperatureDataCharacteristic.descriptor(QBluetoothUuid::ClientCharacteristicConfiguration);
m_temperatureService->writeDescriptor(notificationDescriptor, QByteArray::fromHex("0100"));
// Config characteristic
m_temperatureConfigurationCharacteristic = m_temperatureService->characteristic(temperatureConfigurationCharacteristicUuid);
if (!m_temperatureConfigurationCharacteristic.isValid()) {
qCWarning(dcTexasInstruments()) << "Invalid temperature configuration characteristic.";
m_bluetoothDevice->disconnectDevice();
return;
}
// Period characteristic
m_temperaturePeriodCharacteristic = m_temperatureService->characteristic(temperaturePeriodCharacteristicUuid);
if (!m_temperaturePeriodCharacteristic.isValid()) {
qCWarning(dcTexasInstruments()) << "Invalid temperature period characteristic.";
m_bluetoothDevice->disconnectDevice();
return;
}
configurePeriod(m_temperatureService, m_temperaturePeriodCharacteristic, m_temperaturePeriod);
// Enable/disable measuring
setTemperatureSensorPower(m_temperatureEnabled);
}
void SensorTag::onTemperatureServiceCharacteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &value)
{
if (characteristic == m_temperatureDataCharacteristic) {
m_dataProcessor->processTemperatureData(value);
}
}
void SensorTag::onHumidityServiceStateChanged(const QLowEnergyService::ServiceState &state)
{
// Only continue if discovered
if (state != QLowEnergyService::ServiceDiscovered)
return;
qCDebug(dcTexasInstruments()) << "Humidity sensor service discovered.";
foreach (const QLowEnergyCharacteristic &characteristic, m_humidityService->characteristics()) {
qCDebug(dcTexasInstruments()) << " -->" << characteristic.name() << characteristic.uuid().toString() << characteristic.value();
foreach (const QLowEnergyDescriptor &descriptor, characteristic.descriptors()) {
qCDebug(dcTexasInstruments()) << " -->" << descriptor.name() << descriptor.uuid().toString() << descriptor.value();
}
}
// Data characteristic
m_humidityDataCharacteristic = m_humidityService->characteristic(humidityDataCharacteristicUuid);
if (!m_humidityDataCharacteristic.isValid()) {
qCWarning(dcTexasInstruments()) << "Invalid humidity data characteristic.";
m_bluetoothDevice->disconnectDevice();
return;
}
// Enable notifications
QLowEnergyDescriptor notificationDescriptor = m_humidityDataCharacteristic.descriptor(QBluetoothUuid::ClientCharacteristicConfiguration);
m_humidityService->writeDescriptor(notificationDescriptor, QByteArray::fromHex("0100"));
// Config characteristic
m_humidityConfigurationCharacteristic = m_humidityService->characteristic(humidityConfigurationCharacteristicUuid);
if (!m_humidityConfigurationCharacteristic.isValid()) {
qCWarning(dcTexasInstruments()) << "Invalid humidity configuration characteristic.";
m_bluetoothDevice->disconnectDevice();
return;
}
// Period characteristic
m_humidityPeriodCharacteristic = m_humidityService->characteristic(humidityPeriodCharacteristicUuid);
if (!m_humidityPeriodCharacteristic.isValid()) {
qCWarning(dcTexasInstruments()) << "Invalid humidity period characteristic.";
m_bluetoothDevice->disconnectDevice();
return;
}
configurePeriod(m_humidityService, m_humidityPeriodCharacteristic, m_humidityPeriod);
// Enable measuring
m_humidityService->writeCharacteristic(m_humidityConfigurationCharacteristic, QByteArray::fromHex("01"));
}
void SensorTag::onHumidityServiceCharacteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &value)
{
if (characteristic == m_humidityDataCharacteristic) {
m_dataProcessor->processHumidityData(value);
}
}
void SensorTag::onPressureServiceStateChanged(const QLowEnergyService::ServiceState &state)
{
// Only continue if discovered
if (state != QLowEnergyService::ServiceDiscovered)
return;
qCDebug(dcTexasInstruments()) << "Pressure sensor service discovered.";
foreach (const QLowEnergyCharacteristic &characteristic, m_pressureService->characteristics()) {
qCDebug(dcTexasInstruments()) << " -->" << characteristic.name() << characteristic.uuid().toString() << characteristic.value();
foreach (const QLowEnergyDescriptor &descriptor, characteristic.descriptors()) {
qCDebug(dcTexasInstruments()) << " -->" << descriptor.name() << descriptor.uuid().toString() << descriptor.value();
}
}
// Data characteristic
m_pressureDataCharacteristic = m_pressureService->characteristic(pressureDataCharacteristicUuid);
if (!m_pressureDataCharacteristic.isValid()) {
qCWarning(dcTexasInstruments()) << "Invalid pressure data characteristic.";
m_bluetoothDevice->disconnectDevice();
return;
}
// Enable notifications
QLowEnergyDescriptor notificationDescriptor = m_pressureDataCharacteristic.descriptor(QBluetoothUuid::ClientCharacteristicConfiguration);
m_pressureService->writeDescriptor(notificationDescriptor, QByteArray::fromHex("0100"));
// Config characteristic
m_pressureConfigurationCharacteristic = m_pressureService->characteristic(pressureConfigurationCharacteristicUuid);
if (!m_pressureConfigurationCharacteristic.isValid()) {
qCWarning(dcTexasInstruments()) << "Invalid pressure configuration characteristic.";
m_bluetoothDevice->disconnectDevice();
return;
}
// Period characteristic
m_pressurePeriodCharacteristic = m_pressureService->characteristic(pressurePeriodCharacteristicUuid);
if (!m_pressurePeriodCharacteristic.isValid()) {
qCWarning(dcTexasInstruments()) << "Invalid pressure period characteristic.";
m_bluetoothDevice->disconnectDevice();
return;
}
configurePeriod(m_pressureService, m_pressurePeriodCharacteristic, m_pressurePeriod);
// Enable measuring
m_pressureService->writeCharacteristic(m_pressureConfigurationCharacteristic, QByteArray::fromHex("01"));
}
void SensorTag::onPressureServiceCharacteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &value)
{
if (characteristic == m_pressureDataCharacteristic) {
m_dataProcessor->processPressureData(value);
}
}
void SensorTag::onOpticalServiceStateChanged(const QLowEnergyService::ServiceState &state)
{
// Only continue if discovered
if (state != QLowEnergyService::ServiceDiscovered)
return;
qCDebug(dcTexasInstruments()) << "Optical sensor service discovered.";
foreach (const QLowEnergyCharacteristic &characteristic, m_pressureService->characteristics()) {
qCDebug(dcTexasInstruments()) << " -->" << characteristic.name() << characteristic.uuid().toString() << characteristic.value();
foreach (const QLowEnergyDescriptor &descriptor, characteristic.descriptors()) {
qCDebug(dcTexasInstruments()) << " -->" << descriptor.name() << descriptor.uuid().toString() << descriptor.value();
}
}
// Data characteristic
m_opticalDataCharacteristic = m_opticalService->characteristic(opticalDataCharacteristicUuid);
if (!m_opticalDataCharacteristic.isValid()) {
qCWarning(dcTexasInstruments()) << "Invalid optical data characteristic.";
m_bluetoothDevice->disconnectDevice();
return;
}
// Enable notifications
QLowEnergyDescriptor notificationDescriptor = m_opticalDataCharacteristic.descriptor(QBluetoothUuid::ClientCharacteristicConfiguration);
m_opticalService->writeDescriptor(notificationDescriptor, QByteArray::fromHex("0100"));
// Config characteristic
m_opticalConfigurationCharacteristic = m_opticalService->characteristic(opticalConfigurationCharacteristicUuid);
if (!m_opticalConfigurationCharacteristic.isValid()) {
qCWarning(dcTexasInstruments()) << "Invalid optical configuration characteristic.";
m_bluetoothDevice->disconnectDevice();
}
// Period characteristic
m_opticalPeriodCharacteristic = m_opticalService->characteristic(opticalPeriodCharacteristicUuid);
if (!m_opticalPeriodCharacteristic.isValid()) {
qCWarning(dcTexasInstruments()) << "Invalid optical period characteristic.";
m_bluetoothDevice->disconnectDevice();
}
// Set measurement period
configurePeriod(m_opticalService, m_opticalPeriodCharacteristic, m_opticalPeriod);
// Enable measuring
m_opticalService->writeCharacteristic(m_opticalConfigurationCharacteristic, QByteArray::fromHex("01"));
}
void SensorTag::onOpticalServiceCharacteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &value)
{
if (characteristic == m_opticalDataCharacteristic) {
m_dataProcessor->processOpticalData(value);
}
}
void SensorTag::onKeyServiceStateChanged(const QLowEnergyService::ServiceState &state)
{
// Only continue if discovered
if (state != QLowEnergyService::ServiceDiscovered)
return;
qCDebug(dcTexasInstruments()) << "Key service discovered.";
foreach (const QLowEnergyCharacteristic &characteristic, m_keyService->characteristics()) {
qCDebug(dcTexasInstruments()) << " -->" << characteristic.name() << characteristic.uuid().toString() << characteristic.value();
foreach (const QLowEnergyDescriptor &descriptor, characteristic.descriptors()) {
qCDebug(dcTexasInstruments()) << " -->" << descriptor.name() << descriptor.uuid().toString() << descriptor.value();
}
}
// Data characteristic
m_keyDataCharacteristic = m_keyService->characteristic(keyDataCharacteristicUuid);
if (!m_keyDataCharacteristic.isValid()) {
qCWarning(dcTexasInstruments()) << "Invalid button data characteristic.";
m_bluetoothDevice->disconnectDevice();
}
// Enable notifications
QLowEnergyDescriptor notificationDescriptor = m_keyDataCharacteristic.descriptor(QBluetoothUuid::ClientCharacteristicConfiguration);
m_keyService->writeDescriptor(notificationDescriptor, QByteArray::fromHex("0100"));
}
void SensorTag::onKeyServiceCharacteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &value)
{
if (characteristic == m_keyDataCharacteristic) {
m_dataProcessor->processKeyData(value);
}
}
void SensorTag::onMovementServiceStateChanged(const QLowEnergyService::ServiceState &state)
{
// Only continue if discovered
if (state != QLowEnergyService::ServiceDiscovered)
return;
qCDebug(dcTexasInstruments()) << "Movement sensor service discovered.";
foreach (const QLowEnergyCharacteristic &characteristic, m_pressureService->characteristics()) {
qCDebug(dcTexasInstruments()) << " -->" << characteristic.name() << characteristic.uuid().toString() << characteristic.value();
foreach (const QLowEnergyDescriptor &descriptor, characteristic.descriptors()) {
qCDebug(dcTexasInstruments()) << " -->" << descriptor.name() << descriptor.uuid().toString() << descriptor.value();
}
}
// Data characteristic
m_movementDataCharacteristic = m_movementService->characteristic(movementDataCharacteristicUuid);
if (!m_movementDataCharacteristic.isValid()) {
qCWarning(dcTexasInstruments()) << "Invalid movement data characteristic.";
m_bluetoothDevice->disconnectDevice();
}
// Enable notifications
QLowEnergyDescriptor notificationDescriptor = m_movementDataCharacteristic.descriptor(QBluetoothUuid::ClientCharacteristicConfiguration);
m_movementService->writeDescriptor(notificationDescriptor, QByteArray::fromHex("0100"));
// Config characteristic
m_movementConfigurationCharacteristic = m_movementService->characteristic(movementConfigurationCharacteristicUuid);
if (!m_movementConfigurationCharacteristic.isValid()) {
qCWarning(dcTexasInstruments()) << "Invalid movement configuration characteristic.";
m_bluetoothDevice->disconnectDevice();
}
// Period characteristic
m_movementPeriodCharacteristic = m_movementService->characteristic(movementPeriodCharacteristicUuid);
if (!m_movementPeriodCharacteristic.isValid()) {
qCWarning(dcTexasInstruments()) << "Invalid movement period characteristic.";
m_bluetoothDevice->disconnectDevice();
}
// Set measurement period
configurePeriod(m_movementService, m_movementPeriodCharacteristic, m_movementPeriod);
configureMovement();
// Enable measuring
m_movementService->writeCharacteristic(m_movementConfigurationCharacteristic, QByteArray::fromHex("01"));
}
void SensorTag::onMovementServiceCharacteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &value)
{
if (characteristic == m_movementDataCharacteristic) {
m_dataProcessor->processMovementData(value);
}
}
void SensorTag::onIoServiceStateChanged(const QLowEnergyService::ServiceState &state)
{
// Only continue if discovered
if (state != QLowEnergyService::ServiceDiscovered)
return;
qCDebug(dcTexasInstruments()) << "IO service discovered.";
foreach (const QLowEnergyCharacteristic &characteristic, m_pressureService->characteristics()) {
qCDebug(dcTexasInstruments()) << " -->" << characteristic.name() << characteristic.uuid().toString() << characteristic.value();
foreach (const QLowEnergyDescriptor &descriptor, characteristic.descriptors()) {
qCDebug(dcTexasInstruments()) << " -->" << descriptor.name() << descriptor.uuid().toString() << descriptor.value();
}
}
// Data characteristic
m_ioDataCharacteristic = m_ioService->characteristic(ioDataCharacteristicUuid);
if (!m_ioDataCharacteristic.isValid()) {
qCWarning(dcTexasInstruments()) << "Invalid IO data characteristic.";
m_bluetoothDevice->disconnectDevice();
}
// Enable notifications
QLowEnergyDescriptor notificationDescriptor = m_ioDataCharacteristic.descriptor(QBluetoothUuid::ClientCharacteristicConfiguration);
m_ioService->writeDescriptor(notificationDescriptor, QByteArray::fromHex("0100"));
// Config characteristic
m_ioConfigurationCharacteristic = m_ioService->characteristic(ioConfigurationCharacteristicUuid);
if (!m_ioConfigurationCharacteristic.isValid()) {
qCWarning(dcTexasInstruments()) << "Invalid IO configuration characteristic.";
m_bluetoothDevice->disconnectDevice();
}
configureIo();
configureSensorMode(SensorModeRemote);
configureIo();
}
void SensorTag::onIoServiceCharacteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &value)
{
qCDebug(dcTexasInstruments()) << characteristic.uuid().toString() << value.toHex();
}

View File

@ -0,0 +1,228 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015-2018 Simon Stuerz <simon.stuerz@guh.io> *
* *
* This file is part of guh. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Lesser General Public *
* License as published by the Free Software Foundation; either *
* version 2.1 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; If not, see *
* <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef SENSORTAG_H
#define SENSORTAG_H
#include <QObject>
#include "plugin/device.h"
#include "extern-plugininfo.h"
#include "sensordataprocessor.h"
#include "hardware/bluetoothlowenergy/bluetoothlowenergydevice.h"
// http://processors.wiki.ti.com/index.php/CC2650_SensorTag_User's_Guide
static QBluetoothUuid temperatureServiceUuid = QBluetoothUuid(QUuid("f000aa00-0451-4000-b000-000000000000"));
static QBluetoothUuid temperatureDataCharacteristicUuid = QBluetoothUuid(QUuid("f000aa01-0451-4000-b000-000000000000"));
static QBluetoothUuid temperatureConfigurationCharacteristicUuid = QBluetoothUuid(QUuid("f000aa02-0451-4000-b000-000000000000"));
static QBluetoothUuid temperaturePeriodCharacteristicUuid = QBluetoothUuid(QUuid("f000aa03-0451-4000-b000-000000000000"));
static QBluetoothUuid humidityServiceUuid = QBluetoothUuid(QUuid("f000aa20-0451-4000-b000-000000000000"));
static QBluetoothUuid humidityDataCharacteristicUuid = QBluetoothUuid(QUuid("f000aa21-0451-4000-b000-000000000000"));
static QBluetoothUuid humidityConfigurationCharacteristicUuid = QBluetoothUuid(QUuid("f000aa22-0451-4000-b000-000000000000"));
static QBluetoothUuid humidityPeriodCharacteristicUuid = QBluetoothUuid(QUuid("f000aa23-0451-4000-b000-000000000000"));
static QBluetoothUuid pressureServiceUuid = QBluetoothUuid(QUuid("f000aa40-0451-4000-b000-000000000000"));
static QBluetoothUuid pressureDataCharacteristicUuid = QBluetoothUuid(QUuid("f000aa41-0451-4000-b000-000000000000"));
static QBluetoothUuid pressureConfigurationCharacteristicUuid = QBluetoothUuid(QUuid("f000aa42-0451-4000-b000-000000000000"));
static QBluetoothUuid pressurePeriodCharacteristicUuid = QBluetoothUuid(QUuid("f000aa44-0451-4000-b000-000000000000"));
static QBluetoothUuid opticalServiceUuid = QBluetoothUuid(QUuid("f000aa70-0451-4000-b000-000000000000"));
static QBluetoothUuid opticalDataCharacteristicUuid = QBluetoothUuid(QUuid("f000aa71-0451-4000-b000-000000000000"));
static QBluetoothUuid opticalConfigurationCharacteristicUuid = QBluetoothUuid(QUuid("f000aa72-0451-4000-b000-000000000000"));
static QBluetoothUuid opticalPeriodCharacteristicUuid = QBluetoothUuid(QUuid("f000aa73-0451-4000-b000-000000000000"));
static QBluetoothUuid keyServiceUuid = QBluetoothUuid(QUuid("0000ffe0-0000-1000-8000-00805f9b34fb"));
static QBluetoothUuid keyDataCharacteristicUuid = QBluetoothUuid(QUuid("0000ffe1-0000-1000-8000-00805f9b34fb"));
static QBluetoothUuid ioServiceUuid = QBluetoothUuid(QUuid("f000aa64-0451-4000-b000-000000000000"));
static QBluetoothUuid ioDataCharacteristicUuid = QBluetoothUuid(QUuid("f000aa65-0451-4000-b000-000000000000"));
static QBluetoothUuid ioConfigurationCharacteristicUuid = QBluetoothUuid(QUuid("f000aa66-0451-4000-b000-000000000000"));
static QBluetoothUuid movementServiceUuid = QBluetoothUuid(QUuid("f000aa80-0451-4000-b000-000000000000"));
static QBluetoothUuid movementDataCharacteristicUuid = QBluetoothUuid(QUuid("f000aa81-0451-4000-b000-000000000000"));
static QBluetoothUuid movementConfigurationCharacteristicUuid = QBluetoothUuid(QUuid("f000aa82-0451-4000-b000-000000000000"));
static QBluetoothUuid movementPeriodCharacteristicUuid = QBluetoothUuid(QUuid("f000aa83-0451-4000-b000-000000000000"));
class SensorTag : public QObject
{
Q_OBJECT
public:
enum SensorAccelerometerRange {
SensorAccelerometerRange2G = 2,
SensorAccelerometerRange4G = 4,
SensorAccelerometerRange8G = 8,
SensorAccelerometerRange16G = 16
};
Q_ENUM(SensorAccelerometerRange)
enum SensorMode {
SensorModeLocal = 0,
SensorModeRemote = 1,
SensorModeTest = 2
};
Q_ENUM(SensorMode)
explicit SensorTag(Device *device, BluetoothLowEnergyDevice *bluetoothDevice, QObject *parent = nullptr);
Device *device();
BluetoothLowEnergyDevice *bluetoothDevice();
// Configurations
void setTemperatureSensorEnabled(bool enabled);
void setHumiditySensorEnabled(bool enabled);
void setPressureSensorEnabled(bool enabled);
void setOpticalSensorEnabled(bool enabled);
void setAccelerometerEnabled(bool enabled);
void setGyroscopeEnabled(bool enabled);
void setMagnetometerEnabled(bool enabled);
void setAccelerometerRange(const SensorAccelerometerRange &range);
void setMeasurementPeriod(int period);
void setMeasurementPeriodMovement(int period);
void setMovementSensitivity(int percentage);
// Actions
void setGreenLedPower(bool power);
void setRedLedPower(bool power);
void setBuzzerPower(bool power);
void buzzerImpulse();
private:
Device *m_device;
BluetoothLowEnergyDevice *m_bluetoothDevice;
// Services
QLowEnergyService *m_temperatureService = nullptr;
QLowEnergyService *m_humidityService = nullptr;
QLowEnergyService *m_pressureService = nullptr;
QLowEnergyService *m_opticalService = nullptr;
QLowEnergyService *m_keyService = nullptr;
QLowEnergyService *m_movementService = nullptr;
QLowEnergyService *m_ioService = nullptr;
// Characteristics
QLowEnergyCharacteristic m_temperatureDataCharacteristic;
QLowEnergyCharacteristic m_temperatureConfigurationCharacteristic;
QLowEnergyCharacteristic m_temperaturePeriodCharacteristic;
QLowEnergyCharacteristic m_humidityDataCharacteristic;
QLowEnergyCharacteristic m_humidityConfigurationCharacteristic;
QLowEnergyCharacteristic m_humidityPeriodCharacteristic;
QLowEnergyCharacteristic m_pressureDataCharacteristic;
QLowEnergyCharacteristic m_pressureConfigurationCharacteristic;
QLowEnergyCharacteristic m_pressurePeriodCharacteristic;
QLowEnergyCharacteristic m_opticalDataCharacteristic;
QLowEnergyCharacteristic m_opticalConfigurationCharacteristic;
QLowEnergyCharacteristic m_opticalPeriodCharacteristic;
QLowEnergyCharacteristic m_keyDataCharacteristic;
QLowEnergyCharacteristic m_movementDataCharacteristic;
QLowEnergyCharacteristic m_movementConfigurationCharacteristic;
QLowEnergyCharacteristic m_movementPeriodCharacteristic;
QLowEnergyCharacteristic m_ioDataCharacteristic;
QLowEnergyCharacteristic m_ioConfigurationCharacteristic;
// Measure periods
int m_temperaturePeriod = 2500;
int m_humidityPeriod = 2500;
int m_pressurePeriod = 2500;
int m_opticalPeriod = 2500;
int m_movementPeriod = 500;
double m_movementSensitivity = 0.5;
SensorAccelerometerRange m_accelerometerRange = SensorAccelerometerRange16G;
// States
bool m_greenLedEnabled = false;
bool m_redLedEnabled = false;
bool m_buzzerEnabled = false;
// Plugin configs
bool m_temperatureEnabled = true;
bool m_humidityEnabled = true;
bool m_pressureEnabled = true;
bool m_opticalEnabled = true;
bool m_accelerometerEnabled = true;
bool m_gyroscopeEnabled = false;
bool m_magnetometerEnabled = false;
SensorDataProcessor *m_dataProcessor = nullptr;
// Configuration methods
void configurePeriod(QLowEnergyService *serice, const QLowEnergyCharacteristic &characteristic, int measurementPeriod);
void configureMovement();
void configureSensorMode(const SensorMode &mode);
void configureIo();
void setTemperatureSensorPower(bool power);
void setHumiditySensorPower(bool power);
void setPressureSensorPower(bool power);
void setOpticalSensorPower(bool power);
signals:
void leftButtonPressedChanged(bool pressed);
void rightButtonPressedChanged(bool pressed);
void magnetDetectedChanged(bool detected);
private slots:
void onConnectedChanged(const bool &connected);
void onServiceDiscoveryFinished();
void onBuzzerImpulseTimeout();
// Temperature sensor service
void onTemperatureServiceStateChanged(const QLowEnergyService::ServiceState &state);
void onTemperatureServiceCharacteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &value);
// Humidity sensor service
void onHumidityServiceStateChanged(const QLowEnergyService::ServiceState &state);
void onHumidityServiceCharacteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &value);
// Pressure sensor service
void onPressureServiceStateChanged(const QLowEnergyService::ServiceState &state);
void onPressureServiceCharacteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &value);
// Optical sensor service
void onOpticalServiceStateChanged(const QLowEnergyService::ServiceState &state);
void onOpticalServiceCharacteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &value);
// Button service
void onKeyServiceStateChanged(const QLowEnergyService::ServiceState &state);
void onKeyServiceCharacteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &value);
// Movement service
void onMovementServiceStateChanged(const QLowEnergyService::ServiceState &state);
void onMovementServiceCharacteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &value);
// IO service
void onIoServiceStateChanged(const QLowEnergyService::ServiceState &state);
void onIoServiceCharacteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &value);
};
#endif // SENSORTAG_H

View File

@ -0,0 +1,19 @@
include(../plugins.pri)
QT += bluetooth
TARGET = $$qtLibraryTarget(nymea_deviceplugintexasinstruments)
HEADERS += \
deviceplugintexasinstruments.h \
sensortag.h \
sensordataprocessor.h \
sensorfilter.h
SOURCES += \
deviceplugintexasinstruments.cpp \
sensortag.cpp \
sensordataprocessor.cpp \
sensorfilter.cpp

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1">
</TS>