#ifndef BLUETOOTHLEUART_H #define BLUETOOTHLEUART_H #include #include #include #include #ifdef Q_OS_ANDROID #include #endif #ifdef QBluetoothLeUart_QML #include #endif #include #include /*! * \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 * 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 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 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 avaliableDevices); void avaliableDevicesModelChanged(); void currentDeviceChanged(); void scanFinished(QList availableDevices); void scanningErrorOccured(QBluetoothLeUartClient::BluetoothScanError error); void connectedToDevice(); void dataReceived(QString data); }; #endif // BLUETOOTHLEUART_H