Skip to content

Commit

Permalink
Revise LBLE library (#93)
Browse files Browse the repository at this point in the history
## Bug Fixes

* #89: use `equal_range` when searching for elements in STL multimap, instead of using `find`. `find` is not guaranteed to return the first element in the equal range.

 * #90: Add a new set of interfaces to `LBLEClient` that allows user to identify a characteristic by using service index and characteristic index, instead of using UUID. Note that it is possible for a device to have multiple characteristics with the same UUID.

 * #90: Add a new example `EnumerateCharacteristic.ino` that uses the new indices-based interface of `LBLEClient` to list all the services and characteristics in the connected BLE device.

 * #90: Use `bt_gattc_read_charc` instead of `bt_gattc_read_using_charc_uuid` to read characteristics.

 * #91: when calling `bt_gattc_discover_charc`, we need to wait for multiple `BT_GATTC_DISCOVER_CHARC` event. We added new helper function `waitAndProcessEventMultiple` that supports such event waiting behavior.

 * Refactored `LBLEValueBuffer` to support re-interpreting its raw buffer content into String, float, int, and char types.
  • Loading branch information
pablosun authored Feb 26, 2018
1 parent 5f69fc7 commit c98f4a0
Show file tree
Hide file tree
Showing 4 changed files with 705 additions and 175 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -480,26 +480,31 @@ void LBLEEventDispatcher::removeObserver(bt_msg_type_t msg, LBLEEventObserver* p
// registered to the same key.
// So we loop over the matching elements
// and check the handler pointer.
auto i = m_table.find(msg);
while(i != m_table.end())
auto keyRange = m_table.equal_range(msg);
auto i = keyRange.first;
while(i != keyRange.second)
{
if(i->second == pObserver)
// advance iterator first before we remove some element.
auto toRemove = i++;

// if match, remove the element
if(toRemove->second == pObserver)
{
m_table.erase(i);
m_table.erase(toRemove);
return;
}
++i;
}
}

void LBLEEventDispatcher::dispatch(bt_msg_type_t msg, bt_status_t status, void *buff)
{
// pr_debug("dispatch: msg:0x%x", msg);
auto i = m_table.find(msg);
auto keyRange = m_table.equal_range(msg);
auto i = keyRange.first;

std::vector<EventTable::iterator> removeList;
// execute observer's callback and pop the element found
while(i != m_table.end())
while(i != keyRange.second)
{
if(i->first != msg)
{
Expand Down Expand Up @@ -565,4 +570,73 @@ LBLEValueBuffer::LBLEValueBuffer(const String& strValue)
{
resize(strValue.length() + 1);
strValue.getBytes(&(*this)[0], size());
}

// interprets buffer content as null-terminated character string.
// Empty string is returned when there are errors.
String LBLEValueBuffer::asString() const
{
if(!this->size())
{
return String();
}

// Make sure we have terminating NULL before passing to String().
if(this->back() == '\0')
{
return String((const char*)&this->front());
}
else
{
// since String() does not allow us to initialize
// with buffer + length, we use a temporary buffer object instead.
std::vector<char> tempBuffer;
tempBuffer.resize(this->size() + 1, 0);
if(tempBuffer.size() >= this->size())
{
memcpy(&tempBuffer.front(), &this->front(), this->size());
return String((const char*)&tempBuffer.front());
}
}

return String();
}

int LBLEValueBuffer::asInt() const
{
int ret = 0;
if(this->size() < sizeof(ret))
{
return 0;
}

ret = *((const int*)&this->at(0));

return ret;
}

char LBLEValueBuffer::asChar() const
{
char ret = '\0';
if(this->size() < sizeof(ret))
{
return 0;
}

ret = *((const char*)&this->front());

return ret;
}

float LBLEValueBuffer::asFloat() const
{
float ret = 0.f;
if(this->size() < sizeof(ret))
{
return 0;
}

ret = *((const float*)&this->front());

return ret;
}
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ class LBLEAddress : public Printable
/// values when reading or writing GATT attributes.
class LBLEValueBuffer : public std::vector<uint8_t>
{
// constructors and initialization
public:
/// Default constructor creates an empty buffer.
LBLEValueBuffer();
Expand All @@ -204,6 +205,25 @@ class LBLEValueBuffer : public std::vector<uint8_t>
LBLEValueBuffer(const String& strValue);

template<typename T>void shallowInit(T value);

// type conversion helpers
public:
/// interprets buffer content as int32_t
/// 0 is returned when there are errors.
int asInt() const;

/// interprets buffer content as null-terminated character string.
/// Empty string is returned when there are errors.
String asString() const;

/// interprets buffer content as char
/// 0 is returned when there are errors.
char asChar() const;

/// interprets buffer content as float
/// 0.f is returned when there are errors.
float asFloat() const;

};

template<typename T>void LBLEValueBuffer::shallowInit(T value)
Expand Down Expand Up @@ -386,4 +406,78 @@ template<typename A, typename F> bool waitAndProcessEvent(const A& action, bt_ms

return h.done();
}


// Modified version of EventBlockerMultiple.
// This EventBlockerMultiple relies on the return value of `handler`
// to determine `done()`.
template<typename F> class EventBlockerMultiple : public LBLEEventObserver
{
public:
EventBlockerMultiple(bt_msg_type_t e, const F& handler):
m_handler(handler),
m_event(e),
m_eventArrived(false),
m_allEventProcessed(false)
{

}

bool done() const
{
return m_eventArrived && m_allEventProcessed;
}

virtual bool isOnce()
{
// the client must remove EventBlocker
// manually from the listeners.
return false;
};

virtual void onEvent(bt_msg_type_t msg, bt_status_t status, void* buff)
{
if(m_event == msg)
{
m_eventArrived = true;

// Note that some action, may result in multiple events.
// For example, bt_gattc_discover_charc may invoke
// multiple BT_GATTC_DISCOVER_CHARC events.
//
// We need to rely on the message handlers
// to tell us if all events have been processed or not.
//
// all event processed = `m_handler` returns `true`.
// event not processed or waiting for more event = `m_handler` returns `false`.
m_allEventProcessed = m_handler(msg, status, buff);
}
}

private:
const F& m_handler;
const bt_msg_type_t m_event;
bool m_eventArrived;
bool m_allEventProcessed;
};

// Modified version of waitAndProcessEvent.
// it uses EventBlockerMultiple instead of EventBlocker.
template<typename A, typename F> bool waitAndProcessEventMultiple(const A& action, bt_msg_type_t msg, const F& handler)
{
EventBlockerMultiple<F> h(msg, handler);
LBLE.registerForEvent(msg, &h);

action();

uint32_t start = millis();
while(!h.done() && (millis() - start) < 10 * 1000)
{
delay(50);
}

LBLE.unregisterForEvent(msg, &h);

return h.done();
}
#endif
Loading

0 comments on commit c98f4a0

Please sign in to comment.