#ifndef BLUETOOTH_H #define BLUETOOTH_H #include #include #include #include #include #include #include #include #include #include "ByteBuffer.h" namespace trygvis { namespace bluetooth { using namespace std; template using o = std::experimental::optional; struct Uuid { uint8_t value[16]; explicit Uuid(uint8_t value[16]) noexcept : value() { memcpy(this->value, value, 16); } Uuid(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5, uint8_t b6, uint8_t b7, uint8_t b8, uint8_t b9, uint8_t b10, uint8_t b11, uint8_t b12, uint8_t b13, uint8_t b14, uint8_t b15) noexcept : value{ b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15} {} bool operator==(const Uuid &other) { return std::memcmp(value, other.value, 16) == 0; } bool operator==(const Uuid &other) const { return std::memcmp(value, other.value, 16) == 0; } friend std::ostream &operator<<(std::ostream &s, Uuid const &uuid) { auto &v = uuid.value; s << std::hex << v[0] << v[1] << ":" << v[2] << v[3] << ":" << v[4] << v[5] << ":" << v[6] << v[7] << ":" << v[8] << v[9] << ":" << v[10] << v[11] << ":" << v[12] << v[13] << ":" << v[14] << v[15] << ":" << std::endl; return s; } static Uuid fromShort(uint8_t b2, uint8_t b3) { return {0x00, 0x00, b2, b3, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb}; } }; struct ShortUuid { private: public: explicit ShortUuid(uint16_t value) : value(value) {} Uuid toLong() { auto b2 = static_cast(value >> 8); auto b3 = static_cast(value & 0xff); return Uuid::fromShort(b2, b3); } uint16_t value; }; namespace uuids { const ShortUuid HealthTermometerService{0x1809}; const ShortUuid DeviceInformationService{0x180a}; const ShortUuid BatteryService{0x180f}; const ShortUuid PRIMARY_SERVICE{0x2800}; const ShortUuid SECONDARY_SERVICE{0x2801}; const ShortUuid CHARACTERISTIC{0x2803}; const ShortUuid TemperatureMeasurement{0x2A1C}; } class BluetoothAdapter; class BluetoothDevice; class BluetoothGatt; class BluetoothGattService; class BluetoothGattCharacteristic; typedef shared_ptr BluetoothGattPtr; typedef shared_ptr BluetoothGattCharacteristicPtr; typedef shared_ptr BluetoothGattServicePtr; class BluetoothException : public runtime_error { public: BluetoothException(const BluetoothAdapter *adapter, string const &what) : runtime_error(what), adapter(adapter), device(nullptr) {} BluetoothException(const BluetoothDevice *device, string const &what) : runtime_error(what), adapter(nullptr), device(device) {} explicit BluetoothException(string const &what) : runtime_error(what), adapter(nullptr), device(nullptr) {} const BluetoothAdapter *adapter; const BluetoothDevice *device; }; class Mac { public: explicit Mac(uint8_t _5, uint8_t _4, uint8_t _3, uint8_t _2, uint8_t _1, uint8_t _0) : bytes() { bytes[5] = _5; bytes[4] = _4; bytes[3] = _3; bytes[2] = _2; bytes[1] = _1; bytes[0] = _0; }; string str() const; bool operator==(Mac &other) const; bool operator!=(Mac &other) const; void copy(uint8_t &_5, uint8_t &_4, uint8_t &_3, uint8_t &_2, uint8_t &_1, uint8_t &_0) const; static Mac parseMac(string s); friend bool operator<(const Mac &a, const Mac &b); private: uint8_t bytes[6]; }; template class Iterator { public: private: }; template class Collection { public: Iterator begin(); Iterator end(); }; class BluetoothGattCharacteristic { public: virtual ~BluetoothGattCharacteristic() = default; virtual BluetoothGattServicePtr getService() const = 0; virtual uint16_t getHandle() const = 0; virtual const Uuid getUuid() const = 0; virtual uint8_t getProperties() const = 0; virtual uint16_t getValueHandle() const = 0; }; class BluetoothGattService { public: virtual ~BluetoothGattService() = default; virtual BluetoothDevice &getDevice() const = 0; virtual Uuid getUuid() const = 0; virtual uint16_t getHandle() const = 0; virtual uint16_t getEndGroupHandle() const = 0; virtual vector> getCharacteristics() const = 0; virtual o findCharacteristic(Uuid uuid) = 0; o findCharacteristic(ShortUuid uuid) { return findCharacteristic(uuid.toLong()); }; }; class BluetoothGatt { public: BluetoothGatt(); virtual ~BluetoothGatt(); // No copying BluetoothGatt(const BluetoothGatt &) = delete; BluetoothGatt &operator=(const BluetoothGatt &) = delete; virtual BluetoothDevice &getDevice() const = 0; virtual bool isConnected() const = 0; virtual void writeValue(const BluetoothGattCharacteristicPtr &c, const ByteBuffer &bytes) = 0; virtual ByteBuffer readValue(const BluetoothGattCharacteristicPtr &c) = 0; virtual void discoverServices() = 0; virtual vector> getServices() const = 0; virtual o findService(Uuid uuid) = 0; o findService(ShortUuid uuid) { return findService(uuid.toLong()); } }; class BluetoothDevice { public: BluetoothDevice(); virtual ~BluetoothDevice(); virtual Mac const &getMac() = 0; virtual BluetoothAdapter &getAdapter() = 0; virtual shared_ptr connectGatt() = 0; }; class BluetoothAdapter { public: virtual Mac const &getMac() = 0; virtual void startScan() = 0; virtual void stopScan() = 0; virtual void runScan(std::function &device)>) = 0; virtual shared_ptr getDevice(Mac &mac) = 0; protected: BluetoothAdapter(); virtual ~BluetoothAdapter(); }; /** * Right this is only RAII support to properly call shutdown(). * * TODO: move getAdapter() here. Make this control all shutdowns. */ class BluetoothSystem { public: BluetoothSystem(); ~BluetoothSystem(); shared_ptr getAdapter(string name); private: map> adapters; }; /** * BLUETOOTH SPECIFICATION Version 4.0 [Vol 3] - Attribute Protocol (ATT) - 3.4.8 Attribute Opcode Summary * Table 3.37 */ enum AttPduType { ERROR = 0x01, EXCHANGE_MTU_REQ = 0x02, EXCHANGE_MTU_RES = 0x03, FIND_INFORMATION_REQ = 0x04, FIND_INFORMATION_RES = 0x05, FIND_BY_TYPE_VALUE_REQ = 0x06, FIND_BY_TYPE_VALUE_RES = 0x07, READ_BY_TYPE_REQ = 0x08, READ_BY_TYPE_RES = 0x09, READ_REQ = 0x0a, READ_RES = 0x0b, READ_BLOB_REQ = 0x0c, READ_BLOB_RES = 0x0d, READ_MULTIPLE_REQ = 0x0e, READ_MULTIPLE_RES = 0x0f, READ_BY_GROUP_TYPE_REQ = 0x10, READ_BY_GROUP_TYPE_RES = 0x11, WRITE_REQ = 0x12, WRITE_RES = 0x13, WRITE_CMD = 0x52, PREPARE_WRITE_REQ = 0x16, PREPARE_WRITE_RES = 0x17, EXECUTE_WRITE_REQ = 0x18, EXECUTE_WRITE_RES = 0x19, HANDLE_VALUE_NOTIFICATION = 0x1b, HANDLE_VALUE_INDICATION = 0x1d, HANDLE_VALUE_CONFIRMATION = 0x1e, SIGNED_WRITE_COMMAND = 0xd2, }; class AttributeData; class AttPdu { public: explicit AttPdu(ByteBuffer &bytes); AttPdu(ByteBuffer &bytes, AttPduType type); AttPduType getType(); static vector parseReadByGroupType(ByteBuffer &bytes); static vector parseReadByType(ByteBuffer &bytes); static uint16_t parseExchangeMtuReq(ByteBuffer &bytes); static void parseRead(ByteBuffer &bytes); static void parseWrite(ByteBuffer &bytes); static void makeReadByGroupType(ByteBuffer &bytes, uint16_t startHandle, uint16_t endHandle, ShortUuid uuid); static void makeReadByType(ByteBuffer &bytes, uint16_t startHandle, uint16_t endHandle, ShortUuid uuid); static void makeRead(ByteBuffer &bytes, uint16_t handle); static void makeWrite(ByteBuffer &req, uint16_t handle, const ByteBuffer &bytes); private: // static void checkType(ByteBuffer &bytes, AttPduType type); static vector parse(ByteBuffer &bytes, AttPduType type); ByteBuffer &bytes; }; class AttributeData { public: ~AttributeData(); static AttributeData fromByteBuffer(ByteBuffer &value); const uint16_t handle; ByteBuffer value; private: AttributeData(uint16_t handle, ByteBuffer value); }; Uuid makeUuid(const Uuid& base, uint8_t a, uint8_t b); } } #endif