348 lines
12 KiB
C++
348 lines
12 KiB
C++
#ifndef BLUETOOTHLEUART_H
|
|
#define BLUETOOTHLEUART_H
|
|
|
|
#include <QBluetoothDeviceDiscoveryAgent>
|
|
#include <QBluetoothDeviceInfo>
|
|
#include <QLowEnergyController>
|
|
#include <QLowEnergyService>
|
|
|
|
#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.
|
|
*/
|
|
|
|
/*!
|
|
* \brief The QBluetoothLeUart 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 <qbluetoothleuart.h>
|
|
* class MyBluetoothLeClass : QObject {
|
|
* public:
|
|
* MyBluetoothLeClass(QObject* parent = nullptr) : QObject(parent) {
|
|
* this->ble = new QBluetoothLeUart();
|
|
*
|
|
* connect(this->ble, &QBluetoothLeUart::foundNewDevice, this, &MyBluetoothLeClass::handleFoundNewDevice);
|
|
* connect(this->ble, &QBluetoothLeUart::connectedToDevice, this, &MyBluetoothLeClass::handleBluetoothDeviceConected);
|
|
* connect(this->ble, &QBluetoothLeUart::dataReceived, this, &MyBluetoothLeClass::handleDataReceived);
|
|
*
|
|
* this->ble->startScanningForDevices();
|
|
* }
|
|
*
|
|
* private:
|
|
* QBluetoothLeUart *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
|
|
*
|
|
* QBluetoothLeUart {
|
|
* 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
|
|
*/
|
|
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 */
|
|
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
|
|
};
|
|
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:
|
|
|
|
/*!
|
|
* \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
|