Skip to content

Commit b43f873

Browse files
committed
Comms: Add BluetoothLE Link
1 parent 5e084d4 commit b43f873

File tree

10 files changed

+2195
-367
lines changed

10 files changed

+2195
-367
lines changed

src/Comms/BluetoothLink.cc

Lines changed: 1668 additions & 227 deletions
Large diffs are not rendered by default.

src/Comms/BluetoothLink.h

Lines changed: 194 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,26 @@
99

1010
#pragma once
1111

12+
#include <atomic>
13+
1214
#include <QtBluetooth/QBluetoothAddress>
1315
#include <QtBluetooth/QBluetoothDeviceDiscoveryAgent>
1416
#include <QtBluetooth/QBluetoothDeviceInfo>
15-
#ifdef Q_OS_IOS
16-
#include <QtBluetooth/QBluetoothServiceDiscoveryAgent>
17-
#endif
18-
#include <QtBluetooth/QBluetoothServiceInfo>
1917
#include <QtBluetooth/QBluetoothSocket>
20-
#include <QtBluetooth/QBluetoothUuid>
18+
#include <QtBluetooth/QBluetoothLocalDevice>
19+
#include <QtBluetooth/QBluetoothServiceInfo>
20+
#include <QtBluetooth/QBluetoothServiceDiscoveryAgent>
21+
#include <QtBluetooth/QLowEnergyController>
22+
#include <QtBluetooth/QLowEnergyService>
23+
#include <QtBluetooth/QLowEnergyCharacteristic>
2124
#include <QtCore/QList>
2225
#include <QtCore/QLoggingCategory>
26+
#include <QtCore/QPointer>
27+
#include <QtCore/QQueue>
2328
#include <QtCore/QString>
29+
#include <QtCore/QTimer>
30+
#include <QtCore/QVariantList>
31+
#include <QtQmlIntegration/QtQmlIntegration>
2432

2533
#include "LinkConfiguration.h"
2634
#include "LinkInterface.h"
@@ -31,71 +39,62 @@ Q_DECLARE_LOGGING_CATEGORY(BluetoothLinkLog)
3139

3240
/*===========================================================================*/
3341

