Implemented basic remote control

This commit is contained in:
2021-07-20 23:48:00 +02:00
parent 0614abf4de
commit 7d557133e2
3 changed files with 219 additions and 70 deletions

View File

@@ -6,6 +6,7 @@
#include <QJsonDocument> #include <QJsonDocument>
#include <QJsonObject> #include <QJsonObject>
#include <QJsonArray> #include <QJsonArray>
#include <QTimerEvent>
// local includes // local includes
#include "deviceinfo.h" #include "deviceinfo.h"
@@ -23,26 +24,6 @@ DeviceHandler::DeviceHandler(QObject *parent) :
{ {
} }
void DeviceHandler::setAddressType(AddressType type)
{
switch (type) {
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;
}
void DeviceHandler::setDevice(DeviceInfo *device) void DeviceHandler::setDevice(DeviceInfo *device)
{ {
clearMessages(); clearMessages();
@@ -89,6 +70,110 @@ void DeviceHandler::setDevice(DeviceInfo *device)
} }
} }
void DeviceHandler::setAddressType(AddressType type)
{
switch (type) {
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;
}
}
}
void DeviceHandler::serviceDiscovered(const QBluetoothUuid &gatt) void DeviceHandler::serviceDiscovered(const QBluetoothUuid &gatt)
{ {
if (gatt == bobbycarServiceUuid) if (gatt == bobbycarServiceUuid)
@@ -118,6 +203,7 @@ void DeviceHandler::serviceScanDone()
connect(m_service, &QLowEnergyService::stateChanged, this, &DeviceHandler::serviceStateChanged); connect(m_service, &QLowEnergyService::stateChanged, this, &DeviceHandler::serviceStateChanged);
connect(m_service, &QLowEnergyService::characteristicChanged, this, &DeviceHandler::updateBobbycarValue); connect(m_service, &QLowEnergyService::characteristicChanged, this, &DeviceHandler::updateBobbycarValue);
connect(m_service, &QLowEnergyService::descriptorWritten, this, &DeviceHandler::confirmedDescriptorWrite); connect(m_service, &QLowEnergyService::descriptorWritten, this, &DeviceHandler::confirmedDescriptorWrite);
connect(m_service, &QLowEnergyService::characteristicWritten, this, &DeviceHandler::confirmedCharacteristicWrite);
m_service->discoverDetails(); m_service->discoverDetails();
} }
else else
@@ -126,23 +212,21 @@ void DeviceHandler::serviceScanDone()
} }
} }
namespace {
void logAddr(const QBluetoothUuid &uuid)
{
if (uuid == bobbycarServiceUuid)
qDebug() << "bobbycarServiceUuid";
else if (uuid == livestatsCharacUuid)
qDebug() << "livestatsCharacUuid";
else if (uuid == remotecontrolCharacUuid)
qDebug() << "remotecontrolCharacUuid";
else
qDebug() << "unknown uuid" << uuid;
}
}
void DeviceHandler::serviceStateChanged(QLowEnergyService::ServiceState s) void DeviceHandler::serviceStateChanged(QLowEnergyService::ServiceState s)
{ {
switch (s) { qDebug() << "serviceStateChanged()" << s;
if (m_timerId != -1)
{
killTimer(m_timerId);
m_timerId = -1;
emit remoteControlActiveChanged();
}
m_waitingForWrite = false;
switch (s)
{
case QLowEnergyService::DiscoveringServices: case QLowEnergyService::DiscoveringServices:
setInfo(tr("Discovering services...")); setInfo(tr("Discovering services..."));
break; break;
@@ -162,6 +246,13 @@ void DeviceHandler::serviceStateChanged(QLowEnergyService::ServiceState s)
break; break;
} }
m_remotecontrolCharacteristic = m_service->characteristic(remotecontrolCharacUuid);
if (!m_remotecontrolCharacteristic.isValid())
{
setError("remotecontrolCharacUuid not found.");
break;
}
break; break;
} }
default: default:
@@ -187,6 +278,8 @@ void DeviceHandler::updateBobbycarValue(const QLowEnergyCharacteristic &c, const
return; return;
} }
clearMessages();
const QJsonObject &obj = doc.object(); const QJsonObject &obj = doc.object();
{ {
@@ -253,40 +346,23 @@ void DeviceHandler::confirmedDescriptorWrite(const QLowEnergyDescriptor &d, cons
} }
} }
void DeviceHandler::disconnectService() void DeviceHandler::confirmedCharacteristicWrite(const QLowEnergyCharacteristic &info, const QByteArray &value)
{ {
m_foundBobbycarService = false; qDebug() << "confirmedCharacteristicWrite";
//disable notifications if (info == m_remotecontrolCharacteristic)
if (m_service) m_waitingForWrite = false;
{
if (m_notificationDescLivestats.isValid() && m_notificationDescLivestats.value() == QByteArray::fromHex("0100"))
m_service->writeDescriptor(m_notificationDescLivestats, QByteArray::fromHex("0000"));
disconnectInternal();
}
} }
void DeviceHandler::disconnectInternal() void DeviceHandler::sendRemoteControl()
{ {
if (!m_notificationDescLivestats.isValid()) m_waitingForWrite = true;
{
//disabled notifications -> assume disconnect intent
if (m_control)
m_control->disconnectFromDevice();
if (m_service) qDebug() << "writeCharacteristic()";
{ m_service->writeCharacteristic(m_remotecontrolCharacteristic, QJsonDocument{QJsonObject {
delete m_service; {"fl", m_remoteControlFrontLeft},
m_service = nullptr; {"fr", m_remoteControlFrontRight},
} {"bl", m_remoteControlBackLeft},
} {"br", m_remoteControlBackRight}
} }}.toJson(QJsonDocument::Compact));
bool DeviceHandler::alive() const
{
if (m_service)
return m_service->state() == QLowEnergyService::ServiceDiscovered;
return false;
} }

