#include "qbluetoothleuart.h" QBluetoothLeUart::QBluetoothLeUart(QObject *parent) : QObject(parent) { currentBluetoothDevice = nullptr; bluetoothController = nullptr; bluetoothService = nullptr; state = Idle; this->setUUIDs("6e400001-b5a3-f393-e0a9-e50e24dcca9e", "6e400002-b5a3-f393-e0a9-e50e24dcca9e", "6e400003-b5a3-f393-e0a9-e50e24dcca9e"); // init device discovery agent for scanning this->bluetoothDeviceDiscoveryAgent = new QBluetoothDeviceDiscoveryAgent(this); connect(this->bluetoothDeviceDiscoveryAgent, &QBluetoothDeviceDiscoveryAgent::deviceDiscovered, this, &QBluetoothLeUart::handleDeviceDiscovered); connect(bluetoothDeviceDiscoveryAgent, SIGNAL(error(QBluetoothDeviceDiscoveryAgent::Error)), this, SLOT(handleDeviceScanError(QBluetoothDeviceDiscoveryAgent::Error))); connect(this->bluetoothDeviceDiscoveryAgent, &QBluetoothDeviceDiscoveryAgent::finished, this, &QBluetoothLeUart::handleScanFinished); // device model for QML this->availableDevicesModel = new QBluetoothLeUartDeviceModel(this->availableDevices, this); } QBluetoothLeUart::~QBluetoothLeUart(){ } // ------------------------------ // - Slots for QBluetoothLeUart - // ------------------------------ bool QBluetoothLeUart::startScanningForDevices(){ if(this->state != Idle && this->state != ScanFinished) return false; this->availableDevicesModel->clear(); foreach(QBluetoothLeUartDevice* oldDevice, this->availableDevices) oldDevice->deleteLater(); this->availableDevices.clear(); this->setState(Scanning); this->bluetoothDeviceDiscoveryAgent->start(); return true; } bool QBluetoothLeUart::stopScanningForDevices() { if(this->state != Scanning) return false; this->bluetoothDeviceDiscoveryAgent->stop(); this->setState(ScanFinished); return true; } QList QBluetoothLeUart::getAvailableDevices() { return this->availableDevices; } QVariantList QBluetoothLeUart::getAvailableDevicesDetailList() { QVariantList result; for(int i=0; i < this->availableDevices.length(); i++) { if(this->availableDevices[i]->getName().isEmpty()) continue; QVariantMap device; device.insert("id", i); device.insert("name", this->availableDevices[i]->getName()); device.insert("address", this->availableDevices[i]->getAddress()); result.append(device); } return result; } QBluetoothLeUartDeviceModel* QBluetoothLeUart::getAvailableDevicesModel() { return this->availableDevicesModel; } QBluetoothLeUartDevice* QBluetoothLeUart::getCurrentDevice() { return this->currentBluetoothDevice; } bool QBluetoothLeUart::connectToDevice(int deviceId) { if(deviceId < 0 || deviceId >= this->availableDevices.length()) return false; this->connectToDevice(this->availableDevices[deviceId]); return true; } bool QBluetoothLeUart::connectToDevice(QBluetoothLeUartDevice *device){ if(!this->availableDevices.contains(device)) return false; if(this->state == Scanning) this->stopScanningForDevices(); this->currentBluetoothDevice = device; emit this->currentDeviceChanged(); if (bluetoothController) { bluetoothController->disconnectFromDevice(); delete bluetoothController; bluetoothController = 0; } // initialize QLowEnergyController bluetoothController = new QLowEnergyController(currentBluetoothDevice->getDevice(), this); bluetoothController->setRemoteAddressType(QLowEnergyController::RandomAddress); connect(this->bluetoothController, &QLowEnergyController::serviceDiscovered, this, &QBluetoothLeUart::handleServiceDiscovered); connect(this->bluetoothController, &QLowEnergyController::discoveryFinished, this, &QBluetoothLeUart::handleServiceScanDone); connect(bluetoothController, SIGNAL(error(QLowEnergyController::Error)), this, SLOT(handleControllerError(QLowEnergyController::Error))); connect(this->bluetoothController, &QLowEnergyController::connected, this, &QBluetoothLeUart::handleDeviceConnected); connect(this->bluetoothController, &QLowEnergyController::disconnected, this, &QBluetoothLeUart::handleDeviceDisconnected); /* Start connecting to device */ bluetoothController->connectToDevice(); setState(Connecting); return true; } bool QBluetoothLeUart::disconnectFromDevice() { if(this->state < Connecting) return false; if(this->state >= Connected) this->bluetoothController->disconnectFromDevice(); this->bluetoothController->deleteLater(); this->bluetoothController = nullptr; if(this->bluetoothService != nullptr) { this->bluetoothService->deleteLater(); this->bluetoothService = nullptr; } this->currentBluetoothDevice->deleteLater(); this->currentBluetoothDevice = nullptr; emit this->currentDeviceChanged(); this->setState(Idle); return true; } bool QBluetoothLeUart::sendData(QString data, bool asynchronous){ Q_UNUSED(asynchronous) if(this->state != Connected) return false; const QLowEnergyCharacteristic RxChar = bluetoothService->characteristic(QBluetoothUuid(QUuid(this->txUUID))); QByteArray Data; Data.append(data); bluetoothService->writeCharacteristic(RxChar, Data, QLowEnergyService::WriteWithoutResponse); return true; } // ------------------------------------------- // - Slots for QBluetothDeviceDiscoveryAgent - // ------------------------------------------- void QBluetoothLeUart::handleDeviceDiscovered(const QBluetoothDeviceInfo &device) { // Is it a BLE device? if (device.coreConfigurations() & QBluetoothDeviceInfo::LowEnergyCoreConfiguration) { qWarning() << "Discovered BLE Device: name: " << device.name() << " Address: " << device.address().toString() << " UUIDs: " << device.serviceUuids(); // ignore all devices that to not support our service if(!device.serviceUuids().contains(QBluetoothUuid(this->uartServiceUUID))) return; QBluetoothLeUartDevice *dev = new QBluetoothLeUartDevice(device, this); this->availableDevices.append(dev); this->availableDevicesModel->append(dev); emit this->foundNewDevice(dev); emit this->avaliableDevicesChanged(this->availableDevices); } } void QBluetoothLeUart::handleScanFinished() { if (this->availableDevices.size() == 0) { qWarning() << "No Low Energy devices found"; } emit this->scanFinished(this->availableDevices); setState(ScanFinished); } void QBluetoothLeUart::handleDeviceScanError(QBluetoothDeviceDiscoveryAgent::Error error) { if (error == QBluetoothDeviceDiscoveryAgent::PoweredOffError) emit this->scanningErrorOccured(AdapterTurnedOffError); else if (error == QBluetoothDeviceDiscoveryAgent::InputOutputError) emit this->scanningErrorOccured(InputOutputError); else emit this->scanningErrorOccured(UnknownError); } // ---------------------------------- // - Slots for QLowEnergyController - // ---------------------------------- void QBluetoothLeUart::handleServiceDiscovered(const QBluetoothUuid &uuid){ qDebug() << "Found service with ID: " << uuid; if(uuid == QBluetoothUuid(QUuid(this->uartServiceUUID))){ foundValidUARTService =true; qDebug() << "UART service found!"; } } void QBluetoothLeUart::handleServiceScanDone(){ delete bluetoothService; bluetoothService=0; if(foundValidUARTService){ qDebug() << "Connecting to UART service..."; bluetoothService = bluetoothController->createServiceObject(QBluetoothUuid(QUuid(this->uartServiceUUID)),this); } if(!bluetoothService){ qDebug() <<"UART service not found"; this->disconnectFromDevice(); return; } /* 3 Step: Service Discovery */ connect(this->bluetoothService, &QLowEnergyService::stateChanged, this, &QBluetoothLeUart::handleServiceStateChange); connect(this->bluetoothService, &QLowEnergyService::characteristicChanged, this, &QBluetoothLeUart::handleServiceCharacteristicChange); connect(this->bluetoothService, &QLowEnergyService::descriptorWritten, this, &QBluetoothLeUart::handleServiceDescriptorWritten); bluetoothService->discoverDetails(); setState(ServiceFound); } void QBluetoothLeUart::handleControllerError(QLowEnergyController::Error error) { qDebug() << "Cannot connect to remote device."; qWarning() << "Controller Error:" << error; this->disconnectFromDevice(); } void QBluetoothLeUart::handleDeviceConnected() { qDebug() << "Device connected"; bluetoothController->discoverServices(); setState(ScanningForService); } void QBluetoothLeUart::handleDeviceDisconnected() { this->setState(Idle); qDebug() << "UART service disconnected"; qWarning() << "Remote device disconnected"; } // ------------------------------- // - Slots for QLowEnergyService - // ------------------------------- void QBluetoothLeUart::handleServiceStateChange(QLowEnergyService::ServiceState s) { // A descriptoc can only be written if the service is in the ServiceDiscovered state switch (s) { case QLowEnergyService::ServiceDiscovered: { //looking for the RX characteristic const QLowEnergyCharacteristic TxChar = bluetoothService->characteristic(QBluetoothUuid(QUuid(this->rxUUID))); if (!TxChar.isValid()){ qDebug() << "Rx characteristic not found"; this->disconnectFromDevice(); return; } //looking for the TX characteristic const QLowEnergyCharacteristic RxChar = bluetoothService->characteristic(QBluetoothUuid(QUuid(this->txUUID))); if (!RxChar.isValid()) { qDebug() << "Tx characteristic not found"; this->disconnectFromDevice(); return; } // Bluetooth LE spec Where a characteristic can be notified, a Client Characteristic Configuration descriptor // shall be included in that characteristic as required by the Bluetooth Core Specification // Tx notify is enabled const QLowEnergyDescriptor m_notificationDescTx = TxChar.descriptor(QBluetoothUuid::ClientCharacteristicConfiguration); if (m_notificationDescTx.isValid()) { // enable notification bluetoothService->writeDescriptor(m_notificationDescTx, QByteArray::fromHex("0100")); setState(Connected); emit this->connectedToDevice(); } break; } default: //nothing for now break; } } void QBluetoothLeUart::handleServiceCharacteristicChange(const QLowEnergyCharacteristic &c,const QByteArray &value) { // ignore any other characteristic change if (c.uuid() != QBluetoothUuid(QUuid(this->rxUUID))) return; emit dataReceived((QString) value); } void QBluetoothLeUart::handleServiceDescriptorWritten(const QLowEnergyDescriptor &d, const QByteArray &value) { if (d.isValid() && d == bluetoothTransmissionDescriptor && value == QByteArray("0000")) { //disabled notifications -> assume disconnect intent this->disconnectFromDevice(); } } // -------------------- // - Helper functions - // -------------------- void QBluetoothLeUart::init() { #ifdef QBluetoothLeUart_QML qmlRegisterUncreatableType("de.itsblue.bluetoothleuart", 1, 0, "QBluetoothLeUartDevice", "QBluetoothLeUartDevice cannot be created"); qmlRegisterUncreatableType("de.itsblue.bluetoothleuart", 1, 0, "QBluetoothLeUartDeviceModel", "QBluetoothLeUartDeviceModel cannot be created"); qmlRegisterType("de.itsblue.bluetoothleuart", 1, 0, "QBluetoothLeUart"); qRegisterMetaType("QBluetoothLeUart::BluetoothLeUartState"); qRegisterMetaType("QBluetoothLeUart::BluetoothScanError"); #endif } void QBluetoothLeUart::setState(QBluetoothLeUart::BluetoothLeUartState newState) { if (state == newState) return; state = newState; emit stateChanged(newState); } QBluetoothLeUart::BluetoothLeUartState QBluetoothLeUart::getState() const { return state; } void QBluetoothLeUart::setUUIDs(const char uartServiceUUID[36], const char txUUID[36], const char rxUUID[36]) { this->uartServiceUUID = uartServiceUUID; this->txUUID = txUUID; this->rxUUID = rxUUID; }