1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
|
// Copyright (C) 2022 The Qt Company Ltd.
// Copyright (C) 2019 Ford Motor Company
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "bleiodevice.h"
#include <QtBluetooth/QLowEnergyController>
using namespace Qt::Literals::StringLiterals;
// The BT LE service consists of two characteristics which are used to establish
// a two-way ommunication channel. The Service which contains the characteristics:
const QBluetoothUuid BLEIoDevice::SERVICE_UUID =
QBluetoothUuid{"11223344-ac16-11eb-ae5c-93d3a763feed"_L1};
// Characteristic for data Client => Server
const QBluetoothUuid BLEIoDevice::CLIENT_TX_SERVER_RX_CHAR_UUID =
QBluetoothUuid{"889930f0-b9cc-4c27-8c1b-ebc2bcae5c95"_L1};
// Characteristic for data Server => Client
const QBluetoothUuid BLEIoDevice::CLIENT_RX_SERVER_TX_CHAR_UUID =
QBluetoothUuid{"667730f0-b9cc-4c27-8c1b-ebc2bcae5c95"_L1};
const QByteArray BLEIoDevice::CLIENT_READY_SIGNAL = "clientready"_ba;
BLEIoDevice::BLEIoDevice(QLowEnergyService *service, const QBluetoothUuid &rx,
const QBluetoothUuid &tx, QObject *parent)
: QIODevice(parent)
, m_service(service)
, m_txCharacteristic(service->characteristic(tx))
, m_rxCharacteristicUuid(rx)
{
Q_ASSERT(service);
open(QIODevice::ReadWrite);
QObject::connect(service, &QLowEnergyService::characteristicChanged, this, [this](
const QLowEnergyCharacteristic &info, const QByteArray &value) {
// If the characteristic where we are the receiving end has new value (ie. the remote end
// has written), store the data and indicate that we have it
if (info.uuid() != m_rxCharacteristicUuid)
return;
m_rxBuffer.append(value);
emit readyRead();
});
QObject::connect(service, &QLowEnergyService::errorOccurred, this,
[this](QLowEnergyService::ServiceError error) {
qWarning() << "A BT LE Service error occurred:" << error;
emit disconnected();
});
}
qint64 BLEIoDevice::bytesAvailable() const
{
return QIODevice::bytesAvailable() + m_rxBuffer.size();
}
bool BLEIoDevice::isSequential() const
{
return true;
}
void BLEIoDevice::close()
{
emit closing();
}
qint64 BLEIoDevice::readData(char *data, qint64 maxlen)
{
auto sz = (std::min)(qsizetype(maxlen), m_rxBuffer.size());
if (sz <= 0)
return sz;
memcpy(data, m_rxBuffer.constData(), size_t(sz));
m_rxBuffer.remove(0, sz);
return sz;
}
qint64 BLEIoDevice::writeData(const char *data, qint64 len)
{
// Write data by writing to the characteristic where we are the transmitting end.
// As there may be potentially a lot of data, we need to decide in how big chunks
// we send. Maximum is 512 - 3 = 509 bytes but it depends on the bluetooth stacks.
// We take a very conservative choice which should work with all bluetooth stacks, as this
// doesn't require support for long / prepared writes (ie. writes larger than MTU,
// whose default is 23) and also there is no risk of exceeding the notification mechanism
// payload limit (MTU - 3).
static const int MAX_PAYLOAD = 20;
if (m_service) {
do {
auto sz = (std::min)(qint64(MAX_PAYLOAD) , len);
m_service->writeCharacteristic(m_txCharacteristic, QByteArray{data, qsizetype(sz)});
len -= sz;
data += sz;
// Here we consider bytes now written to the underlying 'channel'. BT LE below
// does any necessary queueing. In principle we could also wait for
// characteristicWritten() signal but it would only be available on BT LE
// Client/Central side, whereas this class is used on both client/server side.
emit bytesWritten(sz);
} while (len > 0);
return len;
}
return -1;
}
|