View File

@@ -34,6 +34,12 @@ class DeviceHandler : public BluetoothBaseClass
Q_PROPERTY(float backLeftDcLink READ backLeftDcLink NOTIFY backLeftDcLinkChanged); Q_PROPERTY(float backLeftDcLink READ backLeftDcLink NOTIFY backLeftDcLinkChanged);
Q_PROPERTY(float backRightDcLink READ backRightDcLink NOTIFY backRightDcLinkChanged); Q_PROPERTY(float backRightDcLink READ backRightDcLink NOTIFY backRightDcLinkChanged);
Q_PROPERTY(bool remoteControlActive READ remoteControlActive WRITE setRemoteControlActive NOTIFY remoteControlActiveChanged);
Q_PROPERTY(int remoteControlFrontLeft WRITE setRemoteControlFrontLeft);
Q_PROPERTY(int remoteControlFrontRight WRITE setRemoteControlFrontRight);
Q_PROPERTY(int remoteControlBackLeft WRITE setRemoteControlBackLeft);
Q_PROPERTY(int remoteControlBackRight WRITE setRemoteControlBackRight);
public: public:
enum class AddressType { enum class AddressType {
PublicAddress, PublicAddress,
@@ -66,6 +72,13 @@ public:
float backLeftDcLink() const { return m_backLeftDcLink; } float backLeftDcLink() const { return m_backLeftDcLink; }
float backRightDcLink() const { return m_backRightDcLink; } float backRightDcLink() const { return m_backRightDcLink; }
bool remoteControlActive() const { return m_timerId != -1; }
void setRemoteControlActive(bool remoteControlActive);
void setRemoteControlFrontLeft(int remoteControlFrontLeft) { m_remoteControlFrontLeft = remoteControlFrontLeft; }
void setRemoteControlFrontRight(int remoteControlFrontRight) { m_remoteControlFrontRight = remoteControlFrontRight; }
void setRemoteControlBackLeft(int remoteControlBackLeft) { m_remoteControlBackLeft = remoteControlBackLeft; }
void setRemoteControlBackRight(int remoteControlBackRight) { m_remoteControlBackRight = remoteControlBackRight; }
signals: signals:
void aliveChanged(); void aliveChanged();
@@ -86,6 +99,11 @@ signals:
void backLeftDcLinkChanged(); void backLeftDcLinkChanged();
void backRightDcLinkChanged(); void backRightDcLinkChanged();
void remoteControlActiveChanged();
protected:
void timerEvent(QTimerEvent *event) override;
public slots: public slots:
void disconnectService(); void disconnectService();
@@ -99,15 +117,20 @@ private:
//QLowEnergyService //QLowEnergyService
void serviceStateChanged(QLowEnergyService::ServiceState s); void serviceStateChanged(QLowEnergyService::ServiceState s);
void updateBobbycarValue(const QLowEnergyCharacteristic &c, void updateBobbycarValue(const QLowEnergyCharacteristic &c,
const QByteArray &value); const QByteArray &value);
void confirmedDescriptorWrite(const QLowEnergyDescriptor &d, void confirmedDescriptorWrite(const QLowEnergyDescriptor &d,
const QByteArray &value); const QByteArray &value);
void confirmedCharacteristicWrite(const QLowEnergyCharacteristic &info,
const QByteArray &value);
void sendRemoteControl();
private: private:
QLowEnergyController::RemoteAddressType m_addressType = QLowEnergyController::PublicAddress; QLowEnergyController::RemoteAddressType m_addressType = QLowEnergyController::PublicAddress;
QLowEnergyController *m_control = nullptr; QLowEnergyController *m_control = nullptr;
QLowEnergyService *m_service = nullptr; QLowEnergyService *m_service = nullptr;
QLowEnergyDescriptor m_notificationDescLivestats; QLowEnergyDescriptor m_notificationDescLivestats;
QLowEnergyCharacteristic m_remotecontrolCharacteristic;
DeviceInfo *m_currentDevice{}; DeviceInfo *m_currentDevice{};
bool m_foundBobbycarService{}; bool m_foundBobbycarService{};
@@ -128,6 +151,15 @@ private:
float m_frontRightDcLink{}; float m_frontRightDcLink{};
float m_backLeftDcLink{}; float m_backLeftDcLink{};
float m_backRightDcLink{}; float m_backRightDcLink{};
int m_timerId{-1};
int m_remoteControlFrontLeft{};
int m_remoteControlFrontRight{};
int m_remoteControlBackLeft{};
int m_remoteControlBackRight{};
bool m_waitingForWrite{};
}; };
#endif // DEVICEHANDLER_H #endif // DEVICEHANDLER_H

