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-
8242class 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
9164public:
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
117139signals:
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
123153private 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
127166private:
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
142193public:
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
155207public slots:
156- void setupSocket ();
208+ void setupConnection ();
157209 void connectLink ();
158210 void disconnectLink ();
159211 void writeData (const QByteArray &data);
160212
161213private 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
173242private:
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
187292public:
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
202308private:
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};
0 commit comments