Refactor server code to use vectors instead of maps.

* Use critical sections to access characteristic/descriptor value data.

* Remove unnecessary code

* Create characteristic semaphore only if needed for indications.

* Fix advertising when not broadcasting a service.
This commit is contained in:
h2zero 2020-06-07 18:42:28 -06:00
parent 44f16e6ee2
commit e987ad58b2
18 changed files with 471 additions and 1370 deletions

View file

@ -12,10 +12,8 @@ idf_component_register(SRCS "src/FreeRTOS.cpp"
"src/NimBLEAdvertising.cpp"
"src/NimBLEBeacon.cpp"
"src/NimBLECharacteristic.cpp"
"src/NimBLECharacteristicMap.cpp"
"src/NimBLEClient.cpp"
"src/NimBLEDescriptor.cpp"
"src/NimBLEDescriptorMap.cpp"
"src/NimBLEDevice.cpp"
"src/NimBLEEddystoneTLM.cpp"
"src/NimBLEEddystoneURL.cpp"
@ -26,9 +24,7 @@ idf_component_register(SRCS "src/FreeRTOS.cpp"
"src/NimBLESecurity.cpp"
"src/NimBLEServer.cpp"
"src/NimBLEService.cpp"
"src/NimBLEServiceMap.cpp"
"src/NimBLEUtils.cpp"
"src/NimBLEUUID.cpp"
"src/NimBLEValue.cpp"
INCLUDE_DIRS "src"
REQUIRES bt)

View file

@ -22,11 +22,16 @@
#include "NimBLEDescriptor.h"
#include <map>
#include <vector>
#define NIMBLE_DESC_FLAG_NOTIFY 0x0001
#define NIMBLE_DESC_FLAG_INDICATE 0x0002
typedef struct {
uint16_t conn_id;
uint16_t sub_val;
} chr_sub_status_t;
/**
* @brief Descriptor for Client Characteristic Configuration.
@ -45,7 +50,7 @@ public:
private:
NimBLE2902(NimBLECharacteristic* pCharacterisitic);
friend class NimBLECharacteristic;
std::map<uint16_t, uint16_t> m_subscribedMap;
std::vector<chr_sub_status_t> m_subscribedVec;
}; // NimBLE2902

View file

@ -31,6 +31,7 @@ struct BLE2904_Data {
} __attribute__((packed));
/**
* @brief Descriptor for Characteristic Presentation Format.
*

View file

@ -100,7 +100,8 @@ void NimBLEAdvertising::setMaxInterval(uint16_t maxinterval) {
m_advParams.itvl_max = maxinterval;
} // setMaxInterval
// These are dummy functions for now for compatibility
/* These are dummy functions for now for compatibility */
void NimBLEAdvertising::setMinPreferred(uint16_t mininterval) {
//m_advData.min_interval = mininterval;
} //
@ -108,7 +109,8 @@ void NimBLEAdvertising::setMinPreferred(uint16_t mininterval) {
void NimBLEAdvertising::setMaxPreferred(uint16_t maxinterval) {
//m_advData.max_interval = maxinterval;
} //
//////////////////////////////////////////////////////////
/*******************************************************/
void NimBLEAdvertising::setScanResponse(bool set) {
m_scanResp = set;
@ -204,19 +206,19 @@ void NimBLEAdvertising::start() {
}
#endif
int numServices = m_serviceUUIDs.size();
int rc = 0;
uint8_t addressType;
uint8_t payloadLen = 3; //start with 3 bytes for the flags data
// If already advertising just return
if(ble_gap_adv_active()) {
return;
}
if (!m_customAdvData && !m_advSvcsSet && numServices > 0) {
for (int i = 0; i < numServices; i++) {
if(m_serviceUUIDs[i].getNative()->u.type == BLE_UUID_TYPE_16) {
int rc = 0;
if (!m_customAdvData && !m_advDataSet) {
//start with 3 bytes for the flags data
uint8_t payloadLen = 3;
for(auto &it : m_serviceUUIDs) {
if(it.getNative()->u.type == BLE_UUID_TYPE_16) {
int add = (m_advData.num_uuids16 > 0) ? 2 : 4;
if((payloadLen + add) > 31){
m_advData.uuids16_is_complete = 0;
@ -231,13 +233,13 @@ void NimBLEAdvertising::start() {
abort();
}
memcpy(&m_advData.uuids16[m_advData.num_uuids16].value,
&m_serviceUUIDs[i].getNative()->u16.value, sizeof(uint16_t));
&it.getNative()->u16.value, 2);
m_advData.uuids16[m_advData.num_uuids16].u.type = BLE_UUID_TYPE_16;
m_advData.uuids16_is_complete = 1;
m_advData.num_uuids16++;
}
if(m_serviceUUIDs[i].getNative()->u.type == BLE_UUID_TYPE_32) {
if(it.getNative()->u.type == BLE_UUID_TYPE_32) {
int add = (m_advData.num_uuids32 > 0) ? 4 : 6;
if((payloadLen + add) > 31){
m_advData.uuids32_is_complete = 0;
@ -252,13 +254,13 @@ void NimBLEAdvertising::start() {
abort();
}
memcpy(&m_advData.uuids32[m_advData.num_uuids32].value,
&m_serviceUUIDs[i].getNative()->u32.value, sizeof(uint32_t));
&it.getNative()->u32.value, 4);
m_advData.uuids32[m_advData.num_uuids32].u.type = BLE_UUID_TYPE_32;
m_advData.uuids32_is_complete = 1;
m_advData.num_uuids32++;
}
if(m_serviceUUIDs[i].getNative()->u.type == BLE_UUID_TYPE_128){
if(it.getNative()->u.type == BLE_UUID_TYPE_128){
int add = (m_advData.num_uuids128 > 0) ? 16 : 18;
if((payloadLen + add) > 31){
m_advData.uuids128_is_complete = 0;
@ -267,12 +269,13 @@ void NimBLEAdvertising::start() {
payloadLen += add;
if(nullptr == (m_advData.uuids128 = (ble_uuid128_t*)realloc(m_advData.uuids128,
(m_advData.num_uuids128 + 1) * sizeof(ble_uuid128_t)))) {
(m_advData.num_uuids128 + 1) * sizeof(ble_uuid128_t))))
{
NIMBLE_LOGE(LOG_TAG, "Error, no mem");
abort();
}
memcpy(&m_advData.uuids128[m_advData.num_uuids128].value,
&m_serviceUUIDs[i].getNative()->u128.value, 16);
&it.getNative()->u128.value, 16);
m_advData.uuids128[m_advData.num_uuids128].u.type = BLE_UUID_TYPE_128;
m_advData.uuids128_is_complete = 1;
@ -315,11 +318,11 @@ void NimBLEAdvertising::start() {
rc = ble_gap_adv_rsp_set_fields(&m_scanData);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "error setting scan response data; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc));
NIMBLE_LOGC(LOG_TAG, "error setting scan response data; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc));
abort();
}
// if not using scan response and there is room,
// throw the tx power data into the advertisment
// put the tx power data into the advertisment
} else if (payloadLen < 29) {
m_advData.tx_pwr_lvl_is_present = 1;
m_advData.tx_pwr_lvl = NimBLEDevice::getPower();
@ -327,7 +330,7 @@ void NimBLEAdvertising::start() {
rc = ble_gap_adv_set_fields(&m_advData);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "error setting advertisement data; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc));
NIMBLE_LOGC(LOG_TAG, "error setting advertisement data; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc));
abort();
}
@ -349,22 +352,16 @@ void NimBLEAdvertising::start() {
m_advData.num_uuids16 = 0;
}
m_advSvcsSet = true;
}
rc = ble_hs_id_infer_auto(0, &addressType);
if (rc != 0) {
NIMBLE_LOGC(LOG_TAG, "Error determining address type; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc));
abort();
m_advDataSet = true;
}
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
rc = ble_gap_adv_start(addressType, NULL, BLE_HS_FOREVER,
rc = ble_gap_adv_start(0, NULL, BLE_HS_FOREVER,
&m_advParams,
(pServer != nullptr) ? NimBLEServer::handleGapEvent : NULL,
pServer);
#else
rc = ble_gap_adv_start(addressType, NULL, BLE_HS_FOREVER,
rc = ble_gap_adv_start(0, NULL, BLE_HS_FOREVER,
&m_advParams, NULL,NULL);
#endif
if (rc != 0) {
@ -398,7 +395,7 @@ void NimBLEAdvertising::stop() {
* we need clear the flag so it reloads it.
*/
void NimBLEAdvertising::onHostReset() {
m_advSvcsSet = false;
m_advDataSet = false;
}

View file

@ -103,7 +103,7 @@ private:
bool m_customAdvData = false; // Are we using custom advertising data?
bool m_customScanResponseData = false; // Are we using custom scan response data?
bool m_scanResp = true;
bool m_advSvcsSet = false;
bool m_advDataSet = false;
};

View file

@ -22,18 +22,16 @@
#include "NimBLEUtils.h"
#include "NimBLELog.h"
#include <string>
#define NULL_HANDLE (0xffff)
static NimBLECharacteristicCallbacks defaultCallback;
static const char* LOG_TAG = "NimBLECharacteristic";
/**
* @brief Construct a characteristic
* @param [in] uuid - UUID (const char*) for the characteristic.
* @param [in] properties - Properties for the characteristic.
* @param [in] pService - pointer to the service instance this characteristic belongs to.
*/
NimBLECharacteristic::NimBLECharacteristic(const char* uuid, uint16_t properties, NimBLEService* pService)
: NimBLECharacteristic(NimBLEUUID(uuid), properties, pService) {
@ -43,6 +41,7 @@ NimBLECharacteristic::NimBLECharacteristic(const char* uuid, uint16_t properties
* @brief Construct a characteristic
* @param [in] uuid - UUID for the characteristic.
* @param [in] properties - Properties for the characteristic.
* @param [in] pService - pointer to the service instance this characteristic belongs to.
*/
NimBLECharacteristic::NimBLECharacteristic(const NimBLEUUID &uuid, uint16_t properties, NimBLEService* pService) {
m_uuid = uuid;
@ -50,39 +49,24 @@ NimBLECharacteristic::NimBLECharacteristic(const NimBLEUUID &uuid, uint16_t prop
m_properties = properties;
m_pCallbacks = &defaultCallback;
m_pService = pService;
// Backward Compatibility - to be removed
/* setBroadcastProperty((properties & PROPERTY_BROADCAST) != 0);
setReadProperty((properties & PROPERTY_READ) != 0);
setWriteProperty((properties & PROPERTY_WRITE) != 0);
setNotifyProperty((properties & PROPERTY_NOTIFY) != 0);
setIndicateProperty((properties & PROPERTY_INDICATE) != 0);
setWriteNoResponseProperty((properties & PROPERTY_WRITE_NR) != 0);
*/
///////////////////////////////////////////
m_value = "";
m_valMux = portMUX_INITIALIZER_UNLOCKED;
if(properties & NIMBLE_PROPERTY::INDICATE){
m_pIndSemaphore = new FreeRTOS::Semaphore("ConfEvt");
} else {
m_pIndSemaphore = nullptr;
}
} // NimBLECharacteristic
/**
* @brief Destructor.
*/
NimBLECharacteristic::~NimBLECharacteristic() {
} // ~NimBLECharacteristic
/**
* @brief Associate a descriptor with this characteristic.
* @param [in] pDescriptor
* @return N/A.
*/
void NimBLECharacteristic::addDescriptor(NimBLEDescriptor* pDescriptor) {
NIMBLE_LOGD(LOG_TAG, ">> addDescriptor(): Adding %s to %s", pDescriptor->toString().c_str(), toString().c_str());
// Check that we don't add the same descriptor twice.
if (m_descriptorMap.getByUUID(pDescriptor->getUUID()) != nullptr) {
NIMBLE_LOGW(LOG_TAG, "<< Adding a new descriptor with the same UUID as a previous one");
//return;
if(m_pIndSemaphore != nullptr) {
delete(m_pIndSemaphore);
}
m_descriptorMap.setByUUID(pDescriptor->getUUID(), pDescriptor);
NIMBLE_LOGD(LOG_TAG, "<< addDescriptor()");
} // addDescriptor
} // ~NimBLECharacteristic
/**
@ -104,25 +88,26 @@ NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const char* uuid, uint3
*/
NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const NimBLEUUID &uuid, uint32_t properties, uint16_t max_len) {
NimBLEDescriptor* pDescriptor = nullptr;
if(uuid.equals(NimBLEUUID((uint16_t)0x2902))) {
if(uuid == NimBLEUUID(uint16_t(0x2902))) {
if(!(m_properties & BLE_GATT_CHR_F_NOTIFY) && !(m_properties & BLE_GATT_CHR_F_INDICATE)) {
assert(0 && "Cannot create 2902 descriptior without characteristic notification or indication property set");
}
// We cannot have more than one 2902 descriptor, if it's already been created just return a pointer to it.
pDescriptor = m_descriptorMap.getByUUID(uuid);
pDescriptor = getDescriptorByUUID(uuid);
if(pDescriptor == nullptr) {
pDescriptor = new NimBLE2902(this);
} else {
return pDescriptor;
}
} else if (uuid.equals(NimBLEUUID((uint16_t)0x2904))) {
} else if (uuid == NimBLEUUID(uint16_t(0x2904))) {
pDescriptor = new NimBLE2904(this);
} else {
pDescriptor = new NimBLEDescriptor(uuid, properties, max_len, this);
}
addDescriptor(pDescriptor);
m_dscVec.push_back(pDescriptor);
return pDescriptor;
} // createCharacteristic
@ -132,8 +117,8 @@ NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const NimBLEUUID &uuid,
* @param [in] descriptorUUID The UUID of the descriptor that we wish to retrieve.
* @return The BLE Descriptor. If no such descriptor is associated with the characteristic, nullptr is returned.
*/
NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const char* descriptorUUID) {
return m_descriptorMap.getByUUID(NimBLEUUID(descriptorUUID));
NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const char* uuid) {
return getDescriptorByUUID(NimBLEUUID(uuid));
} // getDescriptorByUUID
@ -142,8 +127,13 @@ NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const char* descript
* @param [in] descriptorUUID The UUID of the descriptor that we wish to retrieve.
* @return The BLE Descriptor. If no such descriptor is associated with the characteristic, nullptr is returned.
*/
NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const NimBLEUUID &descriptorUUID) {
return m_descriptorMap.getByUUID(descriptorUUID);
NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const NimBLEUUID &uuid) {
for (auto &it : m_dscVec) {
if (it->getUUID() == uuid) {
return it;
}
}
return nullptr;
} // getDescriptorByUUID
@ -155,11 +145,6 @@ uint16_t NimBLECharacteristic::getHandle() {
return m_handle;
} // getHandle
/*
void NimBLECharacteristic::setAccessPermissions(uint16_t perm) {
m_permissions = perm;
}
*/
uint8_t NimBLECharacteristic::getProperties() {
return m_properties;
@ -188,25 +173,24 @@ NimBLEUUID NimBLECharacteristic::getUUID() {
* @return A pointer to storage containing the current characteristic value.
*/
std::string NimBLECharacteristic::getValue() {
return m_value.getValue();
portENTER_CRITICAL(&m_valMux);
std::string retVal = m_value;
portEXIT_CRITICAL(&m_valMux);
return retVal;
} // getValue
/**
* @brief Retrieve the current raw data of the characteristic.
* @return A pointer to storage containing the current characteristic data.
*/
uint8_t* NimBLECharacteristic::getData() {
return m_value.getData();
} // getData
/**
* @brief Retrieve the the current data length of the characteristic.
* @return The length of the current characteristic data.
*/
size_t NimBLECharacteristic::getDataLength() {
return m_value.getLength();
portENTER_CRITICAL(&m_valMux);
size_t len = m_value.length();
portEXIT_CRITICAL(&m_valMux);
return len;
}
@ -225,32 +209,41 @@ int NimBLECharacteristic::handleGapEvent(uint16_t conn_handle, uint16_t attr_han
if(ble_uuid_cmp(uuid, &pCharacteristic->getUUID().getNative()->u) == 0){
switch(ctxt->op) {
case BLE_GATT_ACCESS_OP_READ_CHR: {
//NIMBLE_LOGD(LOG_TAG, "read char pkthdr len:%d flags:%d", ctxt->om->om_pkthdr_len, ctxt->om->om_flags);
// If the packet header is only 8 bytes this is a follow up of a long read
// so we don't want to call the onRead() callback again.
if(ctxt->om->om_pkthdr_len > 8) {
pCharacteristic->m_pCallbacks->onRead(pCharacteristic);
}
rc = os_mbuf_append(ctxt->om, pCharacteristic->getData(), pCharacteristic->m_value.getLength());
portENTER_CRITICAL(&pCharacteristic->m_valMux);
rc = os_mbuf_append(ctxt->om, (uint8_t*)pCharacteristic->m_value.data(),
pCharacteristic->m_value.length());
portEXIT_CRITICAL(&pCharacteristic->m_valMux);
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
case BLE_GATT_ACCESS_OP_WRITE_CHR: {
//NIMBLE_LOGD(LOG_TAG, "write char pkthdr len:%d datalen:%d", ctxt->om->om_pkthdr_len, ctxt->om->om_len);
if (ctxt->om->om_len > BLE_ATT_ATTR_MAX_LEN) {
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
}
//pCharacteristic->setValue(ctxt->om->om_data, ctxt->om->om_len);
pCharacteristic->m_value.addPart(ctxt->om->om_data, ctxt->om->om_len);
uint8_t buf[BLE_ATT_ATTR_MAX_LEN];
size_t len = ctxt->om->om_len;
memcpy(buf, ctxt->om->om_data,len);
os_mbuf *next;
next = SLIST_NEXT(ctxt->om, om_next);
while(next != NULL){
//NIMBLE_LOGD(LOG_TAG, "Found long write data, len:%d", next->om_len);
pCharacteristic->m_value.addPart(next->om_data, next->om_len);
if((len + next->om_len) > BLE_ATT_ATTR_MAX_LEN) {
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
}
memcpy(&buf[len-1], next->om_data, next->om_len);
len += next->om_len;
next = SLIST_NEXT(next, om_next);
}
pCharacteristic->m_value.commit();
pCharacteristic->setValue(buf, len);
pCharacteristic->m_pCallbacks->onWrite(pCharacteristic);
return 0;
@ -278,14 +271,18 @@ void NimBLECharacteristic::setSubscribe(struct ble_gap_event *event) {
subVal |= NIMBLE_DESC_FLAG_INDICATE;
}
m_semaphoreConfEvt.give((subVal | NIMBLE_DESC_FLAG_INDICATE) ? 0 :
if(m_pIndSemaphore != nullptr) {
m_pIndSemaphore->give((subVal & NIMBLE_DESC_FLAG_INDICATE) ? 0 :
NimBLECharacteristicCallbacks::Status::ERROR_INDICATE_DISABLED);
}
NIMBLE_LOGI(LOG_TAG, "New subscribe value for conn: %d val: %d", event->subscribe.conn_handle, subVal);
NIMBLE_LOGI(LOG_TAG, "New subscribe value for conn: %d val: %d",
event->subscribe.conn_handle, subVal);
NimBLE2902* p2902 = (NimBLE2902*)getDescriptorByUUID((uint16_t)0x2902);
NimBLE2902* p2902 = (NimBLE2902*)getDescriptorByUUID(uint16_t(0x2902));
if(p2902 == nullptr){
ESP_LOGE(LOG_TAG, "No 2902 descriptor found for %s", getUUID().toString().c_str());
ESP_LOGE(LOG_TAG, "No 2902 descriptor found for %s",
std::string(getUUID()).c_str());
return;
}
@ -294,21 +291,28 @@ void NimBLECharacteristic::setSubscribe(struct ble_gap_event *event) {
p2902->m_pCallbacks->onWrite(p2902);
auto it = p2902->m_subscribedMap.find(event->subscribe.conn_handle);
if(subVal > 0 && it == p2902->m_subscribedMap.cend()) {
p2902->m_subscribedMap.insert(std::pair<uint16_t, uint16_t>(event->subscribe.conn_handle, subVal));
return;
} else if(it != p2902->m_subscribedMap.cend()) {
p2902->m_subscribedMap.erase(event->subscribe.conn_handle);
auto it = p2902->m_subscribedVec.begin();
for(;it != p2902->m_subscribedVec.end(); ++it) {
if((*it).conn_id == event->subscribe.conn_handle) {
break;
}
}
if(subVal > 0) {
if(it == p2902->m_subscribedVec.end()) {
chr_sub_status_t client_sub;
client_sub.conn_id = event->subscribe.conn_handle;
client_sub.sub_val = subVal;
p2902->m_subscribedVec.push_back(client_sub);
return;
}
/*
if(event->subscribe.reason == BLE_GAP_SUBSCRIBE_REASON_TERM) {
p2902->m_subscribedMap.erase(event->subscribe.conn_handle);
return;
(*it).sub_val = subVal;
} else if(it != p2902->m_subscribedVec.end()) {
p2902->m_subscribedVec.erase(it);
p2902->m_subscribedVec.shrink_to_fit();
}
*/
(*it).second = subVal;
}
@ -319,7 +323,7 @@ void NimBLECharacteristic::setSubscribe(struct ble_gap_event *event) {
* @return N/A
*/
void NimBLECharacteristic::indicate() {
NIMBLE_LOGD(LOG_TAG, ">> indicate: length: %d", m_value.getValue().length());
NIMBLE_LOGD(LOG_TAG, ">> indicate: length: %d", getDataLength());
notify(false);
NIMBLE_LOGD(LOG_TAG, "<< indicate");
} // indicate
@ -331,36 +335,38 @@ void NimBLECharacteristic::indicate() {
* @return N/A.
*/
void NimBLECharacteristic::notify(bool is_notification) {
NIMBLE_LOGD(LOG_TAG, ">> notify: length: %d", m_value.getValue().length());
NIMBLE_LOGD(LOG_TAG, ">> notify: length: %d", getDataLength());
assert(getService() != nullptr);
assert(getService()->getServer() != nullptr);
NimBLE2902* p2902 = (NimBLE2902*)getDescriptorByUUID(uint16_t(0x2902));
if(p2902 == nullptr) {
NIMBLE_LOGE(LOG_TAG,
"<< notify-Error; Notify/indicate not enabled for characterisitc: %s",
std::string(getUUID()).c_str());
}
if (getService()->getServer()->getConnectedCount() == 0) {
NIMBLE_LOGD(LOG_TAG, "<< notify: No connected clients.");
if (p2902->m_subscribedVec.size() == 0) {
NIMBLE_LOGD(LOG_TAG, "<< notify: No clients subscribed.");
return;
}
m_pCallbacks->onNotify(this);
std::string value = getValue();
size_t length = value.length();
int rc = 0;
NimBLE2902* p2902 = (NimBLE2902*)getDescriptorByUUID((uint16_t)0x2902);
for (auto it = p2902->m_subscribedMap.cbegin(); it != p2902->m_subscribedMap.cend(); ++it) {
uint16_t _mtu = getService()->getServer()->getPeerMTU((*it).first);
// Must rebuild the data on each loop iteration as NimBLE will release it.
size_t length = m_value.getValue().length();
uint8_t* data = (uint8_t*)m_value.getValue().data();
for (auto &it : p2902->m_subscribedVec) {
uint16_t _mtu = getService()->getServer()->getPeerMTU(it.conn_id);
os_mbuf *om;
if(_mtu == 0) {
//NIMBLE_LOGD(LOG_TAG, "peer not connected, removing from map");
p2902->m_subscribedMap.erase((*it).first);
it = p2902->m_subscribedMap.cbegin();
if(it == p2902->m_subscribedMap.cend()) {
return;
//NIMBLE_LOGD(LOG_TAG, "peer not connected");
continue;
}
if(it.sub_val == 0) {
//NIMBLE_LOGD(LOG_TAG, "Skipping unsubscribed client");
continue;
}
@ -368,54 +374,52 @@ void NimBLECharacteristic::notify(bool is_notification) {
NIMBLE_LOGW(LOG_TAG, "- Truncating to %d bytes (maximum notify size)", _mtu - 3);
}
if((*it).second == 0) {
//NIMBLE_LOGI(LOG_TAG, "Skipping unsubscribed client");
continue;
}
if(is_notification && (!((*it).second & NIMBLE_DESC_FLAG_NOTIFY))) {
if(is_notification && (!(it.sub_val & NIMBLE_DESC_FLAG_NOTIFY))) {
NIMBLE_LOGW(LOG_TAG,
"Sending notification to client subscribed to indications, sending indication instead");
is_notification = false;
}
if(!is_notification && (!((*it).second & NIMBLE_DESC_FLAG_INDICATE))) {
if(!is_notification && (!(it.sub_val & NIMBLE_DESC_FLAG_INDICATE))) {
NIMBLE_LOGW(LOG_TAG,
"Sending indication to client subscribed to notifications, sending notifications instead");
"Sending indication to client subscribed to notification, sending notification instead");
is_notification = true;
}
// don't create the m_buf until we are sure to send the data or else
// we could be allocating a buffer that doesn't get released.
// We also must create it in each loop iteration because it is consumed with each host call.
om = ble_hs_mbuf_from_flat(data, length);
om = ble_hs_mbuf_from_flat((uint8_t*)value.data(), length);
if(!is_notification) {
m_semaphoreConfEvt.take("indicate");
rc = ble_gattc_indicate_custom((*it).first, m_handle, om);
NimBLECharacteristicCallbacks::Status statusRC;
if(m_pIndSemaphore != nullptr && !is_notification) {
m_pIndSemaphore->take("indicate");
rc = ble_gattc_indicate_custom(it.conn_id, m_handle, om);
if(rc != 0){
m_semaphoreConfEvt.give();
m_pCallbacks->onStatus(this, NimBLECharacteristicCallbacks::Status::ERROR_GATT, rc);
return;
m_pIndSemaphore->give();
statusRC = NimBLECharacteristicCallbacks::Status::ERROR_GATT;
} else {
rc = m_pIndSemaphore->wait();
}
rc = m_semaphoreConfEvt.wait();
if(rc == BLE_HS_ETIMEOUT) {
m_pCallbacks->onStatus(this, NimBLECharacteristicCallbacks::Status::ERROR_INDICATE_TIMEOUT, rc);
} else if(rc == BLE_HS_EDONE) {
m_pCallbacks->onStatus(this, NimBLECharacteristicCallbacks::Status::SUCCESS_INDICATE, rc);
if(rc == 0 || rc == BLE_HS_EDONE) {
rc = 0;
statusRC = NimBLECharacteristicCallbacks::Status::SUCCESS_INDICATE;
} else if(rc == BLE_HS_ETIMEOUT) {
statusRC = NimBLECharacteristicCallbacks::Status::ERROR_INDICATE_TIMEOUT;
} else {
m_pCallbacks->onStatus(this, NimBLECharacteristicCallbacks::Status::ERROR_INDICATE_FAILURE, rc);
statusRC = NimBLECharacteristicCallbacks::Status::ERROR_INDICATE_FAILURE;
}
} else {
rc = ble_gattc_notify_custom((*it).first, m_handle, om);
rc = ble_gattc_notify_custom(it.conn_id, m_handle, om);
if(rc == 0) {
m_pCallbacks->onStatus(this, NimBLECharacteristicCallbacks::Status::SUCCESS_NOTIFY, 0);
statusRC = NimBLECharacteristicCallbacks::Status::SUCCESS_NOTIFY;
} else {
m_pCallbacks->onStatus(this, NimBLECharacteristicCallbacks::Status::ERROR_GATT, rc);
statusRC = NimBLECharacteristicCallbacks::Status::ERROR_GATT;
}
}
m_pCallbacks->onStatus(this, statusRC, rc);
}
NIMBLE_LOGD(LOG_TAG, "<< notify");
@ -434,87 +438,6 @@ void NimBLECharacteristic::setCallbacks(NimBLECharacteristicCallbacks* pCallback
}
} // setCallbacks
// Backward compatibility - to be removed ////////////////////////////////
/**
* @brief Set the permission to broadcast.
* A characteristics has properties associated with it which define what it is capable of doing.
* One of these is the broadcast flag.
* @param [in] value The flag value of the property.
* @return N/A
*/
void NimBLECharacteristic::setBroadcastProperty(bool value) {
if (value) {
m_properties = (m_properties | BLE_GATT_CHR_F_BROADCAST);
} else {
m_properties = (m_properties & ~BLE_GATT_CHR_F_BROADCAST);
}
} // setBroadcastProperty
/**
* @brief Set the Indicate property value.
* @param [in] value Set to true if we are to allow indicate messages.
*/
void NimBLECharacteristic::setIndicateProperty(bool value) {
if (value) {
m_properties = (m_properties | BLE_GATT_CHR_F_INDICATE);
} else {
m_properties = (m_properties & ~BLE_GATT_CHR_F_INDICATE);
}
} // setIndicateProperty
/**
* @brief Set the Notify property value.
* @param [in] value Set to true if we are to allow notification messages.
*/
void NimBLECharacteristic::setNotifyProperty(bool value) {
if (value) {
m_properties = (m_properties | BLE_GATT_CHR_F_NOTIFY);
} else {
m_properties = (m_properties & ~BLE_GATT_CHR_F_NOTIFY);
}
} // setNotifyProperty
/**
* @brief Set the Read property value.
* @param [in] value Set to true if we are to allow reads.
*/
void NimBLECharacteristic::setReadProperty(bool value) {
if (value) {
m_properties = (m_properties | BLE_GATT_CHR_F_READ);
} else {
m_properties = (m_properties & ~BLE_GATT_CHR_F_READ);
}
} // setReadProperty
/**
* @brief Set the Write No Response property value.
* @param [in] value Set to true if we are to allow writes with no response.
*/
void NimBLECharacteristic::setWriteNoResponseProperty(bool value) {
if (value) {
m_properties = (m_properties | BLE_GATT_CHR_F_WRITE_NO_RSP);
} else {
m_properties = (m_properties & ~BLE_GATT_CHR_F_WRITE_NO_RSP);
}
} // setWriteNoResponseProperty
/**
* @brief Set the Write property value.
* @param [in] value Set to true if we are to allow writes.
*/
void NimBLECharacteristic::setWriteProperty(bool value) {
if (value) {
m_properties = (m_properties | BLE_GATT_CHR_F_WRITE );
} else {
m_properties = (m_properties & ~BLE_GATT_CHR_F_WRITE );
}
} // setWriteProperty
//////////////////////////////////////////////////////////////////////////////////
/**
* @brief Set the value of the characteristic.
@ -522,21 +445,20 @@ void NimBLECharacteristic::setWriteProperty(bool value) {
* @param [in] length The length of the data in bytes.
*/
void NimBLECharacteristic::setValue(const uint8_t* data, size_t length) {
#if CONFIG_LOG_DEFAULT_LEVEL > 3 || (ARDUINO_ARCH_ESP32 && CORE_DEBUG_LEVEL >= 4)
char* pHex = NimBLEUtils::buildHexData(nullptr, data, length);
NIMBLE_LOGD(LOG_TAG, ">> setValue: length=%d, data=%s, characteristic UUID=%s", length, pHex, getUUID().toString().c_str());
free(pHex);
#endif
if (length > BLE_ATT_ATTR_MAX_LEN) {
NIMBLE_LOGE(LOG_TAG, "Size %d too large, must be no bigger than %d", length, BLE_ATT_ATTR_MAX_LEN);
return;
}
m_value.setValue(data, length);
// if(m_handle != NULL_HANDLE) {
//ble_gatts_chr_updated(m_handle);
// ble_gattc_notify(getService()->getServer()->m_connId, m_handle);
// }
portENTER_CRITICAL(&m_valMux);
m_value = std::string((char*)data, length);
portEXIT_CRITICAL(&m_valMux);
NIMBLE_LOGD(LOG_TAG, "<< setValue");
} // setValue

View file

@ -42,42 +42,16 @@ typedef enum {
#include "NimBLEService.h"
#include "NimBLEDescriptor.h"
#include "NimBLEUUID.h"
#include "NimBLEValue.h"
#include "FreeRTOS.h"
#include <string>
#include <map>
#include <vector>
class NimBLEService;
class NimBLEDescriptor;
class NimBLECharacteristicCallbacks;
/**
* @brief A management structure for %BLE descriptors.
*/
class NimBLEDescriptorMap {
public:
void setByUUID(const char* uuid, NimBLEDescriptor* pDescriptor);
void setByUUID(const NimBLEUUID &uuid, NimBLEDescriptor* pDescriptor);
// void setByHandle(uint16_t handle, NimBLEDescriptor* pDescriptor);
NimBLEDescriptor* getByUUID(const char* uuid);
NimBLEDescriptor* getByUUID(const NimBLEUUID &uuid);
// NimBLEDescriptor* getByHandle(uint16_t handle);
std::string toString();
NimBLEDescriptor* getFirst();
NimBLEDescriptor* getNext();
uint8_t getSize();
private:
std::map<NimBLEDescriptor*, std::string> m_uuidMap;
// std::map<uint16_t, BLEDescriptor*> m_handleMap;
std::map<NimBLEDescriptor*, std::string>::iterator m_iterator;
};
/**
* @brief The model of a %BLE Characteristic.
*
@ -87,32 +61,24 @@ private:
class NimBLECharacteristic {
public:
NimBLEDescriptor* createDescriptor(const char* uuid,
uint32_t properties = NIMBLE_PROPERTY::READ |
uint32_t properties =
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE,
uint16_t max_len = 100);
NimBLEDescriptor* createDescriptor(const NimBLEUUID &uuid,
uint32_t properties = NIMBLE_PROPERTY::READ |
uint32_t properties =
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE,
uint16_t max_len = 100);
NimBLEDescriptor* getDescriptorByUUID(const char* descriptorUUID);
NimBLEDescriptor* getDescriptorByUUID(const NimBLEUUID &descriptorUUID);
NimBLEDescriptor* getDescriptorByUUID(const char* uuid);
NimBLEDescriptor* getDescriptorByUUID(const NimBLEUUID &uuid);
NimBLEUUID getUUID();
std::string getValue();
uint8_t* getData();
size_t getDataLength();
void indicate();
void notify(bool is_notification = true);
void setCallbacks(NimBLECharacteristicCallbacks* pCallbacks);
// Backward Compatibility - to be removed
void setBroadcastProperty(bool value);
void setIndicateProperty(bool value);
void setNotifyProperty(bool value);
void setReadProperty(bool value);
void setWriteProperty(bool value);
void setWriteNoResponseProperty(bool value);
//////////////////////////////////////////////////////
void setValue(const uint8_t* data, size_t size);
void setValue(const std::string &value);
void setValue(uint16_t& data16);
@ -120,51 +86,42 @@ public:
void setValue(int& data32);
void setValue(float& data32);
void setValue(double& data64);
std::string toString();
uint16_t getHandle();
// void setAccessPermissions(uint16_t perm);
// Backward Compatibility - to be removed
/* static const uint32_t PROPERTY_READ = 1<<0;
static const uint32_t PROPERTY_WRITE = 1<<1;
static const uint32_t PROPERTY_NOTIFY = 1<<2;
static const uint32_t PROPERTY_BROADCAST = 1<<3;
static const uint32_t PROPERTY_INDICATE = 1<<4;
static const uint32_t PROPERTY_WRITE_NR = 1<<5;
*/
//////////////////////////////////////////////////////
private:
friend class NimBLEServer;
friend class NimBLEService;
// friend class NimBLEDescriptor;
// friend class NimBLECharacteristicMap;
NimBLECharacteristic(const char* uuid, uint16_t properties = NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE,
NimBLECharacteristic(const char* uuid,
uint16_t properties =
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE,
NimBLEService* pService = nullptr);
NimBLECharacteristic(const NimBLEUUID &uuid, uint16_t properties = NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE,
NimBLECharacteristic(const NimBLEUUID &uuid,
uint16_t properties =
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE,
NimBLEService* pService = nullptr);
virtual ~NimBLECharacteristic();
NimBLEUUID m_uuid;
NimBLEDescriptorMap m_descriptorMap;
uint16_t m_handle;
uint16_t m_properties;
NimBLECharacteristicCallbacks* m_pCallbacks;
NimBLEService* m_pService;
NimBLEValue m_value;
// uint16_t m_permissions;
~NimBLECharacteristic();
void addDescriptor(NimBLEDescriptor* pDescriptor);
NimBLEService* getService();
uint8_t getProperties();
void setSubscribe(struct ble_gap_event *event);
static int handleGapEvent(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg);
FreeRTOS::Semaphore m_semaphoreConfEvt = FreeRTOS::Semaphore("ConfEvt");
NimBLEUUID m_uuid;
uint16_t m_handle;
uint16_t m_properties;
NimBLECharacteristicCallbacks* m_pCallbacks;
NimBLEService* m_pService;
std::string m_value;
std::vector<NimBLEDescriptor*> m_dscVec;
FreeRTOS::Semaphore *m_pIndSemaphore;
portMUX_TYPE m_valMux;
}; // NimBLECharacteristic

View file

@ -1,132 +0,0 @@
/*
* NimBLECharacteristicMap.cpp
*
* Created: on March 3, 2020
* Author H2zero
*
* BLECharacteristicMap.cpp
*
* Created on: Jun 22, 2017
* Author: kolban
*/
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h"
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#include "NimBLEService.h"
#include "NimBLELog.h"
/**
* @brief Return the characteristic by handle.
* @param [in] handle The handle to look up the characteristic.
* @return The characteristic.
*/
NimBLECharacteristic* NimBLECharacteristicMap::getByHandle(uint16_t handle) {
return m_handleMap.at(handle);
} // getByHandle
/**
* @brief Return the characteristic by UUID.
* @param [in] UUID The UUID to look up the characteristic.
* @return The characteristic.
*/
NimBLECharacteristic* NimBLECharacteristicMap::getByUUID(const char* uuid) {
return getByUUID(NimBLEUUID(uuid));
}
/**
* @brief Return the characteristic by UUID.
* @param [in] UUID The UUID to look up the characteristic.
* @return The characteristic.
*/
NimBLECharacteristic* NimBLECharacteristicMap::getByUUID(const NimBLEUUID &uuid) {
for (auto &myPair : m_uuidMap) {
if (myPair.first->getUUID().equals(uuid)) {
return myPair.first;
}
}
return nullptr;
} // getByUUID
/**
* @brief Get the number of characteristics in the map.
*/
uint8_t NimBLECharacteristicMap::getSize() {
return (uint8_t)m_uuidMap.size();
} // getSize
/**
* @brief Get the first characteristic in the map.
* @return The first characteristic in the map.
*/
NimBLECharacteristic* NimBLECharacteristicMap::getFirst() {
m_iterator = m_uuidMap.begin();
if (m_iterator == m_uuidMap.end()) return nullptr;
NimBLECharacteristic* pRet = m_iterator->first;
m_iterator++;
return pRet;
} // getFirst
/**
* @brief Get the next characteristic in the map.
* @return The next characteristic in the map.
*/
NimBLECharacteristic* NimBLECharacteristicMap::getNext() {
if (m_iterator == m_uuidMap.end()) return nullptr;
NimBLECharacteristic* pRet = m_iterator->first;
m_iterator++;
return pRet;
} // getNext
/**
* @brief Set the characteristic by handle.
* @param [in] handle The handle of the characteristic.
* @param [in] characteristic The characteristic to cache.
* @return N/A.
*/
void NimBLECharacteristicMap::setByHandle(uint16_t handle, NimBLECharacteristic* characteristic) {
m_handleMap.insert(std::pair<uint16_t, NimBLECharacteristic*>(handle, characteristic));
} // setByHandle
/**
* @brief Set the characteristic by UUID.
* @param [in] uuid The uuid of the characteristic.
* @param [in] characteristic The characteristic to cache.
* @return N/A.
*/
void NimBLECharacteristicMap::setByUUID(NimBLECharacteristic* pCharacteristic, const NimBLEUUID &uuid) {
m_uuidMap.insert(std::pair<NimBLECharacteristic*, std::string>(pCharacteristic, uuid.toString()));
} // setByUUID
/**
* @brief Return a string representation of the characteristic map.
* @return A string representation of the characteristic map.
*/
std::string NimBLECharacteristicMap::toString() {
std::string res;
int count = 0;
char hex[5];
for (auto &myPair: m_uuidMap) {
if (count > 0) {res += "\n";}
snprintf(hex, sizeof(hex), "%04x", myPair.first->getHandle());
count++;
res += "handle: 0x";
res += hex;
res += ", uuid: " + myPair.first->getUUID().toString();
}
return res;
} // toString
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#endif /* CONFIG_BT_ENABLED */

View file

@ -50,6 +50,7 @@ NimBLEDescriptor::NimBLEDescriptor(NimBLEUUID uuid, uint16_t properties, uint16_
m_pCharacteristic = nullptr; // No initial characteristic.
m_pCallbacks = &defaultCallbacks; // No initial callback.
m_value.attr_value = (uint8_t*) calloc(max_len,1); // Allocate storage for the value.
m_valMux = portMUX_INITIALIZER_UNLOCKED;
m_properties = 0;
if (properties & BLE_GATT_CHR_F_READ) { // convert uint16_t properties to uint8_t
@ -137,17 +138,37 @@ int NimBLEDescriptor::handleGapEvent(uint16_t conn_handle, uint16_t attr_handle,
if(ble_uuid_cmp(uuid, &pDescriptor->getUUID().getNative()->u) == 0){
switch(ctxt->op) {
case BLE_GATT_ACCESS_OP_READ_DSC: {
// If the packet header is only 8 bytes this is a follow up of a long read
// so we don't want to call the onRead() callback again.
if(ctxt->om->om_pkthdr_len > 8) {
pDescriptor->m_pCallbacks->onRead(pDescriptor);
}
portENTER_CRITICAL(&pDescriptor->m_valMux);
rc = os_mbuf_append(ctxt->om, pDescriptor->getValue(), pDescriptor->getLength());
portEXIT_CRITICAL(&pDescriptor->m_valMux);
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
case BLE_GATT_ACCESS_OP_WRITE_DSC: {
if (ctxt->om->om_len > BLE_ATT_ATTR_MAX_LEN) {
if (ctxt->om->om_len > pDescriptor->m_value.attr_max_len) {
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
}
pDescriptor->setValue(ctxt->om->om_data, ctxt->om->om_len);
uint8_t buf[pDescriptor->m_value.attr_max_len];
size_t len = ctxt->om->om_len;
memcpy(buf, ctxt->om->om_data,len);
os_mbuf *next;
next = SLIST_NEXT(ctxt->om, om_next);
while(next != NULL){
if((len + next->om_len) > pDescriptor->m_value.attr_max_len) {
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
}
memcpy(&buf[len-1], next->om_data, next->om_len);
len += next->om_len;
next = SLIST_NEXT(next, om_next);
}
pDescriptor->setValue(buf, len);
pDescriptor->m_pCallbacks->onWrite(pDescriptor);
return 0;
}
@ -191,12 +212,14 @@ void NimBLEDescriptor::setHandle(uint16_t handle) {
* @param [in] length The length of the data in bytes.
*/
void NimBLEDescriptor::setValue(const uint8_t* data, size_t length) {
if (length > BLE_ATT_ATTR_MAX_LEN) {
NIMBLE_LOGE(LOG_TAG, "Size %d too large, must be no bigger than %d", length, BLE_ATT_ATTR_MAX_LEN);
if (length > m_value.attr_max_len) {
NIMBLE_LOGE(LOG_TAG, "Size %d too large, must be no bigger than %d", length, m_value.attr_max_len);
return;
}
portENTER_CRITICAL(&m_valMux);
m_value.attr_len = length;
memcpy(m_value.attr_value, data, length);
portEXIT_CRITICAL(&m_valMux);
} // setValue
@ -209,13 +232,6 @@ void NimBLEDescriptor::setValue(const std::string &value) {
} // setValue
/*
void NimBLEDescriptor::setAccessPermissions(uint8_t perm) {
m_permissions = perm;
}
*/
/**
* @brief Return a string representation of the descriptor.
* @return A string representation of the descriptor.

View file

@ -34,8 +34,6 @@ typedef struct
uint8_t *attr_value; /*!< the pointer to attribute value */
} attr_value_t;
typedef attr_value_t esp_attr_value_t; /*!< compatibility for esp32 */
class NimBLEService;
class NimBLECharacteristic;
class NimBLEDescriptorCallbacks;
@ -46,20 +44,16 @@ class NimBLEDescriptorCallbacks;
*/
class NimBLEDescriptor {
public:
virtual ~NimBLEDescriptor();
uint16_t getHandle(); // Get the handle of the descriptor.
size_t getLength(); // Get the length of the value of the descriptor.
NimBLEUUID getUUID(); // Get the UUID of the descriptor.
uint8_t* getValue(); // Get a pointer to the value of the descriptor.
// void setAccessPermissions(uint8_t perm); // Set the permissions of the descriptor.
void setCallbacks(NimBLEDescriptorCallbacks* pCallbacks); // Set callbacks to be invoked for the descriptor.
void setValue(const uint8_t* data, size_t size); // Set the value of the descriptor as a pointer to data.
void setValue(const std::string &value); // Set the value of the descriptor as a data buffer.
std::string toString(); // Convert the descriptor to a string representation.
uint16_t getHandle();
size_t getLength();
NimBLEUUID getUUID();
uint8_t* getValue();
void setCallbacks(NimBLEDescriptorCallbacks* pCallbacks);
void setValue(const uint8_t* data, size_t size);
void setValue(const std::string &value);
std::string toString();
private:
friend class NimBLEDescriptorMap;
friend class NimBLECharacteristic;
friend class NimBLEService;
friend class NimBLE2902;
@ -73,18 +67,20 @@ private:
uint16_t max_len,
NimBLECharacteristic* pCharacteristic);
~NimBLEDescriptor();
static int handleGapEvent(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg);
void setHandle(uint16_t handle);
NimBLEUUID m_uuid;
uint16_t m_handle;
NimBLEDescriptorCallbacks* m_pCallbacks;
NimBLECharacteristic* m_pCharacteristic;
uint8_t m_properties;
attr_value_t m_value;
static int handleGapEvent(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg);
void setHandle(uint16_t handle);
}; // BLEDescriptor
portMUX_TYPE m_valMux;
}; // NimBLEDescriptor
/**

View file

@ -1,149 +0,0 @@
/*
* NimBLEDescriptorMap.cpp
*
* Created: on March 10, 2020
* Author H2zero
*
* Originally:
*
* BLEDescriptorMap.cpp
*
* Created on: Jun 22, 2017
* Author: kolban
*/
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h"
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#include "NimBLECharacteristic.h"
#include "NimBLEDescriptor.h"
/**
* @brief Return the descriptor by UUID.
* @param [in] UUID The UUID to look up the descriptor.
* @return The descriptor. If not present, then nullptr is returned.
*/
NimBLEDescriptor* NimBLEDescriptorMap::getByUUID(const char* uuid) {
return getByUUID(NimBLEUUID(uuid));
}
/**
* @brief Return the descriptor by UUID.
* @param [in] UUID The UUID to look up the descriptor.
* @return The descriptor. If not present, then nullptr is returned.
*/
NimBLEDescriptor* NimBLEDescriptorMap::getByUUID(const NimBLEUUID &uuid) {
for (auto &myPair : m_uuidMap) {
if (myPair.first->getUUID().equals(uuid)) {
return myPair.first;
}
}
return nullptr;
} // getByUUID
/**
* @brief Return the descriptor by handle.
* @param [in] handle The handle to look up the descriptor.
* @return The descriptor.
*/
/*
NimBLEDescriptor* NimBLEDescriptorMap::getByHandle(uint16_t handle) {
return m_handleMap.at(handle);
} // getByHandle
*/
/**
* @brief Set the descriptor by UUID.
* @param [in] uuid The uuid of the descriptor.
* @param [in] characteristic The descriptor to cache.
* @return N/A.
*/
void NimBLEDescriptorMap::setByUUID(const char* uuid, NimBLEDescriptor* pDescriptor){
m_uuidMap.insert(std::pair<NimBLEDescriptor*, std::string>(pDescriptor, uuid));
} // setByUUID
/**
* @brief Set the descriptor by UUID.
* @param [in] uuid The uuid of the descriptor.
* @param [in] characteristic The descriptor to cache.
* @return N/A.
*/
void NimBLEDescriptorMap::setByUUID(const NimBLEUUID &uuid, NimBLEDescriptor* pDescriptor) {
m_uuidMap.insert(std::pair<NimBLEDescriptor*, std::string>(pDescriptor, uuid.toString()));
} // setByUUID
/**
* @brief Set the descriptor by handle.
* @param [in] handle The handle of the descriptor.
* @param [in] descriptor The descriptor to cache.
* @return N/A.
*/
/*
void NimBLEDescriptorMap::setByHandle(uint16_t handle, NimBLEDescriptor* pDescriptor) {
m_handleMap.insert(std::pair<uint16_t, NimBLEDescriptor*>(handle, pDescriptor));
} // setByHandle
*/
/**
* @brief Get the number of descriptors in the map.
*/
uint8_t NimBLEDescriptorMap::getSize() {
return (uint8_t)m_uuidMap.size();
} // getSize
/**
* @brief Return a string representation of the descriptor map.
* @return A string representation of the descriptor map.
*/
std::string NimBLEDescriptorMap::toString() {
std::string res;
char hex[5];
int count = 0;
for (auto &myPair : m_uuidMap) {
if (count > 0) {res += "\n";}
snprintf(hex, sizeof(hex), "%04x", myPair.first->getHandle());
count++;
res += "handle: 0x";
res += hex;
res += ", uuid: " + myPair.first->getUUID().toString();
}
return res;
} // toString
/**
* @brief Get the first descriptor in the map.
* @return The first descriptor in the map.
*/
NimBLEDescriptor* NimBLEDescriptorMap::getFirst() {
m_iterator = m_uuidMap.begin();
if (m_iterator == m_uuidMap.end()) return nullptr;
NimBLEDescriptor* pRet = m_iterator->first;
m_iterator++;
return pRet;
} // getFirst
/**
* @brief Get the next descriptor in the map.
* @return The next descriptor in the map.
*/
NimBLEDescriptor* NimBLEDescriptorMap::getNext() {
if (m_iterator == m_uuidMap.end()) return nullptr;
NimBLEDescriptor* pRet = m_iterator->first;
m_iterator++;
return pRet;
} // getNext
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#endif /* CONFIG_BT_ENABLED */

View file

@ -19,7 +19,6 @@
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#include "NimBLEServer.h"
#include "NimBLE2902.h"
#include "NimBLEUtils.h"
#include "NimBLEDevice.h"
#include "NimBLELog.h"
@ -32,51 +31,46 @@ static NimBLEServerCallbacks defaultCallbacks;
* @brief Construct a %BLE Server
*
* This class is not designed to be individually instantiated. Instead one should create a server by asking
* the BLEDevice class.
* the NimBLEDevice class.
*/
NimBLEServer::NimBLEServer() {
m_connId = BLE_HS_CONN_HANDLE_NONE;
m_svcChgChrHdl = 0xffff;
// m_svcChgChrHdl = 0xffff; // Future Use
m_pServerCallbacks = &defaultCallbacks;
m_gattsStarted = false;
} // BLEServer
m_advertiseOnDisconnect = true;
} // NimBLEServer
/**
* @brief Create a %BLE Service.
*
* With a %BLE server, we can host one or more services. Invoking this function causes the creation of a definition
* of a new service. Every service must have a unique UUID.
* @param [in] uuid The UUID of the new service.
* @return A reference to the new service object.
*/
NimBLEService* NimBLEServer::createService(const char* uuid) {
return createService(NimBLEUUID(uuid));
}
} // createService
/**
* @brief Create a %BLE Service.
*
* With a %BLE server, we can host one or more services. Invoking this function causes the creation of a definition
* of a new service. Every service must have a unique UUID.
* @param [in] uuid The UUID of the new service.
* @param [in] numHandles The maximum number of handles associated with this service.
* @param [in] inst_id With multiple services with the same UUID we need to provide inst_id value different for each service.
* @param [in] inst_id if we have multiple services with the same UUID we need
* to provide inst_id value different for each service.
* @return A reference to the new service object.
*/
NimBLEService* NimBLEServer::createService(const NimBLEUUID &uuid, uint32_t numHandles, uint8_t inst_id) {
NIMBLE_LOGD(LOG_TAG, ">> createService - %s", uuid.toString().c_str());
// TODO: add functionality to use inst_id for multiple services with same uuid
(void)inst_id;
// Check that a service with the supplied UUID does not already exist.
if (m_serviceMap.getByUUID(uuid) != nullptr) {
NIMBLE_LOGW(LOG_TAG, "<< Attempt to create a new service with uuid %s but a service with that UUID already exists.",
uuid.toString().c_str());
if(getServiceByUUID(uuid) != nullptr) {
NIMBLE_LOGW(LOG_TAG, "Warning creating a duplicate service UUID: %s",
std::string(uuid).c_str());
}
NimBLEService* pService = new NimBLEService(uuid, numHandles, this);
pService->m_instId = inst_id;
m_serviceMap.setByUUID(uuid, pService); // Save a reference to this service being on this server.
m_svcVec.push_back(pService); // Save a reference to this service being on this server.
NIMBLE_LOGD(LOG_TAG, "<< createService");
return pService;
@ -89,8 +83,8 @@ NimBLEService* NimBLEServer::createService(const NimBLEUUID &uuid, uint32_t numH
* @return A reference to the service object.
*/
NimBLEService* NimBLEServer::getServiceByUUID(const char* uuid) {
return m_serviceMap.getByUUID(uuid);
}
return getServiceByUUID(NimBLEUUID(uuid));
} // getServiceByUUID
/**
@ -99,8 +93,13 @@ NimBLEService* NimBLEServer::getServiceByUUID(const char* uuid) {
* @return A reference to the service object.
*/
NimBLEService* NimBLEServer::getServiceByUUID(const NimBLEUUID &uuid) {
return m_serviceMap.getByUUID(uuid);
for (auto &it : m_svcVec) {
if (it->getUUID() == uuid) {
return it;
}
}
return nullptr;
} // getServiceByUUID
/**
@ -109,18 +108,8 @@ NimBLEService* NimBLEServer::getServiceByUUID(const NimBLEUUID &uuid) {
* @return An advertising object.
*/
NimBLEAdvertising* NimBLEServer::getAdvertising() {
return BLEDevice::getAdvertising();
}
/**
* @brief Retrieve the connection id of the last connected client.
* @todo Not very useful, should refactor or remove.
* @return Client connection id.
*/
uint16_t NimBLEServer::getConnId() {
return m_connId;
}
return NimBLEDevice::getAdvertising();
} // getAdvertising
/**
@ -143,6 +132,8 @@ void NimBLEServer::start() {
#if CONFIG_LOG_DEFAULT_LEVEL > 3 || (ARDUINO_ARCH_ESP32 && CORE_DEBUG_LEVEL >= 4)
ble_gatts_show_local();
#endif
/*** Future use ***
* TODO: implement service changed handling
ble_uuid16_t svc = {BLE_UUID_TYPE_16, 0x1801};
ble_uuid16_t chr = {BLE_UUID_TYPE_16, 0x2a05};
@ -155,36 +146,25 @@ void NimBLEServer::start() {
}
NIMBLE_LOGI(LOG_TAG, "Service changed characterisic handle: %d", m_svcChgChrHdl);
// Build a map of characteristics with Notify / Indicate capabilities for event handling
uint8_t numSvcs = m_serviceMap.getRegisteredServiceCount();
NimBLEService* pService = m_serviceMap.getFirst();
for(int i = 0; i < numSvcs; i++) {
uint8_t numChrs = pService->m_characteristicMap.getSize();
NimBLECharacteristic* pChr = pService->m_characteristicMap.getFirst();
if(pChr != nullptr) {
for( int d = 0; d < numChrs; d++) {
*/
// Build a vector of characteristics with Notify / Indicate capabilities for event handling
for(auto &svc : m_svcVec) {
for(auto &chr : svc->m_chrVec) {
// if Notify / Indicate is enabled but we didn't create the descriptor
// we do it now.
if((pChr->m_properties & BLE_GATT_CHR_F_INDICATE) ||
(pChr->m_properties & BLE_GATT_CHR_F_NOTIFY)) {
if((chr->m_properties & BLE_GATT_CHR_F_INDICATE) ||
(chr->m_properties & BLE_GATT_CHR_F_NOTIFY)) {
if(nullptr == pChr->getDescriptorByUUID("2902")) {
pChr->createDescriptor("2902");
if(nullptr == chr->getDescriptorByUUID(uint16_t(0x2902))) {
chr->createDescriptor(uint16_t(0x2902));
}
m_notifyChrMap.insert(std::pair<uint16_t, NimBLECharacteristic*>
(pChr->getHandle(), pChr));
}
pChr = pService->m_characteristicMap.getNext();
m_notifyChrVec.push_back(chr);
}
}
pService = m_serviceMap.getNext();
}
m_gattsStarted = true;
}
} // start
/**
@ -204,15 +184,24 @@ int NimBLEServer::disconnect(uint16_t connId, uint8_t reason) {
return rc;
NIMBLE_LOGD(LOG_TAG, "<< disconnect()");
}
} // disconnect
/**
* @brief Set the server to automatically start advertising when a client disconnects.
* @param [in] bool true == advertise, false == don't advertise.
*/
void NimBLEServer::advertiseOnDisconnect(bool aod) {
m_advertiseOnDisconnect = aod;
} // advertiseOnDisconnect
/**
* @brief Return the number of connected clients.
* @return The number of connected clients.
*/
uint32_t NimBLEServer::getConnectedCount() {
return m_connectedServersMap.size();
size_t NimBLEServer::getConnectedCount() {
return m_connectedPeersVec.size();
} // getConnectedCount
@ -236,13 +225,11 @@ uint32_t NimBLEServer::getConnectedCount() {
case BLE_GAP_EVENT_CONNECT: {
if (event->connect.status != 0) {
/* Connection failed; resume advertising */
NIMBLE_LOGC(LOG_TAG, "Connection failed");
NIMBLE_LOGE(LOG_TAG, "Connection failed");
NimBLEDevice::startAdvertising();
server->m_connId = BLE_HS_CONN_HANDLE_NONE;
}
else {
server->m_connId = event->connect.conn_handle;
server->addPeerDevice((void*)server, false, server->m_connId);
server->m_connectedPeersVec.push_back(event->connect.conn_handle);
ble_gap_conn_desc desc;
rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
@ -271,10 +258,15 @@ uint32_t NimBLEServer::getConnectedCount() {
break;
}
server->removePeerDevice(event->disconnect.conn.conn_handle, false);
server->m_connId = BLE_HS_CONN_HANDLE_NONE;
server->m_connectedPeersVec.erase(std::remove(server->m_connectedPeersVec.begin(),
server->m_connectedPeersVec.end(),
event->disconnect.conn.conn_handle),
server->m_connectedPeersVec.end());
server->m_pServerCallbacks->onDisconnect(server);
if(server->m_advertiseOnDisconnect) {
server->startAdvertising();
}
return 0;
} // BLE_GAP_EVENT_DISCONNECT
@ -283,9 +275,11 @@ uint32_t NimBLEServer::getConnectedCount() {
"val_handle=%d\n",
event->subscribe.cur_notify, event->subscribe.attr_handle);
auto it = server->m_notifyChrMap.find(event->subscribe.attr_handle);
if(it != server->m_notifyChrMap.cend()) {
(*it).second->setSubscribe(event);
for(auto &it : server->m_notifyChrVec) {
if(it->getHandle() == event->subscribe.attr_handle) {
it->setSubscribe(event);
break;
}
}
return 0;
@ -295,15 +289,18 @@ uint32_t NimBLEServer::getConnectedCount() {
NIMBLE_LOGI(LOG_TAG, "mtu update event; conn_handle=%d mtu=%d",
event->mtu.conn_handle,
event->mtu.value);
server->updatePeerMTU(event->mtu.conn_handle, event->mtu.value);
return 0;
} // BLE_GAP_EVENT_MTU
case BLE_GAP_EVENT_NOTIFY_TX: {
if(event->notify_tx.indication && event->notify_tx.status != 0) {
auto it = server->m_notifyChrMap.find(event->notify_tx.attr_handle);
if(it != server->m_notifyChrMap.cend()) {
(*it).second->m_semaphoreConfEvt.give(event->notify_tx.status);
for(auto &it : server->m_notifyChrVec) {
if(it->getHandle() == event->notify_tx.attr_handle) {
if(it->m_pIndSemaphore != nullptr) {
it->m_pIndSemaphore->give(event->notify_tx.status);
}
break;
}
}
}
@ -349,7 +346,7 @@ uint32_t NimBLEServer::getConnectedCount() {
} // BLE_GAP_EVENT_ENC_CHANGE
case BLE_GAP_EVENT_PASSKEY_ACTION: {
struct ble_sm_io pkey = {0};
struct ble_sm_io pkey = {0,0};
if (event->passkey.params.action == BLE_SM_IOACT_DISP) {
pkey.action = event->passkey.params.action;
@ -416,7 +413,7 @@ uint32_t NimBLEServer::getConnectedCount() {
NIMBLE_LOGD(LOG_TAG, "<< handleGATTServerEvent");
return 0;
} // handleGATTServerEvent
} // handleGapEvent
/**
@ -437,18 +434,6 @@ void NimBLEServer::setCallbacks(NimBLEServerCallbacks* pCallbacks) {
} // setCallbacks
/*
* Remove service
*/
/*
void BLEServer::removeService(BLEService* service) {
service->stop();
service->executeDelete();
m_serviceMap.removeService(service);
}
*/
/**
* @brief Start advertising.
*
@ -456,9 +441,7 @@ void BLEServer::removeService(BLEService* service) {
* retrieving the advertising object and invoking start upon it.
*/
void NimBLEServer::startAdvertising() {
NIMBLE_LOGD(LOG_TAG, ">> startAdvertising");
NimBLEDevice::startAdvertising();
NIMBLE_LOGD(LOG_TAG, "<< startAdvertising");
} // startAdvertising
@ -466,38 +449,43 @@ void NimBLEServer::startAdvertising() {
* @brief Stop advertising.
*/
void NimBLEServer::stopAdvertising() {
NIMBLE_LOGD(LOG_TAG, ">> stopAdvertising");
NimBLEDevice::stopAdvertising();
NIMBLE_LOGD(LOG_TAG, "<< stopAdvertising");
} // startAdvertising
/**
* Allow to connect GATT server to peer device
* Probably can be used in ANCS for iPhone
* @brief Get the MTU of the client.
* @returns The client MTU or 0 if not found/connected.
*/
/*
bool BLEServer::connect(BLEAddress address) {
esp_bd_addr_t addr;
memcpy(&addr, address.getNative(), 6);
// Perform the open connection request against the target BLE Server.
m_semaphoreOpenEvt.take("connect");
esp_err_t errRc = ::esp_ble_gatts_open(
getGattsIf(),
addr, // address
1 // direct connection
);
if (errRc != ESP_OK) {
ESP_LOGE(LOG_TAG, "esp_ble_gattc_open: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
return false;
uint16_t NimBLEServer::getPeerMTU(uint16_t conn_id) {
return ble_att_mtu(conn_id);
} //getPeerMTU
/**
* Update connection parameters can be called only after connection has been established
*/
void NimBLEServer::updateConnParams(uint16_t conn_handle,
uint16_t minInterval, uint16_t maxInterval,
uint16_t latency, uint16_t timeout)
{
ble_gap_upd_params params;
params.latency = latency;
params.itvl_max = maxInterval; // max_int = 0x20*1.25ms = 40ms
params.itvl_min = minInterval; // min_int = 0x10*1.25ms = 20ms
params.supervision_timeout = timeout; // timeout = 400*10ms = 4000ms
params.min_ce_len = BLE_GAP_INITIAL_CONN_MIN_CE_LEN; // Minimum length of connection event in 0.625ms units
params.max_ce_len = BLE_GAP_INITIAL_CONN_MAX_CE_LEN; // Maximum length of connection event in 0.625ms units
int rc = ble_gap_update_params(conn_handle, &params);
if(rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Update params error: %d, %s", rc, NimBLEUtils::returnCodeToString(rc));
}
} // updateConnParams
uint32_t rc = m_semaphoreOpenEvt.wait("connect"); // Wait for the connection to complete.
ESP_LOGD(LOG_TAG, "<< connect(), rc=%d", rc==ESP_GATT_OK);
return rc == ESP_GATT_OK;
} // connect
*/
/** Default callback handlers */
void NimBLEServerCallbacks::onConnect(NimBLEServer* pServer) {
NIMBLE_LOGD("NimBLEServerCallbacks", "onConnect(): Default");
@ -534,80 +522,6 @@ bool NimBLEServerCallbacks::onConfirmPIN(uint32_t pin){
return true;
}
/* multi connect support */
void NimBLEServer::updatePeerMTU(uint16_t conn_id, uint16_t mtu) {
const std::map<uint16_t, conn_status_t>::iterator it = m_connectedServersMap.find(conn_id);
if (it != m_connectedServersMap.end()) {
it->second.mtu = mtu;
}
}
std::map<uint16_t, conn_status_t> NimBLEServer::getPeerDevices() {
return m_connectedServersMap;
}
/**
* @brief Get the MTU of the client.
* @returns The client MTU or 0 if not found/connected.
*/
uint16_t NimBLEServer::getPeerMTU(uint16_t conn_id) {
auto it = m_connectedServersMap.find(conn_id);
if(it != m_connectedServersMap.cend()) {
return (*it).second.mtu;
} else {
return 0;
}
}
void NimBLEServer::addPeerDevice(void* peer, bool _client, uint16_t conn_id) {
conn_status_t status = {
.peer_device = peer,
.connected = true,
.mtu = 23
};
m_connectedServersMap.insert(std::pair<uint16_t, conn_status_t>(conn_id, status));
}
void NimBLEServer::removePeerDevice(uint16_t conn_id, bool _client) {
m_connectedServersMap.erase(conn_id);
}
/* multi connect support */
/**
* Update connection parameters can be called only after connection has been established
*/
void NimBLEServer::updateConnParams(uint16_t conn_handle,
uint16_t minInterval, uint16_t maxInterval,
uint16_t latency, uint16_t timeout,
uint16_t minConnTime, uint16_t maxConnTime)
{
ble_gap_upd_params params;
params.latency = latency;
params.itvl_max = maxInterval; // max_int = 0x20*1.25ms = 40ms
params.itvl_min = minInterval; // min_int = 0x10*1.25ms = 20ms
params.supervision_timeout = timeout; // timeout = 400*10ms = 4000ms
params.min_ce_len = minConnTime; // Minimum length of connection event in 0.625ms units
params.max_ce_len = maxConnTime; // Maximum length of connection event in 0.625ms units
int rc = ble_gap_update_params(conn_handle, &params);
if(rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Update params error: %d, %s", rc, NimBLEUtils::returnCodeToString(rc));
}
}
/* Don't think this is needed
void NimBLEServer::onHostReset() {
for(auto it = m_notifyChrMap.cbegin(); it != m_notifyChrMap.cend(); ++it) {
(*it).second->m_semaphoreConfEvt.give(0);
}
}
*/
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#endif // CONFIG_BT_ENABLED

View file

@ -21,100 +21,56 @@
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#include "NimBLEAddress.h"
#include "NimBLEUUID.h"
#include "NimBLEAdvertising.h"
#include "NimBLEService.h"
#include "NimBLESecurity.h"
#include "FreeRTOS.h"
#include <map>
class NimBLEService;
class NimBLECharacteristic;
class NimBLEServerCallbacks;
/* TODO possibly refactor this struct */
typedef struct {
void *peer_device; // peer device BLEClient or BLEServer - maybe its better to have 2 structures or union here
bool connected; // do we need it?
uint16_t mtu; // every peer device negotiate own mtu
} conn_status_t;
/**
* @brief A data structure that manages the %BLE servers owned by a BLE server.
*/
class NimBLEServiceMap {
public:
// NimBLEService* getByHandle(uint16_t handle);
NimBLEService* getByUUID(const char* uuid);
NimBLEService* getByUUID(const NimBLEUUID &uuid, uint8_t inst_id = 0);
// void setByHandle(uint16_t handle, NimBLEService* service);
void setByUUID(const char* uuid, NimBLEService* service);
void setByUUID(const NimBLEUUID &uuid, NimBLEService* service);
std::string toString();
NimBLEService* getFirst();
NimBLEService* getNext();
void removeService(NimBLEService *service);
int getRegisteredServiceCount();
private:
// std::map<uint16_t, NimBLEService*> m_handleMap;
std::map<NimBLEService*, std::string> m_uuidMap;
std::map<NimBLEService*, std::string>::iterator m_iterator;
};
/**
* @brief The model of a %BLE server.
*/
class NimBLEServer {
public:
uint32_t getConnectedCount();
size_t getConnectedCount();
NimBLEService* createService(const char* uuid);
NimBLEService* createService(const NimBLEUUID &uuid, uint32_t numHandles=15, uint8_t inst_id=0);
NimBLEService* createService(const NimBLEUUID &uuid, uint32_t numHandles=15,
uint8_t inst_id=0);
NimBLEAdvertising* getAdvertising();
void setCallbacks(NimBLEServerCallbacks* pCallbacks);
void startAdvertising();
void stopAdvertising();
void start();
// void removeService(BLEService* service);
NimBLEService* getServiceByUUID(const char* uuid);
NimBLEService* getServiceByUUID(const NimBLEUUID &uuid);
int disconnect(uint16_t connID, uint8_t reason = BLE_ERR_REM_USER_CONN_TERM);
// bool connect(BLEAddress address);
int disconnect(uint16_t connID,
uint8_t reason = BLE_ERR_REM_USER_CONN_TERM);
void updateConnParams(uint16_t conn_handle,
uint16_t minInterval, uint16_t maxInterval,
uint16_t latency, uint16_t timeout,
uint16_t minConnTime=0, uint16_t maxConnTime=0);
/* multi connection support */
std::map<uint16_t, conn_status_t> getPeerDevices();
void addPeerDevice(void* peer, bool is_client, uint16_t conn_id);
void removePeerDevice(uint16_t conn_id, bool client);
NimBLEServer* getServerByConnId(uint16_t conn_id);
void updatePeerMTU(uint16_t connId, uint16_t mtu);
uint16_t latency, uint16_t timeout);
uint16_t getPeerMTU(uint16_t conn_id);
uint16_t getConnId();
std::vector<uint16_t> getPeerDevices();
void advertiseOnDisconnect(bool);
private:
NimBLEServer();
//friend class BLEService;
friend class NimBLECharacteristic;
friend class NimBLEDevice;
friend class NimBLEAdvertising;
// void onHostReset();
// BLEAdvertising m_bleAdvertising;
uint16_t m_connId;
uint16_t m_svcChgChrHdl;
bool m_gattsStarted;
std::map<uint16_t, conn_status_t> m_connectedServersMap;
std::map<uint16_t, NimBLECharacteristic*> m_notifyChrMap;
NimBLEServiceMap m_serviceMap;
bool m_advertiseOnDisconnect;
NimBLEServerCallbacks* m_pServerCallbacks;
std::vector<uint16_t> m_connectedPeersVec;
// uint16_t m_svcChgChrHdl; // Future use
std::vector<NimBLEService*> m_svcVec;
std::vector<NimBLECharacteristic*> m_notifyChrVec;
static int handleGapEvent(struct ble_gap_event *event, void *arg);
}; // NimBLEServer
@ -149,7 +105,7 @@ public:
virtual bool onSecurityRequest(); //{return true;}
virtual void onAuthenticationComplete(ble_gap_conn_desc* desc);//{};
virtual bool onConfirmPIN(uint32_t pin);//{return true;}
}; // BLEServerCallbacks
}; // NimBLEServerCallbacks
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)

View file

@ -32,9 +32,10 @@ static const char* LOG_TAG = "NimBLEService"; // Tag for logging.
/**
* @brief Construct an instance of the BLEService
* @brief Construct an instance of the NimBLEService
* @param [in] uuid The UUID of the service.
* @param [in] numHandles The maximum number of handles associated with the service.
* @param [in] a pointer to the server instance that this service belongs to.
*/
NimBLEService::NimBLEService(const char* uuid, uint16_t numHandles, NimBLEServer* pServer)
: NimBLEService(NimBLEUUID(uuid), numHandles, pServer) {
@ -45,6 +46,7 @@ NimBLEService::NimBLEService(const char* uuid, uint16_t numHandles, NimBLEServer
* @brief Construct an instance of the BLEService
* @param [in] uuid The UUID of the service.
* @param [in] numHandles The maximum number of handles associated with the service.
* @param [in] a pointer to the server instance that this service belongs to.
*/
NimBLEService::NimBLEService(const NimBLEUUID &uuid, uint16_t numHandles, NimBLEServer* pServer) {
m_uuid = uuid;
@ -59,10 +61,22 @@ NimBLEService::NimBLEService(const NimBLEUUID &uuid, uint16_t numHandles, NimBLE
* @return N/A.
*/
void NimBLEService::dump() {
NIMBLE_LOGD(LOG_TAG, "Service: uuid:%s, handle: 0x%.2x",
NIMBLE_LOGD(LOG_TAG, "Service: uuid:%s, handle: 0x%2x",
m_uuid.toString().c_str(),
m_handle);
NIMBLE_LOGD(LOG_TAG, "Characteristics:\n%s", m_characteristicMap.toString().c_str());
std::string res;
int count = 0;
char hex[5];
for (auto &it: m_chrVec) {
if (count > 0) {res += "\n";}
snprintf(hex, sizeof(hex), "%04x", it->getHandle());
count++;
res += "handle: 0x";
res += hex;
res += ", uuid: " + std::string(it->getUUID());
}
NIMBLE_LOGD(LOG_TAG, "Characteristics:\n%s", res.c_str());
} // dump
@ -76,10 +90,9 @@ NimBLEUUID NimBLEService::getUUID() {
/**
* @brief Start the service.
* Here we wish to start the service which means that we will respond to partner requests about it.
* Starting a service also means that we can create the corresponding characteristics.
* @return Start the service.
* @brief Builds the database of characteristics/descriptors for the service
* and registers it with the NimBLE stack.
* @return bool success/failure .
*/
bool NimBLEService::start() {
@ -96,7 +109,7 @@ bool NimBLEService::start() {
svc[0].uuid = &m_uuid.getNative()->u;
svc[0].includes = NULL;
uint8_t numChrs = m_characteristicMap.getSize();
size_t numChrs = m_chrVec.size();
NIMBLE_LOGD(LOG_TAG,"Adding %d characteristics for service %s", numChrs, toString().c_str());
@ -107,16 +120,17 @@ bool NimBLEService::start() {
// of the characteristics for the service. We create 1 extra and set it to null
// for this purpose.
pChr_a = new ble_gatt_chr_def[numChrs+1];
NimBLECharacteristic* pCharacteristic = m_characteristicMap.getFirst();
NimBLECharacteristic* pCharacteristic = *m_chrVec.begin();
for(uint8_t i=0; i < numChrs; i++) {
uint8_t numDscs = pCharacteristic->m_descriptorMap.getSize();
for(uint8_t i=0; i < numChrs;) {
uint8_t numDscs = pCharacteristic->m_dscVec.size();
if(numDscs) {
// skip 2902 as it's automatically created by NimBLE
// if Indicate or Notify flags are set
if(((pCharacteristic->m_properties & BLE_GATT_CHR_F_INDICATE) ||
(pCharacteristic->m_properties & BLE_GATT_CHR_F_NOTIFY)) &&
pCharacteristic->getDescriptorByUUID("2902") != nullptr) {
pCharacteristic->getDescriptorByUUID("2902") != nullptr)
{
numDscs--;
}
}
@ -127,12 +141,12 @@ bool NimBLEService::start() {
// Must have last descriptor uuid = 0 so we have to create 1 extra
//NIMBLE_LOGD(LOG_TAG, "Adding %d descriptors", numDscs);
pDsc_a = new ble_gatt_dsc_def[numDscs+1];
NimBLEDescriptor* pDescriptor = pCharacteristic->m_descriptorMap.getFirst();
NimBLEDescriptor* pDescriptor = *pCharacteristic->m_dscVec.begin();
for(uint8_t d=0; d < numDscs;) {
// skip 2902
if(pDescriptor->m_uuid.equals(NimBLEUUID((uint16_t)0x2902))) {
if(pDescriptor->m_uuid == NimBLEUUID(uint16_t(0x2902))) {
//NIMBLE_LOGD(LOG_TAG, "Skipped 0x2902");
pDescriptor = pCharacteristic->m_descriptorMap.getNext();
pDescriptor = *(pCharacteristic->m_dscVec.begin()+d+1);
continue;
}
pDsc_a[d].uuid = &pDescriptor->m_uuid.getNative()->u;
@ -140,8 +154,8 @@ bool NimBLEService::start() {
pDsc_a[d].min_key_size = 0;
pDsc_a[d].access_cb = NimBLEDescriptor::handleGapEvent;
pDsc_a[d].arg = pDescriptor;
pDescriptor = pCharacteristic->m_descriptorMap.getNext();
d++;
pDescriptor = *(pCharacteristic->m_dscVec.begin() + d);
}
pDsc_a[numDscs].uuid = NULL;
@ -154,7 +168,8 @@ bool NimBLEService::start() {
pChr_a[i].flags = pCharacteristic->m_properties;
pChr_a[i].min_key_size = 0;
pChr_a[i].val_handle = &pCharacteristic->m_handle;
pCharacteristic = m_characteristicMap.getNext();
i++;
pCharacteristic = *(m_chrVec.begin() + i);
}
pChr_a[numChrs].uuid = NULL;
@ -182,21 +197,6 @@ bool NimBLEService::start() {
} // start
/**
* @brief Set the handle associated with this service.
* @param [in] handle The handle associated with the service.
*/
void NimBLEService::setHandle(uint16_t handle) {
NIMBLE_LOGD(LOG_TAG, ">> setHandle - Handle=0x%.2x, service UUID=%s)", handle, getUUID().toString().c_str());
if (m_handle != NULL_HANDLE) {
NIMBLE_LOGE(LOG_TAG, "!!! Handle is already set %.2x", m_handle);
return;
}
m_handle = handle;
NIMBLE_LOGD(LOG_TAG, "<< setHandle");
} // setHandle
/**
* @brief Get the handle associated with this service.
* @return The handle associated with this service.
@ -206,34 +206,6 @@ uint16_t NimBLEService::getHandle() {
} // getHandle
/**
* @brief Add a characteristic to the service.
* @param [in] pCharacteristic A pointer to the characteristic to be added.
*/
void NimBLEService::addCharacteristic(NimBLECharacteristic* pCharacteristic) {
// We maintain a mapping of characteristics owned by this service. These are managed by the
// BLECharacteristicMap class instance found in m_characteristicMap. We add the characteristic
// to the map and then ask the service to add the characteristic at the BLE level (ESP-IDF).
NIMBLE_LOGD(LOG_TAG, ">> addCharacteristic()");
NIMBLE_LOGD(LOG_TAG, "Adding characteristic: uuid=%s to service: %s",
pCharacteristic->getUUID().toString().c_str(),
toString().c_str());
// Check that we don't add the same characteristic twice.
if (m_characteristicMap.getByUUID(pCharacteristic->getUUID()) != nullptr) {
NIMBLE_LOGW(LOG_TAG, "<< Adding a new characteristic with the same UUID as a previous one");
//return;
}
// Remember this characteristic in our map of characteristics. At this point, we can lookup by UUID
// but not by handle. The handle is allocated to us on the ESP_GATTS_ADD_CHAR_EVT.
m_characteristicMap.setByUUID(pCharacteristic, pCharacteristic->getUUID());
NIMBLE_LOGD(LOG_TAG, "<< addCharacteristic()");
} // addCharacteristic
/**
* @brief Create a new BLE Characteristic associated with this service.
* @param [in] uuid - The UUID of the characteristic.
@ -253,8 +225,15 @@ NimBLECharacteristic* NimBLEService::createCharacteristic(const char* uuid, uint
*/
NimBLECharacteristic* NimBLEService::createCharacteristic(const NimBLEUUID &uuid, uint32_t properties) {
NimBLECharacteristic* pCharacteristic = new NimBLECharacteristic(uuid, properties, this);
addCharacteristic(pCharacteristic);
//pCharacteristic->executeCreate(this);
// Check that we don't add the same characteristic twice.
if (getCharacteristic(uuid) != nullptr) {
NIMBLE_LOGW(LOG_TAG, "<< Adding a duplicate characteristic with UUID: %s",
std::string(uuid).c_str());
}
// Remember this characteristic in our vector of characteristics.
m_chrVec.push_back(pCharacteristic);
return pCharacteristic;
} // createCharacteristic
@ -265,7 +244,13 @@ NimBLECharacteristic* NimBLEService::getCharacteristic(const char* uuid) {
NimBLECharacteristic* NimBLEService::getCharacteristic(const NimBLEUUID &uuid) {
return m_characteristicMap.getByUUID(uuid);
for (auto &it : m_chrVec) {
if (it->getUUID() == uuid) {
return it;
}
}
return nullptr;
}

View file

@ -29,28 +29,6 @@
class NimBLEServer;
class NimBLECharacteristic;
/**
* @brief A data mapping used to manage the set of %BLE characteristics known to the server.
*/
class NimBLECharacteristicMap {
public:
void setByUUID(NimBLECharacteristic* pCharacteristic, const char* uuid);
void setByUUID(NimBLECharacteristic* pCharacteristic, const NimBLEUUID &uuid);
void setByHandle(uint16_t handle, NimBLECharacteristic* pCharacteristic);
NimBLECharacteristic* getByUUID(const char* uuid);
NimBLECharacteristic* getByUUID(const NimBLEUUID &uuid);
NimBLECharacteristic* getByHandle(uint16_t handle);
NimBLECharacteristic* getFirst();
NimBLECharacteristic* getNext();
uint8_t getSize();
std::string toString();
private:
std::map<NimBLECharacteristic*, std::string> m_uuidMap;
std::map<uint16_t, NimBLECharacteristic*> m_handleMap;
std::map<NimBLECharacteristic*, std::string>::iterator m_iterator;
};
/**
* @brief The model of a %BLE service.
@ -59,11 +37,13 @@ private:
class NimBLEService {
public:
NimBLECharacteristic* createCharacteristic(const char* uuid,
uint32_t properties = NIMBLE_PROPERTY::READ |
uint32_t properties =
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE);
NimBLECharacteristic* createCharacteristic(const NimBLEUUID &uuid,
uint32_t properties = NIMBLE_PROPERTY::READ |
uint32_t properties =
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE);
void dump();
@ -72,27 +52,24 @@ public:
NimBLEUUID getUUID();
NimBLEServer* getServer();
bool start();
// void stop();
std::string toString();
uint16_t getHandle();
uint8_t m_instId = 0;
private:
NimBLEService(const char* uuid, uint16_t numHandles, NimBLEServer* pServer);
NimBLEService(const NimBLEUUID &uuid, uint16_t numHandles, NimBLEServer* pServer);
friend class NimBLEServer;
friend class NimBLEDevice;
void addCharacteristic(NimBLECharacteristic* pCharacteristic);
NimBLECharacteristicMap m_characteristicMap;
uint16_t m_handle;
NimBLEServer* m_pServer = nullptr;
NimBLEServer* m_pServer;
NimBLEUUID m_uuid;
uint16_t m_numHandles;
void setHandle(uint16_t handle);
}; // BLEService
std::vector<NimBLECharacteristic*> m_chrVec;
}; // NimBLEService
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)

View file

@ -1,145 +0,0 @@
/*
* NimBLEService.cpp
*
* Created: on March 7, 2020
* Author H2zero
*
* Originally:
*
* BLEServiceMap.cpp
*
* Created on: Jun 22, 2017
* Author: kolban
*/
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h"
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#include "NimBLEService.h"
/**
* @brief Return the service by UUID.
* @param [in] UUID The UUID to look up the service.
* @return The characteristic.
*/
NimBLEService* NimBLEServiceMap::getByUUID(const char* uuid) {
return getByUUID(NimBLEUUID(uuid));
}
/**
* @brief Return the service by UUID.
* @param [in] UUID The UUID to look up the service.
* @return The characteristic.
*/
NimBLEService* NimBLEServiceMap::getByUUID(const NimBLEUUID &uuid, uint8_t inst_id) {
for (auto &myPair : m_uuidMap) {
if (myPair.first->getUUID().equals(uuid)) {
return myPair.first;
}
}
//return m_uuidMap.at(uuid.toString());
return nullptr;
} // getByUUID
/**
* @brief Return the service by handle.
* @param [in] handle The handle to look up the service.
* @return The service.
*/
/*
NimBLEService* NimBLEServiceMap::getByHandle(uint16_t handle) {
return m_handleMap.at(handle);
} // getByHandle
*/
/**
* @brief Set the service by UUID.
* @param [in] uuid The uuid of the service.
* @param [in] characteristic The service to cache.
* @return N/A.
*/
void NimBLEServiceMap::setByUUID(const NimBLEUUID &uuid, NimBLEService* service) {
m_uuidMap.insert(std::pair<NimBLEService*, std::string>(service, uuid.toString()));
} // setByUUID
/**
* @brief Set the service by handle.
* @param [in] handle The handle of the service.
* @param [in] service The service to cache.
* @return N/A.
*/
/*
void NimBLEServiceMap::setByHandle(uint16_t handle, NimBLEService* service) {
m_handleMap.insert(std::pair<uint16_t, NimBLEService*>(handle, service));
} // setByHandle
*/
/**
* @brief Return a string representation of the service map.
* @return A string representation of the service map.
*/
std::string NimBLEServiceMap::toString() {
std::string res;
//char hex[5];
for (auto &myPair: m_uuidMap) {
// res += "handle: 0x";
// snprintf(hex, sizeof(hex), "%04x", myPair.first);
// res += hex;
res += ", uuid: " + myPair.second + "\n";
}
return res;
} // toString
/**
* @brief Get the first service in the map.
* @return The first service in the map.
*/
NimBLEService* NimBLEServiceMap::getFirst() {
m_iterator = m_uuidMap.begin();
if (m_iterator == m_uuidMap.end()) return nullptr;
NimBLEService* pRet = m_iterator->first;
m_iterator++;
return pRet;
} // getFirst
/**
* @brief Get the next service in the map.
* @return The next service in the map.
*/
NimBLEService* NimBLEServiceMap::getNext() {
if (m_iterator == m_uuidMap.end()) return nullptr;
NimBLEService* pRet = m_iterator->first;
m_iterator++;
return pRet;
} // getNext
/**
* @brief Removes service from maps.
* @return N/A.
*/
void NimBLEServiceMap::removeService(NimBLEService* service) {
//m_handleMap.erase(service->getHandle());
m_uuidMap.erase(service);
} // removeService
/**
* @brief Returns the amount of registered services
* @return amount of registered services
*/
int NimBLEServiceMap::getRegisteredServiceCount(){
//return m_handleMap.size();
return m_uuidMap.size();
}
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#endif /* CONFIG_BT_ENABLED */

View file

@ -1,143 +0,0 @@
/*
* NimNimBLEValue.cpp
*
* Created: on March 6, 2020
* Author H2zero
*
* Originally:
*
* BLEValue.cpp
*
* Created on: Jul 17, 2017
* Author: kolban
*/
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h"
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#include "NimBLEValue.h"
#include "NimBLELog.h"
static const char* LOG_TAG="NimBLEValue";
NimBLEValue::NimBLEValue() {
m_accumulation = "";
m_value = "";
m_readOffset = 0;
} // NimBLEValue
/**
* @brief Add a message part to the accumulation.
* The accumulation is a growing set of data that is added to until a commit or cancel.
* @param [in] part A message part being added.
*/
void NimBLEValue::addPart(const std::string &part) {
NIMBLE_LOGD(LOG_TAG, ">> addPart: length=%d", part.length());
m_accumulation += part;
} // addPart
/**
* @brief Add a message part to the accumulation.
* The accumulation is a growing set of data that is added to until a commit or cancel.
* @param [in] pData A message part being added.
* @param [in] length The number of bytes being added.
*/
void NimBLEValue::addPart(const uint8_t* pData, size_t length) {
NIMBLE_LOGD(LOG_TAG, ">> addPart: length=%d", length);
m_accumulation += std::string((char*) pData, length);
} // addPart
/**
* @brief Cancel the current accumulation.
*/
void NimBLEValue::cancel() {
NIMBLE_LOGD(LOG_TAG, ">> cancel");
m_accumulation = "";
m_readOffset = 0;
} // cancel
/**
* @brief Commit the current accumulation.
* When writing a value, we may find that we write it in "parts" meaning that the writes come in in pieces
* of the overall message. After the last part has been received, we may perform a commit which means that
* we now have the complete message and commit the change as a unit.
*/
void NimBLEValue::commit() {
NIMBLE_LOGD(LOG_TAG, ">> commit");
// If there is nothing to commit, do nothing.
if (m_accumulation.length() == 0) return;
setValue(m_accumulation);
m_accumulation = "";
m_readOffset = 0;
} // commit
/**
* @brief Get a pointer to the data.
* @return A pointer to the data.
*/
uint8_t* NimBLEValue::getData() {
return (uint8_t*) m_value.data();
}
/**
* @brief Get the length of the data in bytes.
* @return The length of the data in bytes.
*/
size_t NimBLEValue::getLength() {
return m_value.length();
} // getLength
/**
* @brief Get the read offset.
* @return The read offset into the read.
*/
uint16_t NimBLEValue::getReadOffset() {
return m_readOffset;
} // getReadOffset
/**
* @brief Get the current value.
*/
std::string NimBLEValue::getValue() {
return m_value;
} // getValue
/**
* @brief Set the read offset
* @param [in] readOffset The offset into the read.
*/
void NimBLEValue::setReadOffset(uint16_t readOffset) {
m_readOffset = readOffset;
} // setReadOffset
/**
* @brief Set the current value.
*/
void NimBLEValue::setValue(const std::string &value) {
m_value = value;
} // setValue
/**
* @brief Set the current value.
* @param [in] pData The data for the current value.
* @param [in] The length of the new current value.
*/
void NimBLEValue::setValue(const uint8_t* pData, size_t length) {
m_value = std::string((char*) pData, length);
} // setValue
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#endif // CONFIG_BT_ENABLED

View file

@ -1,52 +0,0 @@
/*
* NimBLEValue.h
*
* Created: on March 6, 2020
* Author H2zero
*
* Originally:
*
* BLEValue.h
*
* Created on: Jul 17, 2017
* Author: kolban
*/
#ifndef MAIN_BLEVALUE_H_
#define MAIN_BLEVALUE_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h"
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#include <string>
/**
* @brief The model of a %BLE value.
*/
class NimBLEValue {
public:
NimBLEValue();
void addPart(const std::string &part);
void addPart(const uint8_t* pData, size_t length);
void cancel();
void commit();
uint8_t* getData();
size_t getLength();
uint16_t getReadOffset();
std::string getValue();
void setReadOffset(uint16_t readOffset);
void setValue(const std::string &value);
void setValue(const uint8_t* pData, size_t length);
private:
std::string m_accumulation;
uint16_t m_readOffset;
std::string m_value;
};
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#endif // CONFIG_BT_ENABLED
#endif /* MAIN_BLEVALUE_H_ */