2021-07-17 05:09:39 +02:00
|
|
|
#include "devicehandler.h"
|
2021-07-20 21:45:46 +02:00
|
|
|
|
|
|
|
// Qt includes
|
2021-07-17 05:09:39 +02:00
|
|
|
#include <QtEndian>
|
|
|
|
#include <QRandomGenerator>
|
2021-07-20 21:45:46 +02:00
|
|
|
#include <QJsonDocument>
|
|
|
|
#include <QJsonObject>
|
|
|
|
#include <QJsonArray>
|
2021-07-20 23:48:00 +02:00
|
|
|
#include <QTimerEvent>
|
2021-07-20 21:45:46 +02:00
|
|
|
|
|
|
|
// local includes
|
|
|
|
#include "deviceinfo.h"
|
2021-07-17 05:09:39 +02:00
|
|
|
|
|
|
|
namespace {
|
|
|
|
const QBluetoothUuid bobbycarServiceUuid{QUuid::fromString(QStringLiteral("0335e46c-f355-4ce6-8076-017de08cee98"))};
|
2021-07-20 02:39:01 +02:00
|
|
|
|
2021-07-20 21:45:46 +02:00
|
|
|
const QBluetoothUuid livestatsCharacUuid{QUuid::fromString(QStringLiteral("a48321ea-329f-4eab-a401-30e247211524"))};
|
|
|
|
const QBluetoothUuid remotecontrolCharacUuid{QUuid::fromString(QStringLiteral("4201def0-a264-43e6-946b-6b2d9612dfed"))};
|
2021-08-16 17:45:17 +02:00
|
|
|
|
|
|
|
const QBluetoothUuid settingsSetterUuid{QUuid::fromString(QStringLiteral("4201def1-a264-43e6-946b-6b2d9612dfed"))};
|
|
|
|
const QBluetoothUuid wifiListUuid{QUuid::fromString(QStringLiteral("4201def2-a264-43e6-946b-6b2d9612dfed"))};
|
2021-07-17 05:09:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
DeviceHandler::DeviceHandler(QObject *parent) :
|
|
|
|
BluetoothBaseClass(parent),
|
2021-07-20 02:39:01 +02:00
|
|
|
m_foundBobbycarService(false)
|
2021-07-17 05:09:39 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2021-08-17 14:03:33 +02:00
|
|
|
void DeviceHandler::setDevice(const QBluetoothDeviceInfo &device)
|
2021-07-17 05:09:39 +02:00
|
|
|
{
|
|
|
|
clearMessages();
|
|
|
|
m_currentDevice = device;
|
|
|
|
|
|
|
|
// Disconnect and delete old connection
|
2021-07-20 21:45:46 +02:00
|
|
|
if (m_control)
|
|
|
|
{
|
2021-07-17 05:09:39 +02:00
|
|
|
m_control->disconnectFromDevice();
|
|
|
|
delete m_control;
|
|
|
|
m_control = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create new controller and connect it if device available
|
2021-08-17 14:03:33 +02:00
|
|
|
if (m_currentDevice.isValid())
|
2021-07-20 21:45:46 +02:00
|
|
|
{
|
2021-07-17 05:09:39 +02:00
|
|
|
// Make connections
|
2021-08-17 14:03:33 +02:00
|
|
|
m_control = QLowEnergyController::createCentral(m_currentDevice, this);
|
2021-07-17 05:09:39 +02:00
|
|
|
m_control->setRemoteAddressType(m_addressType);
|
2021-08-17 14:03:33 +02:00
|
|
|
|
2021-07-17 05:09:39 +02:00
|
|
|
connect(m_control, &QLowEnergyController::serviceDiscovered,
|
|
|
|
this, &DeviceHandler::serviceDiscovered);
|
|
|
|
connect(m_control, &QLowEnergyController::discoveryFinished,
|
|
|
|
this, &DeviceHandler::serviceScanDone);
|
|
|
|
|
|
|
|
connect(m_control, static_cast<void (QLowEnergyController::*)(QLowEnergyController::Error)>(&QLowEnergyController::error),
|
|
|
|
this, [this](QLowEnergyController::Error error) {
|
|
|
|
Q_UNUSED(error);
|
|
|
|
setError("Cannot connect to remote device.");
|
|
|
|
});
|
|
|
|
connect(m_control, &QLowEnergyController::connected, this, [this]() {
|
|
|
|
setInfo("Controller connected. Search services...");
|
|
|
|
m_control->discoverServices();
|
|
|
|
});
|
|
|
|
connect(m_control, &QLowEnergyController::disconnected, this, [this]() {
|
|
|
|
setError("LowEnergy controller disconnected");
|
|
|
|
});
|
|
|
|
|
|
|
|
// Connect
|
|
|
|
m_control->connectToDevice();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-20 23:48:00 +02:00
|
|
|
void DeviceHandler::setAddressType(AddressType type)
|
|
|
|
{
|
2021-08-17 14:03:33 +02:00
|
|
|
switch (type)
|
|
|
|
{
|
2021-07-20 23:48:00 +02:00
|
|
|
case DeviceHandler::AddressType::PublicAddress:
|
|
|
|
m_addressType = QLowEnergyController::PublicAddress;
|
|
|
|
break;
|
|
|
|
case DeviceHandler::AddressType::RandomAddress:
|
|
|
|
m_addressType = QLowEnergyController::RandomAddress;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DeviceHandler::AddressType DeviceHandler::addressType() const
|
|
|
|
{
|
|
|
|
if (m_addressType == QLowEnergyController::RandomAddress)
|
|
|
|
return DeviceHandler::AddressType::RandomAddress;
|
|
|
|
|
|
|
|
return DeviceHandler::AddressType::PublicAddress;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DeviceHandler::alive() const
|
|
|
|
{
|
|
|
|
if (m_service)
|
|
|
|
return m_service->state() == QLowEnergyService::ServiceDiscovered;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DeviceHandler::setRemoteControlActive(bool remoteControlActive)
|
|
|
|
{
|
|
|
|
if (!remoteControlActive && m_timerId != -1)
|
|
|
|
{
|
|
|
|
killTimer(m_timerId);
|
|
|
|
m_timerId = -1;
|
|
|
|
emit remoteControlActiveChanged();
|
|
|
|
|
|
|
|
if (m_service && m_remotecontrolCharacteristic.isValid())
|
|
|
|
{
|
|
|
|
m_remoteControlFrontLeft = 0;
|
|
|
|
m_remoteControlFrontRight = 0;
|
|
|
|
m_remoteControlBackLeft = 0;
|
|
|
|
m_remoteControlBackRight = 0;
|
|
|
|
|
|
|
|
sendRemoteControl();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (remoteControlActive && m_timerId == -1 && m_service && m_remotecontrolCharacteristic.isValid())
|
|
|
|
{
|
|
|
|
m_timerId = startTimer(100);
|
|
|
|
emit remoteControlActiveChanged();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void DeviceHandler::timerEvent(QTimerEvent *event)
|
|
|
|
{
|
|
|
|
if (event->timerId() == m_timerId)
|
|
|
|
{
|
|
|
|
if (!m_service || !m_remotecontrolCharacteristic.isValid())
|
|
|
|
{
|
|
|
|
killTimer(m_timerId);
|
|
|
|
m_timerId = -1;
|
|
|
|
emit remoteControlActiveChanged();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_waitingForWrite)
|
|
|
|
qWarning() << "still pending";
|
|
|
|
else
|
|
|
|
sendRemoteControl();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
BluetoothBaseClass::timerEvent(event);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DeviceHandler::disconnectService()
|
|
|
|
{
|
|
|
|
m_foundBobbycarService = false;
|
|
|
|
|
|
|
|
//disable notifications
|
|
|
|
if (m_service)
|
|
|
|
{
|
|
|
|
if (m_notificationDescLivestats.isValid() && m_notificationDescLivestats.value() == QByteArray::fromHex("0100"))
|
|
|
|
m_service->writeDescriptor(m_notificationDescLivestats, QByteArray::fromHex("0000"));
|
|
|
|
|
|
|
|
disconnectInternal();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void DeviceHandler::disconnectInternal()
|
|
|
|
{
|
|
|
|
if (!m_notificationDescLivestats.isValid())
|
|
|
|
{
|
|
|
|
//disabled notifications -> assume disconnect intent
|
|
|
|
if (m_control)
|
|
|
|
m_control->disconnectFromDevice();
|
|
|
|
|
|
|
|
if (m_service)
|
|
|
|
{
|
|
|
|
delete m_service;
|
|
|
|
m_service = nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-17 05:09:39 +02:00
|
|
|
void DeviceHandler::serviceDiscovered(const QBluetoothUuid &gatt)
|
|
|
|
{
|
2021-07-20 21:45:46 +02:00
|
|
|
if (gatt == bobbycarServiceUuid)
|
|
|
|
{
|
2021-07-17 05:09:39 +02:00
|
|
|
setInfo("Bobbycar service discovered. Waiting for service scan to be done...");
|
|
|
|
m_foundBobbycarService = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void DeviceHandler::serviceScanDone()
|
|
|
|
{
|
|
|
|
setInfo("Service scan done.");
|
|
|
|
|
|
|
|
// Delete old service if available
|
2021-07-20 21:45:46 +02:00
|
|
|
if (m_service)
|
|
|
|
{
|
2021-07-17 05:09:39 +02:00
|
|
|
delete m_service;
|
|
|
|
m_service = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If bobbycarService found, create new service
|
|
|
|
if (m_foundBobbycarService)
|
|
|
|
m_service = m_control->createServiceObject(bobbycarServiceUuid, this);
|
|
|
|
|
2021-07-20 21:45:46 +02:00
|
|
|
if (m_service)
|
|
|
|
{
|
2021-07-17 05:09:39 +02:00
|
|
|
connect(m_service, &QLowEnergyService::stateChanged, this, &DeviceHandler::serviceStateChanged);
|
|
|
|
connect(m_service, &QLowEnergyService::characteristicChanged, this, &DeviceHandler::updateBobbycarValue);
|
|
|
|
connect(m_service, &QLowEnergyService::descriptorWritten, this, &DeviceHandler::confirmedDescriptorWrite);
|
2021-07-20 23:48:00 +02:00
|
|
|
connect(m_service, &QLowEnergyService::characteristicWritten, this, &DeviceHandler::confirmedCharacteristicWrite);
|
2021-07-17 05:09:39 +02:00
|
|
|
m_service->discoverDetails();
|
2021-07-20 21:45:46 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-07-17 05:09:39 +02:00
|
|
|
setError("Bobbycar Service not found.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void DeviceHandler::serviceStateChanged(QLowEnergyService::ServiceState s)
|
|
|
|
{
|
2021-07-20 23:48:00 +02:00
|
|
|
qDebug() << "serviceStateChanged()" << s;
|
|
|
|
|
|
|
|
if (m_timerId != -1)
|
|
|
|
{
|
|
|
|
killTimer(m_timerId);
|
|
|
|
m_timerId = -1;
|
|
|
|
emit remoteControlActiveChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
m_waitingForWrite = false;
|
|
|
|
|
|
|
|
switch (s)
|
|
|
|
{
|
2021-07-17 05:09:39 +02:00
|
|
|
case QLowEnergyService::DiscoveringServices:
|
|
|
|
setInfo(tr("Discovering services..."));
|
|
|
|
break;
|
|
|
|
case QLowEnergyService::ServiceDiscovered:
|
|
|
|
{
|
|
|
|
setInfo(tr("Service discovered."));
|
|
|
|
|
2021-07-20 21:45:46 +02:00
|
|
|
if (const QLowEnergyCharacteristic hrChar = m_service->characteristic(livestatsCharacUuid); hrChar.isValid())
|
2021-07-20 02:39:01 +02:00
|
|
|
{
|
2021-07-20 21:45:46 +02:00
|
|
|
m_notificationDescLivestats = hrChar.descriptor(QBluetoothUuid::ClientCharacteristicConfiguration);
|
|
|
|
if (m_notificationDescLivestats.isValid())
|
|
|
|
m_service->writeDescriptor(m_notificationDescLivestats, QByteArray::fromHex("0100"));
|
2021-07-20 02:39:01 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-07-20 21:45:46 +02:00
|
|
|
setError("livestatsCharacUuid not found.");
|
2021-07-20 02:39:01 +02:00
|
|
|
break;
|
|
|
|
}
|
2021-07-17 05:09:39 +02:00
|
|
|
|
2021-07-20 23:48:00 +02:00
|
|
|
m_remotecontrolCharacteristic = m_service->characteristic(remotecontrolCharacUuid);
|
|
|
|
if (!m_remotecontrolCharacteristic.isValid())
|
|
|
|
{
|
|
|
|
setError("remotecontrolCharacUuid not found.");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2021-07-17 05:09:39 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
//nothing for now
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
emit aliveChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
void DeviceHandler::updateBobbycarValue(const QLowEnergyCharacteristic &c, const QByteArray &value)
|
|
|
|
{
|
2021-07-20 02:39:01 +02:00
|
|
|
//qDebug() << "updateBobbycarValue";
|
|
|
|
//logAddr(c.uuid());
|
|
|
|
|
2021-07-20 21:45:46 +02:00
|
|
|
if (c.uuid() == livestatsCharacUuid)
|
2021-07-20 02:39:01 +02:00
|
|
|
{
|
2021-07-20 21:45:46 +02:00
|
|
|
QJsonParseError error;
|
|
|
|
QJsonDocument doc = QJsonDocument::fromJson(value, &error);
|
|
|
|
if (error.error != QJsonParseError::NoError)
|
2021-07-20 02:39:01 +02:00
|
|
|
{
|
2021-07-20 21:45:46 +02:00
|
|
|
qWarning() << "could not parse livestats" << error.errorString();
|
|
|
|
return;
|
2021-07-20 02:39:01 +02:00
|
|
|
}
|
2021-07-20 21:45:46 +02:00
|
|
|
|
2021-07-20 23:48:00 +02:00
|
|
|
clearMessages();
|
|
|
|
|
2021-07-20 21:45:46 +02:00
|
|
|
const QJsonObject &obj = doc.object();
|
|
|
|
|
2021-07-20 02:39:01 +02:00
|
|
|
{
|
2021-07-20 21:45:46 +02:00
|
|
|
const QJsonArray &arr = obj.value("v").toArray();
|
|
|
|
m_frontVoltage = arr.at(0).toDouble();
|
|
|
|
emit frontVoltageChanged();
|
|
|
|
m_backVoltage = arr.at(1).toDouble();
|
2021-07-20 02:39:01 +02:00
|
|
|
emit backVoltageChanged();
|
|
|
|
}
|
|
|
|
{
|
2021-07-20 21:45:46 +02:00
|
|
|
const QJsonArray &arr = obj.value("t").toArray();
|
|
|
|
m_frontTemperature = arr.at(0).toDouble();
|
2021-07-20 02:39:01 +02:00
|
|
|
emit frontTemperatureChanged();
|
2021-07-20 21:45:46 +02:00
|
|
|
m_backTemperature = arr.at(1).toDouble();
|
2021-07-20 02:39:01 +02:00
|
|
|
emit backTemperatureChanged();
|
|
|
|
}
|
|
|
|
{
|
2021-07-20 21:45:46 +02:00
|
|
|
const QJsonArray &arr = obj.value("e").toArray();
|
|
|
|
m_frontLeftError = arr.at(0).toInt();
|
2021-07-20 02:39:01 +02:00
|
|
|
emit frontLeftErrorChanged();
|
2021-07-20 21:45:46 +02:00
|
|
|
m_frontRightError = arr.at(1).toInt();
|
2021-07-20 02:39:01 +02:00
|
|
|
emit frontRightErrorChanged();
|
2021-07-20 21:45:46 +02:00
|
|
|
m_backLeftError = arr.at(2).toInt();
|
2021-07-20 02:39:01 +02:00
|
|
|
emit backLeftErrorChanged();
|
2021-07-20 21:45:46 +02:00
|
|
|
m_backRightError = arr.at(3).toInt();
|
2021-07-20 02:39:01 +02:00
|
|
|
emit backRightErrorChanged();
|
|
|
|
}
|
|
|
|
{
|
2021-07-20 21:45:46 +02:00
|
|
|
const QJsonArray &arr = obj.value("s").toArray();
|
|
|
|
m_frontLeftSpeed = arr.at(0).toDouble();
|
2021-07-20 02:39:01 +02:00
|
|
|
emit frontLeftSpeedChanged();
|
2021-07-20 21:45:46 +02:00
|
|
|
m_frontRightSpeed = arr.at(1).toDouble();
|
2021-07-20 02:39:01 +02:00
|
|
|
emit frontRightSpeedChanged();
|
2021-07-20 21:45:46 +02:00
|
|
|
m_backLeftSpeed = arr.at(2).toDouble();
|
2021-07-20 02:39:01 +02:00
|
|
|
emit backLeftSpeedChanged();
|
2021-07-20 21:45:46 +02:00
|
|
|
m_backRightSpeed = arr.at(3).toDouble();
|
2021-07-20 02:39:01 +02:00
|
|
|
emit backRightSpeedChanged();
|
|
|
|
}
|
|
|
|
{
|
2021-07-20 21:45:46 +02:00
|
|
|
const QJsonArray &arr = obj.value("a").toArray();
|
|
|
|
m_frontLeftDcLink = arr.at(0).toDouble();
|
2021-07-20 02:39:01 +02:00
|
|
|
emit frontLeftDcLinkChanged();
|
2021-07-20 21:45:46 +02:00
|
|
|
m_frontRightDcLink = arr.at(1).toDouble();
|
2021-07-20 02:39:01 +02:00
|
|
|
emit frontRightDcLinkChanged();
|
2021-07-20 21:45:46 +02:00
|
|
|
m_backLeftDcLink = arr.at(2).toDouble();
|
2021-07-20 02:39:01 +02:00
|
|
|
emit backLeftDcLinkChanged();
|
2021-07-20 21:45:46 +02:00
|
|
|
m_backRightDcLink = arr.at(3).toDouble();
|
2021-07-20 02:39:01 +02:00
|
|
|
emit backRightDcLinkChanged();
|
|
|
|
}
|
|
|
|
}
|
2021-07-20 21:45:46 +02:00
|
|
|
else
|
|
|
|
qWarning() << "unknown uuid" << c.uuid();
|
2021-07-17 05:09:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void DeviceHandler::confirmedDescriptorWrite(const QLowEnergyDescriptor &d, const QByteArray &value)
|
|
|
|
{
|
2021-07-20 02:39:01 +02:00
|
|
|
qDebug() << "confirmedDescriptorWrite" << d.uuid() << value;
|
|
|
|
if (d.isValid() && value == QByteArray::fromHex("0000"))
|
|
|
|
{
|
2021-07-20 21:45:46 +02:00
|
|
|
if (d == m_notificationDescLivestats)
|
|
|
|
m_notificationDescLivestats = {};
|
2021-07-20 02:39:01 +02:00
|
|
|
|
|
|
|
disconnectInternal();
|
2021-07-17 05:09:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-20 23:48:00 +02:00
|
|
|
void DeviceHandler::confirmedCharacteristicWrite(const QLowEnergyCharacteristic &info, const QByteArray &value)
|
2021-07-17 05:09:39 +02:00
|
|
|
{
|
2021-08-17 14:03:33 +02:00
|
|
|
Q_UNUSED(value)
|
2021-07-20 23:48:00 +02:00
|
|
|
qDebug() << "confirmedCharacteristicWrite";
|
2021-07-17 05:09:39 +02:00
|
|
|
|
2021-07-20 23:48:00 +02:00
|
|
|
if (info == m_remotecontrolCharacteristic)
|
|
|
|
m_waitingForWrite = false;
|
2021-07-17 05:09:39 +02:00
|
|
|
}
|
|
|
|
|
2021-07-20 23:48:00 +02:00
|
|
|
void DeviceHandler::sendRemoteControl()
|
2021-07-17 05:09:39 +02:00
|
|
|
{
|
2021-07-20 23:48:00 +02:00
|
|
|
m_waitingForWrite = true;
|
|
|
|
|
|
|
|
qDebug() << "writeCharacteristic()";
|
|
|
|
m_service->writeCharacteristic(m_remotecontrolCharacteristic, QJsonDocument{QJsonObject {
|
|
|
|
{"fl", m_remoteControlFrontLeft},
|
|
|
|
{"fr", m_remoteControlFrontRight},
|
|
|
|
{"bl", m_remoteControlBackLeft},
|
|
|
|
{"br", m_remoteControlBackRight}
|
|
|
|
}}.toJson(QJsonDocument::Compact));
|
2021-07-17 05:09:39 +02:00
|
|
|
}
|