34-
struct BluetoothData
35-
{
36-
BluetoothData() = default;
37-
#ifdef Q_OS_IOS
38-
BluetoothData(const QString &name_, QBluetoothUuid uuid_)
39-
: uuid(uuid_)
40-
#else
41-
BluetoothData(const QString &name_, QBluetoothAddress address_)
42-
: address(address_)
43-
#endif
44-
, name(name_)
45-
{}
46-
47-
BluetoothData(const BluetoothData &other)
48-
{
49-
*this = other;
50-
}
51-
52-
bool operator==(const BluetoothData &other) const
53-
{
54-
#ifdef Q_OS_IOS
55-
return ((name == other.name) && (uuid == other.uuid));
56-
#else
57-
return ((name == other.name) && (address == other.address));
58-
#endif
59-
}
60-
61-
BluetoothData &operator=(const BluetoothData &other)
62-
{
63-
name = other.name;
64-
#ifdef Q_OS_IOS
65-
uuid = other.uuid;
66-
#else
67-
address = other.address;
68-
#endif
69-
return *this;
70-
}
71-
72-
QString name;
73-
#ifdef Q_OS_IOS
74-
QBluetoothUuid uuid;
75-
#else
76-
QBluetoothAddress address;
77-
#endif
78-
};
79-
80-
/*===========================================================================*/
81-
8242
class BluetoothConfiguration : public LinkConfiguration
8343
{
8444
Q_OBJECT
85-
86-
Q_PROPERTY(QString deviceName READ deviceName NOTIFY deviceChanged)
87-
Q_PROPERTY(QString address READ address NOTIFY deviceChanged)
88-
Q_PROPERTY(QStringList nameList READ nameList NOTIFY nameListChanged)
89-
Q_PROPERTY(bool scanning READ scanning NOTIFY scanningChanged)
45+
QML_ELEMENT
46+
QML_UNCREATABLE("")
47+
Q_PROPERTY(BluetoothMode mode READ mode WRITE setMode NOTIFY modeChanged)
48+
Q_PROPERTY(QString deviceName READ deviceName NOTIFY deviceChanged)
49+
Q_PROPERTY(QString address READ address NOTIFY deviceChanged)
50+
Q_PROPERTY(QStringList nameList READ nameList NOTIFY nameListChanged)
51+
Q_PROPERTY(QVariantList devicesModel READ devicesModel NOTIFY devicesModelChanged)
52+
Q_PROPERTY(bool scanning READ scanning NOTIFY scanningChanged)
53+
Q_PROPERTY(QString serviceUuid READ serviceUuid WRITE setServiceUuid NOTIFY serviceUuidChanged)
54+
Q_PROPERTY(QString readUuid READ readUuid WRITE setReadUuid NOTIFY readUuidChanged)
55+
Q_PROPERTY(QString writeUuid READ writeUuid WRITE setWriteUuid NOTIFY writeUuidChanged)
56+
Q_PROPERTY(qint16 connectedRssi READ connectedRssi NOTIFY connectedRssiChanged)
57+
Q_PROPERTY(qint16 selectedRssi READ selectedRssi NOTIFY selectedRssiChanged)
58+
Q_PROPERTY(bool adapterAvailable READ isAdapterAvailable NOTIFY adapterStateChanged)
59+
Q_PROPERTY(bool adapterPoweredOn READ isAdapterPoweredOn NOTIFY adapterStateChanged)
60+
Q_PROPERTY(QString adapterName READ getAdapterName NOTIFY adapterStateChanged)
61+
Q_PROPERTY(QString adapterAddress READ getAdapterAddress NOTIFY adapterStateChanged)
62+
Q_PROPERTY(QString hostMode READ getHostMode NOTIFY adapterStateChanged)
9063

9164
public:
9265
explicit BluetoothConfiguration(const QString &name, QObject *parent = nullptr);
9366
explicit BluetoothConfiguration(const BluetoothConfiguration *copy, QObject *parent = nullptr);
94-
virtual ~BluetoothConfiguration();
67+
~BluetoothConfiguration() override;
68+
69+
enum class BluetoothMode {
70+
ModeClassic,
71+
ModeLowEnergy
72+
};
73+
Q_ENUM(BluetoothMode)
9574

9675
Q_INVOKABLE void startScan();
97-
Q_INVOKABLE void stopScan() const;
76+
Q_INVOKABLE void stopScan();
9877
Q_INVOKABLE void setDevice(const QString &name);
78+
Q_INVOKABLE void setDeviceByAddress(const QString &address);
79+
80+
// Pairing support (Classic Bluetooth only)
81+
Q_INVOKABLE void requestPairing(const QString &address);
82+
Q_INVOKABLE void removePairing(const QString &address);
83+
Q_INVOKABLE QString getPairingStatus(const QString &address) const;
84+
85+
// Adapter information
86+
Q_INVOKABLE bool isAdapterAvailable() const;
87+
Q_INVOKABLE QString getAdapterAddress() const;
88+
Q_INVOKABLE QString getAdapterName() const;
89+
Q_INVOKABLE bool isAdapterPoweredOn() const;
90+
Q_INVOKABLE QVariantList getAllPairedDevices() const;
91+
Q_INVOKABLE QVariantList getConnectedDevices() const;
92+
Q_INVOKABLE void powerOnAdapter();
93+
Q_INVOKABLE void powerOffAdapter();
94+
Q_INVOKABLE void setAdapterDiscoverable(bool discoverable);
95+
Q_INVOKABLE QVariantList getAllAvailableAdapters() const;
96+
Q_INVOKABLE void selectAdapter(const QString &address);
97+
Q_INVOKABLE QString getHostMode() const;
9998

10099
LinkType type() const override { return LinkConfiguration::TypeBluetooth; }
101100
void copyFrom(const LinkConfiguration *source) override;
@@ -104,33 +103,85 @@ class BluetoothConfiguration : public LinkConfiguration
104103
QString settingsURL() const override { return QStringLiteral("BluetoothSettings.qml"); }
105104
QString settingsTitle() const override;
106105

107-
BluetoothData device() const { return _device; }
108-
QString deviceName() const { return _device.name; }
109-
#ifdef Q_OS_IOS
110-
QString address() const { return QString(); };
111-
#else
112-
QString address() const { return _device.address.toString(); };
113-
#endif
114-
QStringList nameList() const { return _nameList; }
106+
BluetoothMode mode() const { return _mode; }
107+
void setMode(BluetoothMode mode);
108+
109+
const QBluetoothDeviceInfo& device() const { return _device; }
110+
QString deviceName() const { return _device.name(); }
111+
QString address() const { return _device.address().toString(); }
112+
const QStringList& nameList() const { return _nameList; }
113+
QVariantList devicesModel() const;
115114
bool scanning() const;
115+
qint16 connectedRssi() const { return _connectedRssi; }
116+
void setConnectedRssi(qint16 rssi);
117+
qint16 selectedRssi() const;
118+
119+
// BLE specific settings
120+
QString serviceUuid() const { return _serviceUuid.toString(); }
121+
void setServiceUuid(const QString &uuid);
122+
QString readUuid() const { return _readCharacteristicUuid.toString(); }
123+
void setReadUuid(const QString &uuid);
124+
QString writeUuid() const { return _writeCharacteristicUuid.toString(); }
125+
void setWriteUuid(const QString &uuid);
126+
127+
const QBluetoothUuid& readCharacteristicUuid() const { return _readCharacteristicUuid; }
128+
const QBluetoothUuid& writeCharacteristicUuid() const { return _writeCharacteristicUuid; }
129+
130+
static bool isBluetoothAvailable();
131+
132+
// Known BLE UART-like service UUIDs
133+
static inline const QBluetoothUuid NORDIC_UART_SERVICE{QStringLiteral("6e400001-b5a3-f393-e0a9-e50e24dcca9e")};
134+
static inline const QBluetoothUuid NORDIC_UART_RX_CHAR{QStringLiteral("6e400003-b5a3-f393-e0a9-e50e24dcca9e")};
135+
static inline const QBluetoothUuid NORDIC_UART_TX_CHAR{QStringLiteral("6e400002-b5a3-f393-e0a9-e50e24dcca9e")};
136+
static inline const QBluetoothUuid TI_SENSORTAG_SERVICE{QStringLiteral("0000ffe0-0000-1000-8000-00805f9b34fb")};
137+
static inline const QBluetoothUuid TI_SENSORTAG_CHAR{QStringLiteral("0000ffe1-0000-1000-8000-00805f9b34fb")};
116138

117139
signals:
140+
void modeChanged();
118141
void deviceChanged();
119142
void nameListChanged();
143+
void devicesModelChanged();
120144
void scanningChanged();
145+
void serviceUuidChanged();
146+
void readUuidChanged();
147+
void writeUuidChanged();
148+
void connectedRssiChanged();
149+
void selectedRssiChanged();
121150
void errorOccurred(const QString &errorString);
151+
void adapterStateChanged();
122152

123153
private slots:
124154
void _deviceDiscovered(const QBluetoothDeviceInfo &info);
125-
void _onSocketErrorOccurred(QBluetoothDeviceDiscoveryAgent::Error error);
155+
void _onDiscoveryErrorOccurred(QBluetoothDeviceDiscoveryAgent::Error error);
156+
void _onDiscoveryFinished();
157+
void _deviceUpdated(const QBluetoothDeviceInfo &info, QBluetoothDeviceInfo::Fields updatedFields);
158+
159+
// QBluetoothLocalDevice slots
160+
void _onHostModeStateChanged(QBluetoothLocalDevice::HostMode mode);
161+
void _onDeviceConnected(const QBluetoothAddress &address);
162+
void _onDeviceDisconnected(const QBluetoothAddress &address);
163+
void _onPairingFinished(const QBluetoothAddress &address, QBluetoothLocalDevice::Pairing pairing);
164+
void _onLocalDeviceErrorOccurred(QBluetoothLocalDevice::Error error);
126165

127166
private:
128167
void _initDeviceDiscoveryAgent();
129-
130-
QList<BluetoothData> _deviceList;
131-
BluetoothData _device{};
168+
void _updateDeviceList();
169+
void _applySelectedDevice(const QBluetoothDeviceInfo &info);
170+
bool _createLocalDevice(const QBluetoothAddress &address);
171+
void _connectLocalDeviceSignals();
172+
173+
BluetoothMode _mode = BluetoothMode::ModeClassic;
174+
QBluetoothDeviceInfo _device;
175+
QList<QBluetoothDeviceInfo> _deviceList;
132176
QStringList _nameList;
133-
QBluetoothDeviceDiscoveryAgent *_deviceDiscoveryAgent = nullptr;
177+
QPointer<QBluetoothDeviceDiscoveryAgent> _deviceDiscoveryAgent;
178+
QPointer<QBluetoothLocalDevice> _localDevice;
179+
qint16 _connectedRssi = 0;
180+
181+
// BLE specific - Default to Nordic UART Service
182+
QBluetoothUuid _serviceUuid{NORDIC_UART_SERVICE};
183+
QBluetoothUuid _readCharacteristicUuid{NORDIC_UART_RX_CHAR};
184+
QBluetoothUuid _writeCharacteristicUuid{NORDIC_UART_TX_CHAR};
134185
};
135186

