QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
BluetoothBleWorker.cc
Go to the documentation of this file.
2
3#include <QtBluetooth/QLowEnergyConnectionParameters>
4
5static QString characteristicPropertiesToString(QLowEnergyCharacteristic::PropertyTypes props)
6{
7 QStringList propList;
8 if (props & QLowEnergyCharacteristic::Read) propList << "Read";
9 if (props & QLowEnergyCharacteristic::Write) propList << "Write";
10 if (props & QLowEnergyCharacteristic::WriteNoResponse) propList << "WriteNoResponse";
11 if (props & QLowEnergyCharacteristic::Notify) propList << "Notify";
12 if (props & QLowEnergyCharacteristic::Indicate) propList << "Indicate";
13 if (props & QLowEnergyCharacteristic::WriteSigned) propList << "WriteSigned";
14 if (props & QLowEnergyCharacteristic::ExtendedProperty) propList << "Extended";
15 return propList.isEmpty() ? "None" : propList.join("|");
16}
17
19 : BluetoothWorker(config, parent)
20{
21 _rssiTimer = new QTimer(this);
22 _rssiTimer->setInterval(RSSI_POLL_INTERVAL_MS);
23 _rssiTimer->setSingleShot(false);
24 (void) connect(_rssiTimer.data(), &QTimer::timeout, this, [this]() {
25 if (_controller && _controller->state() != QLowEnergyController::UnconnectedState
26 && _controller->state() != QLowEnergyController::ClosingState) {
27 _controller->readRssi();
28 }
29 });
30}
31
33{
36
37 if (_rssiTimer) {
38 _rssiTimer->stop();
39 }
40
41 if (_service) {
42 _service->disconnect();
43 _service->deleteLater();
44 }
45 if (_controller) {
46 _controller->disconnect();
47 if (_controller->state() != QLowEnergyController::UnconnectedState) {
48 _controller->disconnectFromDevice();
49 }
50 _controller->deleteLater();
51 }
52}
53
55{
56 _setupBleController();
57}
58
60{
61 qCDebug(BluetoothLinkLog) << "Attempting to connect to" << _device.name() << "Mode: BLE";
62
63 if (!_controller) {
64 _setupBleController();
65 }
66
67 if (!_controller) {
68 emit errorOccurred(tr("BLE controller not available"));
69 return;
70 }
71
72 // Prefer automatic address-type selection from discovery data.
73 // If the backend cannot determine it (for example without CAP_NET_ADMIN),
74 // _onControllerErrorOccurred can retry once with RandomAddress.
75 _controller->connectToDevice();
76}
77
79{
80 _clearBleWriteQueue();
81
82 if (_service) {
83 _service->disconnect();
84 _service->deleteLater();
85 _service = nullptr;
86 }
87
88 _readCharacteristic = QLowEnergyCharacteristic();
89 _writeCharacteristic = QLowEnergyCharacteristic();
90
91 if (_controller) {
92 _controller->disconnectFromDevice();
93 }
94}
95
96void BluetoothBleWorker::onWriteData(const QByteArray &data)
97{
98 _writeBleData(data);
99}
100
102{
103 if (_controller) {
104 _controller->disconnectFromDevice();
105 }
106}
107
109{
110 qCDebug(BluetoothLinkLog) << "Recreating BLE controller after" << _consecutiveFailures << "consecutive failures";
111 _recreateBleController();
112}
113
114void BluetoothBleWorker::_setupBleController()
115{
116 if (_controller) {
117 qCWarning(BluetoothLinkLog) << "BLE controller already exists, skipping setup";
118 return;
119 }
120
121 _controller = QLowEnergyController::createCentral(_device, this);
122
123 if (!_controller) {
124 emit errorOccurred(tr("Failed to create BLE controller"));
125 return;
126 }
127
128 if (_forceRandomAddressType) {
129 qCDebug(BluetoothLinkLog) << "Using BLE RandomAddress fallback for" << _device.name();
130 _controller->setRemoteAddressType(QLowEnergyController::RandomAddress);
131 }
132
133 (void) connect(_controller.data(), &QLowEnergyController::connected, this, &BluetoothBleWorker::_onControllerConnected);
134 (void) connect(_controller.data(), &QLowEnergyController::disconnected, this, &BluetoothBleWorker::_onControllerDisconnected);
135 (void) connect(_controller.data(), &QLowEnergyController::errorOccurred, this, &BluetoothBleWorker::_onControllerErrorOccurred);
136 (void) connect(_controller.data(), &QLowEnergyController::serviceDiscovered, this, &BluetoothBleWorker::_onServiceDiscovered);
137 (void) connect(_controller.data(), &QLowEnergyController::discoveryFinished, this, &BluetoothBleWorker::_onServiceDiscoveryFinished);
138
139 (void) connect(_controller.data(), &QLowEnergyController::mtuChanged, this,
140 [this](int mtu) {
141 _mtu = mtu;
142 qCDebug(BluetoothLinkLog) << "MTU changed to:" << mtu;
143 });
144 (void) connect(_controller.data(), &QLowEnergyController::rssiRead, this,
145 [this](qint16 rssi) {
146 _rssi = rssi;
147 emit rssiUpdated(rssi);
148 });
149
150 if (BluetoothLinkLog().isDebugEnabled()) {
151 (void) connect(_controller.data(), &QLowEnergyController::stateChanged, this,
152 [](QLowEnergyController::ControllerState state) {
153 qCDebug(BluetoothLinkLog) << "BLE Controller State Changed:" << state;
154 });
155
156 (void) connect(_controller.data(), &QLowEnergyController::connectionUpdated, this,
157 [](const QLowEnergyConnectionParameters &params) {
158 qCDebug(BluetoothLinkLog) << "BLE connection updated: min(ms)" << params.minimumInterval()
159 << "max(ms)" << params.maximumInterval()
160 << "latency" << params.latency()
161 << "supervision(ms)" << params.supervisionTimeout();
162 });
163 }
164}
165
166void BluetoothBleWorker::_recreateBleController()
167{
168 qCDebug(BluetoothLinkLog) << "Recreating BLE controller";
169
170 if (_service) {
171 _service->disconnect();
172 _service->deleteLater();
173 _service = nullptr;
174 }
175
176 if (_controller) {
177 _controller->disconnect();
178 if (_controller->state() != QLowEnergyController::UnconnectedState) {
179 _controller->disconnectFromDevice();
180 }
181 _controller->deleteLater();
182 _controller = nullptr;
183 }
184
185 _readCharacteristic = QLowEnergyCharacteristic();
186 _writeCharacteristic = QLowEnergyCharacteristic();
187 _clearBleWriteQueue();
188
190 _setupBleController();
191}
192
193void BluetoothBleWorker::_writeBleData(const QByteArray &data)
194{
195 if (!_writeCharacteristic.isValid()) {
196 emit errorOccurred(tr("Write characteristic is not valid"));
197 return;
198 }
199
200 if (!_service) {
201 emit errorOccurred(tr("BLE service not available"));
202 return;
203 }
204
205 // ATT MTU payload is (MTU - 3)
206 const int effectiveMtu = (_mtu > 3) ? (_mtu - 3) : BLE_MIN_PACKET_SIZE;
207 const int packetSize = qBound(BLE_MIN_PACKET_SIZE, effectiveMtu, BLE_MAX_PACKET_SIZE);
208
209 const bool useWriteWithoutResponse = (_writeCharacteristic.properties() & QLowEnergyCharacteristic::WriteNoResponse);
210 const int numChunks = (data.size() + packetSize - 1) / packetSize;
211
212 qCDebug(BluetoothLinkLog) << _device.name() << "BLE Write:" << data.size() << "bytes,"
213 << numChunks << "chunks (MTU:" << _mtu << ", packet:" << packetSize << ")"
214 << (useWriteWithoutResponse ? "WriteNoResponse" : "WriteWithResponse");
215
216 // Queue all chunks and pace writes through _processNextBleWrite
217 const int chunksNeeded = (data.size() + packetSize - 1) / packetSize;
218 if (_bleWriteQueue.size() + chunksNeeded > MAX_BLE_QUEUE_SIZE) {
219 qCWarning(BluetoothLinkLog) << "BLE write queue full:" << _bleWriteQueue.size() << "chunks queued";
220 emit errorOccurred(tr("Write queue full, dropping data"));
221 return;
222 }
223
224 _currentBleWrite = data;
225 for (int i = 0; i < data.size(); i += packetSize) {
226 _bleWriteQueue.enqueue(data.mid(i, packetSize));
227 }
228
229 if (!_bleWriteInProgress) {
230 _processNextBleWrite();
231 }
232}
233
234void BluetoothBleWorker::_processNextBleWrite()
235{
236 if (_bleWriteQueue.isEmpty()) {
237 _bleWriteInProgress = false;
238 if (!_currentBleWrite.isEmpty()) {
239 emit dataSent(_currentBleWrite);
240 _currentBleWrite.clear();
241 }
242 return;
243 }
244
245 if (!_service || !_writeCharacteristic.isValid()) {
246 _clearBleWriteQueue();
247 return;
248 }
249
250 _bleWriteInProgress = true;
251 const QByteArray chunk = _bleWriteQueue.dequeue();
252 const bool useWriteWithoutResponse = (_writeCharacteristic.properties() & QLowEnergyCharacteristic::WriteNoResponse);
253
254 if (useWriteWithoutResponse) {
255 _service->writeCharacteristic(_writeCharacteristic, chunk, QLowEnergyService::WriteWithoutResponse);
256 // No confirmation callback for WriteWithoutResponse — pace via timer
257 QTimer::singleShot(1, this, &BluetoothBleWorker::_processNextBleWrite);
258 } else {
259 _service->writeCharacteristic(_writeCharacteristic, chunk);
260 // _onCharacteristicWritten will call _processNextBleWrite
261 }
262}
263
264void BluetoothBleWorker::_clearBleWriteQueue()
265{
266 _bleWriteQueue.clear();
267 _currentBleWrite.clear();
268 _bleWriteInProgress = false;
269}
270
271void BluetoothBleWorker::_onControllerConnected()
272{
273 qCDebug(BluetoothLinkLog) << "BLE Controller connected to device:" << _device.name();
274
275 if (_controller) {
277 _serviceDiscoveryTimer->start();
278 }
279 _controller->discoverServices();
280 if (_rssiTimer) {
281 _rssiTimer->start();
282 }
283 }
284}
285
286void BluetoothBleWorker::_onControllerDisconnected()
287{
288 qCDebug(BluetoothLinkLog) << "BLE Controller disconnected from device:" << _device.name();
289
290 if (_rssiTimer) {
291 _rssiTimer->stop();
292 _rssi = 0;
293 emit rssiUpdated(_rssi);
294 }
295
296 _clearBleWriteQueue();
297
298 if (_service) {
299 _service->deleteLater();
300 _service = nullptr;
301 }
302
303 _readCharacteristic = QLowEnergyCharacteristic();
304 _writeCharacteristic = QLowEnergyCharacteristic();
305 _connected = false;
306
307 emit disconnected();
308
309 if (!_intentionalDisconnect.load() && _controller && _reconnectTimer) {
310 qCDebug(BluetoothLinkLog) << "Starting reconnect timer";
311 _reconnectTimer->start();
312 }
313}
314
315void BluetoothBleWorker::_onControllerErrorOccurred(QLowEnergyController::Error error)
316{
317 // Stop service discovery timer to prevent timeout firing after error
320 }
321
322 // On Linux BlueZ, without CAP_NET_ADMIN the backend may fail to determine
323 // remote address type and default to public, which can fail for private BLE devices.
324 // Retry once with RandomAddress before surfacing the error.
325 if (!_connected.load() && !_intentionalDisconnect.load() && !_forceRandomAddressType &&
326 ((error == QLowEnergyController::UnknownRemoteDeviceError) ||
327 (error == QLowEnergyController::ConnectionError) ||
328 (error == QLowEnergyController::NetworkError))) {
329 qCDebug(BluetoothLinkLog) << "BLE connect failed; retrying with RandomAddress fallback:" << error;
330 _forceRandomAddressType = true;
331 _recreateBleController();
332 if (_controller) {
333 qCDebug(BluetoothLinkLog) << "Issuing BLE reconnect using RandomAddress fallback";
334 _controller->connectToDevice();
335 return;
336 }
337 }
338
339 QString errorString;
340 if (_controller) {
341 errorString = _controller->errorString();
342 } else {
343 errorString = tr("Controller error: %1").arg(error);
344 }
345
346 qCWarning(BluetoothLinkLog) << "BLE Controller error:" << error << errorString;
348
349 if (!_connected.load()) {
350 qCDebug(BluetoothLinkLog) << "Connection attempt failed, emitting disconnected signal";
351 emit disconnected();
352 }
353}
354
355void BluetoothBleWorker::_onServiceDiscovered(const QBluetoothUuid &uuid)
356{
357 qCDebug(BluetoothLinkLog) << "Service discovered:" << uuid.toString();
358
359 // Only auto-select during discovery if user specified a target UUID
360 if (!_serviceUuid.isNull() && uuid == _serviceUuid) {
363 }
364 _setupBleService();
365 }
366}
367
368void BluetoothBleWorker::_onServiceDiscoveryFinished()
369{
370 qCDebug(BluetoothLinkLog) << "Service discovery finished";
371
374 }
375
376 if (!_service && _controller) {
377 const QList<QBluetoothUuid> services = _controller->services();
378 if (!services.isEmpty()) {
379 qCDebug(BluetoothLinkLog) << "Using first available service";
380 _setupBleService();
381 } else {
383 emit errorOccurred(tr("No services found on BLE device"));
384 }
385 }
386}
387
388void BluetoothBleWorker::_setupBleService()
389{
390 if (!_controller) {
391 emit errorOccurred(tr("Controller not available"));
392 return;
393 }
394
395 QBluetoothUuid serviceUuid = _serviceUuid;
396
397 if (serviceUuid.isNull()) {
398 const QList<QBluetoothUuid> services = _controller->services();
399 if (services.isEmpty()) {
400 emit errorOccurred(tr("No services available"));
401 return;
402 }
403
404 if (services.contains(BluetoothConfiguration::NORDIC_UART_SERVICE)) {
405 serviceUuid = BluetoothConfiguration::NORDIC_UART_SERVICE;
406 qCDebug(BluetoothLinkLog) << "Auto-selected Nordic UART service";
407 } else if (services.contains(BluetoothConfiguration::TI_SENSORTAG_SERVICE)) {
408 serviceUuid = BluetoothConfiguration::TI_SENSORTAG_SERVICE;
409 qCDebug(BluetoothLinkLog) << "Auto-selected TI SensorTag service";
410 } else {
411 serviceUuid = services.first();
412 qCDebug(BluetoothLinkLog) << "Auto-selected first available service:" << serviceUuid.toString();
413 }
414 }
415
416 _service = _controller->createServiceObject(serviceUuid, this);
417
418 if (!_service) {
419 emit errorOccurred(tr("Failed to create service object"));
420 return;
421 }
422
423 (void) connect(_service.data(), &QLowEnergyService::stateChanged, this, &BluetoothBleWorker::_onServiceStateChanged);
424 (void) connect(_service.data(), &QLowEnergyService::characteristicChanged, this, &BluetoothBleWorker::_onCharacteristicChanged);
425 (void) connect(_service.data(), &QLowEnergyService::characteristicRead, this, &BluetoothBleWorker::_onCharacteristicRead);
426 (void) connect(_service.data(), &QLowEnergyService::characteristicWritten, this, &BluetoothBleWorker::_onCharacteristicWritten);
427 (void) connect(_service.data(), &QLowEnergyService::descriptorRead, this, &BluetoothBleWorker::_onDescriptorRead);
428 (void) connect(_service.data(), &QLowEnergyService::descriptorWritten, this, &BluetoothBleWorker::_onDescriptorWritten);
429 (void) connect(_service.data(), &QLowEnergyService::errorOccurred, this, &BluetoothBleWorker::_onServiceError);
430
431 _discoverServiceDetails();
432}
433
434void BluetoothBleWorker::_discoverServiceDetails()
435{
436 if (!_service) {
437 return;
438 }
439
440 qCDebug(BluetoothLinkLog) << "Discovering service details";
441 _service->discoverDetails();
442}
443
444void BluetoothBleWorker::_findCharacteristics()
445{
446 if (!_service) {
447 return;
448 }
449
450 const QList<QLowEnergyCharacteristic> characteristics = _service->characteristics();
451 qCDebug(BluetoothLinkLog) << "Service has" << characteristics.size() << "characteristics";
452
453 for (const QLowEnergyCharacteristic &c : characteristics) {
454 qCDebug(BluetoothLinkLog) << " Characteristic:" << c.uuid().toString()
455 << "Properties:" << characteristicPropertiesToString(c.properties());
456 }
457
458 const QBluetoothUuid readUuid = _readCharacteristicUuid;
459 const QBluetoothUuid writeUuid = _writeCharacteristicUuid;
460
461 for (const QLowEnergyCharacteristic &c : characteristics) {
462 if (!readUuid.isNull() && (c.uuid() == readUuid)) {
463 _readCharacteristic = c;
464 qCDebug(BluetoothLinkLog) << "Read characteristic found by UUID:" << c.uuid().toString();
465 }
466 if (!writeUuid.isNull() && (c.uuid() == writeUuid)) {
467 _writeCharacteristic = c;
468 qCDebug(BluetoothLinkLog) << "Write characteristic found by UUID:" << c.uuid().toString();
469 }
470 }
471
472 if (!_readCharacteristic.isValid() || !_writeCharacteristic.isValid()) {
473 for (const QLowEnergyCharacteristic &c : characteristics) {
474 if (!_readCharacteristic.isValid() &&
475 (c.properties() & (QLowEnergyCharacteristic::Read | QLowEnergyCharacteristic::Notify))) {
476 _readCharacteristic = c;
477 qCDebug(BluetoothLinkLog) << "Read characteristic auto-detected:" << c.uuid().toString();
478 }
479
480 if (!_writeCharacteristic.isValid() &&
481 (c.properties() & (QLowEnergyCharacteristic::Write | QLowEnergyCharacteristic::WriteNoResponse))) {
482 _writeCharacteristic = c;
483 qCDebug(BluetoothLinkLog) << "Write characteristic auto-detected:" << c.uuid().toString();
484 }
485 }
486 }
487
488 // Summary of what we found
489 if (_readCharacteristic.isValid()) {
490 qCDebug(BluetoothLinkLog) << "Using read characteristic:" << _readCharacteristic.uuid().toString()
491 << "Properties:" << characteristicPropertiesToString(_readCharacteristic.properties());
492 } else {
493 qCDebug(BluetoothLinkLog) << "No read characteristic found";
494 }
495
496 if (_writeCharacteristic.isValid()) {
497 qCDebug(BluetoothLinkLog) << "Using write characteristic:" << _writeCharacteristic.uuid().toString()
498 << "Properties:" << characteristicPropertiesToString(_writeCharacteristic.properties());
499 } else {
500 qCDebug(BluetoothLinkLog) << "No write characteristic found";
501 }
502
503 // Warn if read and write resolved to the same characteristic
504 if (_readCharacteristic.isValid() && _writeCharacteristic.isValid() &&
505 _readCharacteristic.uuid() == _writeCharacteristic.uuid()) {
506 qCWarning(BluetoothLinkLog) << "Read and write characteristics resolved to the same UUID:"
507 << _readCharacteristic.uuid().toString()
508 << "- bidirectional communication may not work as intended";
509 }
510}
511
512void BluetoothBleWorker::_onServiceStateChanged(QLowEnergyService::ServiceState state)
513{
514 qCDebug(BluetoothLinkLog) << "Service state changed:" << state;
515
516 if (state == QLowEnergyService::RemoteServiceDiscovered) {
517 if (!_service) {
518 return;
519 }
520
521 _findCharacteristics();
522
523 if (!_writeCharacteristic.isValid()) {
524 emit errorOccurred(tr("Write characteristic not found"));
525 return;
526 }
527
528 if (_readCharacteristic.isValid()) {
529 _enableNotifications();
530 }
531
532 // Request optimized connection parameters (not supported on iOS, may not emit signal on Android)
533#ifndef Q_OS_IOS
534 if (_controller && _controller->state() == QLowEnergyController::ConnectedState) {
535 QLowEnergyConnectionParameters params;
536 params.setIntervalRange(6, 12);
537 params.setLatency(0);
538 params.setSupervisionTimeout(500);
539 _controller->requestConnectionUpdate(params);
540 }
541#endif
542
543 _connected = true;
546
547 qCDebug(BluetoothLinkLog) << "=== BLE Connection Established ===" << _device.name();
548 qCDebug(BluetoothLinkLog) << " Service:" << _service->serviceUuid().toString();
549 qCDebug(BluetoothLinkLog) << " MTU:" << _mtu;
550 qCDebug(BluetoothLinkLog) << " Read:" << (_readCharacteristic.isValid() ? _readCharacteristic.uuid().toString() : "N/A");
551 qCDebug(BluetoothLinkLog) << " Write:" << (_writeCharacteristic.isValid() ? _writeCharacteristic.uuid().toString() : "N/A");
552
553 emit connected();
554 }
555}
556
557void BluetoothBleWorker::_enableNotifications()
558{
559 if (!_service || !_readCharacteristic.isValid()) {
560 return;
561 }
562
563 const QLowEnergyDescriptor notificationDescriptor = _readCharacteristic.descriptor(
564 QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration);
565
566 if (!notificationDescriptor.isValid()) {
567 qCWarning(BluetoothLinkLog) << "CCCD descriptor missing on read characteristic — notifications will not work";
568 return;
569 }
570
571 QByteArray value;
572 if (_readCharacteristic.properties() & QLowEnergyCharacteristic::Notify) {
573 value = QLowEnergyCharacteristic::CCCDEnableNotification;
574 qCDebug(BluetoothLinkLog) << "Enabling notifications for read characteristic";
575 } else if (_readCharacteristic.properties() & QLowEnergyCharacteristic::Indicate) {
576 value = QLowEnergyCharacteristic::CCCDEnableIndication;
577 qCDebug(BluetoothLinkLog) << "Enabling indications for read characteristic";
578 }
579
580 if (!value.isEmpty()) {
581 _service->writeDescriptor(notificationDescriptor, value);
582 }
583}
584
585void BluetoothBleWorker::_onCharacteristicChanged(const QLowEnergyCharacteristic &characteristic,
586 const QByteArray &value)
587{
588 if ((characteristic.uuid() == _readCharacteristic.uuid()) && !value.isEmpty()) {
589 qCDebug(BluetoothLinkLog) << _device.name() << "BLE Received" << value.size() << "bytes";
590 emit dataReceived(value);
591 }
592}
593
594void BluetoothBleWorker::_onCharacteristicRead(const QLowEnergyCharacteristic &characteristic,
595 const QByteArray &value)
596{
597 qCDebug(BluetoothLinkVerboseLog) << "Characteristic read:" << characteristic.uuid().toString()
598 << "Value length:" << value.size();
599}
600
601void BluetoothBleWorker::_onCharacteristicWritten(const QLowEnergyCharacteristic &characteristic,
602 const QByteArray &value)
603{
604 Q_UNUSED(value)
605
606 if (characteristic.uuid() == _writeCharacteristic.uuid()) {
607 _processNextBleWrite();
608 }
609}
610
611void BluetoothBleWorker::_onDescriptorRead(const QLowEnergyDescriptor &descriptor,
612 const QByteArray &value)
613{
614 qCDebug(BluetoothLinkVerboseLog) << "Descriptor read:" << descriptor.uuid().toString()
615 << "Value:" << value.toHex();
616}
617
618void BluetoothBleWorker::_onDescriptorWritten(const QLowEnergyDescriptor &descriptor,
619 const QByteArray &value)
620{
621 qCDebug(BluetoothLinkLog) << "Descriptor written:" << descriptor.uuid().toString()
622 << "Value:" << value.toHex();
623}
624
625void BluetoothBleWorker::_onServiceError(QLowEnergyService::ServiceError error)
626{
627 const QString errorString = tr("Service error: %1").arg(error);
628 qCWarning(BluetoothLinkLog) << "BLE Service error:" << error;
630
631 _clearBleWriteQueue();
632
633 if (_service) {
634 _service->deleteLater();
635 _service = nullptr;
636 }
637
638 _readCharacteristic = QLowEnergyCharacteristic();
639 _writeCharacteristic = QLowEnergyCharacteristic();
640
641 const bool wasConnected = _connected.exchange(false);
642 if (!wasConnected) {
643 qCDebug(BluetoothLinkLog) << "Connection attempt failed, emitting disconnected signal";
644 }
645 emit disconnected();
646
648 _reconnectTimer->start();
649 }
650}
static QString characteristicPropertiesToString(QLowEnergyCharacteristic::PropertyTypes props)
QString errorString
Error error
void onSetupConnection() override
void onResetAfterConsecutiveFailures() override
void onConnectLink() override
BluetoothBleWorker(const BluetoothConfiguration *config, QObject *parent=nullptr)
void onDisconnectLink() override
void onServiceDiscoveryTimeout() override
void onWriteData(const QByteArray &data) override
void dataReceived(const QByteArray &data)
const QBluetoothUuid _readCharacteristicUuid
QPointer< QTimer > _serviceDiscoveryTimer
void dataSent(const QByteArray &data)
const QBluetoothUuid _serviceUuid
void errorOccurred(const QString &errorString)
const QBluetoothUuid _writeCharacteristicUuid
void rssiUpdated(qint16 rssi)
QPointer< QTimer > _reconnectTimer
std::atomic< int > _reconnectAttempts
std::atomic< bool > _connected
const QBluetoothDeviceInfo _device
std::atomic< bool > _intentionalDisconnect