From 4671571c93db7f083db21cfca526ac382cdfc7ad Mon Sep 17 00:00:00 2001 From: Trygve Laugstøl Date: Mon, 23 Feb 2015 23:04:38 +0100 Subject: Major improvement in the sm-get-value utility: o Adding command line option parsing through boost::program_option Current options: --device, --sensor --format selects between CSV and JSON output. --sleep controls single reads vs continuous reads. o Adding JSON output. o Adding support for Release builds without debugging info. Needs improvement. --- CMakeLists.txt | 8 +++ README.md | 10 ++++ apps/CMakeLists.txt | 16 +++++- apps/sm-get-value.cpp | 139 +++++++++++++++++++++++++++++++++++++++++++++----- ble/log.h | 11 ++++ 5 files changed, 170 insertions(+), 14 deletions(-) create mode 100644 README.md diff --git a/CMakeLists.txt b/CMakeLists.txt index 0cf3b20..0d48449 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,6 +4,14 @@ find_package(PkgConfig) # Use Clang by default: http://stackoverflow.com/a/7031553/245614 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14") +set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DDEBUG") +set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DDEBUG") + +IF(NOT CMAKE_BUILD_TYPE) + SET(CMAKE_BUILD_TYPE Debug CACHE STRING + "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel." + FORCE) +ENDIF(NOT CMAKE_BUILD_TYPE) # Boost set(Boost_USE_STATIC_LIBS OFF) diff --git a/README.md b/README.md new file mode 100644 index 0000000..ec9c88c --- /dev/null +++ b/README.md @@ -0,0 +1,10 @@ +# My BLE (Bluetooth Low Energy) Toys + +Building: + + mkdir build + cd build + cmake .. + make -j + +This will build a Debug release by default. Add `-DCMAKE_BUILD_TYPE=Release` to build a release build. diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt index e210716..9d68f79 100644 --- a/apps/CMakeLists.txt +++ b/apps/CMakeLists.txt @@ -2,7 +2,7 @@ set(APPS sm-get-value ble-inspect-device) set(shared_sources SoilMoisture.cpp) # Boost -find_package(Boost COMPONENTS system log thread REQUIRED) +find_package(Boost COMPONENTS system log thread program_options REQUIRED) # Bluez pkg_check_modules(BLUEZ bluez REQUIRED) @@ -10,8 +10,21 @@ pkg_check_modules(BLUEZ bluez REQUIRED) # pthreads find_package(Threads REQUIRED) +pkg_check_modules(PQXX libpqxx REQUIRED) + +include(ExternalProject) +ExternalProject_Add( + JSON + PREFIX json + GIT_REPOSITORY https://github.com/nlohmann/json.git + GIT_TAG ec42245951fceb7594bfb24746c7449986c3c2a4 + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "") + foreach(app ${APPS}) include_directories("${PROJECT_SOURCE_DIR}/include") + include_directories("${CMAKE_BINARY_DIR}/apps/json/src/JSON/src") add_executable(${app} ${app}.cpp ${shared_sources}) add_dependencies(${app} ble) @@ -19,5 +32,6 @@ foreach(app ${APPS}) target_link_libraries(${app} ble) target_link_libraries(${app} ${Boost_LIBRARIES}) target_link_libraries(${app} ${BLUEZ_LIBRARIES}) + target_link_libraries(${app} ${PQXX_LIBRARIES}) target_link_libraries(${app} ${CMAKE_THREAD_LIBS_INIT}) endforeach(app) diff --git a/apps/sm-get-value.cpp b/apps/sm-get-value.cpp index 46177f2..97858f4 100644 --- a/apps/sm-get-value.cpp +++ b/apps/sm-get-value.cpp @@ -1,34 +1,137 @@ #include +#include +#include #include #include +#include +#include #include "ble/Bluetooth.h" #include "SoilMoisture.h" +#include "json.hpp" +// I'm lazy using namespace std; +using namespace std::chrono; using namespace trygvis::bluetooth; using namespace trygvis::soil_moisture; +using json = nlohmann::json; -bool loop = true; +enum class Format { + PLAIN, + JSON +}; + +void validate(boost::any &v, const std::vector &values, Format *, int) { + using namespace boost::program_options; + + const std::string &s = validators::get_single_string(values); + + if (s == "json") { + v = boost::any(Format::JSON); + } else if (s == "plain") { + v = boost::any(Format::PLAIN); + } else { + throw validation_error(validation_error::invalid_option_value); + } +} + +namespace boost { + +template<> +string lexical_cast(const Format &arg) { + if (arg == Format::PLAIN) + return "plain"; + else if (arg == Format::JSON) + return "json"; + else + throw std::runtime_error("Unknown Format value: " + to_string(static_cast::type>(arg))); +} + +} + +bool loop; +Format format; +time_point targetTime; +unsigned int sleepTime; +vector sensors; void withConnection(BluetoothGatt &gatt) { SoilMoisture soilMoisture = SoilMoisture::create(gatt); - int sensorCount = soilMoisture.getSensorCount(); + const int sensorCount = soilMoisture.getSensorCount(); - while (loop) { - for (uint8_t i = 0; i < sensorCount; i++) { - uint16_t value = soilMoisture.getValue(i); + if (sensorCount == 0) { + throw runtime_error("Sensor count is 0"); + } - cout << "sensor=" << to_string(i) << ", value=" << (int) value << endl; + // If the user didn't specify any sensors, add all. + if (sensors.size() == 0) { + for (int i = 0; i < sensorCount; i++) { + sensors.push_back(i); } - - sleep(1); } + + auto device = gatt.getDevice().getMac(); + + targetTime = system_clock::now(); + + do { + for (auto sensor : sensors) { + if (sensor >= sensorCount) { + // Ignore invalid sensors + continue; + } + + auto epoch = system_clock::now().time_since_epoch(); + auto timestamp = duration_cast(epoch).count(); + uint16_t value = soilMoisture.getValue((uint8_t) sensor); + + if (format == Format::PLAIN) { + cout << "device=" << device.str() + << ", sensor=" << to_string(sensor) + << ", timestamp=" << to_string(timestamp) + << ", value=" << (int) value << endl; + } else if (format == Format::JSON) { + json j; + j["device"] = device.str(); + j["sensor"] = sensor; + j["timestamp"] = timestamp; + j["value"] = value; + cout << j << endl; + } + } + + targetTime = targetTime + seconds(sleepTime); + this_thread::sleep_until(targetTime); + } while (loop); } int main(int argc, char *argv[]) { - if (argc != 2) { - cerr << "usage: " << argv[0] << " [mac]" << endl; + namespace po = boost::program_options; + + po::options_description desc("Options"); + desc.add_options() + ("help", "produce help message") + ("device", po::value()->required(), "MAC of device to poll") + ("sensor", po::value>(&sensors)->multitoken(), "Sensor to poll, defaults to all") + ("sleep", po::value(&sleepTime)->default_value(0), + "How long to sleep in seconds between each poll. If not give, it will exit after first poll") + ("format", po::value(&format)->default_value(Format::PLAIN), "Output format"); + + po::variables_map vm; + auto parsed = po::parse_command_line(argc, argv, desc); + po::store(parsed, vm); + po::notify(vm); + + auto unrecognized = po::collect_unrecognized(parsed.options, po::include_positional); + + if (vm.count("help")) { + cerr << desc << "\n"; + return EXIT_FAILURE; + } + + if (unrecognized.size()) { + cerr << "Unrecognized option: " << unrecognized.at(0) << "\n"; return EXIT_FAILURE; } @@ -36,13 +139,23 @@ int main(int argc, char *argv[]) { BluetoothSystem bluetoothSystem; try { - Mac mac = Mac::parseMac(argv[1]); + if (!vm.count("device")) { + cerr << "Missing required option: device" << endl; + cerr << desc << "\n"; + return EXIT_FAILURE; + } + + auto MAC = vm["device"].as(); + cout << "MAC " << MAC << "wat" << endl; + Mac mac = Mac::parseMac(MAC); auto &adapter = getAdapter(0); auto &device = adapter.getDevice(mac); - while (loop) { + loop = sleepTime > 0; + + do { cout << "Connecting to device: " << device.getMac().str() << endl; auto &gatt = device.connectGatt(); @@ -52,7 +165,7 @@ int main(int argc, char *argv[]) { cout << "exception: " << e.what() << endl; } gatt.disconnect(); - } + } while (loop); return EXIT_SUCCESS; } catch (std::runtime_error ex) { diff --git a/ble/log.h b/ble/log.h index 1d62121..479c81c 100644 --- a/ble/log.h +++ b/ble/log.h @@ -3,6 +3,8 @@ #include #include + +#ifdef DEBUG #define D BOOST_LOG_TRIVIAL(debug) #define I BOOST_LOG_TRIVIAL(info) #define W BOOST_LOG_TRIVIAL(warning) @@ -10,5 +12,14 @@ #define DF BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << ": " #define IF BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": " #define WF BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ": " +#else +#define D if (false) BOOST_LOG_TRIVIAL(debug) +#define I if (false) BOOST_LOG_TRIVIAL(info) +#define W if (false) BOOST_LOG_TRIVIAL(warning) + +#define DF if (false) BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << ": " +#define IF if (false) BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": " +#define WF if (false) BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ": " +#endif #endif -- cgit v1.2.3