aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTrygve Laugstøl <trygvis@inamo.no>2018-11-17 22:38:32 +0100
committerTrygve Laugstøl <trygvis@inamo.no>2018-11-17 22:38:32 +0100
commit52450ed3034b0ba058ea2c9f9baa2d5f78df6a94 (patch)
tree97fe35d5d7fe6fe354b2effe8c0ef4836787eb83
parenta167d6e68e634a70af442cd86e43fd9223b1431c (diff)
downloadble-toys-52450ed3034b0ba058ea2c9f9baa2d5f78df6a94.tar.gz
ble-toys-52450ed3034b0ba058ea2c9f9baa2d5f78df6a94.tar.bz2
ble-toys-52450ed3034b0ba058ea2c9f9baa2d5f78df6a94.tar.xz
ble-toys-52450ed3034b0ba058ea2c9f9baa2d5f78df6a94.zip
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.
-rw-r--r--apps/CMakeLists.txt3
-rw-r--r--apps/ble-bts.cpp14
-rw-r--r--apps/ble-inspect-device.cpp10
-rw-r--r--apps/ble-read-characteristic.cpp94
-rw-r--r--ble/LinuxBluetooth.cpp21
-rw-r--r--ble/misc.cpp69
-rw-r--r--include/ble/att.h4
-rw-r--r--include/ble/misc.h15
-rw-r--r--test/UuidTest.cpp29
9 files changed, 228 insertions, 31 deletions
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<string>()->required(), "The MAC of the device to inspect");
}
- void scan_callback(const shared_ptr<BluetoothDevice> &device) {
+ void with_device(const shared_ptr<BluetoothDevice> &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 <stdexcept>
+#include <iostream>
+#include <vector>
+#include <boost/uuid/uuid_io.hpp>
+#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<string>()->required(), "The MAC of the device to inspect");
+ options("service", po::value<string>()->required(), "The UUID of the service to read");
+ options("characteristic", po::value<string>()->required(), "The UUID of the characteristic to read");
+ }
+
+ int with_device(const shared_ptr<BluetoothDevice> &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>();
+ string service_str = execution.vm["service"].as<string>();
+ string characteristic_str = execution.vm["characteristic"].as<string>();
+
+ 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<DefaultBluetoothGattCharacteristic>(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 <ble/misc.h>
+#include <sstream>
#include <string>
+#include <ble/Bluetooth.h>
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> Uuid::fromString(const std::string &str) {
+ try {
+ if (str.size() == 4) {
+ unsigned long x = std::stoul(str, nullptr, 16);
+
+ if (x > std::numeric_limits<uint16_t>::max()) {
+ return {};
+ }
+
+ return {ShortUuid{static_cast<uint16_t>(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<uint8_t>::max()) {
+ return {};
+ }
+ bytes[byte] = static_cast<uint8_t>(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 <experimental/optional>
#include <variant>
#include <vector>
@@ -10,9 +9,6 @@
namespace trygvis {
namespace bluetooth {
-template<typename T>
-using o = std::experimental::optional<T>;
-
/**
* 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 <experimental/optional>
+
#include <cstring>
#include <cctype>
#include <stdexcept>
@@ -7,6 +9,9 @@
namespace trygvis {
namespace bluetooth {
+template<typename T>
+using o = std::experimental::optional<T>;
+
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<Uuid> 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<uint8_t>(value >> 8);
auto b3 = static_cast<uint8_t>(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 <boost/test/unit_test.hpp>
+
+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());
+}