QBluetoothLeUart/qbluetoothleuartclient.h

368 lines
12 KiB
C++

#ifndef BLUETOOTHLEUART_H
#define BLUETOOTHLEUART_H
#include <QBluetoothDeviceDiscoveryAgent>
#include <QBluetoothDeviceInfo>
#include <QLowEnergyController>
#include <QLowEnergyService>
#ifdef Q_OS_ANDROID
#include <QtAndroidExtras>
#endif
#ifdef QBluetoothLeUart_QML
#include <QQmlApplicationEngine>
#endif
#include <qbluetoothleuartdevice.h>
#include <qbluetoothleuartdevicemodel.h>
/*!
* \mainpage Qt BluetoothLE UART library
*
* \section intro_sec Introduction
*
* This library can be used to talk to BLE devices via UART in Qt.
* It was designed to make talking to devices like the ESP32 from Qt easier.
*
* \section section Installation
* \code{.sh}
* cd yourRepo
* git submodule add https://itsblue.dev/itsblue-development/QBluetoothLeUart.git
* git submodule update --init --recursive
* \endcode
*
* And in your MyProject.pro include the .pri file:
* \code{.pro}
* # Optional: enable QML stuff
* CONFIG += QBluetoothLeUart_QML
* # Include library
* include($$PWD/QBluetoothLeUart/QBluetoothLeUart.pri)
* \endcode
*
* To enable the QML module you need to call
* \code{.cpp}
* QBluetoothLeUart::init();
* \endcode
* somewhere before app.exec(); in your main.cpp.
*
* \section Getting_started
* This library currently supports: BluetoothLE UART client
* \subsection BluetoothLE UART client
* To get started with the BLE client, see the docs of QBluetoothLeUartClient.
*
*/
/*!
* \brief The QBluetoothLeUartClient class can be used to talk to BluetoothLE devices via UART effordlessly.
* It can be used via C++ and QML.
*
* C++ example:
* \code{.cpp}
* #include <qbluetoothleuartclient.h>
* class MyBluetoothLeClass : QObject {
* public:
* MyBluetoothLeClass(QObject* parent = nullptr) : QObject(parent) {
* this->ble = new QBluetoothLeUartClient();
*
* connect(this->ble, &QBluetoothLeUartClient::foundNewDevice, this, &MyBluetoothLeClass::handleFoundNewDevice);
* connect(this->ble, &QBluetoothLeUartClient::connectedToDevice, this, &MyBluetoothLeClass::handleBluetoothDeviceConected);
* connect(this->ble, &QBluetoothLeUartClient::dataReceived, this, &MyBluetoothLeClass::handleDataReceived);
*
* this->ble->startScanningForDevices();
* }
*
* private:
* QBluetoothLeUartClient *ble;
*
* private slots:
* void handleFoundNewDevice(QBluetoothLeUartDevice* device) {
* qDebug() << "Found a device: name: " << device->getName() << " address: " << device->getAddress();
*
* if(device->getName() == "My device name"){
* this->ble->stopScanningForDevices();
* this->ble->connectToDevice(device);
* }
* }
*
* void handleBluetoothDeviceConected() {
* this->ble->sendData("This is my test message");
* }
*
* void handleDataReceived(const QString &s) {
* qDebug() << "Data received: " << s;
* }
* };
* \endcode
*
* QML example:
* \code{.qml}
* import de.itsblue.bluetoothleuart 1.0
*
* QBluetoothLeUartClient {
* id: ble
* Component.onCompleted: {
* ble.startScanningForDevices()
* }
*
* onFoundNewDevice: {
* console.log("Found a device: name: " + device.name + " address: " + device.address)
* if(device.name === "My device name") {
* ble.stopScanningForDevices()
* ble.connectToDevice(device)
* }
* }
*
* onConnectedToDevice: {
* ble.sendData("This is my test message")
* }
*
* onDataReceived: {
* console.log("Data received: " + data)
* }
* }
* \endcode
*
* To get all available devices in a model, use the QBluetoothLeUartDeviceModel.
*/
class QBluetoothLeUartClient : public QObject
{
Q_OBJECT
Q_PROPERTY(QVariantList availableDevices READ getAvailableDevicesDetailList NOTIFY avaliableDevicesChanged)
Q_PROPERTY(QBluetoothLeUartDeviceModel* availableDevicesModel READ getAvailableDevicesModel NOTIFY avaliableDevicesModelChanged)
Q_PROPERTY(QBluetoothLeUartDevice* currentDevice READ getCurrentDevice NOTIFY currentDeviceChanged)
Q_PROPERTY(BluetoothLeUartClientState state READ getState NOTIFY stateChanged)
public:
/*!
* \brief The BluetoothLeUartState enum contains all state of the QBluetoothLeUart class.
*/
enum BluetoothLeUartClientState {
Idle = 0, /*!< Waiting for instrucions */
AdapterTurnedOff, /*!< The bluetooth adapter is turned off */
LocationPermissionDenied, /*!< The location permssion was denied and we are therfor unable to scan! */
Scanning, /*!< Scanning for devices */
ScanFinished, /*!< Scanning has finished, we are ready to connect */
Connecting, /*!< Trying to connect */
ScanningForService, /*!< Connection was successfull, now scanning for services */
ServiceFound, /*!< Services were found */
Connected /*!< Connected. We are now ready to send and receive */
};
Q_ENUM(BluetoothLeUartClientState)
enum BluetoothScanError {
UnknownError,
AdapterTurnedOffError,
InputOutputError,
LocationPermissionDeniedError
};
Q_ENUM(BluetoothScanError);
QBluetoothLeUartClient(QObject *parent = nullptr);
~QBluetoothLeUartClient();
/*!
* \brief Function to register QMl types
*/
static void init();
/*!
* \brief Function to set the UUIDs of the bluetooth service
* \param uartServiceUUID Service UUID
* \param txUUID UUID of the characteristic used to send data
* \param rxUUID UUID of the characteristic used to receive data
*/
void setUUIDs(const char uartServiceUUID[36], const char txUUID[36], const char rxUUID[36]);
private:
// The UUIDs
QString uartServiceUUID;
QString txUUID;
QString rxUUID;
// current state
QBluetoothLeUartClient::BluetoothLeUartClientState state;
// QBluetooth controllers
QBluetoothDeviceDiscoveryAgent *bluetoothDeviceDiscoveryAgent;
QLowEnergyController *bluetoothController;
QLowEnergyService *bluetoothService;
// Bluetooth device
QBluetoothLeUartDevice *currentBluetoothDevice;
QList<QBluetoothLeUartDevice*> availableDevices;
QLowEnergyDescriptor bluetoothTransmissionDescriptor;
bool foundValidUARTService;
// for QML
QBluetoothLeUartDeviceModel* availableDevicesModel;
public slots:
Q_INVOKABLE bool requestLocationPermission();
Q_INVOKABLE bool isLocationPermissionGranted();
/*!
* \brief Fuction to start scanning for devices
*
* This function will start the device scanning process and might emit
* the following signals during scanning:
* - foundNewDevice() when a new device is found
* - avaliableDevicesChanged() when a new device is found
* - scanFinished() when the scan has finished
* - scanningErrorOccured() when an error occured
*
* \see foundNewDevice()
* \see avaliableDevicesChanged()
* \see scanFinished()
* \see scanningErrorOccured()
*
* \return true if the scan started, false if the current state was not Idle
*/
Q_INVOKABLE bool startScanningForDevices();
/*!
* \brief Function to stop scanning for devices
*
* \return true if the scan was stopped, false if the current state was not Scanning
*/
Q_INVOKABLE bool stopScanningForDevices();
/*!
* \brief Function to get all devices that were found during the last scan
*
* A QBluetoothLeUartDevice object can be used to connect to the specific device
*
* \see connectToDevice()
*
* \return List of all devices found during last scan
*/
Q_INVOKABLE QList<QBluetoothLeUartDevice*> getAvailableDevices();
/*!
* \brief Function to get a variant list of all devices that were found during the last scan
*
* This will return a QVariantList that contains QVariantMaps.
* The maps contain the following keys:
* - "id" (int): the internal id of the device (used to connect to it)
* - "name" (QString): the name of the device
* - "address" (QString): the bluetooth address of the device
*
* \see connectToDevice()
*
* \return Variant list of all devices found during last scan
*/
Q_INVOKABLE QVariantList getAvailableDevicesDetailList();
/*!
* \brief Function to get a QBluetoothLeUartDeviceModel with all available devices
*
* \see QBluetoothLeUartDeviceModel
*
* \return A QBluetoothLeUartDeviceModel with all available devices
*/
Q_INVOKABLE QBluetoothLeUartDeviceModel* getAvailableDevicesModel();
/*!
* \brief Function to get the currently connected device
* \return The currently connected device or nullptr if no device is connected
*/
Q_INVOKABLE QBluetoothLeUartDevice* getCurrentDevice();
/*!
* \brief Function connect to a device using its internal id
*
* The id can be found using getAvailableDevicesDetailList()
* The connectedToDevice() signal will be emited as soon as the connection was successfull.
* As soon as that signal was emited, the sendData() slot can be used to send data and
* the dataReceived() signal will be emited whenever data is received.
*
* \param deviceId the internal id of the device
*
* \see getAvailableDevicesDetailList()
* \see connectedToDevice()
* \see dataReceived()
*
* \return false if the device was not found in the internal list of discovered devices, true otherwise
*/
Q_INVOKABLE bool connectToDevice(int deviceId);
/*!
* \brief Function connect to a device using a QBluetoothLeUartDevice object
*
* The QBluetoothLeUartDevice can be found using getAvailableDevices()
* The connectedToDevice() signal will be emited as soon as the connection was successfull.
* As soon as that signal was emited, the sendData() slot can be used to send data and
* the dataReceived() signal will be emited whenever data is received.
*
* \param device The device to connect to
*
* \see getAvailableDevices()
* \see connectedToDevice()
* \see dataReceived()
*
* \return false if the device was not found in the internal list of discovered devices, true otherwise
*/
Q_INVOKABLE bool connectToDevice(QBluetoothLeUartDevice *device);
/*!
* \brief Function to disconnect from the current device
*
* \return false if no device was connected, true otherwise
*/
Q_INVOKABLE bool disconnectFromDevice();
/*!
* \brief Function to send data to the connected device
* \param data The data to send
* \return false if there was not device connected, true otherwise
*/
Q_INVOKABLE bool sendData(QString data, bool asynchronous = true);
/*!
* \brief Function to get the current state of QBluetoothLeUart
* \see BluetoothLeUartState
* \return The current state
*/
Q_INVOKABLE QBluetoothLeUartClient::BluetoothLeUartClientState getState() const;
private slots:
void setState(QBluetoothLeUartClient::BluetoothLeUartClientState newState);
// Slots for QBluetothDeviceDiscoveryAgent
void handleDeviceDiscovered(const QBluetoothDeviceInfo&);
void handleScanFinished();
void handleDeviceScanError(QBluetoothDeviceDiscoveryAgent::Error);
// Slots for QLowEnergyController
void handleServiceDiscovered(const QBluetoothUuid & uuid);
void handleServiceScanDone();
void handleControllerError(QLowEnergyController::Error);
void handleDeviceConnected();
void handleDeviceDisconnected();
// Slots for QLowEnergyService
void handleServiceStateChange(QLowEnergyService::ServiceState s);
void handleServiceCharacteristicChange(const QLowEnergyCharacteristic &c, const QByteArray &value);
void handleServiceDescriptorWritten(const QLowEnergyDescriptor &d, const QByteArray &value);
signals:
void stateChanged(QBluetoothLeUartClient::BluetoothLeUartClientState newState);
void foundNewDevice(QBluetoothLeUartDevice* device);
void avaliableDevicesChanged(QList<QBluetoothLeUartDevice*> avaliableDevices);
void avaliableDevicesModelChanged();
void currentDeviceChanged();
void scanFinished(QList<QBluetoothLeUartDevice*> availableDevices);
void scanningErrorOccured(QBluetoothLeUartClient::BluetoothScanError error);
void connectedToDevice();
void dataReceived(QString data);
};
#endif // BLUETOOTHLEUART_H