New Plugin: TexasInstruments, replaces MultiSensor plugin

master
Michael Zanetti 2019-04-03 20:35:50 +02:00
parent 1fe3d94440
commit 57e2c66ef8
12 changed files with 2429 additions and 0 deletions

View File

@ -39,6 +39,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>