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