136187
/*===========================================================================*/
@@ -141,7 +192,7 @@ class BluetoothWorker : public QObject
141192

142193
public:
143194
explicit BluetoothWorker(const BluetoothConfiguration *config, QObject *parent = nullptr);
144-
~BluetoothWorker();
195+
~BluetoothWorker() override;
145196

146197
bool isConnected() const;
147198

@@ -151,31 +202,85 @@ class BluetoothWorker : public QObject
151202
void errorOccurred(const QString &errorString);
152203
void dataReceived(const QByteArray &data);
153204
void dataSent(const QByteArray &data);
205+
void rssiUpdated(qint16 rssi);
154206

155207
public slots:
156-
void setupSocket();
208+
void setupConnection();
157209
void connectLink();
158210
void disconnectLink();
159211
void writeData(const QByteArray &data);
160212

161213
private slots:
214+
// Classic Bluetooth slots
162215
void _onSocketConnected();
163216
void _onSocketDisconnected();
164217
void _onSocketReadyRead();
165218
void _onSocketBytesWritten(qint64 bytes);
166219
void _onSocketErrorOccurred(QBluetoothSocket::SocketError socketError);
167-
#ifdef Q_OS_IOS
168-
void _onServiceErrorOccurred(QBluetoothServiceDiscoveryAgent::Error error);
169-
void _serviceDiscovered(const QBluetoothServiceInfo &info);
170-
void _discoveryFinished();
171-
#endif
220+
void _onClassicServiceDiscovered(const QBluetoothServiceInfo &serviceInfo);
221+
void _onClassicServiceDiscoveryFinished();
222+
void _onClassicServiceDiscoveryCanceled();
223+
void _onClassicServiceDiscoveryError(QBluetoothServiceDiscoveryAgent::Error error);
224+
225+
// BLE slots
226+
void _onControllerConnected();
227+
void _onControllerDisconnected();
228+
void _onControllerErrorOccurred(QLowEnergyController::Error error);
229+
void _onServiceDiscovered(const QBluetoothUuid &uuid);
230+
void _onServiceDiscoveryFinished();
231+
void _onServiceStateChanged(QLowEnergyService::ServiceState state);
232+
void _onCharacteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &value);
233+
void _onCharacteristicRead(const QLowEnergyCharacteristic &characteristic, const QByteArray &value);
234+
void _onCharacteristicWritten(const QLowEnergyCharacteristic &characteristic, const QByteArray &value);
235+
void _onDescriptorRead(const QLowEnergyDescriptor &descriptor, const QByteArray &value);
236+
void _onDescriptorWritten(const QLowEnergyDescriptor &descriptor, const QByteArray &value);
237+
void _onServiceError(QLowEnergyService::ServiceError error);
238+
239+
// Common slots
240+
void _reconnectTimeout();
172241

