Implemented basic remote control
This commit is contained in:
@@ -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;
|
|
||||||
}
|
}
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user