From 38023712ae69ce8b32935503584434bde3669b29 Mon Sep 17 00:00:00 2001 From: Raphael Grimm <raphael.grimm@kit.edu> Date: Wed, 28 Nov 2018 17:57:02 +0100 Subject: [PATCH] Add KITProstheticHandDriver and example --- source/RobotAPI/drivers/CMakeLists.txt | 1 + .../BLEProthesisInterface.cpp | 151 ++++++++ .../BLEProthesisInterface.h | 145 ++++++++ .../BLEProthesisInterfaceQtWorker.cpp | 325 ++++++++++++++++++ .../BLEProthesisInterfaceQtWorker.h | 78 +++++ .../BLEProthesisInterfaceQtWorkerThread.cpp | 33 ++ .../BLEProthesisInterfaceQtWorkerThread.h | 30 ++ .../KITProstheticHandDriver/CMakeLists.txt | 35 ++ .../example/CMakeLists.txt | 9 + ...KITProstheticHandDriverExampleNoArmarX.cpp | 46 +++ 10 files changed, 853 insertions(+) create mode 100644 source/RobotAPI/drivers/KITProstheticHandDriver/BLEProthesisInterface.cpp create mode 100644 source/RobotAPI/drivers/KITProstheticHandDriver/BLEProthesisInterface.h create mode 100644 source/RobotAPI/drivers/KITProstheticHandDriver/BLEProthesisInterfaceQtWorker.cpp create mode 100644 source/RobotAPI/drivers/KITProstheticHandDriver/BLEProthesisInterfaceQtWorker.h create mode 100644 source/RobotAPI/drivers/KITProstheticHandDriver/BLEProthesisInterfaceQtWorkerThread.cpp create mode 100644 source/RobotAPI/drivers/KITProstheticHandDriver/BLEProthesisInterfaceQtWorkerThread.h create mode 100644 source/RobotAPI/drivers/KITProstheticHandDriver/CMakeLists.txt create mode 100644 source/RobotAPI/drivers/KITProstheticHandDriver/example/CMakeLists.txt create mode 100644 source/RobotAPI/drivers/KITProstheticHandDriver/example/KITProstheticHandDriverExampleNoArmarX.cpp diff --git a/source/RobotAPI/drivers/CMakeLists.txt b/source/RobotAPI/drivers/CMakeLists.txt index 90331c6e0..c51339f3a 100644 --- a/source/RobotAPI/drivers/CMakeLists.txt +++ b/source/RobotAPI/drivers/CMakeLists.txt @@ -5,4 +5,5 @@ add_subdirectory(OptoForceUnit) add_subdirectory(OrientedTactileSensor) add_subdirectory(GamepadUnit) add_subdirectory(MetaWearIMU) +add_subdirectory(KITProstheticHandDriver) diff --git a/source/RobotAPI/drivers/KITProstheticHandDriver/BLEProthesisInterface.cpp b/source/RobotAPI/drivers/KITProstheticHandDriver/BLEProthesisInterface.cpp new file mode 100644 index 000000000..90db3dadd --- /dev/null +++ b/source/RobotAPI/drivers/KITProstheticHandDriver/BLEProthesisInterface.cpp @@ -0,0 +1,151 @@ +#include <sstream> + +#include "BLEProthesisInterface.h" +#include "BLEProthesisInterfaceQtWorkerThread.h" + +BLEProthesisInterface::BLEProthesisInterface(const std::string &mac) : + _worker{new BLEProthesisInterfaceQtWorkerThread{mac, *this}} +{ + _worker->start(); +} + +BLEProthesisInterface::~BLEProthesisInterface() +{ + _worker->kill(); + _worker->quit(); + _worker->exit(); + _worker->wait(1000); // ms + _worker->terminate(); + _worker->wait(); +} + +std::int64_t BLEProthesisInterface::getThumbPWM() const +{ + return _thumbPWM; +} + +std::int64_t BLEProthesisInterface::getThumbPos() const +{ + return _thumbPos; +} + +std::int64_t BLEProthesisInterface::getFingerPWM() const +{ + return _fingerPWM; +} + +std::int64_t BLEProthesisInterface::getFingerPos() const +{ + return _fingerPos; +} + +BLEProthesisInterface::State BLEProthesisInterface::getState() const +{ + return _state; +} + +void BLEProthesisInterface::sendRaw(const std::string &cmd) +{ + if(_state != State::Running) + { + throw std::invalid_argument + { + "BLEProthesisInterface::sendRaw( cmd = " + cmd + + " ): The UART service is not connected!" + }; + } + _worker->sendCommand(cmd); +} + +void BLEProthesisInterface::sendGrasp(std::uint64_t n) +{ + if(n > _maxG) + { + throw std::invalid_argument + { + "BLEProthesisInterface::sendGrasp( n = " + std::to_string(n) + + " ): the maximal value for n is " + std::to_string(_maxG) + }; + } + sendRaw("g" + std::to_string(n) + "\n"); +} + +void BLEProthesisInterface::sendThumbPWM(uint64_t v, int64_t maxPWM, uint64_t pos) +{ + if( v < _minV || v > _maxV ) + { + throw std::invalid_argument + { + "sendThumbPWM( v = " + std::to_string(v) + + " , maxPWM = " + std::to_string(maxPWM) + + " , pos = " + std::to_string(pos) + + " ): The interval for v is [" + std::to_string(_minV) + + ", " + std::to_string(_maxV) + "]" + }; + } + if( maxPWM < _minPWM || maxPWM > _maxPWM ) + { + throw std::invalid_argument + { + "sendThumbPWM( v = " + std::to_string(v) + + " , maxPWM = " + std::to_string(maxPWM) + + " , pos = " + std::to_string(pos) + + " ): The interval for maxPWM is [" + std::to_string(_minPWM) + + ", " + std::to_string(_maxPWM) + "]" + }; + } + if( pos > _maxPosT ) + { + throw std::invalid_argument + { + "sendThumbPWM( v = " + std::to_string(v) + + " , maxPWM = " + std::to_string(maxPWM) + + " , pos = " + std::to_string(pos) + + " ): The interval for pos is [0, " + + std::to_string(_maxPosT) + "]" + }; + } + std::stringstream str; + str << 'M' << 2 << ',' << v << ',' << maxPWM << ',' << pos << '\n'; + sendRaw(str.str()); +} + +void BLEProthesisInterface::sendFingerPWM(uint64_t v, int64_t maxPWM, uint64_t pos) +{ + if( v < _minV || v > _maxV ) + { + throw std::invalid_argument + { + "sendThumbPWM( v = " + std::to_string(v) + + " , maxPWM = " + std::to_string(maxPWM) + + " , pos = " + std::to_string(pos) + + " ): The interval for v is [" + std::to_string(_minV) + + ", " + std::to_string(_maxV) + "]" + }; + } + if( maxPWM < _minPWM || maxPWM > _maxPWM ) + { + throw std::invalid_argument + { + "sendThumbPWM( v = " + std::to_string(v) + + " , maxPWM = " + std::to_string(maxPWM) + + " , pos = " + std::to_string(pos) + + " ): The interval for maxPWM is [" + std::to_string(_minPWM) + + ", " + std::to_string(_maxPWM) + "]" + }; + } + if( pos > _maxPosF ) + { + throw std::invalid_argument + { + "sendThumbPWM( v = " + std::to_string(v) + + " , maxPWM = " + std::to_string(maxPWM) + + " , pos = " + std::to_string(pos) + + " ): The interval for pos is [0, " + + std::to_string(_maxPosF) + "]" + }; + } + std::stringstream str; + str << 'M' << 3 << ',' << v << ',' << maxPWM << ',' << pos << '\n'; + sendRaw(str.str()); +} diff --git a/source/RobotAPI/drivers/KITProstheticHandDriver/BLEProthesisInterface.h b/source/RobotAPI/drivers/KITProstheticHandDriver/BLEProthesisInterface.h new file mode 100644 index 000000000..b3c3428a0 --- /dev/null +++ b/source/RobotAPI/drivers/KITProstheticHandDriver/BLEProthesisInterface.h @@ -0,0 +1,145 @@ +#pragma once + +#include <atomic> +#include <string> +#include <memory> + +class BLEProthesisInterfaceQtWorkerThread; +class BLEProthesisInterfaceQtWorker; + +class BLEProthesisInterface +{ +public: + BLEProthesisInterface(const std::string& mac); + ~BLEProthesisInterface(); + enum class State + { + Created, + DiscoveringDevices, + DiscoveringDevicesDone, + Disconnected, + Connecting, + ConnectingDone, + DiscoveringServices, + DiscoveringServicesDone, + ConnectingService, + Running, + Killed + }; + std::int64_t getThumbPWM() const; + std::int64_t getThumbPos() const; + std::int64_t getFingerPWM() const; + std::int64_t getFingerPos() const; + + State getState() const; + + void sendRaw(const std::string& cmd); + + void sendGrasp(std::uint64_t n); + void sendThumbPWM(std::uint64_t v, std::int64_t maxPWM, std::uint64_t pos); + void sendFingerPWM(std::uint64_t v, std::int64_t maxPWM, std::uint64_t pos); +private: + friend class BLEProthesisInterfaceQtWorker; + std::atomic_int64_t _thumbPWM{0}; + std::atomic_int64_t _thumbPos{0}; + std::atomic_int64_t _fingerPWM{0}; + std::atomic_int64_t _fingerPos{0}; + + std::unique_ptr<BLEProthesisInterfaceQtWorkerThread> _worker; + std::atomic<State> _state{State::Created}; + + static constexpr std::uint64_t _maxG = 8; + static constexpr std::uint64_t _minV = 10; + static constexpr std::uint64_t _maxV = 200; + static constexpr std::int64_t _minPWM = -2999; + static constexpr std::int64_t _maxPWM = 2999; + static constexpr std::uint64_t _maxPosT = 100'000; + static constexpr std::uint64_t _maxPosF = 200'000; +}; + +inline std::string to_string(BLEProthesisInterface::State s) +{ + switch(s) + { + case BLEProthesisInterface::State::Created: return "Created"; + case BLEProthesisInterface::State::DiscoveringDevices: return "DiscoveringDevices"; + case BLEProthesisInterface::State::DiscoveringDevicesDone: return "DiscoveringDevicesDone"; + case BLEProthesisInterface::State::Disconnected: return "Disconnected"; + case BLEProthesisInterface::State::Connecting: return "Connecting"; + case BLEProthesisInterface::State::ConnectingDone: return "ConnectingDone"; + case BLEProthesisInterface::State::DiscoveringServices: return "DiscoveringServices"; + case BLEProthesisInterface::State::DiscoveringServicesDone: return "DiscoveringServicesDone"; + case BLEProthesisInterface::State::ConnectingService: return "ConnectingService"; + case BLEProthesisInterface::State::Running: return "Running"; + case BLEProthesisInterface::State::Killed: return "Killed"; + } + return "UNKNOWN VALUE FOR BLEProthesisInterface::State: " + std::to_string(static_cast<int>(s)); +} + + + + + + + +//#include <QBluetoothDeviceDiscoveryAgent> +//#include <QBluetoothDeviceInfo> +//#include <QBluetoothSocket> +//#include <QLowEnergyController> + + +//class BLEProthesisInterface : public QObject +//{ +// Q_OBJECT +//public: +// explicit BLEProthesisInterface(const QString& mac, QObject *parent = 0); +// ~BLEProthesisInterface(); + +//// void startClient(const QBluetoothServiceInfo &remoteService); +//// void stopClient(); +//private: +// void removeAll(); +//public slots: +//// void sendMessage(const QString &message); + +// void reset(QString mac); + +//signals: +//// void messageReceived(const QString &sender, const QString &message); +//// void connected(const QString &name); +//// void disconnected(); + +//private slots: +// void deviceDiscovered(const QBluetoothDeviceInfo &info); +// void deviceDiscoverFinished(); +// void discoveryFinished(); +//// void readSocket(); +// void connected(); +// void disconnected() +// { + +// } +// void stateChanged(QLowEnergyController::ControllerState state); +// void serviceDiscovered(const QBluetoothUuid &newService); +// void controllerError(QLowEnergyController::Error error); + +// void serviceCharacteristicChanged(const QLowEnergyCharacteristic& cha, const QByteArray& arr) +// { + +// } +// void serviceDescriptorWritten(const QLowEnergyDescriptor& des, const QByteArray& arr) +// { + +// } +// void serviceStateChanged(const QLowEnergyService::ServiceState& state); + +//private: +// QBluetoothDeviceDiscoveryAgent* _discoveryAgent{nullptr}; +// QString _mac; +// QBluetoothSocket* _socket{nullptr}; +// QLowEnergyController* _leController{nullptr}; +// QLowEnergyService* _service{nullptr}; +// bool _connected{false}; +// bool _reconnect{true}; +// bool _serviceUp{false}; +//}; diff --git a/source/RobotAPI/drivers/KITProstheticHandDriver/BLEProthesisInterfaceQtWorker.cpp b/source/RobotAPI/drivers/KITProstheticHandDriver/BLEProthesisInterfaceQtWorker.cpp new file mode 100644 index 000000000..046e5bdf0 --- /dev/null +++ b/source/RobotAPI/drivers/KITProstheticHandDriver/BLEProthesisInterfaceQtWorker.cpp @@ -0,0 +1,325 @@ +#include "BLEProthesisInterfaceQtWorker.h" + + +#define UARTSERVICEUUID "6e400001-b5a3-f393-e0a9-e50e24dcca9e" +#define RXUUID "6e400002-b5a3-f393-e0a9-e50e24dcca9e" +#define TXUUID "6e400003-b5a3-f393-e0a9-e50e24dcca9e" + +BLEProthesisInterfaceQtWorker::BLEProthesisInterfaceQtWorker(const QString& mac, BLEProthesisInterface &owner) : + _owner{&owner}, + _mac{mac} +{ + _timer = startTimer(std::chrono::milliseconds{10}); +} + +BLEProthesisInterfaceQtWorker::~BLEProthesisInterfaceQtWorker() +{ + cleanup(); +} + +void BLEProthesisInterfaceQtWorker::kill() +{ + _killed = true; +} + +void BLEProthesisInterfaceQtWorker::sendCommand(const std::string &cmd) +{ + std::lock_guard guard(_cmdMutex); + _cmd += QString::fromStdString(cmd); +} + +void BLEProthesisInterfaceQtWorker::cleanup() +{ + //stop services etc + if(_timer != -1) + { + killTimer(_timer); + _timer = -1; + } + //disconnect + if(_control) + { + _control->disconnectFromDevice(); + } + + //delete + if(_service) + { + delete _service; + _service = nullptr; + } + if(_control) + { + delete _control; + _control = nullptr; + } + if(_deviceDiscoveryAgent) + { + delete _deviceDiscoveryAgent; + _deviceDiscoveryAgent = nullptr; + } +} + +void BLEProthesisInterfaceQtWorker::timerEvent(QTimerEvent*) +{ + if(_killed) + { + qDebug() << '[' << _mac << ']' << " Stopping NOW!"; + cleanup(); + quit(); + return; + } + //discovering + if(_owner->_state == State::Created) + { + _deviceDiscoveryAgent = new QBluetoothDeviceDiscoveryAgent; + connect(_deviceDiscoveryAgent, SIGNAL(deviceDiscovered(const QBluetoothDeviceInfo&)), + this, SLOT(deviceDiscovered(const QBluetoothDeviceInfo&))); + connect(_deviceDiscoveryAgent, SIGNAL(error(QBluetoothDeviceDiscoveryAgent::Error)), + this, SLOT(deviceDiscoverError(QBluetoothDeviceDiscoveryAgent::Error))); + connect(_deviceDiscoveryAgent, SIGNAL(finished()), this, SLOT(deviceDiscoverFinished())); + connect(_deviceDiscoveryAgent, SIGNAL(canceled()), this, SLOT(deviceDiscoverFinished())); + qDebug() << '[' << _mac << ']' << " State DiscoveringDevices"; + _owner->_state = State::DiscoveringDevices; + _deviceDiscoveryAgent->start(); + } + else if(_owner->_state == State::DiscoveringDevicesDone) + { + if(!_deviceDiscovered) + { + qDebug() << '[' << _mac << ']' << " Device discovering failed!"; + kill(); + return; + } + qDebug() << '[' << _mac << ']' << " State Disconnected"; + _owner->_state = State::Disconnected; + } + else if(_owner->_state == State::Disconnected) + { + if(_service) + { + delete _service; + _service = nullptr; + } + if(_control) + { + delete _control; + _control = nullptr; + } + _serviceDiscovered = false; + _control = new QLowEnergyController(_currentDevice); + _control ->setRemoteAddressType(QLowEnergyController::RandomAddress); + + connect(_control, SIGNAL(serviceDiscovered(QBluetoothUuid)), + this, SLOT(serviceDiscovered(QBluetoothUuid))); + connect(_control, SIGNAL(discoveryFinished()), + this, SLOT(serviceScanDone())); + connect(_control, SIGNAL(error(QLowEnergyController::Error)), + this, SLOT(controllerError(QLowEnergyController::Error))); + connect(_control, SIGNAL(connected()), + this, SLOT(deviceConnected())); + connect(_control, SIGNAL(disconnected()), + this, SLOT(deviceDisconnected())); + _control->connectToDevice(); + qDebug() << '[' << _mac << ']' << " State Connecting"; + _owner->_state = State::Connecting; + } + else if(_owner->_state == State::ConnectingDone) + { + _control->discoverServices(); + qDebug() << '[' << _mac << ']' << " State DiscoveringServices"; + _owner->_state = State::DiscoveringServices; + } + else if(_owner->_state == State::DiscoveringServicesDone) + { + if(!_serviceDiscovered) + { + qDebug() << '[' << _mac << ']' << " Service discovering failed!"; + kill(); + return; + } + _service = _control->createServiceObject(QBluetoothUuid(QUuid(UARTSERVICEUUID))); + + connect(_service, SIGNAL(stateChanged(QLowEnergyService::ServiceState)), + this, SLOT(serviceStateChanged(QLowEnergyService::ServiceState))); + connect(_service, SIGNAL(characteristicChanged(QLowEnergyCharacteristic,QByteArray)), + this, SLOT(readData(QLowEnergyCharacteristic,QByteArray))); + connect(_service, SIGNAL(descriptorWritten(QLowEnergyDescriptor,QByteArray)), + this, SLOT(receiveDeviceDisconnec(QLowEnergyDescriptor,QByteArray))); + + _service->discoverDetails(); + + qDebug() << '[' << _mac << ']' << " State ConnectingService"; + _owner->_state = State::ConnectingService; + } + else if (_owner->_state == State::Running) //send data + { + std::lock_guard g(_cmdMutex); + if(_service && _cmd.size() != 0) + { + const QLowEnergyCharacteristic RxChar = _service->characteristic(QBluetoothUuid(QUuid(RXUUID))); + QByteArray data; + data.append(_cmd); + _service->writeCharacteristic(RxChar, data, QLowEnergyService::WriteWithoutResponse); + qDebug() << '[' << _mac << ']' << " send: " << _cmd; + _cmd.clear(); + } + } +} + +void BLEProthesisInterfaceQtWorker::deviceDiscovered(const QBluetoothDeviceInfo &device) +{ + if (device.address().toString() == _mac) + { + qDebug() << '[' << _mac << ']' << " Discovered target device " << device.address().toString(); + _currentDevice = device; + _deviceDiscovered = true; + qDebug() << '[' << _mac << ']' << " State DiscoveringDevicesDone"; + _owner->_state = State::DiscoveringDevicesDone; + _deviceDiscoveryAgent->stop(); + } + else + { + qDebug() << '[' << _mac << ']' << " Discovered device " << device.address().toString(); + } +} + +void BLEProthesisInterfaceQtWorker::deviceDiscoverFinished() +{ + qDebug() << '[' << _mac << ']' << " Discovering of services done."; + _owner->_state = State::DiscoveringDevicesDone; +} + +void BLEProthesisInterfaceQtWorker::deviceDiscoverError(QBluetoothDeviceDiscoveryAgent::Error error) +{ + if (error == QBluetoothDeviceDiscoveryAgent::PoweredOffError) + { + qDebug() << '[' << _mac << ']' << "The Bluetooth adaptor is powered off, power it on before doing discovery."; + } + else if (error == QBluetoothDeviceDiscoveryAgent::InputOutputError) + { + qDebug() << '[' << _mac << ']' << "Writing or reading from the device resulted in an error."; + } + else + { + qDebug() << '[' << _mac << ']' << "An unknown error has occurred."; + } + kill(); +} + +void BLEProthesisInterfaceQtWorker::serviceDiscovered(const QBluetoothUuid &gatt) +{ + qDebug() << '[' << _mac << ']' << " Discovered service " << gatt.toString(); + if(gatt==QBluetoothUuid(QUuid(UARTSERVICEUUID))) + { + qDebug() << '[' << _mac << ']' << "Discovered UART service " << gatt.toString(); + _serviceDiscovered =true; + _owner->_state = State::DiscoveringServicesDone; + } +} + +void BLEProthesisInterfaceQtWorker::serviceDiscoverFinished() +{ + qDebug() << '[' << _mac << ']' << " State DiscoveringServicesDone"; + _owner->_state = State::DiscoveringServicesDone; +} + +void BLEProthesisInterfaceQtWorker::controllerError(QLowEnergyController::Error error) +{ + qDebug() << '[' << _mac << ']' << " Cannot connect to remote device."; + qWarning() << '[' << _mac << ']' << " Controller Error:" << error; + kill(); +} + +void BLEProthesisInterfaceQtWorker::receiveDeviceDisconnec(const QLowEnergyDescriptor &d, const QByteArray &value) +{ + if (d.isValid() && d == _notificationDescTx && value == QByteArray("0000")) + { + qDebug() << '[' << _mac << ']' << "Device requests disconnect."; + //disabled notifications -> assume disconnect intent + _control->disconnectFromDevice(); + if(_service) + { + delete _service; + _service = nullptr; + } + } +} + +void BLEProthesisInterfaceQtWorker::serviceStateChanged(QLowEnergyService::ServiceState s) +{ + // A descriptoc can only be written if the service is in the ServiceDiscovered state + qDebug() << '[' << _mac << ']' << " serviceStateChanged -> " << s; + switch (s) + { + case QLowEnergyService::ServiceDiscovered: + { + + //looking for the TX characteristic + const QLowEnergyCharacteristic TxChar = _service->characteristic(QBluetoothUuid(QUuid(TXUUID))); + if (!TxChar.isValid()) + { + qDebug() << '[' << _mac << ']' << " Tx characteristic not found"; + break; + } + + //looking for the RX characteristic + const QLowEnergyCharacteristic RxChar = _service->characteristic(QBluetoothUuid(QUuid(RXUUID))); + if (!RxChar.isValid()) + { + qDebug() << '[' << _mac << ']' << " Rx characteristic not found"; + break; + } + + // 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 + _notificationDescTx = TxChar.descriptor(QBluetoothUuid::ClientCharacteristicConfiguration); + if (_notificationDescTx.isValid()) + { + // enable notification + _service->writeDescriptor(_notificationDescTx, QByteArray::fromHex("0100")); + qDebug() << '[' << _mac << ']' << " State Running"; + _owner->_state = State::Running; + } + } + break; + default: + break; + } +} + +void BLEProthesisInterfaceQtWorker::deviceConnected() +{ + qDebug() << '[' << _mac << ']' << " State ConnectingDone"; + _owner->_state = State::ConnectingDone; +} + +void BLEProthesisInterfaceQtWorker::deviceDisconnected() +{ + qDebug() << '[' << _mac << ']' << " State Disconnected"; + _owner->_state = State::Disconnected; +} + +void BLEProthesisInterfaceQtWorker::readData(const QLowEnergyCharacteristic &c, const QByteArray &value) +{ + // ignore any other characteristic change + if (c.uuid() != QBluetoothUuid(QUuid(TXUUID))) + return; + //qDebug() << '[' << _mac << ']' << " received : " << value; + + _valueAkk += QString{value}; + if(!_valueAkk.contains('\n')) + { + return; + } + auto listPacks = _valueAkk.split('\n'); + auto listVals = listPacks.at(listPacks.size() - 2).split(' '); + + _owner->_thumbPos = listVals.at(0).toLong(); + _owner->_thumbPWM = listVals.at(1).toLong(); + _owner->_fingerPos = listVals.at(2).toLong(); + _owner->_fingerPWM = listVals.at(3).toLong(); + + _valueAkk = listPacks.back(); +} diff --git a/source/RobotAPI/drivers/KITProstheticHandDriver/BLEProthesisInterfaceQtWorker.h b/source/RobotAPI/drivers/KITProstheticHandDriver/BLEProthesisInterfaceQtWorker.h new file mode 100644 index 000000000..322ab5a3f --- /dev/null +++ b/source/RobotAPI/drivers/KITProstheticHandDriver/BLEProthesisInterfaceQtWorker.h @@ -0,0 +1,78 @@ +#pragma once + +#include <mutex> + +#include <QBluetoothDeviceDiscoveryAgent> +#include <QBluetoothDeviceInfo> +#include <QLowEnergyController> +#include <QLowEnergyService> + +#include "BLEProthesisInterface.h" + +#include <QThread> + + +class BLEProthesisInterfaceQtWorker : public QThread +{ + Q_OBJECT +public: + using State = BLEProthesisInterface::State; + BLEProthesisInterfaceQtWorker(const QString& mac, BLEProthesisInterface& owner); + ~BLEProthesisInterfaceQtWorker(); + + void kill(); + void sendCommand(const std::string& cmd); +private: + void cleanup(); +protected: + void timerEvent(QTimerEvent *event) override; +signals: + void resultReady(const QString &s); +private slots: + //dev discover + void deviceDiscovered(const QBluetoothDeviceInfo &device); + void deviceDiscoverFinished(); + void deviceDiscoverError(QBluetoothDeviceDiscoveryAgent::Error); + void deviceConnected(); + void deviceDisconnected(); + //service discovery + void serviceDiscovered(const QBluetoothUuid &gatt); + void serviceDiscoverFinished(); + void controllerError(QLowEnergyController::Error error); + //communication + void receiveDeviceDisconnec(const QLowEnergyDescriptor &d, const QByteArray &value); + + void serviceStateChanged(QLowEnergyService::ServiceState s); + void readData(const QLowEnergyCharacteristic &c,const QByteArray &value); +private: +// friend class BLEProthesisInterface; + BLEProthesisInterface* _owner; + QString _mac; + + //management (sensor/control) + int _timer{-1}; + std::atomic_bool _killed{false}; + + enum class SensorValue + { + ThumbPWM = 0, + ThumbPos = 1, + FingerPWM = 2, + FingerPos = 3 + }; + SensorValue _nextSensorValue{SensorValue::ThumbPos}; + QString _valueAkk; + + QString _cmd; + std::mutex _cmdMutex; + //ble discover + QBluetoothDeviceDiscoveryAgent* _deviceDiscoveryAgent{nullptr}; + QBluetoothDeviceInfo _currentDevice; + bool _deviceDiscovered{false}; + //ble dev + QLowEnergyController* _control{nullptr}; + //ble ser + bool _serviceDiscovered{false}; + QLowEnergyDescriptor _notificationDescTx; + QLowEnergyService* _service{nullptr}; +}; diff --git a/source/RobotAPI/drivers/KITProstheticHandDriver/BLEProthesisInterfaceQtWorkerThread.cpp b/source/RobotAPI/drivers/KITProstheticHandDriver/BLEProthesisInterfaceQtWorkerThread.cpp new file mode 100644 index 000000000..5f665d077 --- /dev/null +++ b/source/RobotAPI/drivers/KITProstheticHandDriver/BLEProthesisInterfaceQtWorkerThread.cpp @@ -0,0 +1,33 @@ +#include "BLEProthesisInterfaceQtWorkerThread.h" + + +BLEProthesisInterfaceQtWorkerThread::BLEProthesisInterfaceQtWorkerThread(const std::string &mac, BLEProthesisInterface &owner) : + _owner{&owner}, + _mac{QString::fromStdString(mac)} +{} + +BLEProthesisInterfaceQtWorkerThread::~BLEProthesisInterfaceQtWorkerThread() +{} + +void BLEProthesisInterfaceQtWorkerThread::kill() +{ + _worker->kill(); +} + +void BLEProthesisInterfaceQtWorkerThread::sendCommand(const std::string &cmd) +{ + _worker->sendCommand(cmd); +} + +void BLEProthesisInterfaceQtWorkerThread::run() +{ + _worker = new BLEProthesisInterfaceQtWorker{_mac, *_owner}; + qDebug() << '[' << _mac << ']' << " Starting qt event loop."; + int r = exec(); + if(_worker) + { + _worker->kill(); + delete _worker; + } + qDebug() << '[' << _mac << ']' << " Sopped qt event loop -> " << r; +} diff --git a/source/RobotAPI/drivers/KITProstheticHandDriver/BLEProthesisInterfaceQtWorkerThread.h b/source/RobotAPI/drivers/KITProstheticHandDriver/BLEProthesisInterfaceQtWorkerThread.h new file mode 100644 index 000000000..258194b17 --- /dev/null +++ b/source/RobotAPI/drivers/KITProstheticHandDriver/BLEProthesisInterfaceQtWorkerThread.h @@ -0,0 +1,30 @@ +#pragma once + +#include <mutex> + +#include <QBluetoothDeviceDiscoveryAgent> +#include <QBluetoothDeviceInfo> +#include <QLowEnergyController> +#include <QLowEnergyService> + +#include "BLEProthesisInterface.h" +#include "BLEProthesisInterfaceQtWorker.h" + +#include <QThread> + + +class BLEProthesisInterfaceQtWorkerThread : public QThread +{ + Q_OBJECT +public: + BLEProthesisInterfaceQtWorkerThread(const std::string& mac, BLEProthesisInterface& owner); + ~BLEProthesisInterfaceQtWorkerThread(); + void kill(); + void sendCommand(const std::string& cmd); +private: + void run() override; +private: + BLEProthesisInterface* _owner{nullptr}; + QString _mac; + BLEProthesisInterfaceQtWorker* _worker{nullptr}; +}; diff --git a/source/RobotAPI/drivers/KITProstheticHandDriver/CMakeLists.txt b/source/RobotAPI/drivers/KITProstheticHandDriver/CMakeLists.txt new file mode 100644 index 000000000..e70462472 --- /dev/null +++ b/source/RobotAPI/drivers/KITProstheticHandDriver/CMakeLists.txt @@ -0,0 +1,35 @@ +#requires the Packages libqt5bluetooth5 qtconnectivity5-dev +set(LIB_NAME KITProstheticHandDriver) + +armarx_component_set_name("${LIB_NAME}") +armarx_set_target("Library: ${LIB_NAME}") + +find_package(Qt5 COMPONENTS Core Bluetooth QUIET) + +set(CMAKE_AUTOMOC ON) + +armarx_build_if(Qt5_FOUND "Qt5 Core or Bluetooth not available") + +set(LIBS + ${Qt5Core_LIBRARIES} + ${Qt5Bluetooth_LIBRARIES} +) + +include_directories(SYSTEM ${Qt5Core_INCLUDE_DIRS}) +include_directories(SYSTEM ${Qt5Bluetooth_INCLUDE_DIRS}) + +set(LIB_FILES + BLEProthesisInterface.cpp + BLEProthesisInterfaceQtWorker.cpp + BLEProthesisInterfaceQtWorkerThread.cpp +) + +set(LIB_HEADERS + BLEProthesisInterface.h + BLEProthesisInterfaceQtWorker.h + BLEProthesisInterfaceQtWorkerThread.h +) + +armarx_add_library("${LIB_NAME}" "${LIB_FILES}" "${LIB_HEADERS}" "${LIBS}") + +add_subdirectory(example) diff --git a/source/RobotAPI/drivers/KITProstheticHandDriver/example/CMakeLists.txt b/source/RobotAPI/drivers/KITProstheticHandDriver/example/CMakeLists.txt new file mode 100644 index 000000000..8d23177e3 --- /dev/null +++ b/source/RobotAPI/drivers/KITProstheticHandDriver/example/CMakeLists.txt @@ -0,0 +1,9 @@ +armarx_component_set_name("KITProstheticHandDriverExampleNoArmarX") + +set(COMPONENT_LIBS + KITProstheticHandDriver +) + +set(EXE_SOURCE KITProstheticHandDriverExampleNoArmarX.cpp) + +armarx_add_component_executable("${EXE_SOURCE}") diff --git a/source/RobotAPI/drivers/KITProstheticHandDriver/example/KITProstheticHandDriverExampleNoArmarX.cpp b/source/RobotAPI/drivers/KITProstheticHandDriver/example/KITProstheticHandDriverExampleNoArmarX.cpp new file mode 100644 index 000000000..e544ffe12 --- /dev/null +++ b/source/RobotAPI/drivers/KITProstheticHandDriver/example/KITProstheticHandDriverExampleNoArmarX.cpp @@ -0,0 +1,46 @@ +#include <QCoreApplication> + +#include <RobotAPI/drivers/KITProstheticHandDriver/BLEProthesisInterface.h> + +#include <iostream> +#include <thread> + +int main(int argc, char *argv[]) +{ + QCoreApplication a(argc, argv); + { + BLEProthesisInterface iface{"DF:70:E8:81:DB:D6"}; + std::size_t dcCounter = 0; + while(iface.getState() != BLEProthesisInterface::State::Running) + { + std::cout << "Waiting for Running State: " << to_string(iface.getState()) << std::endl; + + if(iface.getState() == BLEProthesisInterface::State::Disconnected) + { + ++dcCounter; + } + + if(dcCounter > 50) + { + return 1; + } + + std::this_thread::sleep_for(std::chrono::milliseconds{100}); + } + + for(std::size_t i = 0; i < 10; ++i) + { + iface.sendGrasp(i%2); + std::cout << iface.getThumbPos() << "\t" + << iface.getThumbPWM() << "\t" + << iface.getFingerPos() << "\t" + << iface.getFingerPWM() << std::endl; + std::this_thread::sleep_for(std::chrono::milliseconds{2500}); + } + iface.sendThumbPWM(200, -2999, 0); + iface.sendFingerPWM(200, -2999, 0); + std::this_thread::sleep_for(std::chrono::milliseconds{2500}); + } + return 0; +} + -- GitLab