173242
private:
243+
void _setupClassicSocket();
244+
void _startClassicServiceDiscovery();
245+
void _setupBleController();
246+
void _setupBleService();
247+
void _discoverServiceDetails();
248+
void _enableNotifications();
249+
void _writeBleData(const QByteArray &data);
250+
void _writeClassicData(const QByteArray &data);
251+
void _findCharacteristics();
252+
void _processNextBleWrite();
253+
void _clearBleWriteQueue();
254+
174255
const BluetoothConfiguration *_config = nullptr;
175-
QBluetoothSocket *_socket = nullptr;
176-
#ifdef Q_OS_IOS
177-
QBluetoothServiceDiscoveryAgent *_serviceDiscoveryAgent = nullptr;
178-
#endif
256+
257+
// Classic Bluetooth
258+
QPointer<QBluetoothSocket> _socket;
259+
260+
// BLE
261+
QPointer<QLowEnergyController> _controller;
262+
QPointer<QLowEnergyService> _service;
263+
QLowEnergyCharacteristic _readCharacteristic;
264+
QLowEnergyCharacteristic _writeCharacteristic;
265+
int _mtu = 23; // default ATT MTU
266+
qint16 _rssi = 0;
267+
QPointer<QTimer> _rssiTimer;
268+
QQueue<QByteArray> _bleWriteQueue;
269+
QByteArray _currentBleWrite;
270+
bool _bleWriteInProgress = false;
271+
272+
// Common
273+
QPointer<QTimer> _reconnectTimer;
274+
std::atomic<bool> _intentionalDisconnect{false};
275+
std::atomic<bool> _connected{false};
276+
int _reconnectAttempts = 0;
277+
static constexpr int MAX_RECONNECT_ATTEMPTS = 10;
278+
QPointer<QBluetoothServiceDiscoveryAgent> _classicDiscovery;
279+
QBluetoothServiceInfo _classicDiscoveredService;
280+
281+
// BLE packet size constraints
282+
static constexpr int BLE_MIN_PACKET_SIZE = 20;
283+
static constexpr int BLE_MAX_PACKET_SIZE = 512;
179284
};
180285