View File

@@ -22,19 +22,60 @@ GamePage {
radius: GameSettings.buttonRadius radius: GameSettings.buttonRadius
color: GameSettings.viewColor color: GameSettings.viewColor
property real maxSpeed: 100
property real remoteControlFrontLeft: maxSpeed * handler.relativeY * (handler.relativeX < 0 ? 1 + handler.relativeX : 1)
property real remoteControlFrontRight: maxSpeed * handler.relativeY * (handler.relativeX > 0 ? 1 - handler.relativeX : 1)
property real remoteControlBackLeft: 0
property real remoteControlBackRight: 0
onRemoteControlFrontLeftChanged: deviceHandler.remoteControlFrontLeft = remoteControlFrontLeft
onRemoteControlFrontRightChanged: deviceHandler.remoteControlFrontRight = remoteControlFrontRight
onRemoteControlBackLeftChanged: deviceHandler.remoteControlBackLeft = remoteControlBackLeft
onRemoteControlBackRightChanged: deviceHandler.remoteControlBackRight = remoteControlBackRight
Rectangle {
parent: container
color: "white"
anchors.centerIn: parent
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
width: 20
height: width
radius: width / 2
}
Text {
anchors.top: parent.top
minimumPixelSize: 10
font.pixelSize: GameSettings.mediumFontSize
color: GameSettings.textColor
text: {
return "x:" + handler.relativeX.toFixed(1) + " y:" + handler.relativeY.toFixed(1) + "\n" +
"fl:" + Math.round(container.remoteControlFrontLeft) + " fr:" + Math.round(container.remoteControlFrontRight) + "\n" +
"bl:" + Math.round(container.remoteControlBackLeft) + " br:" + Math.round(container.remoteControlBackRight);
}
}
PointHandler { PointHandler {
id: handler id: handler
//acceptedDevices: PointerDevice.TouchScreen //acceptedDevices: PointerDevice.TouchScreen
property real clampedX: handler.active ? Math.min(Math.max(handler.point.position.x, 0), container.width) : (container.width / 2)
property real clampedY: handler.active ? Math.min(Math.max(handler.point.position.y, 0), container.height) : (container.height / 2)
property real relativeX: ((clampedX / container.width) - 0.5) * 2
property real relativeY: ((clampedY / container.height) - 0.5) * -2
target: Rectangle { target: Rectangle {
parent: container parent: container
color: "red" color: "red"
visible: handler.active visible: handler.active
x: handler.point.position.x - width / 2 x: handler.clampedX - width / 2
y: handler.point.position.y - height / 2 y: handler.clampedY - height / 2
width: 20 width: 50
height: width height: width
radius: width / 2 radius: width / 2
} }
onActiveChanged: deviceHandler.remoteControlActive = handler.active
} }
} }
} }