Skip to content

Commit

Permalink
Refactor and better logging
Browse files Browse the repository at this point in the history
  • Loading branch information
anders617 committed Oct 10, 2020
1 parent fb9d984 commit 729507d
Show file tree
Hide file tree
Showing 10 changed files with 264 additions and 115 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

# production
/build
*.out

# misc
.vscode
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

CXX := -c++
CXXFLAGS := -pedantic-errors -Wall -Wextra -Werror -Wno-psabi -lbluetooth -std=c++17 -laws-cpp-sdk-core -laws-cpp-sdk-kinesis -lpthread
CXXFLAGS := -pedantic-errors -Wall -Wextra -Werror -Wno-psabi -lbluetooth -std=c++2a -laws-cpp-sdk-core -laws-cpp-sdk-kinesis -lpthread
LDFLAGS := -L/usr/lib -lstdc++ -lm
BUILD := ./build
OBJ_DIR := $(BUILD)/objects
Expand Down
2 changes: 0 additions & 2 deletions src/BTScanner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

#include <iostream>

#include "Util.h"

const int ON = 1;
const int OFF = 0;

Expand Down
32 changes: 32 additions & 0 deletions src/BTScanner.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>

#include "Util.h"

class BTScanner {
public:
// Scan from device with addr "XX:XX:XX:XX:XX:XX"
Expand All @@ -27,6 +29,8 @@ class BTScanner {

bool scan(std::function<bool(evt_le_meta_event *)> handle_message,
std::chrono::seconds::rep scan_duration);
template<typename T>
bool scan(T &event_handler, std::chrono::seconds::rep scan_duration);
void stop_scanning();

private:
Expand All @@ -39,4 +43,32 @@ class BTScanner {
struct hci_filter original_filter;
};

template<typename T>
bool BTScanner::scan(T &event_handler, std::chrono::seconds::rep scan_duration) {
bool error = false;
scanning = true;
auto scanStartTime = std::chrono::steady_clock::now();
// Enable scanning
CHECK(hci_le_set_scan_enable(device_handle, 0x01, 1, 1000),
"Failed to enable scan");
while (scanning && !error) {
if (std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::steady_clock::now() - scanStartTime)
.count() > scan_duration) {
break;
}
auto [len, buf] = read_device();
if (scanning && len != -1) {
evt_le_meta_event *meta =
(evt_le_meta_event *)(buf.get() + (1 + HCI_EVENT_HDR_SIZE));
len -= (1 + HCI_EVENT_HDR_SIZE);
event_handler.parse(meta);
}
}
// Disable scanning
CHECK(hci_le_set_scan_enable(device_handle, 0x00, 1, 1000),
"Failed to disable scan");
return false;
}

#endif
22 changes: 22 additions & 0 deletions src/GoveeData.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#ifndef GOVEE_DATA_H
#define GOVEE_DATA_H

#include <string>

struct GoveeData {
long long int timestamp;
std::string name;
float temp, humidity;
int battery;
};

std::string to_json(const GoveeData &data) {
std::stringstream s;
s << "{\"timestamp\":" << data.timestamp << ",\"temp\":" << data.temp
<< ",\"humidity\":" << data.humidity << ",\"battery\":" << data.battery
<< ",\"name\":\"" << data.name << "\""
<< "}";
return s.str();
}

#endif
96 changes: 96 additions & 0 deletions src/GoveeEventHandler.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
#include "GoveeEventHandler.h"

#include "Util.h"

int GoveeEventParser::add_name_handler(NameEventHandler name_handler) {
int id = next_handler_id++;
name_handlers[id] = name_handler;
return next_handler_id;
}

int GoveeEventParser::add_data_handler(DataEventHandler data_handler) {
int id = next_handler_id++;
data_handlers[id] = data_handler;
return next_handler_id;
}

void GoveeEventParser::remove_name_handler(int id) {
name_handlers.erase(id);
}

void GoveeEventParser::remove_data_handler(int id) {
data_handlers.erase(id);
}

std::optional<std::tuple<float, float, int>> GoveeEventParser::read_data(const uint8_t *data, const size_t data_len) {
if (data_len != 9 && data_len != 10) return std::nullopt;
if ((data[1] == 0x88) && (data[2] == 0xEC)) {
float temp = -1, humidity = -1;
int battery = -1;
if (data_len == 9) {
// This data came from https://github.com/Thrilleratplay/GoveeWatcher
// 88ec00 03519e 6400 Temp: 21.7502°C Temp: 71.1504°F Humidity: 50.2%
// 1 2 3 4 5 6 7 8
int temp_humidity = int(data[4]) << 16 | int(data[5]) << 8 | int(data[6]);
int humidity_component = temp_humidity % 1000;
int temp_component = temp_humidity - humidity_component;
temp = ((float(temp_component) / 10000.0) * 9.0 / 5.0) + 32.0;
humidity = float(humidity_component % 1000) / 10.0;
battery = int(data[7]);
} else if (data_len == 10) {
Log("Data length 10");
// This data came from
// https://github.com/neilsheps/GoveeTemperatureAndHumidity 88ec00 dd07
// 9113 64 02 1 2 3 4 5 6 7 8 9
int iTemp = int(data[5]) << 8 | int(data[4]);
int iHumidity = int(data[7]) << 8 | int(data[6]);
temp = ((float(iTemp) / 100.0) * 9.0 / 5.0) + 32.0;
humidity = float(iHumidity) / 100.0;
battery = int(data[8]);
}
return std::make_tuple(temp, humidity, battery);
}
return std::nullopt;
}

void GoveeEventParser::parse(evt_le_meta_event *meta) {
if (meta->subevent != EVT_LE_ADVERTISING_REPORT)
return;
le_advertising_info *info = (le_advertising_info *)(meta->data + 1);
if (!info->length)
return;
int current_offset = 0;
bool data_error = false;
while (!data_error && current_offset < info->length) {
size_t data_len = info->data[current_offset];
if (data_len + 1 > info->length) {
Log("EIR data length is longer than EIR packet length. %d + 1 %d",
data_len, info->length);
data_error = true;
} else {
// Bluetooth Extended Inquiry Response
// I'm paying attention to only three types of EIR, Short Name,
// Complete Name, and Manufacturer Specific Data The names are how I
// learn which Bluetooth Addresses I'm going to listen to
char addr[19] = {0};
ba2str(&info->bdaddr, addr);
std::string_view strAddr(addr);
if ((info->data + current_offset + 1)[0] == EIR_NAME_SHORT ||
(info->data + current_offset + 1)[0] == EIR_NAME_COMPLETE) {
std::string name((char *)&((info->data + current_offset + 1)[1]), data_len-1);
for (const auto &[id, name_handler] : name_handlers) {
name_handler(strAddr, name);
}
} else if ((info->data + current_offset + 1)[0] == EIR_MANUFACTURE_SPECIFIC) {
if (auto new_data =
read_data((info->data + current_offset + 1), data_len)) {
const auto [temp, humidity, battery] = *new_data;
for (const auto &[id, data_handler] : data_handlers) {
data_handler(strAddr, temp, humidity, battery);
}
}
}
current_offset += data_len + 1;
}
}
}
44 changes: 44 additions & 0 deletions src/GoveeEventHandler.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#ifndef GOVEE_EVENT_HANDLER_H
#define GOVEE_EVENT_HANDLER_H

#include <functional>
#include <optional>
#include <unordered_map>

#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>

/**
* Class for parsing bluetooth evt_le_meta_event messages.
*
* Name handlers are called when a message advertising the name for a device is parsed.
*
* Data handlers are called when a message containing temp/humidity/battery for a device is parsed.
*/
class GoveeEventParser {
public:
// Associate `name` with the bluetooth `addr`
using NameEventHandler = std::function<void(std::string_view addr, std::string_view name)>;
// New data from the given `addr`
using DataEventHandler = std::function<void(std::string_view addr, float temp, float humidity, int battery)>;

GoveeEventParser() : next_handler_id(1) {}

// Returns the id of the handler which can be used when calling remove_name_handler
int add_name_handler(NameEventHandler name_handler);
// Returns the id of the handler which can be used when calling remove_name_handler
int add_data_handler(DataEventHandler data_handler);

void remove_name_handler(int id);
void remove_data_handler(int id);

void parse(evt_le_meta_event *meta);
private:
std::optional<std::tuple<float, float, int>> read_data(const uint8_t *data, const size_t data_len);

int next_handler_id;
std::unordered_map<int, NameEventHandler> name_handlers;
std::unordered_map<int, DataEventHandler> data_handlers;
};

#endif
3 changes: 2 additions & 1 deletion src/Util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@

void Log(const char *format, ...) {
printf("[%lld] ",
std::chrono::steady_clock::now().time_since_epoch().count());
std::chrono::system_clock::now().time_since_epoch().count());
va_list arglist;
va_start(arglist, format);
vprintf(format, arglist);
va_end(arglist);
printf("\n");
fflush(stdout);
}
5 changes: 5 additions & 0 deletions src/Util.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
#include <functional>
#include <cstring>

constexpr uint8_t EIR_FLAGS = 0X01;
constexpr uint8_t EIR_NAME_SHORT = 0x08;
constexpr uint8_t EIR_NAME_COMPLETE = 0x09;
constexpr uint8_t EIR_MANUFACTURE_SPECIFIC = 0xFF;

void Log(const char *format, ...);

struct Defer {
Expand Down
Loading

0 comments on commit 729507d

Please sign in to comment.