From 496db05c4c85a749ab8f6f5b4e9cd7153622fa3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Fri, 23 Feb 2018 20:53:01 +0100 Subject: [PATCH] Add sensor actions --- multisensor/devicepluginmultisensor.cpp | 39 +++-- multisensor/devicepluginmultisensor.h | 2 + multisensor/devicepluginmultisensor.json | 37 +++++ multisensor/sensortag.cpp | 189 ++++++++++++++++++++++- multisensor/sensortag.h | 40 ++++- 5 files changed, 283 insertions(+), 24 deletions(-) diff --git a/multisensor/devicepluginmultisensor.cpp b/multisensor/devicepluginmultisensor.cpp index df77c2da..e356dfd8 100644 --- a/multisensor/devicepluginmultisensor.cpp +++ b/multisensor/devicepluginmultisensor.cpp @@ -117,7 +117,6 @@ void DevicePluginMultiSensor::postSetupDevice(Device *device) sensor->bluetoothDevice()->connectDevice(); } - void DevicePluginMultiSensor::deviceRemoved(Device *device) { if (!m_sensors.contains(device)) @@ -129,6 +128,26 @@ void DevicePluginMultiSensor::deviceRemoved(Device *device) sensor->deleteLater(); } +DeviceManager::DeviceError DevicePluginMultiSensor::executeAction(Device *device, const Action &action) +{ + SensorTag *sensor = m_sensors.value(device); + if (action.actionTypeId() == sensortagBuzzerActionTypeId) { + sensor->setBuzzerPower(action.param(sensortagBuzzerStateParamTypeId).value().toBool()); + return DeviceManager::DeviceErrorNoError; + } else if (action.actionTypeId() == sensortagGreenLedActionTypeId) { + sensor->setGreenLedPower(action.param(sensortagGreenLedStateParamTypeId).value().toBool()); + return DeviceManager::DeviceErrorNoError; + } else if (action.actionTypeId() == sensortagRedLedActionTypeId) { + sensor->setRedLedPower(action.param(sensortagRedLedStateParamTypeId).value().toBool()); + return DeviceManager::DeviceErrorNoError; + } else if (action.actionTypeId() == sensortagBuzzerImpulseActionTypeId) { + sensor->buzzerImpulse(); + return DeviceManager::DeviceErrorNoError; + } + + return DeviceManager::DeviceErrorActionTypeNotFound; +} + bool DevicePluginMultiSensor::verifyExistingDevices(const QBluetoothDeviceInfo &deviceInfo) { foreach (Device *device, m_sensors.keys()) { @@ -188,21 +207,11 @@ void DevicePluginMultiSensor::onPluginConfigurationChanged(const ParamTypeId &pa } else if (paramTypeId == MultiSensorMagnetometerEnabledParamTypeId) { sensor->setMagnetometerEnabled(value.toBool()); } else if (paramTypeId == MultiSensorMeasurementPeriodParamTypeId) { - int valueInt = value.toInt(); - if (valueInt % 10 != 0) { - qCWarning(dcMultiSensor()) << "Measurement period of sensors" << valueInt << "must be a multiple of 10ms"; - // FIXME: force to valid value - return; - } - sensor->setMeasurementPeriod(valueInt); + sensor->setMeasurementPeriod(value.toInt()); } else if (paramTypeId == MultiSensorMeasurementPeriodParamTypeId) { - int valueInt = value.toInt(); - if (valueInt % 10 != 0) { - qCWarning(dcMultiSensor()) << "Measurement period of movement sensor" << valueInt << "must be a multiple of 10ms"; - // FIXME: force to valid value - return; - } - sensor->setMeasurementPeriodMovement(valueInt); + sensor->setMeasurementPeriodMovement(value.toInt()); + } else if (paramTypeId == MultiSensorMovementSensitivityParamTypeId) { + sensor->setMovementSensitivity(value.toInt()); } } } diff --git a/multisensor/devicepluginmultisensor.h b/multisensor/devicepluginmultisensor.h index 91e055ca..9b761764 100644 --- a/multisensor/devicepluginmultisensor.h +++ b/multisensor/devicepluginmultisensor.h @@ -49,6 +49,8 @@ public: void postSetupDevice(Device *device) override; void deviceRemoved(Device *device) override; + DeviceManager::DeviceError executeAction(Device *device, const Action &action) override; + private: PluginTimer *m_reconnectTimer = nullptr; QHash m_sensors; diff --git a/multisensor/devicepluginmultisensor.json b/multisensor/devicepluginmultisensor.json index 64f71a2d..0acad317 100644 --- a/multisensor/devicepluginmultisensor.json +++ b/multisensor/devicepluginmultisensor.json @@ -178,6 +178,43 @@ "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", + "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", + "defaultValue": false, + "writable": true + }, + { + "id": "d522c536-4427-4c53-9980-7820b2649aad", + "name": "buzzer", + "displayName": "Buzzer", + "displayNameEvent": "Buzzer power changed", + "displayNameAction": "Set buzzer power", + "type": "bool", + "defaultValue": false, + "writable": true + } + ], + "actionTypes": [ + { + "id": "a048ab2e-4f17-4467-a166-a7572156c07e", + "name": "buzzerImpulse", + "displayName": "Buzzer impulse" } ] } diff --git a/multisensor/sensortag.cpp b/multisensor/sensortag.cpp index 11555462..0b2e75f9 100644 --- a/multisensor/sensortag.cpp +++ b/multisensor/sensortag.cpp @@ -24,6 +24,7 @@ #include "extern-plugininfo.h" #include "math.h" +#include #include #include @@ -83,6 +84,12 @@ void SensorTag::setMeasurementPeriod(int period) { qCDebug(dcMultiSensor()) << "Set sensor measurement period to" << period << "ms"; + if (period % 10 != 0) { + int adjustedValue = qRound(static_cast(period) / 10.0) * 10; + qCWarning(dcMultiSensor()) << "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); @@ -105,6 +112,12 @@ void SensorTag::setMeasurementPeriodMovement(int period) { qCDebug(dcMultiSensor()) << "Set movement sensor measurement period to" << period << "ms"; + if (period % 10 != 0) { + int adjustedValue = qRound(static_cast(period) / 10.0) * 10; + qCWarning(dcMultiSensor()) << "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); @@ -116,6 +129,37 @@ void SensorTag::setMovementSensitivity(int percentage) m_movementSensitivity = static_cast(percentage) / 100.0; } +void SensorTag::setGreenLedPower(bool power) +{ + m_greenLedEnabled = power; + qCDebug(dcMultiSensor()) << "Green LED" << (power ? "enabled" : "disabled"); + configureIo(); + m_device->setStateValue(sensortagGreenLedStateTypeId, m_greenLedEnabled); +} + +void SensorTag::setRedLedPower(bool power) +{ + m_redLedEnabled = power; + qCDebug(dcMultiSensor()) << "Red LED" << (power ? "enabled" : "disabled"); + configureIo(); + m_device->setStateValue(sensortagRedLedStateTypeId, m_redLedEnabled); +} + +void SensorTag::setBuzzerPower(bool power) +{ + m_buzzerEnabled = power; + qCDebug(dcMultiSensor()) << "Buzzer" << (power ? "enabled" : "disabled"); + configureIo(); + m_device->setStateValue(sensortagBuzzerStateTypeId, m_buzzerEnabled); +} + +void SensorTag::buzzerImpulse() +{ + qCDebug(dcMultiSensor()) << "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); @@ -153,7 +197,28 @@ void SensorTag::configureMovement(bool gyroscopeEnabled, bool accelerometerEnabl configuration |= (1 << 8); // enable } - // Accelerometer 2 Bit ( 0 = 2G, 1 = 4G, 2 = 8G, 3 = 16G) - Default 0 + // Accelerometer range 2 Bit ( 0 = 2G, 1 = 4G, 2 = 8G, 3 = 16G) - Default 0 + 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); @@ -163,6 +228,43 @@ void SensorTag::configureMovement(bool gyroscopeEnabled, bool accelerometerEnabl m_movementService->writeCharacteristic(m_movementConfigurationCharacteristic, data); } +void SensorTag::configureSensorMode(const SensorTag::SensorMode &mode) +{ + if (!m_ioService || !m_ioDataCharacteristic.isValid()) + return; + + qCDebug(dcMultiSensor()) << "Setting" << mode; + QByteArray data; + QDataStream stream(&data, QIODevice::WriteOnly); + stream.setByteOrder(QDataStream::LittleEndian); + stream << static_cast(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::processTemperatureData(const QByteArray &data) { Q_ASSERT(data.count() == 4); @@ -281,9 +383,9 @@ void SensorTag::processMovementData(const QByteArray &data) double gyroY = static_cast(gyroYRaw) / (65536 / 500); double gyroZ = static_cast(gyroZRaw) / (65536 / 500); - double accX = static_cast(accXRaw) / (32768 / m_accelerometerRange); - double accY = static_cast(accYRaw) / (32768 / m_accelerometerRange); - double accZ = static_cast(accZRaw) / (32768 / m_accelerometerRange); + double accX = static_cast(accXRaw) / (32768 / static_cast(m_accelerometerRange)); + double accY = static_cast(accYRaw) / (32768 / static_cast(m_accelerometerRange)); + double accZ = static_cast(accZRaw) / (32768 / static_cast(m_accelerometerRange)); // Calculate magnetism [uT], Range +- 4900 double magX = static_cast(magXRaw); @@ -291,7 +393,7 @@ void SensorTag::processMovementData(const QByteArray &data) double magZ = static_cast(magZRaw); - qCDebug(dcMultiSensor()) << "Accelerometer x:" << accX << " y:" << accY << " z:" << accZ; + //qCDebug(dcMultiSensor()) << "Accelerometer x:" << accX << " y:" << accY << " z:" << accZ; //qCDebug(dcMultiSensor()) << "Gyroscope x:" << gyroX << " y:" << gyroY << " z:" << gyroZ; //qCDebug(dcMultiSensor()) << "Magnetometer x:" << magX << " y:" << magY << " z:" << magZ; @@ -302,7 +404,11 @@ void SensorTag::processMovementData(const QByteArray &data) Q_UNUSED(gyroscopeVector) Q_UNUSED(magnetometerVector) - qCDebug(dcMultiSensor()) << accelerometerVector.length(); + double limit = m_accelerometerRange * m_movementSensitivity; + bool motionDetected = (accelerometerVector.length() >= limit); + qCDebug(dcMultiSensor()) << accelerometerVector.length() << " | " << limit << (motionDetected ? "motion" : "-"); + + m_device->setStateValue(sensortagMovingStateTypeId, motionDetected); } void SensorTag::setLeftButtonPressed(bool pressed) @@ -414,6 +520,12 @@ void SensorTag::onServiceDiscoveryFinished() return; } + if (!m_bluetoothDevice->serviceUuids().contains(ioServiceUuid)) { + qCWarning(dcMultiSensor()) << "Could not find IO service"; + m_bluetoothDevice->disconnectDevice(); + return; + } + // IR Temperature if (!m_temperatureService) { m_temperatureService = m_bluetoothDevice->controller()->createServiceObject(temperatureServiceUuid, this); @@ -514,6 +626,23 @@ void SensorTag::onServiceDiscoveryFinished() m_movementService->discoverDetails(); } } + + // IO + if (!m_ioService) { + m_ioService = m_bluetoothDevice->controller()->createServiceObject(ioServiceUuid, this); + if (!m_ioService) { + qCWarning(dcMultiSensor()) << "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::onTemperatureServiceStateChanged(const QLowEnergyService::ServiceState &state) @@ -825,7 +954,6 @@ void SensorTag::onMovementServiceStateChanged(const QLowEnergyService::ServiceSt // Set measurement period configurePeriod(m_movementService, m_movementPeriodCharacteristic, m_movementPeriod); - configureMovement(m_gyroscopeEnabled, m_accelerometerEnabled, m_magnetometerEnabled, true); // Enable measuring @@ -838,3 +966,50 @@ void SensorTag::onMovementServiceCharacteristicChanged(const QLowEnergyCharacter processMovementData(value); } } + +void SensorTag::onIoServiceStateChanged(const QLowEnergyService::ServiceState &state) +{ + // Only continue if discovered + if (state != QLowEnergyService::ServiceDiscovered) + return; + + qCDebug(dcMultiSensor()) << "IO service discovered."; + + foreach (const QLowEnergyCharacteristic &characteristic, m_pressureService->characteristics()) { + qCDebug(dcMultiSensor()) << " -->" << characteristic.name() << characteristic.uuid().toString() << characteristic.value(); + foreach (const QLowEnergyDescriptor &desciptor, characteristic.descriptors()) { + qCDebug(dcMultiSensor()) << " -->" << desciptor.name() << desciptor.uuid().toString() << desciptor.value(); + } + } + + // Data characteristic + m_ioDataCharacteristic = m_ioService->characteristic(ioDataCharacteristicUuid); + if (!m_ioDataCharacteristic.isValid()) { + qCWarning(dcMultiSensor()) << "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(dcMultiSensor()) << "Invalid IO configuration characteristic."; + m_bluetoothDevice->disconnectDevice(); + } + + configureSensorMode(SensorModeRemote); + configureIo(); +} + +void SensorTag::onIoServiceCharacteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &value) +{ + qCDebug(dcMultiSensor()) << characteristic.uuid().toString() << value.toHex(); +} + +void SensorTag::onBuzzerImpulseTimeout() +{ + setBuzzerPower(false); +} diff --git a/multisensor/sensortag.h b/multisensor/sensortag.h index 29c17fae..b39e4fd3 100644 --- a/multisensor/sensortag.h +++ b/multisensor/sensortag.h @@ -68,11 +68,27 @@ 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 setAccelerometerEnabled(bool enabled); void setGyroscopeEnabled(bool enabled); void setMagnetometerEnabled(bool enabled); @@ -80,6 +96,12 @@ public: 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; @@ -91,6 +113,7 @@ private: QLowEnergyService *m_opticalService = nullptr; QLowEnergyService *m_keyService = nullptr; QLowEnergyService *m_movementService = nullptr; + QLowEnergyService *m_ioService = nullptr; // Characteristics QLowEnergyCharacteristic m_temperatureDataCharacteristic; @@ -115,19 +138,25 @@ private: 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 = 50; + double m_movementSensitivity = 0.5; // Note: possible value: 2G, 4G, 8G, 16G, default 2G - int m_accelerometerRange = 2; + SensorAccelerometerRange m_accelerometerRange = SensorAccelerometerRange16G; bool m_leftButtonPressed = false; bool m_rightButtonPressed = false; bool m_magnetDetected = false; + bool m_greenLedEnabled = false; + bool m_redLedEnabled = false; + bool m_buzzerEnabled = false; // Plugin configs bool m_accelerometerEnabled = true; @@ -136,6 +165,8 @@ private: void configurePeriod(QLowEnergyService *serice, const QLowEnergyCharacteristic &characteristic, int measurementPeriod); void configureMovement(bool gyroscopeEnabled = false, bool accelerometerEnabled = true, bool magnetometerEnabled = true, bool wakeOnMotion = true); + void configureSensorMode(const SensorMode &mode); + void configureIo(); void processTemperatureData(const QByteArray &data); void processKeyData(const QByteArray &data); @@ -186,6 +217,11 @@ private slots: 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); + + void onBuzzerImpulseTimeout(); }; #endif // SENSORTAG_H