aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTrygve Laugstøl <trygvis@inamo.no>2015-06-21 12:27:48 +0200
committerTrygve Laugstøl <trygvis@inamo.no>2015-06-21 12:27:48 +0200
commitf4afc04ee7a085a89ad84ba89344bb50ca9e6e04 (patch)
tree404058c5ffd4773dfe13ddcd7a1cafc39864ae21
parent7e2fb834d8ba2eb3fd6453f135d3e8ef30c852c6 (diff)
downloadble-toys-f4afc04ee7a085a89ad84ba89344bb50ca9e6e04.tar.gz
ble-toys-f4afc04ee7a085a89ad84ba89344bb50ca9e6e04.tar.bz2
ble-toys-f4afc04ee7a085a89ad84ba89344bb50ca9e6e04.tar.xz
ble-toys-f4afc04ee7a085a89ad84ba89344bb50ca9e6e04.zip
o Trying to make the Bluetooth API more C++ idiomatic, a GATT connection has the same lifecycle as a BluetoothGatt.
sm-get-value: o Better error handling.
-rw-r--r--apps/SoilMoisture.cpp14
-rw-r--r--apps/SoilMoisture.h6
-rw-r--r--apps/ble-inspect-device.h8
-rw-r--r--apps/generate.cpp8
-rw-r--r--apps/launcher.cpp12
-rw-r--r--apps/sm-get-value.h180
-rw-r--r--ble/CMakeLists.txt2
-rw-r--r--ble/LinuxBluetooth.cpp62
-rw-r--r--include/ble/Bluetooth.h6
-rw-r--r--sensor/include/trygvis/sensor.h4
10 files changed, 157 insertions, 145 deletions
diff --git a/apps/SoilMoisture.cpp b/apps/SoilMoisture.cpp
index 89c2fe5..d1d8b7c 100644
--- a/apps/SoilMoisture.cpp
+++ b/apps/SoilMoisture.cpp
@@ -62,10 +62,10 @@ ByteBuffer createSetUpdateInterval(uint8_t sensor, uint8_t interval_in_seconds)
write8(interval_in_seconds);
}
-SoilMoisture SoilMoisture::create(BluetoothGatt &gatt) {
- gatt.discoverServices();
+SoilMoisture SoilMoisture::create(shared_ptr<BluetoothGatt> gatt) {
+ gatt->discoverServices();
- auto service = gatt.findService(soil_moisture_service);
+ auto service = gatt->findService(soil_moisture_service);
if (!service) {
throw runtime_error("The device is missing the soil moisture service");
@@ -80,8 +80,8 @@ SoilMoisture SoilMoisture::create(BluetoothGatt &gatt) {
return SoilMoisture(gatt, *service, *c);
}
-SoilMoisture::SoilMoisture(BluetoothGatt &gatt, BluetoothGattService &s, BluetoothGattCharacteristic &c) :
- gatt(gatt), s(s), c(c) {
+SoilMoisture::SoilMoisture(shared_ptr<BluetoothGatt> gatt, BluetoothGattService &s, BluetoothGattCharacteristic &c) :
+ gatt(std::move(gatt)), s(s), c(c) {
}
ByteBuffer SoilMoisture::writeAndRead(ByteBuffer &requestBytes) {
@@ -89,9 +89,9 @@ ByteBuffer SoilMoisture::writeAndRead(ByteBuffer &requestBytes) {
uint8_t expectedCode = requestBytes.get8(0);
- gatt.writeValue(c, requestBytes);
+ gatt->writeValue(c, requestBytes);
- auto responseBytes = gatt.readValue(c);
+ auto responseBytes = gatt->readValue(c);
if (responseBytes.getSize() < 1) {
throw runtime_error("Unexpected number of bytes read: " + to_string(requestBytes.getSize()));
diff --git a/apps/SoilMoisture.h b/apps/SoilMoisture.h
index 2016c9a..938da07 100644
--- a/apps/SoilMoisture.h
+++ b/apps/SoilMoisture.h
@@ -24,18 +24,18 @@ extern const boost::uuids::uuid soil_moisture_characteristic;
class SoilMoisture {
public:
- static SoilMoisture create(BluetoothGatt &gatt);
+ static SoilMoisture create(shared_ptr<BluetoothGatt> gatt);
uint8_t getSensorCount();
uint16_t getValue(uint8_t sensor);
private:
- SoilMoisture(BluetoothGatt &gatt, BluetoothGattService &s, BluetoothGattCharacteristic &c);
+ SoilMoisture(shared_ptr<BluetoothGatt> gatt, BluetoothGattService &s, BluetoothGattCharacteristic &c);
ByteBuffer writeAndRead(ByteBuffer &requestBytes);
- BluetoothGatt &gatt;
+ shared_ptr<BluetoothGatt> gatt;
BluetoothGattService &s;
BluetoothGattCharacteristic &c;
};
diff --git a/apps/ble-inspect-device.h b/apps/ble-inspect-device.h
index bf79b2c..7d93f95 100644
--- a/apps/ble-inspect-device.h
+++ b/apps/ble-inspect-device.h
@@ -28,11 +28,11 @@ public:
void scan_callback(BluetoothDevice &device) {
cout << "Inspecting device: " << device.getMac().str() << endl;
- auto &gatt = device.connectGatt();
+ auto gatt = device.connectGatt();
- gatt.discoverServices();
+ gatt->discoverServices();
- vector < BluetoothGattService * > services = gatt.getServices();
+ vector < BluetoothGattService * > services = gatt->getServices();
cout << "Device has " << services.size() << " services" << endl;
for (auto &s: services) {
@@ -46,8 +46,6 @@ public:
endl;
}
}
-
- gatt.disconnect();
}
int main(app_execution &execution) override {
diff --git a/apps/generate.cpp b/apps/generate.cpp
index ca6f8de..9dcc271 100644
--- a/apps/generate.cpp
+++ b/apps/generate.cpp
@@ -46,7 +46,10 @@ int main(int argc, char *argv[]) {
bool first = true;
- out << "template<typename App>" << endl
+ out << "namespace trygvis {" << endl
+ << "namespace apps {" << endl
+ << endl
+ << "template<typename App>" << endl
<< "int launch_app(int argc, const char *argv[]);" << endl
<< endl;
@@ -67,6 +70,9 @@ int main(int argc, char *argv[]) {
out << " } else {" << endl
<< " return EXIT_FAILURE;" << endl
<< " }" << endl
+ << "}" << endl
+ << endl
+ << "}" << endl
<< "}" << endl;
return EXIT_SUCCESS;
diff --git a/apps/launcher.cpp b/apps/launcher.cpp
index 4c1f687..2a1e039 100644
--- a/apps/launcher.cpp
+++ b/apps/launcher.cpp
@@ -2,9 +2,10 @@
#include "apps-list.gen.h"
#include <log4cplus/consoleappender.h>
#include <log4cplus/configurator.h>
-#include <boost/algorithm/string/predicate.hpp>
-using namespace trygvis::apps;
+namespace trygvis {
+namespace apps {
+
using namespace std;
const po::options_description logging_options() {
@@ -16,7 +17,7 @@ const po::options_description logging_options() {
void setup_logging(string app_name) {
Appender *console = new ConsoleAppender(true, true);
- string pattern = string("%-5p ") /*"%6r "*/ + app_name + "/%-20c %m%n";
+ string pattern = string("%-5p ") /*"%6r "*/ + app_name + "/%-20c %m%n"; // add %M (function name)
PatternLayout *layout = new PatternLayout(LOG4CPLUS_TEXT(pattern));
console->setLayout(auto_ptr<Layout>(layout));
@@ -74,6 +75,11 @@ int launch_app(int argc, const char *argv[]) {
return EXIT_FAILURE;
}
}
+}
+}
+
+using namespace std;
+using namespace trygvis::apps;
int main(int argc, const char *argv[]) {
if (argc == 0) {
diff --git a/apps/sm-get-value.h b/apps/sm-get-value.h
index 11e797f..4083f8e 100644
--- a/apps/sm-get-value.h
+++ b/apps/sm-get-value.h
@@ -6,9 +6,11 @@
#include "ble/Bluetooth.h"
#include "SoilMoisture.h"
#include "trygvis/sensor.h"
-#include "json.hpp"
#include "apps.h"
+namespace trygvis {
+namespace apps {
+
// I'm lazy
using namespace std;
using namespace std::chrono;
@@ -18,92 +20,6 @@ using namespace trygvis::sensor;
using namespace trygvis::sensor::io;
using json = nlohmann::json;
-bool loop;
-sample_format_type format;
-time_point<system_clock> targetTime;
-unsigned int sleepTime;
-vector<unsigned int> sensors;
-
-void withConnection(sample_format_type format, BluetoothGatt &gatt) {
- SoilMoisture soilMoisture = SoilMoisture::create(gatt);
-
- const int sensorCount = soilMoisture.getSensorCount();
-
- if (sensorCount == 0) {
- throw runtime_error("Sensor count is 0");
- }
-
- // If the user didn't specify any sensors, add all.
- if (sensors.size() == 0) {
- for (unsigned int i = 0; i < sensorCount; i++) {
- sensors.push_back(i);
- }
- }
-
- auto mac = gatt.getDevice().getMac();
-
- targetTime = system_clock::now();
-
- do {
- KeyDictionary dict;
- auto hostname_key = dict.indexOf("hostname");
- auto device_key = dict.indexOf("device");
- auto sensor_key = dict.indexOf("sensor");
- auto timestamp_key = dict.indexOf("timestamp");
- auto value_key = dict.indexOf("value");
-
- auto unique_output_stream = open_sample_output_stream(shared_ptr<ostream>(&cout, noop_deleter), dict, format);
- shared_ptr<SampleOutputStream> output_stream{std::move(unique_output_stream)};
-
- for (auto sensor : sensors) {
- if (sensor >= sensorCount) {
- // Ignore invalid sensors
- continue;
- }
-
- auto epoch = system_clock::now().time_since_epoch();
- auto timestamp = duration_cast<seconds>(epoch).count();
- uint16_t value = soilMoisture.getValue((uint8_t) sensor);
-
- SampleRecord sample(dict);
-
- sample.set(hostname_key, get_hostname());
- sample.set(device_key, mac.str());
- sample.set(sensor_key, std::to_string(sensor));
- sample.set(timestamp_key, std::to_string(timestamp));
- sample.set(value_key, std::to_string(value));
-
- output_stream->write(sample);
-
-// if (format == sample_format_type::KEY_VALUE) {
-// cout << "device=" << device.str()
-// << ", sensor=" << to_string(sensor)
-// << ", timestamp=" << to_string(timestamp)
-// << ", value=" << (int) value << endl;
-// } else if (format == sample_format_type::JSON) {
-// json j;
-// j["device"] = device.str();
-// j["sensor"] = sensor;
-// j["timestamp"] = timestamp;
-// j["value"] = value;
-// cout << j << endl;
-// } else if (format == sample_format_type::SQL) {
-// cout << "INSERT INTO soil_moisture_sample(device, sensor, timestamp, value) VALUES("
-// << "'" << device.str() << "', "
-// << sensor << ", "
-// << timestamp << ", "
-// << value << ";" << endl;
-// }
- }
-
- targetTime = targetTime + seconds(sleepTime);
- this_thread::sleep_until(targetTime);
- } while (loop);
-}
-
-namespace trygvis {
-namespace apps {
-
class sm_get_value : public app {
public:
@@ -112,15 +28,21 @@ public:
~sm_get_value() = default;
+ bool loop;
+ sample_format_type format;
+ unsigned int sleepTime;
+ vector<unsigned int> sensors;
+
void add_options(po::options_description_easy_init &options) override {
auto default_sleep = po::value<>(&sleepTime)->default_value(0);
+ auto default_format = po::value<sample_format_type>(&format)->default_value(sample_format_type::KEY_VALUE);
options
("device", po::value<string>()->required(), "MAC of device to poll")
("sensor", po::value<vector<unsigned int>>(&sensors)->multitoken(), "Sensor to poll, defaults to all")
- ("sleep", default_sleep, "How long to sleep in seconds between each poll. If not given, it will exit after first poll")
- ("format", po::value<sample_format_type>(&format)->default_value(sample_format_type::KEY_VALUE),
- "Output format");
+ ("sleep", default_sleep,
+ "How long to sleep in seconds between each poll. If not given, it will exit after first poll")
+ ("format", default_format, "Output format");
}
int main(app_execution &execution) override {
@@ -148,15 +70,21 @@ public:
loop = sleepTime > 0;
do {
- LOG4CPLUS_INFO(execution.logger, "Connecting to device: " + device.getMac().str());
-
- auto &gatt = device.connectGatt();
try {
+ LOG4CPLUS_INFO(execution.logger, "Connecting to device: " + device.getMac().str());
+ auto gatt = device.connectGatt();
+
withConnection(format, gatt);
} catch (runtime_error &e) {
- cout << "exception: " << e.what() << endl;
+ LOG4CPLUS_ERROR(execution.logger, "Exception: " << e.what());
+ }
+
+ if (loop) {
+ LOG4CPLUS_DEBUG(execution.logger, "Sleeping for " + std::to_string(sleepTime) + " seconds after failure.");
+
+ auto targetTime = system_clock::now() + seconds(sleepTime);
+ this_thread::sleep_until(targetTime);
}
- gatt.disconnect();
} while (loop);
return EXIT_SUCCESS;
@@ -168,6 +96,68 @@ public:
return EXIT_FAILURE;
}
}
+
+ void withConnection(sample_format_type format, shared_ptr<BluetoothGatt> gatt) {
+ SoilMoisture soilMoisture = SoilMoisture::create(gatt);
+
+ const int sensorCount = soilMoisture.getSensorCount();
+
+ if (sensorCount == 0) {
+ throw runtime_error("Sensor count is 0");
+ }
+
+ // If the user didn't specify any sensors, add all.
+ if (sensors.size() == 0) {
+ for (unsigned int i = 0; i < sensorCount; i++) {
+ sensors.push_back(i);
+ }
+ }
+
+ auto mac = gatt->getDevice().getMac();
+
+ KeyDictionary dict;
+ auto hostname_key = dict.indexOf("hostname");
+ auto device_key = dict.indexOf("device");
+ auto sensor_key = dict.indexOf("sensor");
+ auto timestamp_key = dict.indexOf("timestamp");
+ auto value_key = dict.indexOf("value");
+
+ time_point<system_clock> targetTime;
+ targetTime = system_clock::now();
+
+ do {
+ auto unique_output_stream = open_sample_output_stream(shared_ptr<ostream>(&cout, noop_deleter), dict,
+ format);
+ shared_ptr<SampleOutputStream> output_stream{std::move(unique_output_stream)};
+
+ auto epoch = system_clock::now().time_since_epoch();
+ auto timestamp = duration_cast<seconds>(epoch).count();
+
+ for (auto sensor : sensors) {
+ if (sensor >= sensorCount) {
+ // Ignore invalid sensors
+ continue;
+ }
+
+ uint16_t value = soilMoisture.getValue((uint8_t) sensor);
+
+ auto sample = SampleRecord(dict)
+ .set(hostname_key, get_hostname())
+ .set(device_key, mac.str())
+ .set(sensor_key, std::to_string(sensor))
+ .set(timestamp_key, std::to_string(timestamp))
+ .set(value_key, std::to_string(value));
+
+ output_stream->write(sample);
+ }
+
+ do {
+ targetTime = targetTime + seconds(sleepTime);
+ } while (targetTime < system_clock::now());
+
+ this_thread::sleep_until(targetTime);
+ } while (loop);
+ }
};
}
diff --git a/ble/CMakeLists.txt b/ble/CMakeLists.txt
index 405f125..543e85d 100644
--- a/ble/CMakeLists.txt
+++ b/ble/CMakeLists.txt
@@ -1,4 +1,4 @@
-file(GLOB SOURCE_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cpp)
+file(GLOB SOURCE_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cpp *.h)
add_library(ble ${SOURCE_FILES})
include_directories("${PROJECT_SOURCE_DIR}/include")
diff --git a/ble/LinuxBluetooth.cpp b/ble/LinuxBluetooth.cpp
index ae8f984..6e3c9da 100644
--- a/ble/LinuxBluetooth.cpp
+++ b/ble/LinuxBluetooth.cpp
@@ -5,7 +5,6 @@
#include <bluetooth/hci_lib.h>
#include <bluetooth/l2cap.h>
#include <map>
-#include <sstream>
#include <iomanip>
// Got to love magic constants. Taken from bluez.git/tools/btgatt-client.c
@@ -56,10 +55,10 @@ public:
~LinuxBluetoothDevice();
- BluetoothGatt &connectGatt() override;
+ shared_ptr<BluetoothGatt> connectGatt() override;
private:
- LinuxBluetoothGatt *gatt;
+ weak_ptr<LinuxBluetoothGatt> gatt;
int l2cap;
};
@@ -69,9 +68,7 @@ public:
~LinuxBluetoothGatt();
- void connect() override;
-
- void disconnect() override;
+ bool isConnected() const override;
void discoverServices() override;
@@ -80,6 +77,10 @@ public:
ByteBuffer readValue(const BluetoothGattCharacteristic &c) override;
private:
+ void connect();
+
+ void disconnect();
+
vector<AttributeData> discoverServices(uint16_t startHandle);
vector<AttributeData> discoverCharacteristics(uint16_t startHandle, uint16_t endHandle);
@@ -87,6 +88,8 @@ private:
ByteBuffer writeAndRead(ByteBuffer &out, shared_ptr<uint8_t> buffer, size_t size);
int l2cap;
+
+ bool connected;
};
// Utilities
@@ -108,23 +111,24 @@ Mac parseMac(bdaddr_t &a) {
// -----------------------------------------------------------------------
LinuxBluetoothDevice::LinuxBluetoothDevice(LinuxBluetoothAdapter &adapter, Mac &mac) :
- DefaultBluetoothDevice(adapter, mac), gatt(nullptr) {
+ DefaultBluetoothDevice(adapter, mac) {
}
LinuxBluetoothDevice::~LinuxBluetoothDevice() {
- if (gatt) {
- delete gatt;
- }
+// if (gatt) {
+// delete gatt;
+// }
};
-BluetoothGatt &LinuxBluetoothDevice::connectGatt() {
- if (!gatt) {
- gatt = new LinuxBluetoothGatt(*this, l2cap);
+shared_ptr<BluetoothGatt> LinuxBluetoothDevice::connectGatt() {
+ if (auto p = gatt.lock()) {
+ return p;
}
+ auto ref = make_shared<LinuxBluetoothGatt>(*this, l2cap);
- gatt->connect();
+ gatt = ref;
- return *gatt;
+ return ref;
}
// -----------------------------------------------------------------------
@@ -133,9 +137,15 @@ BluetoothGatt &LinuxBluetoothDevice::connectGatt() {
LinuxBluetoothGatt::LinuxBluetoothGatt(LinuxBluetoothDevice &device, int l2cap) :
DefaultBluetoothGatt(device), l2cap(l2cap) {
+ connect();
}
LinuxBluetoothGatt::~LinuxBluetoothGatt() {
+ disconnect();
+}
+
+bool LinuxBluetoothGatt::isConnected() const {
+ return connected;
}
void LinuxBluetoothGatt::connect() {
@@ -145,7 +155,7 @@ void LinuxBluetoothGatt::connect() {
l2cap = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
if (l2cap < 0) {
- throw BluetoothException(&device, "LinuxBluetoothGatt::connect(): socket(): " + errnoAsString());
+ throw BluetoothException(&device, "connect(): socket(): " + errnoAsString());
}
memset(&addr, 0, sizeof(addr));
@@ -156,7 +166,7 @@ void LinuxBluetoothGatt::connect() {
if (bind(l2cap, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
close(l2cap);
- throw BluetoothException(&device, "LinuxBluetoothGatt::connect(): bind(): " + errnoAsString());
+ throw BluetoothException(&device, "bind(): " + errnoAsString());
}
struct bt_security btsec;
@@ -164,7 +174,7 @@ void LinuxBluetoothGatt::connect() {
btsec.level = BT_SECURITY_LOW;
if (setsockopt(l2cap, SOL_BLUETOOTH, BT_SECURITY, &btsec, sizeof(btsec)) != 0) {
close(l2cap);
- throw BluetoothException(&device, "LinuxBluetoothGatt::connect(): setsockopt(): " + errnoAsString());
+ throw BluetoothException(&device, "setsockopt(): " + errnoAsString());
}
memset(&addr, 0, sizeof(addr));
@@ -181,11 +191,14 @@ void LinuxBluetoothGatt::connect() {
if (::connect(l2cap, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
close(l2cap);
- throw BluetoothException(&device, "LinuxBluetoothGatt::connect(): connect(): " + errnoAsString());
+ throw BluetoothException(&device, "connect(): " + errnoAsString());
}
}
void LinuxBluetoothGatt::disconnect() {
+ if (!connected) {
+ return;
+ }
LOG_DEBUG("mac = " << device.getMac().str());
close(l2cap);
}
@@ -271,7 +284,7 @@ void LinuxBluetoothGatt::discoverServices() {
break;
}
- uint16_t endGroupHandle;
+ uint16_t endGroupHandle = startHandle;
for (auto &data: values) {
endGroupHandle = data.value.read16le();
@@ -285,7 +298,7 @@ void LinuxBluetoothGatt::discoverServices() {
addService(new DefaultBluetoothGattService(device, u, data.handle, endGroupHandle));
}
- auto last = values.back();
+// auto last = values.back();
startHandle = endGroupHandle;
} while (startHandle != 0xffff);
@@ -303,7 +316,6 @@ void LinuxBluetoothGatt::discoverServices() {
}
startHandle = 0x0001;
- vector<AttributeData> chars;
auto s = *it;
@@ -343,10 +355,10 @@ void LinuxBluetoothGatt::discoverServices() {
}
ByteBuffer LinuxBluetoothGatt::writeAndRead(ByteBuffer &out, shared_ptr<uint8_t> buffer, size_t size) {
- LOG_DEBUG("pdu size=" << out.getCursor());
+// LOG_DEBUG("pdu size=" << out.getCursor());
ssize_t written = write(l2cap, buffer.get(), out.getCursor());
- LOG_DEBUG("written=" << written);
+// LOG_DEBUG("written=" << written);
ssize_t r = read(l2cap, buffer.get(), size);
@@ -356,7 +368,7 @@ ByteBuffer LinuxBluetoothGatt::writeAndRead(ByteBuffer &out, shared_ptr<uint8_t>
auto in = ByteBuffer(buffer, (size_t) r, (size_t) r);
- LOG_DEBUG("read: " << r << " bytes: " << in.toString());
+// LOG_DEBUG("read: " << r << " bytes: " << in.toString());
return in;
}
diff --git a/include/ble/Bluetooth.h b/include/ble/Bluetooth.h
index 1a1394e..80729af 100644
--- a/include/ble/Bluetooth.h
+++ b/include/ble/Bluetooth.h
@@ -124,9 +124,7 @@ public:
virtual BluetoothDevice &getDevice() const = 0;
- virtual void connect() = 0;
-
- virtual void disconnect() = 0;
+ virtual bool isConnected() const = 0;
virtual void writeValue(const BluetoothGattCharacteristic &c, const ByteBuffer &bytes) = 0;
@@ -149,7 +147,7 @@ public:
virtual BluetoothAdapter &getAdapter() = 0;
- virtual BluetoothGatt &connectGatt() = 0;
+ virtual shared_ptr<BluetoothGatt> connectGatt() = 0;
};
class BluetoothAdapter {
diff --git a/sensor/include/trygvis/sensor.h b/sensor/include/trygvis/sensor.h
index bcbd43b..48fb509 100644
--- a/sensor/include/trygvis/sensor.h
+++ b/sensor/include/trygvis/sensor.h
@@ -184,10 +184,12 @@ public:
return values.at(index);
}
- void set(const SampleKey *key, const std::string &value) {
+ SampleRecord& set(const SampleKey *key, const std::string &value) {
values.resize(max(values.size(), key->index + 1));
values.at(key->index).reset(value);
+
+ return *this;
}
template<class A>