181286
/*===========================================================================*/
@@ -186,7 +291,7 @@ class BluetoothLink : public LinkInterface
186291

187292
public:
188293
explicit BluetoothLink(SharedLinkConfigurationPtr &config, QObject *parent = nullptr);
189-
virtual ~BluetoothLink();
294+
~BluetoothLink() override;
190295

191296
bool isConnected() const override;
192297
void disconnect() override;
@@ -198,13 +303,14 @@ private slots:
198303
void _onErrorOccurred(const QString &errorString);
199304
void _onDataReceived(const QByteArray &data);
200305
void _onDataSent(const QByteArray &data);
306+
void _onRssiUpdated(qint16 rssi);
201307

202308
private:
203309
bool _connect() override;
204310
void _checkPermission();
205311
void _handlePermissionStatus(Qt::PermissionStatus permissionStatus);
206312

207-
const BluetoothConfiguration *_bluetoothConfig = nullptr;
208-
BluetoothWorker *_worker = nullptr;
209-
QThread *_workerThread = nullptr;
313+
BluetoothConfiguration *_bluetoothConfig = nullptr;
314+
QPointer<BluetoothWorker> _worker;
315+
QPointer<QThread> _workerThread;
210316
};

src/Comms/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ if(QGC_ENABLE_BLUETOOTH)
7474
BluetoothLink.cc
7575
BluetoothLink.h
7676
)
77+
7778
target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE QGC_ENABLE_BLUETOOTH)
7879
endif()
7980

0 commit comments

Comments
 (0)