Add support for getting multiple services data from advertisments. (#20)

* Add support for getting multiple services data from advertisments.

* Adds new methods for getting advertised service data and UUIDS.
- getServiceData(index), gets the service data by index value.
- getServiceData(NimBLEUUID), gets the service data by UUID.
- getServiceDataCount(), gets the number of services with data advertised.

* Templates added for getServiceData(index) and getServiceData(NimBLEUUID)
  to be able to specify the data type returned by these methods
  Example:
      getServiceData<uint32_t>(NimBLEUUID("ABCD");

* Also added:
- getServiceUUID(index), gets the advertised service UUID by index value.
- getServiceUUIDCount(), gets the number of advertised services.
This commit is contained in:
h2zero 2020-07-28 20:09:54 -06:00 committed by GitHub
parent f1a13d5949
commit b2df8384b3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 201 additions and 180 deletions

View file

@ -17,6 +17,7 @@
#include "nimconfig.h"
#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
#include "NimBLEDevice.h"
#include "NimBLEAdvertisedDevice.h"
#include "NimBLEUtils.h"
#include "NimBLELog.h"
@ -30,15 +31,11 @@ static const char* LOG_TAG = "NimBLEAdvertisedDevice";
NimBLEAdvertisedDevice::NimBLEAdvertisedDevice() {
m_advType = 0;
m_appearance = 0;
m_deviceType = 0;
m_manufacturerData = "";
m_name = "";
m_rssi = -9999;
m_serviceData = "";
m_txPower = 0;
m_payloadLength = 0;
m_pScan = nullptr;
m_payloadLength = 0;
m_payload = nullptr;
m_haveAppearance = false;
@ -121,36 +118,74 @@ int NimBLEAdvertisedDevice::getRSSI() {
* @return The scan object.
*/
NimBLEScan* NimBLEAdvertisedDevice::getScan() {
return m_pScan;
return NimBLEDevice::getScan();
} // getScan
/**
* @brief Get the service data.
* @return The ServiceData of the advertised device.
* @param [in] index The vector index of the service data requested.
* @return The advertised service data or empty string if no data.
*/
std::string NimBLEAdvertisedDevice::getServiceData() {
return m_serviceData;
std::string NimBLEAdvertisedDevice::getServiceData(uint8_t index) {
if(index > m_serviceDataVec.size()) {
NIMBLE_LOGW(LOG_TAG, "getServiceData: index out of range");
return "";
}
return m_serviceDataVec[index].second;
} //getServiceData
/**
* @brief Get the service data.
* @param [in] uuid The uuid of the service data requested.
* @return The advertised service data or empty string if no data.
*/
std::string NimBLEAdvertisedDevice::getServiceData(const NimBLEUUID &uuid) const {
for(auto &it : m_serviceDataVec) {
if(it.first == uuid) {
return it.second;
}
}
NIMBLE_LOGW(LOG_TAG, "getServiceData: uuid not found");
return "";
} //getServiceData
/**
* @brief Get the advertised service UUID.
* @return The advertise service UUID.
* @param [in] index The vector index of the service data UUID requested.
* @return The advertised service UUID or an empty UUID if not found.
*/
NimBLEUUID NimBLEAdvertisedDevice::getServiceDataUUID() {
return m_serviceDataUUID;
NimBLEUUID NimBLEAdvertisedDevice::getServiceDataUUID(uint8_t index) {
if(!haveServiceData() || index > m_serviceDataVec.size()) {
NIMBLE_LOGW(LOG_TAG, "getServiceDataUUID: index out of range");
return NimBLEUUID("");
}
return m_serviceDataVec[index].first;
} // getServiceDataUUID
/**
* @brief Get the Service UUID.
* @return The Service UUID of the advertised device.
* @brief Get the count of advertised service data UUIDS
* @return The number of service data UUIDS in the vector.
*/
size_t NimBLEAdvertisedDevice::getServiceDataCount() {
return m_serviceDataVec.size();
} // getServiceDataCount
NimBLEUUID NimBLEAdvertisedDevice::getServiceUUID() { //TODO Remove it eventually, is no longer useful
return m_serviceUUIDs[0];
/**
* @brief Get the Service UUID.
* @param [in] index The vector index of the service UUID requested.
* @return The Service UUID of the advertised service, or an empty UUID if not found.
*/
NimBLEUUID NimBLEAdvertisedDevice::getServiceUUID(uint8_t index) {
if(!haveServiceUUID() || index > m_serviceUUIDs.size()) {
NIMBLE_LOGW(LOG_TAG, "getServiceUUID: index out of range");
return NimBLEUUID("");
}
return m_serviceUUIDs[index];
} // getServiceUUID
@ -158,9 +193,8 @@ NimBLEUUID NimBLEAdvertisedDevice::getServiceUUID() { //TODO Remove it eventual
* @brief Check advertised services for existance of the required UUID
* @return Return true if service is advertised
*/
bool NimBLEAdvertisedDevice::isAdvertisingService(const NimBLEUUID &uuid){
bool NimBLEAdvertisedDevice::isAdvertisingService(const NimBLEUUID &uuid) const {
for (int i = 0; i < m_serviceUUIDs.size(); i++) {
NIMBLE_LOGI(LOG_TAG, "Comparing UUIDS: %s %s", m_serviceUUIDs[i].toString().c_str(), uuid.toString().c_str());
if (m_serviceUUIDs[i].equals(uuid)) return true;
}
return false;
@ -251,121 +285,126 @@ bool NimBLEAdvertisedDevice::haveTXPower() {
*
* https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile
*/
void NimBLEAdvertisedDevice::parseAdvertisement(ble_hs_adv_fields *fields) {
//char s[BLE_HS_ADV_MAX_SZ];
uint8_t *u8p;
uint8_t length;
int i;
void NimBLEAdvertisedDevice::parseAdvertisement(uint8_t* payload, uint8_t length) {
struct ble_hs_adv_fields fields;
int rc = ble_hs_adv_parse_fields(&fields, payload, length);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Gap Event Parse ERROR.");
return;
}
if (fields->uuids16 != NULL) {
for (i = 0; i < fields->num_uuids16; i++) {
setServiceUUID(NimBLEUUID(fields->uuids16[i].value));
m_payload = payload;
m_payloadLength = length;
#if CONFIG_LOG_DEFAULT_LEVEL > 3 || (ARDUINO_ARCH_ESP32 && CORE_DEBUG_LEVEL >= 4)
char* pHex = NimBLEUtils::buildHexData(nullptr, m_payload, m_payloadLength);
NIMBLE_LOGD(LOG_TAG,"payload: %s", pHex);
free(pHex);
#endif
if (fields.uuids16 != NULL) {
for (int i = 0; i < fields.num_uuids16; i++) {
setServiceUUID(NimBLEUUID(fields.uuids16[i].value));
}
}
if (fields->uuids32 != NULL) {
for (i = 0; i < fields->num_uuids32; i++) {
setServiceUUID(NimBLEUUID(fields->uuids32[i].value));
if (fields.uuids32 != NULL) {
for (int i = 0; i < fields.num_uuids32; i++) {
setServiceUUID(NimBLEUUID(fields.uuids32[i].value));
}
}
if (fields->uuids128 != NULL) {
for (i = 0; i < fields->num_uuids128; i++) {
setServiceUUID(NimBLEUUID(&fields->uuids128[i]));
if (fields.uuids128 != NULL) {
for (int i = 0; i < fields.num_uuids128; i++) {
setServiceUUID(NimBLEUUID(&fields.uuids128[i]));
}
}
if (fields->name != NULL) {
setName(std::string(reinterpret_cast<char*>(fields->name), fields->name_len));
if (fields.name != NULL) {
setName(std::string(reinterpret_cast<char*>(fields.name), fields.name_len));
}
if (fields->tx_pwr_lvl_is_present) {
setTXPower(fields->tx_pwr_lvl);
if (fields.tx_pwr_lvl_is_present) {
setTXPower(fields.tx_pwr_lvl);
}
if (fields->svc_data_uuid16 != NULL) {
if (fields.svc_data_uuid16 != NULL ||
fields.svc_data_uuid32 != NULL ||
fields.svc_data_uuid128 != NULL)
{
ble_hs_adv_field *field;
uint8_t *data = payload;
while(length > 1) {
field = (ble_hs_adv_field*)data;
u8p = fields->svc_data_uuid16;
length = fields->svc_data_uuid16_len;
if (length < 2) {
NIMBLE_LOGE(LOG_TAG,"Length too small for ESP_BLE_AD_TYPE_SERVICE_DATA");
}
else{
uint16_t uuid = *(uint16_t*)u8p;
setServiceDataUUID(NimBLEUUID(uuid));
if (length > 2) {
setServiceData(std::string(reinterpret_cast<char*>(u8p + 2), length - 2));
}
}
if(field->length > length) {
break;
}
if (fields->svc_data_uuid32 != NULL) {
u8p = fields->svc_data_uuid16;
length = fields->svc_data_uuid16_len;
if (length < 4) {
NIMBLE_LOGE(LOG_TAG,"Length too small for ESP_BLE_AD_TYPE_32SERVICE_DATA");
}
uint32_t uuid = *(uint32_t*) u8p;
setServiceDataUUID(NimBLEUUID(uuid));
if (length > 4) {
setServiceData(std::string(reinterpret_cast<char*>(u8p + 4), length - 4));
if(field->type == BLE_HS_ADV_TYPE_SVC_DATA_UUID16) {
if(field->length > 2) {
uint16_t uuid;
memcpy(&uuid, field->value, 2);
setServiceData(NimBLEUUID(uuid), std::string(reinterpret_cast<char*>(field->value + 2), field->length - 3));
}
}
if (fields->svc_data_uuid128 != NULL) {
u8p = fields->svc_data_uuid16;
length = fields->svc_data_uuid16_len;
if (length < 16) {
NIMBLE_LOGE(LOG_TAG,"Length too small for ESP_BLE_AD_TYPE_128SERVICE_DATA");
}
setServiceDataUUID(NimBLEUUID(u8p, (size_t)16, false));
if (length > 16) {
setServiceData(std::string(reinterpret_cast<char*>(u8p + 16), length - 16));
if(field->type == BLE_HS_ADV_TYPE_SVC_DATA_UUID32) {
if(field->length > 4) {
uint32_t uuid;
memcpy(&uuid, field->value, 4);
setServiceData(NimBLEUUID(uuid), std::string(reinterpret_cast<char*>(field->value + 4), field->length - 5));
}
}
if (fields->appearance_is_present) {
NIMBLE_LOGD(LOG_TAG, " appearance=0x%04x", fields->appearance);
setAppearance(fields->appearance);
if(field->type == BLE_HS_ADV_TYPE_SVC_DATA_UUID128) {
if(field->length > 16) {
NimBLEUUID uuid(field->value, (size_t)16, false);
setServiceData(uuid, std::string(reinterpret_cast<char*>(field->value + 16), field->length - 17));
}
}
length -= 1 + field->length;
data += 1 + field->length;
}
}
if (fields.appearance_is_present) {
setAppearance(fields.appearance);
}
if (fields.mfg_data != NULL) {
setManufacturerData(std::string(reinterpret_cast<char*>(fields.mfg_data), fields.mfg_data_len));
}
/* TODO: create storage and fucntions for these parameters
if (fields->public_tgt_addr != NULL) {
if (fields.public_tgt_addr != NULL) {
NIMBLE_LOGD(LOG_TAG, " public_tgt_addr=");
u8p = fields->public_tgt_addr;
for (i = 0; i < fields->num_public_tgt_addrs; i++) {
u8p = fields.public_tgt_addr;
for (i = 0; i < fields.num_public_tgt_addrs; i++) {
NIMBLE_LOGD(LOG_TAG, "public_tgt_addr=%s ", addr_str(u8p));
u8p += BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN;
}
NIMBLE_LOGD(LOG_TAG, "\n");
}
if (fields->slave_itvl_range != NULL) {
if (fields.slave_itvl_range != NULL) {
NIMBLE_LOGD(LOG_TAG, " slave_itvl_range=");
print_bytes(fields->slave_itvl_range, BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN);
print_bytes(fields.slave_itvl_range, BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN);
NIMBLE_LOGD(LOG_TAG, "\n");
}
if (fields->adv_itvl_is_present) {
NIMBLE_LOGD(LOG_TAG, " adv_itvl=0x%04x\n", fields->adv_itvl);
if (fields.adv_itvl_is_present) {
NIMBLE_LOGD(LOG_TAG, " adv_itvl=0x%04x\n", fields.adv_itvl);
}
if (fields->uri != NULL) {
if (fields.uri != NULL) {
NIMBLE_LOGD(LOG_TAG, " uri=");
print_bytes(fields->uri, fields->uri_len);
print_bytes(fields.uri, fields.uri_len);
NIMBLE_LOGD(LOG_TAG, "\n");
}
*/
if (fields->mfg_data != NULL) {
setManufacturerData(std::string(reinterpret_cast<char*>(fields->mfg_data), fields->mfg_data_len));
}
} //parseAdvertisement
@ -394,7 +433,6 @@ void NimBLEAdvertisedDevice::setAdvType(uint8_t advType) {
void NimBLEAdvertisedDevice::setAppearance(uint16_t appearance) {
m_appearance = appearance;
m_haveAppearance = true;
NIMBLE_LOGD(LOG_TAG,"- appearance: %d", m_appearance);
} // setAppearance
@ -405,10 +443,6 @@ void NimBLEAdvertisedDevice::setAppearance(uint16_t appearance) {
void NimBLEAdvertisedDevice::setManufacturerData(std::string manufacturerData) {
m_manufacturerData = manufacturerData;
m_haveManufacturerData = true;
char* pHex = NimBLEUtils::buildHexData(nullptr, (uint8_t*) m_manufacturerData.data(), (uint8_t) m_manufacturerData.length());
NIMBLE_LOGD(LOG_TAG,"- manufacturer data: %s", pHex);
free(pHex);
} // setManufacturerData
@ -419,7 +453,6 @@ void NimBLEAdvertisedDevice::setManufacturerData(std::string manufacturerData) {
void NimBLEAdvertisedDevice::setName(std::string name) {
m_name = name;
m_haveName = true;
NIMBLE_LOGD(LOG_TAG,"- setName(): name: %s", m_name.c_str());
} // setName
@ -430,19 +463,9 @@ void NimBLEAdvertisedDevice::setName(std::string name) {
void NimBLEAdvertisedDevice::setRSSI(int rssi) {
m_rssi = rssi;
m_haveRSSI = true;
NIMBLE_LOGD(LOG_TAG,"- setRSSI(): rssi: %d", m_rssi);
} // setRSSI
/**
* @brief Set the Scan that created this advertised device.
* @param [in] pScan The Scan that created this advertised device.
*/
void NimBLEAdvertisedDevice::setScan(NimBLEScan* pScan) {
m_pScan = pScan;
} // setScan
/**
* @brief Set the Service UUID for this device.
* @param [in] serviceUUID The discovered serviceUUID
@ -466,30 +489,26 @@ void NimBLEAdvertisedDevice::setServiceUUID(NimBLEUUID serviceUUID) {
}
m_serviceUUIDs.push_back(serviceUUID);
m_haveServiceUUID = true;
NIMBLE_LOGD(LOG_TAG,"- addServiceUUID(): serviceUUID: %s", serviceUUID.toString().c_str());
} // setServiceUUID
/**
* @brief Set the ServiceData value.
* @param [in] data ServiceData value.
* @param [in] uuid The UUID that the service data belongs to.
* @param [in] data The service data.
*/
void NimBLEAdvertisedDevice::setServiceData(std::string serviceData) {
m_haveServiceData = true; // Set the flag that indicates we have service data.
m_serviceData = serviceData; // Save the service data that we received.
void NimBLEAdvertisedDevice::setServiceData(NimBLEUUID uuid, std::string data) {
m_haveServiceData = true;
for(auto &it : m_serviceDataVec) {
if(it.first == uuid) {
it.second = data;
return;
}
}
m_serviceDataVec.push_back({uuid, data});
} //setServiceData
/**
* @brief Set the ServiceDataUUID value.
* @param [in] data ServiceDataUUID value.
*/
void NimBLEAdvertisedDevice::setServiceDataUUID(NimBLEUUID uuid) {
m_haveServiceData = true; // Set the flag that indicates we have service data.
m_serviceDataUUID = uuid;
} // setServiceDataUUID
/**
* @brief Set the power level for this device.
* @param [in] txPower The discovered power level.
@ -497,7 +516,6 @@ void NimBLEAdvertisedDevice::setServiceDataUUID(NimBLEUUID uuid) {
void NimBLEAdvertisedDevice::setTXPower(int8_t txPower) {
m_txPower = txPower;
m_haveTXPower = true;
NIMBLE_LOGD(LOG_TAG,"- txPower: %d", m_txPower);
} // setTXPower
@ -533,7 +551,15 @@ std::string NimBLEAdvertisedDevice::toString() {
res += val;
}
res += ", advType: " + std::string(NimBLEUtils::advTypeToString(m_advType));
if(haveServiceData()) {
size_t count = getServiceDataCount();
res += "\nService Data:";
for(size_t i = 0; i < count; i++) {
res += "\nUUID: " + std::string(getServiceDataUUID(i));
res += ", Data: " + getServiceData(i);
}
}
res += "\nadvType: " + std::string(NimBLEUtils::advTypeToString(m_advType));
return res;
@ -593,16 +619,6 @@ size_t NimBLEAdvertisedDevice::getPayloadLength() {
} // getPayloadLength
/**
* @brief Set the advertisment payload data.
* @param [in] payload A pointer to the device payload data.
* @param [in] length The length of the payload data in bytes.
*/
void NimBLEAdvertisedDevice::setAdvertisementResult(uint8_t* payload, uint8_t length){
m_payload = payload;
m_payloadLength = length;
} // setAdvertisementResult
#endif // #if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#endif /* CONFIG_BT_ENABLED */

View file

@ -46,7 +46,7 @@ public:
uint16_t getAppearance();
std::string getManufacturerData();
/**
/**
* @brief A template to convert the service data to <type\>.
* @tparam T The type to convert the data to.
* @param [in] skipSizeCheck If true it will skip checking if the data size is less than <tt>sizeof(<type\>)</tt>.
@ -65,26 +65,47 @@ public:
std::string getName();
int getRSSI();
NimBLEScan* getScan();
std::string getServiceData();
size_t getServiceDataCount();
std::string getServiceData(uint8_t index = 0);
std::string getServiceData(const NimBLEUUID &uuid) const;
/**
/**
* @brief A template to convert the service data to <tt><type\></tt>.
* @tparam T The type to convert the data to.
* @param [in] index The vector index of the service data requested.
* @param [in] skipSizeCheck If true it will skip checking if the data size is less than <tt>sizeof(<type\>)</tt>.
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is
* less than <tt>sizeof(<type\>)</tt>.
* @details <b>Use:</b> <tt>getServiceData<type>(skipSizeCheck);</tt>
*/
template<typename T>
T getServiceData(bool skipSizeCheck = false) {
std::string data = getServiceData();
T getServiceData(uint8_t index = 0, bool skipSizeCheck = false) {
std::string data = getServiceData(index);
if(!skipSizeCheck && data.size() < sizeof(T)) return T();
const char *pData = data.data();
return *((T *)pData);
}
NimBLEUUID getServiceDataUUID();
NimBLEUUID getServiceUUID();
/**
* @brief A template to convert the service data to <tt><type\></tt>.
* @tparam T The type to convert the data to.
* @param [in] uuid The uuid of the service data requested.
* @param [in] skipSizeCheck If true it will skip checking if the data size is less than <tt>sizeof(<type\>)</tt>.
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is
* less than <tt>sizeof(<type\>)</tt>.
* @details <b>Use:</b> <tt>getServiceData<type>(skipSizeCheck);</tt>
*/
template<typename T>
T getServiceData(const NimBLEUUID &uuid, bool skipSizeCheck = false) {
std::string data = getServiceData(uuid);
if(!skipSizeCheck && data.size() < sizeof(T)) return T();
const char *pData = data.data();
return *((T *)pData);
}
NimBLEUUID getServiceDataUUID(uint8_t index = 0);
NimBLEUUID getServiceUUID(uint8_t index = 0);
size_t getServiceUUIDCount();
int8_t getTXPower();
uint8_t* getPayload();
size_t getPayloadLength();
@ -93,7 +114,7 @@ public:
void setAddressType(uint8_t type);
bool isAdvertisingService(const NimBLEUUID &uuid);
bool isAdvertisingService(const NimBLEUUID &uuid) const;
bool haveAppearance();
bool haveManufacturerData();
bool haveName();
@ -107,17 +128,14 @@ public:
private:
friend class NimBLEScan;
void parseAdvertisement(ble_hs_adv_fields *fields);
void parseAdvertisement(uint8_t* payload, uint8_t length);
void setAddress(NimBLEAddress address);
void setAdvType(uint8_t advType);
void setAdvertisementResult(uint8_t* payload, uint8_t length);
void setAppearance(uint16_t appearance);
void setManufacturerData(std::string manufacturerData);
void setName(std::string name);
void setRSSI(int rssi);
void setScan(NimBLEScan* pScan);
void setServiceData(std::string data);
void setServiceDataUUID(NimBLEUUID uuid);
void setServiceData(NimBLEUUID serviceUUID, std::string data);
void setServiceUUID(const char* serviceUUID);
void setServiceUUID(NimBLEUUID serviceUUID);
void setTXPower(int8_t txPower);
@ -134,14 +152,10 @@ private:
NimBLEAddress m_address = NimBLEAddress("");
uint8_t m_advType;
uint16_t m_appearance;
int m_deviceType;
std::string m_manufacturerData;
std::string m_name;
NimBLEScan* m_pScan;
int m_rssi;
int8_t m_txPower;
std::string m_serviceData;
NimBLEUUID m_serviceDataUUID;
uint8_t* m_payload;
size_t m_payloadLength;
uint8_t m_addressType;
@ -149,6 +163,7 @@ private:
bool m_callbackSent;
std::vector<NimBLEUUID> m_serviceUUIDs;
std::vector<std::pair<NimBLEUUID, std::string>>m_serviceDataVec;
};
/**
@ -167,7 +182,6 @@ public:
* As we are scanning, we will find new devices. When found, this call back is invoked with a reference to the
* device that was found. During any individual scan, a device will only be detected one time.
*/
//virtual void onResult(NimBLEAdvertisedDevice advertisedDevice) = 0;
virtual void onResult(NimBLEAdvertisedDevice* advertisedDevice) = 0;
};

View file

@ -52,8 +52,6 @@ NimBLEScan::NimBLEScan() {
/*STATIC*/int NimBLEScan::handleGapEvent(ble_gap_event* event, void* arg) {
NimBLEScan* pScan = (NimBLEScan*)arg;
struct ble_hs_adv_fields fields;
int rc = 0;
switch(event->type) {
@ -63,13 +61,6 @@ NimBLEScan::NimBLEScan() {
return 0;
}
rc = ble_hs_adv_parse_fields(&fields, event->disc.data,
event->disc.length_data);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Gap Event Parse ERROR.");
return 0;
}
NimBLEAddress advertisedAddress(event->disc.addr);
// Examine our list of ignored addresses and stop processing if we don't want to see it or are already connected
@ -102,9 +93,9 @@ NimBLEScan::NimBLEScan() {
NIMBLE_LOGI(LOG_TAG, "UPDATING PREVIOUSLY FOUND DEVICE: %s", advertisedAddress.toString().c_str());
}
advertisedDevice->setRSSI(event->disc.rssi);
advertisedDevice->parseAdvertisement(&fields);
advertisedDevice->setScan(pScan);
advertisedDevice->setAdvertisementResult(event->disc.data, event->disc.length_data);
if(event->disc.length_data > 0) {
advertisedDevice->parseAdvertisement(event->disc.data, event->disc.length_data);
}
advertisedDevice->m_timestamp = time(nullptr);
if (pScan->m_pAdvertisedDeviceCallbacks) {