From 52450ed3034b0ba058ea2c9f9baa2d5f78df6a94 Mon Sep 17 00:00:00 2001 From: Trygve Laugstøl Date: Sat, 17 Nov 2018 22:38:32 +0100 Subject: apps/ble-bts: o Adding start of health termometer service tool. apps/ble-read-characteristic: o Sart of new tool. apps/ble-inspect-device o Make adapter configurable. other: o UUID fixes and tests. --- apps/CMakeLists.txt | 3 +- apps/ble-bts.cpp | 14 +++--- apps/ble-inspect-device.cpp | 10 +++-- apps/ble-read-characteristic.cpp | 94 ++++++++++++++++++++++++++++++++++++++++ ble/LinuxBluetooth.cpp | 21 +++++---- ble/misc.cpp | 69 ++++++++++++++++++++++++++--- include/ble/att.h | 4 -- include/ble/misc.h | 15 +++++-- test/UuidTest.cpp | 29 +++++++++++++ 9 files changed, 228 insertions(+), 31 deletions(-) create mode 100644 apps/ble-read-characteristic.cpp create mode 100644 test/UuidTest.cpp diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt index 6491ef1..3b1235c 100644 --- a/apps/CMakeLists.txt +++ b/apps/CMakeLists.txt @@ -98,9 +98,10 @@ function(add_app) install(TARGETS ${app} RUNTIME DESTINATION bin) endfunction() +add_app(NAME ble-bts) add_app(NAME ble-inspect-device) +add_app(NAME ble-read-characteristic) add_app(NAME ble-scan) -add_app(NAME ble-bts) add_app(NAME sample-add-timestamp) add_app(NAME sample-convert) add_app(NAME sample-select) diff --git a/apps/ble-bts.cpp b/apps/ble-bts.cpp index 82d6d43..3f79f2c 100644 --- a/apps/ble-bts.cpp +++ b/apps/ble-bts.cpp @@ -59,9 +59,11 @@ public: auto gatt = device->connectGatt(); cout << "Connected" << endl; + gatt->discoverServices(); + auto bts = gatt->findService(uuids::HealthTermometerService); if (!bts) { - cout << "Device does not implement Health thermometer service" << endl; + cout << "Device does not implement Health thermometer service (" << uuids::HealthTermometerService.str() << ")" << endl; return EXIT_FAILURE; } @@ -72,7 +74,7 @@ public: return EXIT_FAILURE; } - with_connection(gatt, tmc.value()); + with_characteristic(gatt, tmc.value()); return EXIT_SUCCESS; } catch (std::runtime_error &ex) { @@ -84,7 +86,7 @@ public: } } - void with_connection(BluetoothGattPtr gatt, BluetoothGattCharacteristicPtr tmc) { + void with_characteristic(const BluetoothGattPtr &gatt, const BluetoothGattCharacteristicPtr &tmc) { auto svc = tmc->getService(); cout << "Reading temp value" << endl; @@ -93,13 +95,13 @@ public: cout << "bytes " << buf.getSize() << endl; - for (int i = 0; i < buf.getSize(); i++) { + for (size_t i = 0; i < buf.getSize(); i++) { cout << "byte " << i << " = " << hex << buf.peek8(i) << endl; } } }; -} -} +} // namespace apps +} // namespace trygvis int main(int argc, const char *argv[]) { using namespace trygvis::apps; diff --git a/apps/ble-inspect-device.cpp b/apps/ble-inspect-device.cpp index 883faed..a06d86a 100644 --- a/apps/ble-inspect-device.cpp +++ b/apps/ble-inspect-device.cpp @@ -18,11 +18,15 @@ public: ~ble_inspect_device() override = default; + string adapter_name; + void add_options(po::options_description_easy_init &options) override { + auto adapter_value = po::value<>(&adapter_name)->default_value("0"); + options("adapter", adapter_value, "Which adapter to use."); options("device", po::value()->required(), "The MAC of the device to inspect"); } - void scan_callback(const shared_ptr &device) { + void with_device(const shared_ptr &device) { cout << "Inspecting device: " << device->getMac().str() << endl; auto gatt = device->connectGatt(); @@ -54,11 +58,11 @@ public: try { Mac mac = Mac::parseMac(mac_str); - auto adapter = bluetoothSystem.getAdapter("0"); + auto adapter = bluetoothSystem.getAdapter(adapter_name); auto device = adapter->getDevice(mac); - scan_callback(device); + with_device(device); return EXIT_SUCCESS; } catch (std::runtime_error &ex) { diff --git a/apps/ble-read-characteristic.cpp b/apps/ble-read-characteristic.cpp new file mode 100644 index 0000000..0aa5fdc --- /dev/null +++ b/apps/ble-read-characteristic.cpp @@ -0,0 +1,94 @@ +#include +#include +#include +#include +#include "ble/Bluetooth.h" +#include "apps.h" + +namespace trygvis { +namespace apps { + +using namespace std; +using namespace trygvis::bluetooth; +using namespace trygvis::apps; + +class ble_inspect_device : public app { +public: + ble_inspect_device() : app("ble-inspect-device") {} + + ~ble_inspect_device() override = default; + + string adapter_name; + + void add_options(po::options_description_easy_init &options) override { + auto adapter_value = po::value<>(&adapter_name)->default_value("0"); + options("adapter", adapter_value, "Which adapter to use."); + options("device", po::value()->required(), "The MAC of the device to inspect"); + options("service", po::value()->required(), "The UUID of the service to read"); + options("characteristic", po::value()->required(), "The UUID of the characteristic to read"); + } + + int with_device(const shared_ptr &device, const Uuid &service_uuid, const Uuid &characteristic_uuid) { + cout << "Inspecting device: " << device->getMac().str() << endl; + + auto gatt = device->connectGatt(); + cout << "Connected, discovering services" << endl; + + gatt->discoverServices(); + + auto serviceO = gatt->findService(service_uuid); + if (!serviceO) { + cout << "Device does not requested service (" << service_uuid.str() << ")" << endl; + return EXIT_FAILURE; + } + + auto service = serviceO->get(); + auto characteristicO = service->findCharacteristic(characteristic_uuid); + + if (!characteristicO) { + cout << "The device does not have requested characteristic (" << characteristic_uuid << ")" << endl; + return EXIT_FAILURE; + } + + auto characteristic = characteristicO.value(); + + gatt->readValue(characteristic); + + return EXIT_SUCCESS; + } + + int main(app_execution &execution) override { + string mac_str = execution.vm["device"].as(); + string service_str = execution.vm["service"].as(); + string characteristic_str = execution.vm["characteristic"].as(); + + auto service_uuid = Uuid::fromString(service_str); + auto characteristic_uuid = Uuid::fromString(characteristic_str); + + BluetoothSystem bluetoothSystem; + + try { + Mac mac = Mac::parseMac(mac_str); + + auto adapter = bluetoothSystem.getAdapter(adapter_name); + + auto device = adapter->getDevice(mac); + + return with_device(device, service_uuid.value(), characteristic_uuid.value()); + } catch (std::runtime_error &ex) { + cout << "std::runtime_error: " << ex.what() << endl; + return EXIT_FAILURE; + } catch (std::exception &ex) { + cout << "std::exception: " << ex.what() << endl; + return EXIT_FAILURE; + } + } +}; +} +} + +int main(int argc, const char *argv[]) { + using namespace trygvis::apps; + + return real_main(new ble_inspect_device(), argc, argv); +} diff --git a/ble/LinuxBluetooth.cpp b/ble/LinuxBluetooth.cpp index 541093b..e567d62 100644 --- a/ble/LinuxBluetooth.cpp +++ b/ble/LinuxBluetooth.cpp @@ -251,15 +251,18 @@ Uuid readUuid(BluetoothDevice *device, ByteBuffer &bytes) { if (bytesLeft == 2) { uint8_t bs[16] = BLUETOOTH_UUID_INITIALIZER; - bs[2] = bytes.read8(); bs[3] = bytes.read8(); + bs[2] = bytes.read8(); return Uuid(bs); } else if (bytesLeft == 16) { - return {bytes.read8(), bytes.read8(), bytes.read8(), bytes.read8(), - bytes.read8(), bytes.read8(), bytes.read8(), bytes.read8(), - bytes.read8(), bytes.read8(), bytes.read8(), bytes.read8(), - bytes.read8(), bytes.read8(), bytes.read8(), bytes.read8()}; + uint8_t bs[16] = { + bytes.get8(15), bytes.get8(14), bytes.get8(13), bytes.get8(12), bytes.get8(11), bytes.get8(10), bytes.get8(9), bytes.get8(8), + bytes.get8(7), bytes.get8(6), bytes.get8(5), bytes.get8(4), bytes.get8(3), bytes.get8(2), bytes.get8(1), bytes.get8(0), + }; + bytes.skip(16); + + return Uuid{bs}; } else { throw BluetoothException(device, "Unexpected bytes left: " + to_string(bytesLeft)); } @@ -368,10 +371,10 @@ void LinuxBluetoothGatt::discoverServices() { uint16_t valueHandle = c.value.read16le(); auto uuid = readUuid(&device, c.value); -// D << "characteristic: handle: " << setw(2) << setfill('0') << hex << (int) c.handle << -// ", properties: " << setw(2) << setfill('0') << hex << (int) properties << -// ", valueHandle: 0x" << setw(4) << setfill('0') << hex << (int) valueHandle << -// ", uuid: " << uuid; + LOG_DEBUG("characteristic: handle: " << setw(2) << setfill('0') << hex << (int) c.handle << + ", properties: " << setw(2) << setfill('0') << hex << (int) properties << + ", valueHandle: 0x" << setw(4) << setfill('0') << hex << (int) valueHandle << + ", uuid: " << uuid); s->addCharacteristic(std::move(make_shared(s, c.handle, uuid, properties, valueHandle))); } diff --git a/ble/misc.cpp b/ble/misc.cpp index b26b99e..a8df2cb 100644 --- a/ble/misc.cpp +++ b/ble/misc.cpp @@ -1,11 +1,13 @@ -#include "ble/misc.h" - #include +#include #include +#include namespace trygvis { namespace bluetooth { +using namespace std; + std::ostream &operator<<(std::ostream &s, Uuid const &uuid) { s << uuid.str(); return s; @@ -14,13 +16,62 @@ std::ostream &operator<<(std::ostream &s, Uuid const &uuid) { std::string Uuid::str() const { char str[37]; - std::sprintf(str, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", - value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], - value[8], value[9], value[10], value[11], value[12], value[13], value[14], value[15]); + std::snprintf(str, sizeof(str), "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", + value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], + value[8], value[9], value[10], value[11], value[12], value[13], value[14], value[15]); return {str}; } +o Uuid::fromString(const std::string &str) { + try { + if (str.size() == 4) { + unsigned long x = std::stoul(str, nullptr, 16); + + if (x > std::numeric_limits::max()) { + return {}; + } + + return {ShortUuid{static_cast(x)}.toLong()}; + } else if (str.size() == 36) { + uint8_t bytes[16]; + + unsigned i = 0; + for (unsigned byte = 0; byte < 16; byte++) { + switch (byte) { + case 4: + case 6: + case 8: + case 10: + i++; + break; + default: + break; + } + + auto tmp = str.substr(i, 2); + cout << "str=" << tmp << endl; + unsigned long x = std::stoul(tmp, nullptr, 16); + + if (x > std::numeric_limits::max()) { + return {}; + } + bytes[byte] = static_cast(x); + + i += 2; + } + + return {Uuid{bytes}}; + } else { + return {}; + } + } catch (std::invalid_argument &) { + return {}; + } catch (std::out_of_range &) { + return {}; + } +} + Uuid makeUuid(const Uuid &base, uint8_t a, uint8_t b) { uint8_t value[16]; memcpy(value, base.value, 16); @@ -29,5 +80,13 @@ Uuid makeUuid(const Uuid &base, uint8_t a, uint8_t b) { return Uuid{value}; } +std::string ShortUuid::str() const noexcept { + char str[sizeof("abcd")]; + + std::snprintf(str, sizeof(str), "%02x%02x", int(value >> 8), int(value & 0xff)); + + return {str}; +} + } // namespace bluetooth } // namespace trygvis diff --git a/include/ble/att.h b/include/ble/att.h index 3c7914f..1db560e 100644 --- a/include/ble/att.h +++ b/include/ble/att.h @@ -1,6 +1,5 @@ #pragma once -#include #include #include @@ -10,9 +9,6 @@ namespace trygvis { namespace bluetooth { -template -using o = std::experimental::optional; - /** * BLUETOOTH SPECIFICATION Version 4.0 [Vol 3] - Attribute Protocol (ATT) - 3.4.8 Attribute Opcode Summary * Table 3.37 diff --git a/include/ble/misc.h b/include/ble/misc.h index 089c3a1..4ba3309 100644 --- a/include/ble/misc.h +++ b/include/ble/misc.h @@ -1,5 +1,7 @@ #pragma once +#include + #include #include #include @@ -7,6 +9,9 @@ namespace trygvis { namespace bluetooth { +template +using o = std::experimental::optional; + class BluetoothAdapter; class BluetoothDevice; @@ -31,7 +36,7 @@ struct Uuid { 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} {} + 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; @@ -48,20 +53,24 @@ struct Uuid { 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}; } + + static o fromString(const std::string &str); }; struct ShortUuid { private: public: - explicit ShortUuid(uint16_t value) : value(value) {} + explicit ShortUuid(uint16_t value) noexcept : value(value) {} - Uuid toLong() + Uuid toLong() const noexcept { auto b2 = static_cast(value >> 8); auto b3 = static_cast(value & 0xff); return Uuid::fromShort(b2, b3); } + std::string str() const noexcept; + uint16_t value; }; diff --git a/test/UuidTest.cpp b/test/UuidTest.cpp new file mode 100644 index 0000000..b31c121 --- /dev/null +++ b/test/UuidTest.cpp @@ -0,0 +1,29 @@ +#include "ble/Bluetooth.h" + +#define BOOST_TEST_MODULE "UuidTest" + +#include + +using namespace std; +using namespace trygvis::bluetooth; + +BOOST_AUTO_TEST_CASE(uuid) { + BOOST_CHECK_EQUAL("0001", ShortUuid{0x01}.str()); + BOOST_CHECK_EQUAL("2a1c", uuids::TemperatureMeasurement.str()); + BOOST_CHECK_EQUAL("00001809-0000-1000-8000-00805f9b34fb", uuids::HealthTermometerService.toLong().str()); +} + +BOOST_AUTO_TEST_CASE(fromString) { + auto o = Uuid::fromString("abcd"); + BOOST_CHECK(!!o); + if (o) { + BOOST_CHECK_EQUAL(ShortUuid(0xabcd).toLong().str(), o.value().str()); + } + + o = Uuid::fromString("0xabcd"); + BOOST_CHECK(!o); + + o = Uuid::fromString("00001809-0000-1000-8000-00805f9b34fb"); + BOOST_CHECK(!!o); + BOOST_CHECK_EQUAL(o.value().str(), uuids::HealthTermometerService.toLong().str()); +} -- cgit v1.2.3