Link bluetooth.
Implement class that talks to BLE miflora devices. Have UI show current values from new class.
This commit is contained in:
@@ -1,8 +1,10 @@
|
|||||||
|
|
||||||
set(qiflora_SRCS
|
set(qiflora_SRCS
|
||||||
|
miflora/miflora.cpp
|
||||||
main.cpp
|
main.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
qt5_add_resources(RESOURCES resources.qrc)
|
qt5_add_resources(RESOURCES resources.qrc)
|
||||||
add_executable(qiflora ${qiflora_SRCS} ${RESOURCES})
|
add_executable(qiflora ${qiflora_SRCS} ${RESOURCES})
|
||||||
target_link_libraries(qiflora Qt5::Core Qt5::Qml Qt5::Quick Qt5::Svg)
|
target_link_libraries(qiflora Qt5::Core Qt5::Qml Qt5::Quick Qt5::Svg Qt5::Bluetooth)
|
||||||
install(TARGETS qiflora ${KF5_INSTALL_TARGETS_DEFAULT_ARGS})
|
install(TARGETS qiflora ${KF5_INSTALL_TARGETS_DEFAULT_ARGS})
|
||||||
|
|||||||
@@ -2,14 +2,20 @@ import QtQuick 2.6
|
|||||||
import org.kde.kirigami 2.4 as Kirigami
|
import org.kde.kirigami 2.4 as Kirigami
|
||||||
import QtQuick.Controls 2.0 as Controls
|
import QtQuick.Controls 2.0 as Controls
|
||||||
import QtQuick.Layouts 1.12 as Layouts
|
import QtQuick.Layouts 1.12 as Layouts
|
||||||
|
import org.eyecreate.qiflora 1.0
|
||||||
|
|
||||||
Kirigami.ApplicationWindow {
|
Kirigami.ApplicationWindow {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
title: "QiFlora"
|
title: "QiFlora"
|
||||||
|
|
||||||
|
|
||||||
pageStack.initialPage: mainPageComponent
|
pageStack.initialPage: mainPageComponent
|
||||||
|
|
||||||
|
QiFlora {
|
||||||
|
id: qiflora
|
||||||
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: mainPageComponent
|
id: mainPageComponent
|
||||||
|
|
||||||
@@ -17,6 +23,9 @@ Kirigami.ApplicationWindow {
|
|||||||
mainAction: Kirigami.Action {
|
mainAction: Kirigami.Action {
|
||||||
iconName: "view-refresh"
|
iconName: "view-refresh"
|
||||||
text: i18n("Query Device")
|
text: i18n("Query Device")
|
||||||
|
onTriggered: {
|
||||||
|
//TODO
|
||||||
|
}
|
||||||
}
|
}
|
||||||
title: "Monitor"
|
title: "Monitor"
|
||||||
|
|
||||||
@@ -53,7 +62,12 @@ Kirigami.ApplicationWindow {
|
|||||||
Kirigami.Heading {
|
Kirigami.Heading {
|
||||||
Layouts.Layout.alignment: Qt.AlignCenter
|
Layouts.Layout.alignment: Qt.AlignCenter
|
||||||
level: 3
|
level: 3
|
||||||
text: "40" //TODO: replace
|
text: {
|
||||||
|
if(model.chartType == "temperature") qiflora.temperature + "°C"
|
||||||
|
else if(model.chartType == "moisture") qiflora.moisture + "%"
|
||||||
|
else if(model.chartType == "conductivity") qiflora.conduction + " µS/cm"
|
||||||
|
else if(model.chartType == "brightness") qiflora.brightness + " lux"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#include <QQmlApplicationEngine>
|
#include <QQmlApplicationEngine>
|
||||||
#include <QtQml>
|
#include <QtQml>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
#include "miflora/miflora.h"
|
||||||
|
|
||||||
Q_DECL_EXPORT int main(int argc, char *argv[])
|
Q_DECL_EXPORT int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
@@ -12,7 +13,7 @@ Q_DECL_EXPORT int main(int argc, char *argv[])
|
|||||||
QCoreApplication::setApplicationName("qiflora");
|
QCoreApplication::setApplicationName("qiflora");
|
||||||
|
|
||||||
QQmlApplicationEngine engine;
|
QQmlApplicationEngine engine;
|
||||||
|
qmlRegisterType<MiFlora>("org.eyecreate.qiflora",1,0,"QiFlora");
|
||||||
engine.load(QUrl(QStringLiteral("qrc:///main.qml")));
|
engine.load(QUrl(QStringLiteral("qrc:///main.qml")));
|
||||||
|
|
||||||
if (engine.rootObjects().isEmpty()) {
|
if (engine.rootObjects().isEmpty()) {
|
||||||
|
|||||||
143
src/miflora/miflora.cpp
Normal file
143
src/miflora/miflora.cpp
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2019 Kevin Whitaker <eyecreate@eyecreate.org>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "miflora.h"
|
||||||
|
#include <QDataStream>
|
||||||
|
|
||||||
|
void MiFlora::startSearch()
|
||||||
|
{
|
||||||
|
qDebug() << "Starting search...";
|
||||||
|
agent = new QBluetoothDeviceDiscoveryAgent();
|
||||||
|
agent->setLowEnergyDiscoveryTimeout(5000);
|
||||||
|
devices.clear();
|
||||||
|
connect(agent, &QBluetoothDeviceDiscoveryAgent::deviceDiscovered, this, &MiFlora::foundDevice);
|
||||||
|
agent->start(QBluetoothDeviceDiscoveryAgent::LowEnergyMethod);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MiFlora::stopSearch()
|
||||||
|
{
|
||||||
|
if(agent != NULL) {
|
||||||
|
agent->stop();
|
||||||
|
agent->disconnect();
|
||||||
|
delete agent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MiFlora::updateDataFromDevice ( QString mac )
|
||||||
|
{
|
||||||
|
for(QBluetoothDeviceInfo info: devices) {
|
||||||
|
if(info.address().toString() == mac) {
|
||||||
|
qDebug() << "Discovering on:" + info.name();
|
||||||
|
currentController = QLowEnergyController::createCentral(info);
|
||||||
|
currentController->setRemoteAddressType(QLowEnergyController::PublicAddress);
|
||||||
|
connect(currentController, &QLowEnergyController::serviceDiscovered, this, &MiFlora::serviceDiscovered);
|
||||||
|
connect(currentController, static_cast<void (QLowEnergyController::*)(QLowEnergyController::Error)>(&QLowEnergyController::error), this, &MiFlora::logControllerError);
|
||||||
|
connect(currentController, &QLowEnergyController::connected, this, [this]{
|
||||||
|
currentController->discoverServices();
|
||||||
|
});
|
||||||
|
currentController->connectToDevice();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MiFlora::foundDevice ( const QBluetoothDeviceInfo& device )
|
||||||
|
{
|
||||||
|
if(device.coreConfigurations() & QBluetoothDeviceInfo::LowEnergyCoreConfiguration && device.address().toString().startsWith("C4:7C")) {
|
||||||
|
devices.append(device);
|
||||||
|
emit newDeviceFound(device.name(), device.address().toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MiFlora::logControllerError ( QLowEnergyController::Error err )
|
||||||
|
{
|
||||||
|
qDebug() << "Error:" << err;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MiFlora::logServiceError(QLowEnergyService::ServiceError err)
|
||||||
|
{
|
||||||
|
qDebug() << "Service Error:" << err;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void MiFlora::serviceStateChanges ( QLowEnergyService::ServiceState state )
|
||||||
|
{
|
||||||
|
if(state == QLowEnergyService::ServiceState::ServiceDiscovered) {
|
||||||
|
for(QLowEnergyCharacteristic chrt : sensorService->characteristics()) {
|
||||||
|
if(chrt.uuid().toUInt16() == magicChar) {
|
||||||
|
sensorService->writeCharacteristic(chrt, QByteArray::fromHex("a01f"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MiFlora::serviceCharWritten(QLowEnergyCharacteristic changedChar, QByteArray value)
|
||||||
|
{
|
||||||
|
if(changedChar.uuid().toUInt16() == magicChar) {
|
||||||
|
//Used to only check sensor data after writting magic bytes.
|
||||||
|
for(QLowEnergyCharacteristic chrt : sensorService->characteristics()) {
|
||||||
|
if(chrt.uuid().toUInt16() == batteryFirmwareChar) {
|
||||||
|
sensorService->readCharacteristic(chrt);
|
||||||
|
}
|
||||||
|
if(chrt.uuid().toUInt16() == sensorsChar) {
|
||||||
|
sensorService->readCharacteristic(chrt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MiFlora::serviceCharRead(QLowEnergyCharacteristic readChar, QByteArray value)
|
||||||
|
{
|
||||||
|
if(readChar.uuid().toUInt16() == batteryFirmwareChar) {
|
||||||
|
QDataStream parser(value);
|
||||||
|
parser >> battery;
|
||||||
|
parser.skipRawData(1);
|
||||||
|
QByteArray version_buf(5, Qt::Uninitialized);
|
||||||
|
parser.readRawData(version_buf.data(), 5);
|
||||||
|
version = QString(version_buf);
|
||||||
|
emit batteryChanged(battery);
|
||||||
|
qDebug() << "Firmware: " << version;
|
||||||
|
} else if(readChar.uuid().toUInt16() == sensorsChar) {
|
||||||
|
quint16 origTemp;
|
||||||
|
QDataStream parser(value);
|
||||||
|
parser.setByteOrder(QDataStream::LittleEndian);
|
||||||
|
parser >> origTemp;
|
||||||
|
temp = origTemp/ 10.0; //original value in 0.1 C
|
||||||
|
parser.skipRawData(1);
|
||||||
|
parser >> bright;
|
||||||
|
parser >> moisture;
|
||||||
|
parser >> conduct;
|
||||||
|
emit brightnessChanged(bright);
|
||||||
|
emit temperatureChanged(temp);
|
||||||
|
emit moistureChanged(moisture);
|
||||||
|
emit conductionChanged(conduct);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MiFlora::serviceDiscovered ( const QBluetoothUuid& gatt )
|
||||||
|
{
|
||||||
|
if(gatt == QBluetoothUuid(dataService)) {
|
||||||
|
sensorService = currentController->createServiceObject(gatt);
|
||||||
|
connect(sensorService, &QLowEnergyService::stateChanged, this, &MiFlora::serviceStateChanges);
|
||||||
|
connect(sensorService, static_cast<void (QLowEnergyService::*)(QLowEnergyService::ServiceError)>(&QLowEnergyService::error), this, &MiFlora::logServiceError);
|
||||||
|
connect(sensorService, &QLowEnergyService::characteristicWritten, this, &MiFlora::serviceCharWritten);
|
||||||
|
connect(sensorService, &QLowEnergyService::characteristicRead, this, &MiFlora::serviceCharRead);
|
||||||
|
sensorService->discoverDetails();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
83
src/miflora/miflora.h
Normal file
83
src/miflora/miflora.h
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2019 Kevin Whitaker <eyecreate@eyecreate.org>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MIFLORA_H
|
||||||
|
#define MIFLORA_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QtBluetooth/QBluetoothDeviceDiscoveryAgent>
|
||||||
|
#include <QtBluetooth/QBluetoothDeviceInfo>
|
||||||
|
#include <QtBluetooth/QLowEnergyController>
|
||||||
|
#include <QtBluetooth/QLowEnergyService>
|
||||||
|
#include <QtBluetooth/QLowEnergyCharacteristic>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class using QtBluetooth to find and retrive info from Mi Flora devices.
|
||||||
|
*/
|
||||||
|
class MiFlora : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(float temperature NOTIFY temperatureChanged MEMBER temp)
|
||||||
|
Q_PROPERTY(quint32 brightness NOTIFY brightnessChanged MEMBER bright)
|
||||||
|
Q_PROPERTY(quint8 moisture NOTIFY moistureChanged MEMBER moisture)
|
||||||
|
Q_PROPERTY(quint16 conduction NOTIFY conductionChanged MEMBER conduct)
|
||||||
|
Q_PROPERTY(quint8 battery NOTIFY batteryChanged MEMBER battery)
|
||||||
|
|
||||||
|
public:
|
||||||
|
Q_INVOKABLE void startSearch();
|
||||||
|
Q_INVOKABLE void stopSearch();
|
||||||
|
|
||||||
|
Q_INVOKABLE void updateDataFromDevice(QString mac);
|
||||||
|
//TODO:History?
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void newDeviceFound(QString name, QString address);
|
||||||
|
void temperatureChanged(float temperature);
|
||||||
|
void brightnessChanged(quint32 brightness);
|
||||||
|
void moistureChanged(quint8 moisture);
|
||||||
|
void conductionChanged(quint16 conduction);
|
||||||
|
void batteryChanged(quint8 battery);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void foundDevice(const QBluetoothDeviceInfo &device);
|
||||||
|
void serviceDiscovered(const QBluetoothUuid &gatt);
|
||||||
|
void logControllerError(QLowEnergyController::Error err);
|
||||||
|
void logServiceError(QLowEnergyService::ServiceError err);
|
||||||
|
void serviceStateChanges(QLowEnergyService::ServiceState state);
|
||||||
|
void serviceCharWritten(QLowEnergyCharacteristic changedChar, QByteArray value);
|
||||||
|
void serviceCharRead(QLowEnergyCharacteristic readChar, QByteArray value);
|
||||||
|
|
||||||
|
QBluetoothDeviceDiscoveryAgent *agent;
|
||||||
|
QList<QBluetoothDeviceInfo> devices;
|
||||||
|
QLowEnergyController *currentController;
|
||||||
|
QLowEnergyService *sensorService;
|
||||||
|
|
||||||
|
const quint16 dataService = 4612; //0x1204
|
||||||
|
const quint16 batteryFirmwareChar = 6658;//0x1a02
|
||||||
|
const quint16 sensorsChar = 6657; //0x1a01
|
||||||
|
const quint16 magicChar = 6656; //0x1a00
|
||||||
|
|
||||||
|
float temp = 0.0;
|
||||||
|
quint32 bright = 0;
|
||||||
|
quint8 moisture = 0;
|
||||||
|
quint16 conduct = 0;
|
||||||
|
quint8 battery = 0;
|
||||||
|
QString version;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // MIFLORA_H
|
||||||
Reference in New Issue
Block a user