Skip to content

Commit c98f4a0

Browse files
authored
Revise LBLE library (#93)
## 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.
1 parent 5f69fc7 commit c98f4a0

File tree

4 files changed

+705
-175
lines changed

4 files changed

+705
-175
lines changed

middleware/third_party/arduino/hardware/arduino/mt7697/libraries/LBLE/src/LBLE.cpp

Lines changed: 81 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -480,26 +480,31 @@ void LBLEEventDispatcher::removeObserver(bt_msg_type_t msg, LBLEEventObserver* p
480480
// registered to the same key.
481481
// So we loop over the matching elements
482482
// and check the handler pointer.
483-
auto i = m_table.find(msg);
484-
while(i != m_table.end())
483+
auto keyRange = m_table.equal_range(msg);
484+
auto i = keyRange.first;
485+
while(i != keyRange.second)
485486
{
486-
if(i->second == pObserver)
487+
// advance iterator first before we remove some element.
488+
auto toRemove = i++;
489+
490+
// if match, remove the element
491+
if(toRemove->second == pObserver)
487492
{
488-
m_table.erase(i);
493+
m_table.erase(toRemove);
489494
return;
490495
}
491-
++i;
492496
}
493497
}
494498

495499
void LBLEEventDispatcher::dispatch(bt_msg_type_t msg, bt_status_t status, void *buff)
496500
{
497501
// pr_debug("dispatch: msg:0x%x", msg);
498-
auto i = m_table.find(msg);
502+
auto keyRange = m_table.equal_range(msg);
503+
auto i = keyRange.first;
499504

500505
std::vector<EventTable::iterator> removeList;
501506
// execute observer's callback and pop the element found
502-
while(i != m_table.end())
507+
while(i != keyRange.second)
503508
{
504509
if(i->first != msg)
505510
{
@@ -565,4 +570,73 @@ LBLEValueBuffer::LBLEValueBuffer(const String& strValue)
565570
{
566571
resize(strValue.length() + 1);
567572
strValue.getBytes(&(*this)[0], size());
573+
}
574+
575+
// interprets buffer content as null-terminated character string.
576+
// Empty string is returned when there are errors.
577+
String LBLEValueBuffer::asString() const
578+
{
579+
if(!this->size())
580+
{
581+
return String();
582+
}
583+
584+
// Make sure we have terminating NULL before passing to String().
585+
if(this->back() == '\0')
586+
{
587+
return String((const char*)&this->front());
588+
}
589+
else
590+
{
591+
// since String() does not allow us to initialize
592+
// with buffer + length, we use a temporary buffer object instead.
593+
std::vector<char> tempBuffer;
594+
tempBuffer.resize(this->size() + 1, 0);
595+
if(tempBuffer.size() >= this->size())
596+
{
597+
memcpy(&tempBuffer.front(), &this->front(), this->size());
598+
return String((const char*)&tempBuffer.front());
599+
}
600+
}
601+
602+
return String();
603+
}
604+
605+
int LBLEValueBuffer::asInt() const
606+
{
607+
int ret = 0;
608+
if(this->size() < sizeof(ret))
609+
{
610+
return 0;
611+
}
612+
613+
ret = *((const int*)&this->at(0));
614+
615+
return ret;
616+
}
617+
618+
char LBLEValueBuffer::asChar() const
619+
{
620+
char ret = '\0';
621+
if(this->size() < sizeof(ret))
622+
{
623+
return 0;
624+
}
625+
626+
ret = *((const char*)&this->front());
627+
628+
return ret;
629+
}
630+
631+
float LBLEValueBuffer::asFloat() const
632+
{
633+
float ret = 0.f;
634+
if(this->size() < sizeof(ret))
635+
{
636+
return 0;
637+
}
638+
639+
ret = *((const float*)&this->front());
640+
641+
return ret;
568642
}

middleware/third_party/arduino/hardware/arduino/mt7697/libraries/LBLE/src/LBLE.h

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ class LBLEAddress : public Printable
186186
/// values when reading or writing GATT attributes.
187187
class LBLEValueBuffer : public std::vector<uint8_t>
188188
{
189+
// constructors and initialization
189190
public:
190191
/// Default constructor creates an empty buffer.
191192
LBLEValueBuffer();
@@ -204,6 +205,25 @@ class LBLEValueBuffer : public std::vector<uint8_t>
204205
LBLEValueBuffer(const String& strValue);
205206

206207
template<typename T>void shallowInit(T value);
208+
209+
// type conversion helpers
210+
public:
211+
/// interprets buffer content as int32_t
212+
/// 0 is returned when there are errors.
213+
int asInt() const;
214+
215+
/// interprets buffer content as null-terminated character string.
216+
/// Empty string is returned when there are errors.
217+
String asString() const;
218+
219+
/// interprets buffer content as char
220+
/// 0 is returned when there are errors.
221+
char asChar() const;
222+
223+
/// interprets buffer content as float
224+
/// 0.f is returned when there are errors.
225+
float asFloat() const;
226+
207227
};
208228

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

387407
return h.done();
388408
}
409+
410+
411+
// Modified version of EventBlockerMultiple.
412+
// This EventBlockerMultiple relies on the return value of `handler`
413+
// to determine `done()`.
414+
template<typename F> class EventBlockerMultiple : public LBLEEventObserver
415+
{
416+
public:
417+
EventBlockerMultiple(bt_msg_type_t e, const F& handler):
418+
m_handler(handler),
419+
m_event(e),
420+
m_eventArrived(false),
421+
m_allEventProcessed(false)
422+
{
423+
424+
}
425+
426+
bool done() const
427+
{
428+
return m_eventArrived && m_allEventProcessed;
429+
}
430+
431+
virtual bool isOnce()
432+
{
433+
// the client must remove EventBlocker
434+
// manually from the listeners.
435+
return false;
436+
};
437+
438+
virtual void onEvent(bt_msg_type_t msg, bt_status_t status, void* buff)
439+
{
440+
if(m_event == msg)
441+
{
442+
m_eventArrived = true;
443+
444+
// Note that some action, may result in multiple events.
445+
// For example, bt_gattc_discover_charc may invoke
446+
// multiple BT_GATTC_DISCOVER_CHARC events.
447+
//
448+
// We need to rely on the message handlers
449+
// to tell us if all events have been processed or not.
450+
//
451+
// all event processed = `m_handler` returns `true`.
452+
// event not processed or waiting for more event = `m_handler` returns `false`.
453+
m_allEventProcessed = m_handler(msg, status, buff);
454+
}
455+
}
456+
457+
private:
458+
const F& m_handler;
459+
const bt_msg_type_t m_event;
460+
bool m_eventArrived;
461+
bool m_allEventProcessed;
462+
};
463+
464+
// Modified version of waitAndProcessEvent.
465+
// it uses EventBlockerMultiple instead of EventBlocker.
466+
template<typename A, typename F> bool waitAndProcessEventMultiple(const A& action, bt_msg_type_t msg, const F& handler)
467+
{
468+
EventBlockerMultiple<F> h(msg, handler);
469+
LBLE.registerForEvent(msg, &h);
470+
471+
action();
472+
473+
uint32_t start = millis();
474+
while(!h.done() && (millis() - start) < 10 * 1000)
475+
{
476+
delay(50);
477+
}
478+
479+
LBLE.unregisterForEvent(msg, &h);
480+
481+
return h.done();
482+
}
389483
#endif

0 commit comments

Comments
 (0)