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

View file

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

View file

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

View file

@ -100,7 +100,8 @@ void NimBLEAdvertising::setMaxInterval(uint16_t maxinterval) {
m_advParams.itvl_max = maxinterval; m_advParams.itvl_max = maxinterval;
} // setMaxInterval } // setMaxInterval
// These are dummy functions for now for compatibility
/* These are dummy functions for now for compatibility */
void NimBLEAdvertising::setMinPreferred(uint16_t mininterval) { void NimBLEAdvertising::setMinPreferred(uint16_t mininterval) {
//m_advData.min_interval = mininterval; //m_advData.min_interval = mininterval;
} // } //
@ -108,7 +109,8 @@ void NimBLEAdvertising::setMinPreferred(uint16_t mininterval) {
void NimBLEAdvertising::setMaxPreferred(uint16_t maxinterval) { void NimBLEAdvertising::setMaxPreferred(uint16_t maxinterval) {
//m_advData.max_interval = maxinterval; //m_advData.max_interval = maxinterval;
} // } //
////////////////////////////////////////////////////////// /*******************************************************/
void NimBLEAdvertising::setScanResponse(bool set) { void NimBLEAdvertising::setScanResponse(bool set) {
m_scanResp = set; m_scanResp = set;
@ -204,19 +206,19 @@ void NimBLEAdvertising::start() {
} }
#endif #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 already advertising just return
if(ble_gap_adv_active()) { if(ble_gap_adv_active()) {
return; return;
} }
if (!m_customAdvData && !m_advSvcsSet && numServices > 0) { int rc = 0;
for (int i = 0; i < numServices; i++) {
if(m_serviceUUIDs[i].getNative()->u.type == BLE_UUID_TYPE_16) { 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; int add = (m_advData.num_uuids16 > 0) ? 2 : 4;
if((payloadLen + add) > 31){ if((payloadLen + add) > 31){
m_advData.uuids16_is_complete = 0; m_advData.uuids16_is_complete = 0;
@ -225,19 +227,19 @@ void NimBLEAdvertising::start() {
payloadLen += add; payloadLen += add;
if(nullptr == (m_advData.uuids16 = (ble_uuid16_t*)realloc(m_advData.uuids16, if(nullptr == (m_advData.uuids16 = (ble_uuid16_t*)realloc(m_advData.uuids16,
(m_advData.num_uuids16 + 1) * sizeof(ble_uuid16_t)))) (m_advData.num_uuids16 + 1) * sizeof(ble_uuid16_t))))
{ {
NIMBLE_LOGE(LOG_TAG, "Error, no mem"); NIMBLE_LOGE(LOG_TAG, "Error, no mem");
abort(); abort();
} }
memcpy(&m_advData.uuids16[m_advData.num_uuids16].value, 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[m_advData.num_uuids16].u.type = BLE_UUID_TYPE_16;
m_advData.uuids16_is_complete = 1; m_advData.uuids16_is_complete = 1;
m_advData.num_uuids16++; 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; int add = (m_advData.num_uuids32 > 0) ? 4 : 6;
if((payloadLen + add) > 31){ if((payloadLen + add) > 31){
m_advData.uuids32_is_complete = 0; m_advData.uuids32_is_complete = 0;
@ -246,19 +248,19 @@ void NimBLEAdvertising::start() {
payloadLen += add; payloadLen += add;
if(nullptr == (m_advData.uuids32 = (ble_uuid32_t*)realloc(m_advData.uuids32, if(nullptr == (m_advData.uuids32 = (ble_uuid32_t*)realloc(m_advData.uuids32,
(m_advData.num_uuids32 + 1) * sizeof(ble_uuid32_t)))) (m_advData.num_uuids32 + 1) * sizeof(ble_uuid32_t))))
{ {
NIMBLE_LOGE(LOG_TAG, "Error, no mem"); NIMBLE_LOGE(LOG_TAG, "Error, no mem");
abort(); abort();
} }
memcpy(&m_advData.uuids32[m_advData.num_uuids32].value, 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[m_advData.num_uuids32].u.type = BLE_UUID_TYPE_32;
m_advData.uuids32_is_complete = 1; m_advData.uuids32_is_complete = 1;
m_advData.num_uuids32++; 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; int add = (m_advData.num_uuids128 > 0) ? 16 : 18;
if((payloadLen + add) > 31){ if((payloadLen + add) > 31){
m_advData.uuids128_is_complete = 0; m_advData.uuids128_is_complete = 0;
@ -267,12 +269,13 @@ void NimBLEAdvertising::start() {
payloadLen += add; payloadLen += add;
if(nullptr == (m_advData.uuids128 = (ble_uuid128_t*)realloc(m_advData.uuids128, 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"); NIMBLE_LOGE(LOG_TAG, "Error, no mem");
abort(); abort();
} }
memcpy(&m_advData.uuids128[m_advData.num_uuids128].value, 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[m_advData.num_uuids128].u.type = BLE_UUID_TYPE_128;
m_advData.uuids128_is_complete = 1; m_advData.uuids128_is_complete = 1;
@ -315,11 +318,11 @@ void NimBLEAdvertising::start() {
rc = ble_gap_adv_rsp_set_fields(&m_scanData); rc = ble_gap_adv_rsp_set_fields(&m_scanData);
if (rc != 0) { 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(); abort();
} }
// if not using scan response and there is room, // 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) { } else if (payloadLen < 29) {
m_advData.tx_pwr_lvl_is_present = 1; m_advData.tx_pwr_lvl_is_present = 1;
m_advData.tx_pwr_lvl = NimBLEDevice::getPower(); m_advData.tx_pwr_lvl = NimBLEDevice::getPower();
@ -327,7 +330,7 @@ void NimBLEAdvertising::start() {
rc = ble_gap_adv_set_fields(&m_advData); rc = ble_gap_adv_set_fields(&m_advData);
if (rc != 0) { 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(); abort();
} }
@ -349,22 +352,16 @@ void NimBLEAdvertising::start() {
m_advData.num_uuids16 = 0; m_advData.num_uuids16 = 0;
} }
m_advSvcsSet = true; m_advDataSet = 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();
} }
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) #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, &m_advParams,
(pServer != nullptr) ? NimBLEServer::handleGapEvent : NULL, (pServer != nullptr) ? NimBLEServer::handleGapEvent : NULL,
pServer); pServer);
#else #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); &m_advParams, NULL,NULL);
#endif #endif
if (rc != 0) { if (rc != 0) {
@ -398,7 +395,7 @@ void NimBLEAdvertising::stop() {
* we need clear the flag so it reloads it. * we need clear the flag so it reloads it.
*/ */
void NimBLEAdvertising::onHostReset() { 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_customAdvData = false; // Are we using custom advertising data?
bool m_customScanResponseData = false; // Are we using custom scan response data? bool m_customScanResponseData = false; // Are we using custom scan response data?
bool m_scanResp = true; bool m_scanResp = true;
bool m_advSvcsSet = false; bool m_advDataSet = false;
}; };

View file

@ -22,18 +22,16 @@
#include "NimBLEUtils.h" #include "NimBLEUtils.h"
#include "NimBLELog.h" #include "NimBLELog.h"
#include <string>
#define NULL_HANDLE (0xffff) #define NULL_HANDLE (0xffff)
static NimBLECharacteristicCallbacks defaultCallback; static NimBLECharacteristicCallbacks defaultCallback;
static const char* LOG_TAG = "NimBLECharacteristic"; static const char* LOG_TAG = "NimBLECharacteristic";
/** /**
* @brief Construct a characteristic * @brief Construct a characteristic
* @param [in] uuid - UUID (const char*) for the characteristic. * @param [in] uuid - UUID (const char*) for the characteristic.
* @param [in] properties - Properties 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::NimBLECharacteristic(const char* uuid, uint16_t properties, NimBLEService* pService)
: NimBLECharacteristic(NimBLEUUID(uuid), properties, pService) { : NimBLECharacteristic(NimBLEUUID(uuid), properties, pService) {
@ -43,6 +41,7 @@ NimBLECharacteristic::NimBLECharacteristic(const char* uuid, uint16_t properties
* @brief Construct a characteristic * @brief Construct a characteristic
* @param [in] uuid - UUID for the characteristic. * @param [in] uuid - UUID for the characteristic.
* @param [in] properties - Properties 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) { NimBLECharacteristic::NimBLECharacteristic(const NimBLEUUID &uuid, uint16_t properties, NimBLEService* pService) {
m_uuid = uuid; m_uuid = uuid;
@ -50,39 +49,24 @@ NimBLECharacteristic::NimBLECharacteristic(const NimBLEUUID &uuid, uint16_t prop
m_properties = properties; m_properties = properties;
m_pCallbacks = &defaultCallback; m_pCallbacks = &defaultCallback;
m_pService = pService; m_pService = pService;
// Backward Compatibility - to be removed m_value = "";
/* setBroadcastProperty((properties & PROPERTY_BROADCAST) != 0); m_valMux = portMUX_INITIALIZER_UNLOCKED;
setReadProperty((properties & PROPERTY_READ) != 0);
setWriteProperty((properties & PROPERTY_WRITE) != 0); if(properties & NIMBLE_PROPERTY::INDICATE){
setNotifyProperty((properties & PROPERTY_NOTIFY) != 0); m_pIndSemaphore = new FreeRTOS::Semaphore("ConfEvt");
setIndicateProperty((properties & PROPERTY_INDICATE) != 0); } else {
setWriteNoResponseProperty((properties & PROPERTY_WRITE_NR) != 0); m_pIndSemaphore = nullptr;
*/ }
///////////////////////////////////////////
} // NimBLECharacteristic } // NimBLECharacteristic
/** /**
* @brief Destructor. * @brief Destructor.
*/ */
NimBLECharacteristic::~NimBLECharacteristic() { NimBLECharacteristic::~NimBLECharacteristic() {
} // ~NimBLECharacteristic if(m_pIndSemaphore != nullptr) {
delete(m_pIndSemaphore);
/**
* @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;
} }
m_descriptorMap.setByUUID(pDescriptor->getUUID(), pDescriptor); } // ~NimBLECharacteristic
NIMBLE_LOGD(LOG_TAG, "<< addDescriptor()");
} // addDescriptor
/** /**
@ -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* NimBLECharacteristic::createDescriptor(const NimBLEUUID &uuid, uint32_t properties, uint16_t max_len) {
NimBLEDescriptor* pDescriptor = nullptr; 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)) { 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"); 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. // 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) { if(pDescriptor == nullptr) {
pDescriptor = new NimBLE2902(this); pDescriptor = new NimBLE2902(this);
} else { } else {
return pDescriptor; return pDescriptor;
} }
} else if (uuid.equals(NimBLEUUID((uint16_t)0x2904))) { } else if (uuid == NimBLEUUID(uint16_t(0x2904))) {
pDescriptor = new NimBLE2904(this); pDescriptor = new NimBLE2904(this);
} else { } else {
pDescriptor = new NimBLEDescriptor(uuid, properties, max_len, this); pDescriptor = new NimBLEDescriptor(uuid, properties, max_len, this);
} }
addDescriptor(pDescriptor);
m_dscVec.push_back(pDescriptor);
return pDescriptor; return pDescriptor;
} // createCharacteristic } // createCharacteristic
@ -132,8 +117,8 @@ NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const NimBLEUUID &uuid,
* @param [in] descriptorUUID The UUID of the descriptor that we wish to retrieve. * @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. * @return The BLE Descriptor. If no such descriptor is associated with the characteristic, nullptr is returned.
*/ */
NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const char* descriptorUUID) { NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const char* uuid) {
return m_descriptorMap.getByUUID(NimBLEUUID(descriptorUUID)); return getDescriptorByUUID(NimBLEUUID(uuid));
} // getDescriptorByUUID } // getDescriptorByUUID
@ -142,8 +127,13 @@ NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const char* descript
* @param [in] descriptorUUID The UUID of the descriptor that we wish to retrieve. * @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. * @return The BLE Descriptor. If no such descriptor is associated with the characteristic, nullptr is returned.
*/ */
NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const NimBLEUUID &descriptorUUID) { NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const NimBLEUUID &uuid) {
return m_descriptorMap.getByUUID(descriptorUUID); for (auto &it : m_dscVec) {
if (it->getUUID() == uuid) {
return it;
}
}
return nullptr;
} // getDescriptorByUUID } // getDescriptorByUUID
@ -155,11 +145,6 @@ uint16_t NimBLECharacteristic::getHandle() {
return m_handle; return m_handle;
} // getHandle } // getHandle
/*
void NimBLECharacteristic::setAccessPermissions(uint16_t perm) {
m_permissions = perm;
}
*/
uint8_t NimBLECharacteristic::getProperties() { uint8_t NimBLECharacteristic::getProperties() {
return m_properties; return m_properties;
@ -188,25 +173,24 @@ NimBLEUUID NimBLECharacteristic::getUUID() {
* @return A pointer to storage containing the current characteristic value. * @return A pointer to storage containing the current characteristic value.
*/ */
std::string NimBLECharacteristic::getValue() { std::string NimBLECharacteristic::getValue() {
return m_value.getValue(); portENTER_CRITICAL(&m_valMux);
std::string retVal = m_value;
portEXIT_CRITICAL(&m_valMux);
return retVal;
} // getValue } // 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. * @brief Retrieve the the current data length of the characteristic.
* @return The length of the current characteristic data. * @return The length of the current characteristic data.
*/ */
size_t NimBLECharacteristic:: getDataLength() { 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){ if(ble_uuid_cmp(uuid, &pCharacteristic->getUUID().getNative()->u) == 0){
switch(ctxt->op) { switch(ctxt->op) {
case BLE_GATT_ACCESS_OP_READ_CHR: { 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 // 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. // so we don't want to call the onRead() callback again.
if(ctxt->om->om_pkthdr_len > 8) { if(ctxt->om->om_pkthdr_len > 8) {
pCharacteristic->m_pCallbacks->onRead(pCharacteristic); 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; return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
} }
case BLE_GATT_ACCESS_OP_WRITE_CHR: { 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) { if (ctxt->om->om_len > BLE_ATT_ATTR_MAX_LEN) {
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
} }
//pCharacteristic->setValue(ctxt->om->om_data, ctxt->om->om_len); uint8_t buf[BLE_ATT_ATTR_MAX_LEN];
pCharacteristic->m_value.addPart(ctxt->om->om_data, ctxt->om->om_len); size_t len = ctxt->om->om_len;
memcpy(buf, ctxt->om->om_data,len);
os_mbuf *next; os_mbuf *next;
next = SLIST_NEXT(ctxt->om, om_next); next = SLIST_NEXT(ctxt->om, om_next);
while(next != NULL){ while(next != NULL){
//NIMBLE_LOGD(LOG_TAG, "Found long write data, len:%d", next->om_len); if((len + next->om_len) > BLE_ATT_ATTR_MAX_LEN) {
pCharacteristic->m_value.addPart(next->om_data, next->om_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); next = SLIST_NEXT(next, om_next);
} }
pCharacteristic->m_value.commit();
pCharacteristic->setValue(buf, len);
pCharacteristic->m_pCallbacks->onWrite(pCharacteristic); pCharacteristic->m_pCallbacks->onWrite(pCharacteristic);
return 0; return 0;
@ -278,14 +271,18 @@ void NimBLECharacteristic::setSubscribe(struct ble_gap_event *event) {
subVal |= NIMBLE_DESC_FLAG_INDICATE; subVal |= NIMBLE_DESC_FLAG_INDICATE;
} }
m_semaphoreConfEvt.give((subVal | NIMBLE_DESC_FLAG_INDICATE) ? 0 : if(m_pIndSemaphore != nullptr) {
NimBLECharacteristicCallbacks::Status::ERROR_INDICATE_DISABLED); 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){ 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; return;
} }
@ -294,21 +291,28 @@ void NimBLECharacteristic::setSubscribe(struct ble_gap_event *event) {
p2902->m_pCallbacks->onWrite(p2902); p2902->m_pCallbacks->onWrite(p2902);
auto it = p2902->m_subscribedMap.find(event->subscribe.conn_handle); auto it = p2902->m_subscribedVec.begin();
if(subVal > 0 && it == p2902->m_subscribedMap.cend()) { for(;it != p2902->m_subscribedVec.end(); ++it) {
p2902->m_subscribedMap.insert(std::pair<uint16_t, uint16_t>(event->subscribe.conn_handle, subVal)); if((*it).conn_id == event->subscribe.conn_handle) {
return; break;
} else if(it != p2902->m_subscribedMap.cend()) { }
p2902->m_subscribedMap.erase(event->subscribe.conn_handle);
return;
} }
/*
if(event->subscribe.reason == BLE_GAP_SUBSCRIBE_REASON_TERM) { if(subVal > 0) {
p2902->m_subscribedMap.erase(event->subscribe.conn_handle); if(it == p2902->m_subscribedVec.end()) {
return; 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;
}
(*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 * @return N/A
*/ */
void NimBLECharacteristic::indicate() { void NimBLECharacteristic::indicate() {
NIMBLE_LOGD(LOG_TAG, ">> indicate: length: %d", m_value.getValue().length()); NIMBLE_LOGD(LOG_TAG, ">> indicate: length: %d", getDataLength());
notify(false); notify(false);
NIMBLE_LOGD(LOG_TAG, "<< indicate"); NIMBLE_LOGD(LOG_TAG, "<< indicate");
} // indicate } // indicate
@ -331,36 +335,38 @@ void NimBLECharacteristic::indicate() {
* @return N/A. * @return N/A.
*/ */
void NimBLECharacteristic::notify(bool is_notification) { 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); NimBLE2902* p2902 = (NimBLE2902*)getDescriptorByUUID(uint16_t(0x2902));
assert(getService()->getServer() != nullptr);
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) { if (p2902->m_subscribedVec.size() == 0) {
NIMBLE_LOGD(LOG_TAG, "<< notify: No connected clients."); NIMBLE_LOGD(LOG_TAG, "<< notify: No clients subscribed.");
return; return;
} }
m_pCallbacks->onNotify(this); m_pCallbacks->onNotify(this);
std::string value = getValue();
size_t length = value.length();
int rc = 0; int rc = 0;
NimBLE2902* p2902 = (NimBLE2902*)getDescriptorByUUID((uint16_t)0x2902);
for (auto it = p2902->m_subscribedMap.cbegin(); it != p2902->m_subscribedMap.cend(); ++it) { for (auto &it : p2902->m_subscribedVec) {
uint16_t _mtu = getService()->getServer()->getPeerMTU((*it).first); uint16_t _mtu = getService()->getServer()->getPeerMTU(it.conn_id);
// 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();
os_mbuf *om; os_mbuf *om;
if(_mtu == 0) { if(_mtu == 0) {
//NIMBLE_LOGD(LOG_TAG, "peer not connected, removing from map"); //NIMBLE_LOGD(LOG_TAG, "peer not connected");
p2902->m_subscribedMap.erase((*it).first); continue;
it = p2902->m_subscribedMap.cbegin(); }
if(it == p2902->m_subscribedMap.cend()) {
return; if(it.sub_val == 0) {
} //NIMBLE_LOGD(LOG_TAG, "Skipping unsubscribed client");
continue; continue;
} }
@ -368,54 +374,52 @@ void NimBLECharacteristic::notify(bool is_notification) {
NIMBLE_LOGW(LOG_TAG, "- Truncating to %d bytes (maximum notify size)", _mtu - 3); NIMBLE_LOGW(LOG_TAG, "- Truncating to %d bytes (maximum notify size)", _mtu - 3);
} }
if((*it).second == 0) { if(is_notification && (!(it.sub_val & NIMBLE_DESC_FLAG_NOTIFY))) {
//NIMBLE_LOGI(LOG_TAG, "Skipping unsubscribed client");
continue;
}
if(is_notification && (!((*it).second & NIMBLE_DESC_FLAG_NOTIFY))) {
NIMBLE_LOGW(LOG_TAG, NIMBLE_LOGW(LOG_TAG,
"Sending notification to client subscribed to indications, sending indication instead"); "Sending notification to client subscribed to indications, sending indication instead");
is_notification = false; 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, 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; is_notification = true;
} }
// don't create the m_buf until we are sure to send the data or else // 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 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. // 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) { NimBLECharacteristicCallbacks::Status statusRC;
m_semaphoreConfEvt.take("indicate"); if(m_pIndSemaphore != nullptr && !is_notification) {
rc = ble_gattc_indicate_custom((*it).first, m_handle, om); m_pIndSemaphore->take("indicate");
rc = ble_gattc_indicate_custom(it.conn_id, m_handle, om);
if(rc != 0){ if(rc != 0){
m_semaphoreConfEvt.give(); m_pIndSemaphore->give();
m_pCallbacks->onStatus(this, NimBLECharacteristicCallbacks::Status::ERROR_GATT, rc); statusRC = NimBLECharacteristicCallbacks::Status::ERROR_GATT;
return; } else {
rc = m_pIndSemaphore->wait();
} }
rc = m_semaphoreConfEvt.wait(); if(rc == 0 || rc == BLE_HS_EDONE) {
rc = 0;
if(rc == BLE_HS_ETIMEOUT) { statusRC = NimBLECharacteristicCallbacks::Status::SUCCESS_INDICATE;
m_pCallbacks->onStatus(this, NimBLECharacteristicCallbacks::Status::ERROR_INDICATE_TIMEOUT, rc); } else if(rc == BLE_HS_ETIMEOUT) {
} else if(rc == BLE_HS_EDONE) { statusRC = NimBLECharacteristicCallbacks::Status::ERROR_INDICATE_TIMEOUT;
m_pCallbacks->onStatus(this, NimBLECharacteristicCallbacks::Status::SUCCESS_INDICATE, rc);
} else { } else {
m_pCallbacks->onStatus(this, NimBLECharacteristicCallbacks::Status::ERROR_INDICATE_FAILURE, rc); statusRC = NimBLECharacteristicCallbacks::Status::ERROR_INDICATE_FAILURE;
} }
} else { } 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) { if(rc == 0) {
m_pCallbacks->onStatus(this, NimBLECharacteristicCallbacks::Status::SUCCESS_NOTIFY, 0); statusRC = NimBLECharacteristicCallbacks::Status::SUCCESS_NOTIFY;
} else { } 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"); NIMBLE_LOGD(LOG_TAG, "<< notify");
@ -434,87 +438,6 @@ void NimBLECharacteristic::setCallbacks(NimBLECharacteristicCallbacks* pCallback
} }
} // setCallbacks } // 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. * @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. * @param [in] length The length of the data in bytes.
*/ */
void NimBLECharacteristic::setValue(const uint8_t* data, size_t length) { 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); 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()); NIMBLE_LOGD(LOG_TAG, ">> setValue: length=%d, data=%s, characteristic UUID=%s", length, pHex, getUUID().toString().c_str());
free(pHex); free(pHex);
#endif
if (length > BLE_ATT_ATTR_MAX_LEN) { 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); NIMBLE_LOGE(LOG_TAG, "Size %d too large, must be no bigger than %d", length, BLE_ATT_ATTR_MAX_LEN);
return; return;
} }
m_value.setValue(data, length); portENTER_CRITICAL(&m_valMux);
m_value = std::string((char*)data, length);
// if(m_handle != NULL_HANDLE) { portEXIT_CRITICAL(&m_valMux);
//ble_gatts_chr_updated(m_handle);
// ble_gattc_notify(getService()->getServer()->m_connId, m_handle);
// }
NIMBLE_LOGD(LOG_TAG, "<< setValue"); NIMBLE_LOGD(LOG_TAG, "<< setValue");
} // setValue } // setValue

View file

@ -42,42 +42,16 @@ typedef enum {
#include "NimBLEService.h" #include "NimBLEService.h"
#include "NimBLEDescriptor.h" #include "NimBLEDescriptor.h"
#include "NimBLEUUID.h"
#include "NimBLEValue.h"
#include "FreeRTOS.h" #include "FreeRTOS.h"
#include <string> #include <string>
#include <map> #include <vector>
class NimBLEService; class NimBLEService;
class NimBLEDescriptor; class NimBLEDescriptor;
class NimBLECharacteristicCallbacks; 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. * @brief The model of a %BLE Characteristic.
* *
@ -87,84 +61,67 @@ private:
class NimBLECharacteristic { class NimBLECharacteristic {
public: public:
NimBLEDescriptor* createDescriptor(const char* uuid, NimBLEDescriptor* createDescriptor(const char* uuid,
uint32_t properties = NIMBLE_PROPERTY::READ | uint32_t properties =
NIMBLE_PROPERTY::WRITE, NIMBLE_PROPERTY::READ |
uint16_t max_len = 100); NIMBLE_PROPERTY::WRITE,
uint16_t max_len = 100);
NimBLEDescriptor* createDescriptor(const NimBLEUUID &uuid, NimBLEDescriptor* createDescriptor(const NimBLEUUID &uuid,
uint32_t properties = NIMBLE_PROPERTY::READ | uint32_t properties =
NIMBLE_PROPERTY::WRITE, NIMBLE_PROPERTY::READ |
uint16_t max_len = 100); NIMBLE_PROPERTY::WRITE,
uint16_t max_len = 100);
NimBLEDescriptor* getDescriptorByUUID(const char* descriptorUUID); NimBLEDescriptor* getDescriptorByUUID(const char* uuid);
NimBLEDescriptor* getDescriptorByUUID(const NimBLEUUID &descriptorUUID); NimBLEDescriptor* getDescriptorByUUID(const NimBLEUUID &uuid);
NimBLEUUID getUUID(); NimBLEUUID getUUID();
std::string getValue(); std::string getValue();
uint8_t* getData();
size_t getDataLength(); size_t getDataLength();
void indicate();
void indicate(); void notify(bool is_notification = true);
void notify(bool is_notification = true); void setCallbacks(NimBLECharacteristicCallbacks* pCallbacks);
void setCallbacks(NimBLECharacteristicCallbacks* pCallbacks); void setValue(const uint8_t* data, size_t size);
// Backward Compatibility - to be removed void setValue(const std::string &value);
void setBroadcastProperty(bool value); void setValue(uint16_t& data16);
void setIndicateProperty(bool value); void setValue(uint32_t& data32);
void setNotifyProperty(bool value); void setValue(int& data32);
void setReadProperty(bool value); void setValue(float& data32);
void setWriteProperty(bool value); void setValue(double& data64);
void setWriteNoResponseProperty(bool value); std::string toString();
////////////////////////////////////////////////////// uint16_t getHandle();
void setValue(const uint8_t* data, size_t size);
void setValue(const std::string &value);
void setValue(uint16_t& data16);
void setValue(uint32_t& data32);
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: private:
friend class NimBLEServer; friend class NimBLEServer;
friend class NimBLEService; 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,
NimBLEService* pService = nullptr); uint16_t properties =
NimBLECharacteristic(const NimBLEUUID &uuid, uint16_t properties = NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE, NIMBLE_PROPERTY::READ |
NimBLEService* pService = nullptr); NIMBLE_PROPERTY::WRITE,
virtual ~NimBLECharacteristic(); NimBLEService* pService = nullptr);
NimBLECharacteristic(const NimBLEUUID &uuid,
uint16_t properties =
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE,
NimBLEService* pService = nullptr);
NimBLEUUID m_uuid; ~NimBLECharacteristic();
NimBLEDescriptorMap m_descriptorMap;
uint16_t m_handle;
uint16_t m_properties;
NimBLECharacteristicCallbacks* m_pCallbacks;
NimBLEService* m_pService;
NimBLEValue m_value;
// uint16_t m_permissions;
void addDescriptor(NimBLEDescriptor* pDescriptor);
NimBLEService* getService(); NimBLEService* getService();
uint8_t getProperties(); uint8_t getProperties();
void setSubscribe(struct ble_gap_event *event); void setSubscribe(struct ble_gap_event *event);
static int handleGapEvent(uint16_t conn_handle, uint16_t attr_handle, static int handleGapEvent(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg); 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 }; // 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_pCharacteristic = nullptr; // No initial characteristic.
m_pCallbacks = &defaultCallbacks; // No initial callback. m_pCallbacks = &defaultCallbacks; // No initial callback.
m_value.attr_value = (uint8_t*) calloc(max_len,1); // Allocate storage for the value. m_value.attr_value = (uint8_t*) calloc(max_len,1); // Allocate storage for the value.
m_valMux = portMUX_INITIALIZER_UNLOCKED;
m_properties = 0; m_properties = 0;
if (properties & BLE_GATT_CHR_F_READ) { // convert uint16_t properties to uint8_t 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){ if(ble_uuid_cmp(uuid, &pDescriptor->getUUID().getNative()->u) == 0){
switch(ctxt->op) { switch(ctxt->op) {
case BLE_GATT_ACCESS_OP_READ_DSC: { case BLE_GATT_ACCESS_OP_READ_DSC: {
pDescriptor->m_pCallbacks->onRead(pDescriptor); // 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()); rc = os_mbuf_append(ctxt->om, pDescriptor->getValue(), pDescriptor->getLength());
portEXIT_CRITICAL(&pDescriptor->m_valMux);
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
} }
case BLE_GATT_ACCESS_OP_WRITE_DSC: { 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; 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); pDescriptor->m_pCallbacks->onWrite(pDescriptor);
return 0; return 0;
} }
@ -191,12 +212,14 @@ void NimBLEDescriptor::setHandle(uint16_t handle) {
* @param [in] length The length of the data in bytes. * @param [in] length The length of the data in bytes.
*/ */
void NimBLEDescriptor::setValue(const uint8_t* data, size_t length) { void NimBLEDescriptor::setValue(const uint8_t* data, size_t length) {
if (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, BLE_ATT_ATTR_MAX_LEN); NIMBLE_LOGE(LOG_TAG, "Size %d too large, must be no bigger than %d", length, m_value.attr_max_len);
return; return;
} }
portENTER_CRITICAL(&m_valMux);
m_value.attr_len = length; m_value.attr_len = length;
memcpy(m_value.attr_value, data, length); memcpy(m_value.attr_value, data, length);
portEXIT_CRITICAL(&m_valMux);
} // setValue } // setValue
@ -209,13 +232,6 @@ void NimBLEDescriptor::setValue(const std::string &value) {
} // setValue } // setValue
/*
void NimBLEDescriptor::setAccessPermissions(uint8_t perm) {
m_permissions = perm;
}
*/
/** /**
* @brief Return a string representation of the descriptor. * @brief Return a string representation of the descriptor.
* @return A string representation of the descriptor. * @return A string representation of the descriptor.

View file

@ -29,13 +29,11 @@
typedef struct typedef struct
{ {
uint16_t attr_max_len; /*!< attribute max value length */ uint16_t attr_max_len; /*!< attribute max value length */
uint16_t attr_len; /*!< attribute current value length */ uint16_t attr_len; /*!< attribute current value length */
uint8_t *attr_value; /*!< the pointer to attribute value */ uint8_t *attr_value; /*!< the pointer to attribute value */
} attr_value_t; } attr_value_t;
typedef attr_value_t esp_attr_value_t; /*!< compatibility for esp32 */
class NimBLEService; class NimBLEService;
class NimBLECharacteristic; class NimBLECharacteristic;
class NimBLEDescriptorCallbacks; class NimBLEDescriptorCallbacks;
@ -46,32 +44,34 @@ class NimBLEDescriptorCallbacks;
*/ */
class NimBLEDescriptor { class NimBLEDescriptor {
public: public:
virtual ~NimBLEDescriptor(); uint16_t getHandle();
uint16_t getHandle(); // Get the handle of the descriptor. size_t getLength();
size_t getLength(); // Get the length of the value of the descriptor. NimBLEUUID getUUID();
NimBLEUUID getUUID(); // Get the UUID of the descriptor. uint8_t* getValue();
uint8_t* getValue(); // Get a pointer to the value of the descriptor. void setCallbacks(NimBLEDescriptorCallbacks* pCallbacks);
// void setAccessPermissions(uint8_t perm); // Set the permissions of the descriptor. void setValue(const uint8_t* data, size_t size);
void setCallbacks(NimBLEDescriptorCallbacks* pCallbacks); // Set callbacks to be invoked for the descriptor. void setValue(const std::string &value);
void setValue(const uint8_t* data, size_t size); // Set the value of the descriptor as a pointer to data. std::string toString();
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.
private: private:
friend class NimBLEDescriptorMap;
friend class NimBLECharacteristic; friend class NimBLECharacteristic;
friend class NimBLEService; friend class NimBLEService;
friend class NimBLE2902; friend class NimBLE2902;
friend class NimBLE2904; friend class NimBLE2904;
NimBLEDescriptor(const char* uuid, uint16_t properties, NimBLEDescriptor(const char* uuid, uint16_t properties,
uint16_t max_len, uint16_t max_len,
NimBLECharacteristic* pCharacteristic); NimBLECharacteristic* pCharacteristic);
NimBLEDescriptor(NimBLEUUID uuid, uint16_t properties, NimBLEDescriptor(NimBLEUUID uuid, uint16_t properties,
uint16_t max_len, uint16_t max_len,
NimBLECharacteristic* pCharacteristic); 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; NimBLEUUID m_uuid;
uint16_t m_handle; uint16_t m_handle;
@ -79,12 +79,8 @@ private:
NimBLECharacteristic* m_pCharacteristic; NimBLECharacteristic* m_pCharacteristic;
uint8_t m_properties; uint8_t m_properties;
attr_value_t m_value; attr_value_t m_value;
portMUX_TYPE m_valMux;
static int handleGapEvent(uint16_t conn_handle, uint16_t attr_handle, }; // NimBLEDescriptor
struct ble_gatt_access_ctxt *ctxt, void *arg);
void setHandle(uint16_t handle);
}; // BLEDescriptor
/** /**

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) #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#include "NimBLEServer.h" #include "NimBLEServer.h"
#include "NimBLE2902.h"
#include "NimBLEUtils.h" #include "NimBLEUtils.h"
#include "NimBLEDevice.h" #include "NimBLEDevice.h"
#include "NimBLELog.h" #include "NimBLELog.h"
@ -32,51 +31,46 @@ static NimBLEServerCallbacks defaultCallbacks;
* @brief Construct a %BLE Server * @brief Construct a %BLE Server
* *
* This class is not designed to be individually instantiated. Instead one should create a server by asking * 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() { NimBLEServer::NimBLEServer() {
m_connId = BLE_HS_CONN_HANDLE_NONE; // m_svcChgChrHdl = 0xffff; // Future Use
m_svcChgChrHdl = 0xffff; m_pServerCallbacks = &defaultCallbacks;
m_pServerCallbacks = &defaultCallbacks; m_gattsStarted = false;
m_gattsStarted = false; m_advertiseOnDisconnect = true;
} // BLEServer } // NimBLEServer
/** /**
* @brief Create a %BLE Service. * @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] uuid The UUID of the new service.
* @return A reference to the new service object. * @return A reference to the new service object.
*/ */
NimBLEService* NimBLEServer::createService(const char* uuid) { NimBLEService* NimBLEServer::createService(const char* uuid) {
return createService(NimBLEUUID(uuid)); return createService(NimBLEUUID(uuid));
} } // createService
/** /**
* @brief Create a %BLE Service. * @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] uuid The UUID of the new service.
* @param [in] numHandles The maximum number of handles associated with this 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. * @return A reference to the new service object.
*/ */
NimBLEService* NimBLEServer::createService(const NimBLEUUID &uuid, uint32_t numHandles, uint8_t inst_id) { NimBLEService* NimBLEServer::createService(const NimBLEUUID &uuid, uint32_t numHandles, uint8_t inst_id) {
NIMBLE_LOGD(LOG_TAG, ">> createService - %s", uuid.toString().c_str()); 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. // Check that a service with the supplied UUID does not already exist.
if (m_serviceMap.getByUUID(uuid) != nullptr) { if(getServiceByUUID(uuid) != nullptr) {
NIMBLE_LOGW(LOG_TAG, "<< Attempt to create a new service with uuid %s but a service with that UUID already exists.", NIMBLE_LOGW(LOG_TAG, "Warning creating a duplicate service UUID: %s",
uuid.toString().c_str()); std::string(uuid).c_str());
} }
NimBLEService* pService = new NimBLEService(uuid, numHandles, this); NimBLEService* pService = new NimBLEService(uuid, numHandles, this);
pService->m_instId = inst_id; m_svcVec.push_back(pService); // Save a reference to this service being on this server.
m_serviceMap.setByUUID(uuid, pService); // Save a reference to this service being on this server.
NIMBLE_LOGD(LOG_TAG, "<< createService"); NIMBLE_LOGD(LOG_TAG, "<< createService");
return pService; return pService;
@ -89,8 +83,8 @@ NimBLEService* NimBLEServer::createService(const NimBLEUUID &uuid, uint32_t numH
* @return A reference to the service object. * @return A reference to the service object.
*/ */
NimBLEService* NimBLEServer::getServiceByUUID(const char* uuid) { 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. * @return A reference to the service object.
*/ */
NimBLEService* NimBLEServer::getServiceByUUID(const NimBLEUUID &uuid) { 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. * @return An advertising object.
*/ */
NimBLEAdvertising* NimBLEServer::getAdvertising() { NimBLEAdvertising* NimBLEServer::getAdvertising() {
return BLEDevice::getAdvertising(); return NimBLEDevice::getAdvertising();
} } // 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;
}
/** /**
@ -143,6 +132,8 @@ void NimBLEServer::start() {
#if CONFIG_LOG_DEFAULT_LEVEL > 3 || (ARDUINO_ARCH_ESP32 && CORE_DEBUG_LEVEL >= 4) #if CONFIG_LOG_DEFAULT_LEVEL > 3 || (ARDUINO_ARCH_ESP32 && CORE_DEBUG_LEVEL >= 4)
ble_gatts_show_local(); ble_gatts_show_local();
#endif #endif
/*** Future use ***
* TODO: implement service changed handling
ble_uuid16_t svc = {BLE_UUID_TYPE_16, 0x1801}; ble_uuid16_t svc = {BLE_UUID_TYPE_16, 0x1801};
ble_uuid16_t chr = {BLE_UUID_TYPE_16, 0x2a05}; 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); NIMBLE_LOGI(LOG_TAG, "Service changed characterisic handle: %d", m_svcChgChrHdl);
*/
// 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((chr->m_properties & BLE_GATT_CHR_F_INDICATE) ||
(chr->m_properties & BLE_GATT_CHR_F_NOTIFY)) {
// Build a map of characteristics with Notify / Indicate capabilities for event handling if(nullptr == chr->getDescriptorByUUID(uint16_t(0x2902))) {
uint8_t numSvcs = m_serviceMap.getRegisteredServiceCount(); chr->createDescriptor(uint16_t(0x2902));
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++) {
// 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(nullptr == pChr->getDescriptorByUUID("2902")) {
pChr->createDescriptor("2902");
}
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; m_gattsStarted = true;
} } // start
/** /**
@ -204,15 +184,24 @@ int NimBLEServer::disconnect(uint16_t connId, uint8_t reason) {
return rc; return rc;
NIMBLE_LOGD(LOG_TAG, "<< disconnect()"); 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. * @brief Return the number of connected clients.
* @return The number of connected clients. * @return The number of connected clients.
*/ */
uint32_t NimBLEServer::getConnectedCount() { size_t NimBLEServer::getConnectedCount() {
return m_connectedServersMap.size(); return m_connectedPeersVec.size();
} // getConnectedCount } // getConnectedCount
@ -227,7 +216,7 @@ uint32_t NimBLEServer::getConnectedCount() {
/*STATIC*/int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) { /*STATIC*/int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) {
NimBLEServer* server = (NimBLEServer*)arg; NimBLEServer* server = (NimBLEServer*)arg;
NIMBLE_LOGD(LOG_TAG, ">> handleGapEvent: %s", NIMBLE_LOGD(LOG_TAG, ">> handleGapEvent: %s",
NimBLEUtils::gapEventToString(event->type)); NimBLEUtils::gapEventToString(event->type));
int rc = 0; int rc = 0;
struct ble_gap_conn_desc desc; struct ble_gap_conn_desc desc;
@ -236,13 +225,11 @@ uint32_t NimBLEServer::getConnectedCount() {
case BLE_GAP_EVENT_CONNECT: { case BLE_GAP_EVENT_CONNECT: {
if (event->connect.status != 0) { if (event->connect.status != 0) {
/* Connection failed; resume advertising */ /* Connection failed; resume advertising */
NIMBLE_LOGC(LOG_TAG, "Connection failed"); NIMBLE_LOGE(LOG_TAG, "Connection failed");
NimBLEDevice::startAdvertising(); NimBLEDevice::startAdvertising();
server->m_connId = BLE_HS_CONN_HANDLE_NONE;
} }
else { else {
server->m_connId = event->connect.conn_handle; server->m_connectedPeersVec.push_back(event->connect.conn_handle);
server->addPeerDevice((void*)server, false, server->m_connId);
ble_gap_conn_desc desc; ble_gap_conn_desc desc;
rc = ble_gap_conn_find(event->connect.conn_handle, &desc); rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
@ -271,10 +258,15 @@ uint32_t NimBLEServer::getConnectedCount() {
break; break;
} }
server->removePeerDevice(event->disconnect.conn.conn_handle, false); server->m_connectedPeersVec.erase(std::remove(server->m_connectedPeersVec.begin(),
server->m_connId = BLE_HS_CONN_HANDLE_NONE; server->m_connectedPeersVec.end(),
event->disconnect.conn.conn_handle),
server->m_connectedPeersVec.end());
server->m_pServerCallbacks->onDisconnect(server); server->m_pServerCallbacks->onDisconnect(server);
if(server->m_advertiseOnDisconnect) {
server->startAdvertising();
}
return 0; return 0;
} // BLE_GAP_EVENT_DISCONNECT } // BLE_GAP_EVENT_DISCONNECT
@ -283,9 +275,11 @@ uint32_t NimBLEServer::getConnectedCount() {
"val_handle=%d\n", "val_handle=%d\n",
event->subscribe.cur_notify, event->subscribe.attr_handle); event->subscribe.cur_notify, event->subscribe.attr_handle);
auto it = server->m_notifyChrMap.find(event->subscribe.attr_handle); for(auto &it : server->m_notifyChrVec) {
if(it != server->m_notifyChrMap.cend()) { if(it->getHandle() == event->subscribe.attr_handle) {
(*it).second->setSubscribe(event); it->setSubscribe(event);
break;
}
} }
return 0; return 0;
@ -295,15 +289,18 @@ uint32_t NimBLEServer::getConnectedCount() {
NIMBLE_LOGI(LOG_TAG, "mtu update event; conn_handle=%d mtu=%d", NIMBLE_LOGI(LOG_TAG, "mtu update event; conn_handle=%d mtu=%d",
event->mtu.conn_handle, event->mtu.conn_handle,
event->mtu.value); event->mtu.value);
server->updatePeerMTU(event->mtu.conn_handle, event->mtu.value);
return 0; return 0;
} // BLE_GAP_EVENT_MTU } // BLE_GAP_EVENT_MTU
case BLE_GAP_EVENT_NOTIFY_TX: { case BLE_GAP_EVENT_NOTIFY_TX: {
if(event->notify_tx.indication && event->notify_tx.status != 0) { if(event->notify_tx.indication && event->notify_tx.status != 0) {
auto it = server->m_notifyChrMap.find(event->notify_tx.attr_handle); for(auto &it : server->m_notifyChrVec) {
if(it != server->m_notifyChrMap.cend()) { if(it->getHandle() == event->notify_tx.attr_handle) {
(*it).second->m_semaphoreConfEvt.give(event->notify_tx.status); 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 } // BLE_GAP_EVENT_ENC_CHANGE
case BLE_GAP_EVENT_PASSKEY_ACTION: { 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) { if (event->passkey.params.action == BLE_SM_IOACT_DISP) {
pkey.action = event->passkey.params.action; pkey.action = event->passkey.params.action;
@ -416,7 +413,7 @@ uint32_t NimBLEServer::getConnectedCount() {
NIMBLE_LOGD(LOG_TAG, "<< handleGATTServerEvent"); NIMBLE_LOGD(LOG_TAG, "<< handleGATTServerEvent");
return 0; return 0;
} // handleGATTServerEvent } // handleGapEvent
/** /**
@ -437,18 +434,6 @@ void NimBLEServer::setCallbacks(NimBLEServerCallbacks* pCallbacks) {
} // setCallbacks } // setCallbacks
/*
* Remove service
*/
/*
void BLEServer::removeService(BLEService* service) {
service->stop();
service->executeDelete();
m_serviceMap.removeService(service);
}
*/
/** /**
* @brief Start advertising. * @brief Start advertising.
* *
@ -456,9 +441,7 @@ void BLEServer::removeService(BLEService* service) {
* retrieving the advertising object and invoking start upon it. * retrieving the advertising object and invoking start upon it.
*/ */
void NimBLEServer::startAdvertising() { void NimBLEServer::startAdvertising() {
NIMBLE_LOGD(LOG_TAG, ">> startAdvertising");
NimBLEDevice::startAdvertising(); NimBLEDevice::startAdvertising();
NIMBLE_LOGD(LOG_TAG, "<< startAdvertising");
} // startAdvertising } // startAdvertising
@ -466,38 +449,43 @@ void NimBLEServer::startAdvertising() {
* @brief Stop advertising. * @brief Stop advertising.
*/ */
void NimBLEServer::stopAdvertising() { void NimBLEServer::stopAdvertising() {
NIMBLE_LOGD(LOG_TAG, ">> stopAdvertising");
NimBLEDevice::stopAdvertising(); NimBLEDevice::stopAdvertising();
NIMBLE_LOGD(LOG_TAG, "<< stopAdvertising");
} // startAdvertising } // startAdvertising
/** /**
* Allow to connect GATT server to peer device * @brief Get the MTU of the client.
* Probably can be used in ANCS for iPhone * @returns The client MTU or 0 if not found/connected.
*/ */
/* uint16_t NimBLEServer::getPeerMTU(uint16_t conn_id) {
bool BLEServer::connect(BLEAddress address) { return ble_att_mtu(conn_id);
esp_bd_addr_t addr; } //getPeerMTU
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( * Update connection parameters can be called only after connection has been established
getGattsIf(), */
addr, // address void NimBLEServer::updateConnParams(uint16_t conn_handle,
1 // direct connection uint16_t minInterval, uint16_t maxInterval,
); uint16_t latency, uint16_t timeout)
if (errRc != ESP_OK) { {
ESP_LOGE(LOG_TAG, "esp_ble_gattc_open: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); ble_gap_upd_params params;
return false;
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) { void NimBLEServerCallbacks::onConnect(NimBLEServer* pServer) {
NIMBLE_LOGD("NimBLEServerCallbacks", "onConnect(): Default"); NIMBLE_LOGD("NimBLEServerCallbacks", "onConnect(): Default");
@ -534,80 +522,6 @@ bool NimBLEServerCallbacks::onConfirmPIN(uint32_t pin){
return true; 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 // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#endif // CONFIG_BT_ENABLED #endif // CONFIG_BT_ENABLED

View file

@ -21,102 +21,58 @@
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#include "NimBLEAddress.h" #include "NimBLEAddress.h"
#include "NimBLEUUID.h"
#include "NimBLEAdvertising.h" #include "NimBLEAdvertising.h"
#include "NimBLEService.h" #include "NimBLEService.h"
#include "NimBLESecurity.h" #include "NimBLESecurity.h"
#include "FreeRTOS.h" #include "FreeRTOS.h"
#include <map>
class NimBLEService; class NimBLEService;
class NimBLECharacteristic; class NimBLECharacteristic;
class NimBLEServerCallbacks; 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. * @brief The model of a %BLE server.
*/ */
class NimBLEServer { class NimBLEServer {
public: public:
uint32_t getConnectedCount(); size_t getConnectedCount();
NimBLEService* createService(const char* uuid); 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,
NimBLEAdvertising* getAdvertising(); uint8_t inst_id=0);
void setCallbacks(NimBLEServerCallbacks* pCallbacks); NimBLEAdvertising* getAdvertising();
void startAdvertising(); void setCallbacks(NimBLEServerCallbacks* pCallbacks);
void stopAdvertising(); void startAdvertising();
void start(); void stopAdvertising();
// void removeService(BLEService* service); void start();
NimBLEService* getServiceByUUID(const char* uuid); NimBLEService* getServiceByUUID(const char* uuid);
NimBLEService* getServiceByUUID(const NimBLEUUID &uuid); NimBLEService* getServiceByUUID(const NimBLEUUID &uuid);
int disconnect(uint16_t connID, uint8_t reason = BLE_ERR_REM_USER_CONN_TERM); int disconnect(uint16_t connID,
// bool connect(BLEAddress address); uint8_t reason = BLE_ERR_REM_USER_CONN_TERM);
void updateConnParams(uint16_t conn_handle, void updateConnParams(uint16_t conn_handle,
uint16_t minInterval, uint16_t maxInterval, uint16_t minInterval, uint16_t maxInterval,
uint16_t latency, uint16_t timeout, uint16_t latency, uint16_t timeout);
uint16_t minConnTime=0, uint16_t maxConnTime=0); uint16_t getPeerMTU(uint16_t conn_id);
std::vector<uint16_t> getPeerDevices();
/* multi connection support */ void advertiseOnDisconnect(bool);
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 getPeerMTU(uint16_t conn_id);
uint16_t getConnId();
private: private:
NimBLEServer(); NimBLEServer();
//friend class BLEService; friend class NimBLECharacteristic;
friend class NimBLECharacteristic; friend class NimBLEDevice;
friend class NimBLEDevice; friend class NimBLEAdvertising;
friend class NimBLEAdvertising;
// void onHostReset();
// BLEAdvertising m_bleAdvertising;
uint16_t m_connId;
uint16_t m_svcChgChrHdl;
bool m_gattsStarted; bool m_gattsStarted;
bool m_advertiseOnDisconnect;
std::map<uint16_t, conn_status_t> m_connectedServersMap;
std::map<uint16_t, NimBLECharacteristic*> m_notifyChrMap;
NimBLEServiceMap m_serviceMap;
NimBLEServerCallbacks* m_pServerCallbacks; NimBLEServerCallbacks* m_pServerCallbacks;
std::vector<uint16_t> m_connectedPeersVec;
static int handleGapEvent(struct ble_gap_event *event, void *arg); // 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 }; // NimBLEServer
@ -149,7 +105,7 @@ public:
virtual bool onSecurityRequest(); //{return true;} virtual bool onSecurityRequest(); //{return true;}
virtual void onAuthenticationComplete(ble_gap_conn_desc* desc);//{}; virtual void onAuthenticationComplete(ble_gap_conn_desc* desc);//{};
virtual bool onConfirmPIN(uint32_t pin);//{return true;} virtual bool onConfirmPIN(uint32_t pin);//{return true;}
}; // BLEServerCallbacks }; // NimBLEServerCallbacks
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) #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] uuid The UUID of the service.
* @param [in] numHandles The maximum number of handles associated with 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::NimBLEService(const char* uuid, uint16_t numHandles, NimBLEServer* pServer)
: NimBLEService(NimBLEUUID(uuid), numHandles, pServer) { : NimBLEService(NimBLEUUID(uuid), numHandles, pServer) {
@ -45,11 +46,12 @@ NimBLEService::NimBLEService(const char* uuid, uint16_t numHandles, NimBLEServer
* @brief Construct an instance of the BLEService * @brief Construct an instance of the BLEService
* @param [in] uuid The UUID of the service. * @param [in] uuid The UUID of the service.
* @param [in] numHandles The maximum number of handles associated with 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) { NimBLEService::NimBLEService(const NimBLEUUID &uuid, uint16_t numHandles, NimBLEServer* pServer) {
m_uuid = uuid; m_uuid = uuid;
m_handle = NULL_HANDLE; m_handle = NULL_HANDLE;
m_pServer = pServer; m_pServer = pServer;
m_numHandles = numHandles; m_numHandles = numHandles;
} // NimBLEService } // NimBLEService
@ -59,10 +61,22 @@ NimBLEService::NimBLEService(const NimBLEUUID &uuid, uint16_t numHandles, NimBLE
* @return N/A. * @return N/A.
*/ */
void NimBLEService::dump() { 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_uuid.toString().c_str(),
m_handle); 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 } // dump
@ -76,10 +90,9 @@ NimBLEUUID NimBLEService::getUUID() {
/** /**
* @brief Start the service. * @brief Builds the database of characteristics/descriptors for the service
* Here we wish to start the service which means that we will respond to partner requests about it. * and registers it with the NimBLE stack.
* Starting a service also means that we can create the corresponding characteristics. * @return bool success/failure .
* @return Start the service.
*/ */
bool NimBLEService::start() { bool NimBLEService::start() {
@ -96,7 +109,7 @@ bool NimBLEService::start() {
svc[0].uuid = &m_uuid.getNative()->u; svc[0].uuid = &m_uuid.getNative()->u;
svc[0].includes = NULL; 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()); 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 // of the characteristics for the service. We create 1 extra and set it to null
// for this purpose. // for this purpose.
pChr_a = new ble_gatt_chr_def[numChrs+1]; 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++) { for(uint8_t i=0; i < numChrs;) {
uint8_t numDscs = pCharacteristic->m_descriptorMap.getSize(); uint8_t numDscs = pCharacteristic->m_dscVec.size();
if(numDscs) { if(numDscs) {
// skip 2902 as it's automatically created by NimBLE // skip 2902 as it's automatically created by NimBLE
// if Indicate or Notify flags are set // if Indicate or Notify flags are set
if(((pCharacteristic->m_properties & BLE_GATT_CHR_F_INDICATE) || if(((pCharacteristic->m_properties & BLE_GATT_CHR_F_INDICATE) ||
(pCharacteristic->m_properties & BLE_GATT_CHR_F_NOTIFY)) && (pCharacteristic->m_properties & BLE_GATT_CHR_F_NOTIFY)) &&
pCharacteristic->getDescriptorByUUID("2902") != nullptr) { pCharacteristic->getDescriptorByUUID("2902") != nullptr)
{
numDscs--; numDscs--;
} }
} }
@ -127,12 +141,12 @@ bool NimBLEService::start() {
// Must have last descriptor uuid = 0 so we have to create 1 extra // Must have last descriptor uuid = 0 so we have to create 1 extra
//NIMBLE_LOGD(LOG_TAG, "Adding %d descriptors", numDscs); //NIMBLE_LOGD(LOG_TAG, "Adding %d descriptors", numDscs);
pDsc_a = new ble_gatt_dsc_def[numDscs+1]; 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;) { for(uint8_t d=0; d < numDscs;) {
// skip 2902 // 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"); //NIMBLE_LOGD(LOG_TAG, "Skipped 0x2902");
pDescriptor = pCharacteristic->m_descriptorMap.getNext(); pDescriptor = *(pCharacteristic->m_dscVec.begin()+d+1);
continue; continue;
} }
pDsc_a[d].uuid = &pDescriptor->m_uuid.getNative()->u; 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].min_key_size = 0;
pDsc_a[d].access_cb = NimBLEDescriptor::handleGapEvent; pDsc_a[d].access_cb = NimBLEDescriptor::handleGapEvent;
pDsc_a[d].arg = pDescriptor; pDsc_a[d].arg = pDescriptor;
pDescriptor = pCharacteristic->m_descriptorMap.getNext();
d++; d++;
pDescriptor = *(pCharacteristic->m_dscVec.begin() + d);
} }
pDsc_a[numDscs].uuid = NULL; pDsc_a[numDscs].uuid = NULL;
@ -154,7 +168,8 @@ bool NimBLEService::start() {
pChr_a[i].flags = pCharacteristic->m_properties; pChr_a[i].flags = pCharacteristic->m_properties;
pChr_a[i].min_key_size = 0; pChr_a[i].min_key_size = 0;
pChr_a[i].val_handle = &pCharacteristic->m_handle; pChr_a[i].val_handle = &pCharacteristic->m_handle;
pCharacteristic = m_characteristicMap.getNext(); i++;
pCharacteristic = *(m_chrVec.begin() + i);
} }
pChr_a[numChrs].uuid = NULL; pChr_a[numChrs].uuid = NULL;
@ -182,21 +197,6 @@ bool NimBLEService::start() {
} // 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. * @brief Get the handle associated with this service.
* @return The handle associated with this service. * @return The handle associated with this service.
@ -206,34 +206,6 @@ uint16_t NimBLEService::getHandle() {
} // 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. * @brief Create a new BLE Characteristic associated with this service.
* @param [in] uuid - The UUID of the characteristic. * @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* NimBLEService::createCharacteristic(const NimBLEUUID &uuid, uint32_t properties) {
NimBLECharacteristic* pCharacteristic = new NimBLECharacteristic(uuid, properties, this); NimBLECharacteristic* pCharacteristic = new NimBLECharacteristic(uuid, properties, this);
addCharacteristic(pCharacteristic); // Check that we don't add the same characteristic twice.
//pCharacteristic->executeCreate(this); 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; return pCharacteristic;
} // createCharacteristic } // createCharacteristic
@ -265,7 +244,13 @@ NimBLECharacteristic* NimBLEService::getCharacteristic(const char* uuid) {
NimBLECharacteristic* NimBLEService::getCharacteristic(const NimBLEUUID &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 NimBLEServer;
class NimBLECharacteristic; 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. * @brief The model of a %BLE service.
@ -59,40 +37,39 @@ private:
class NimBLEService { class NimBLEService {
public: public:
NimBLECharacteristic* createCharacteristic(const char* uuid, NimBLECharacteristic* createCharacteristic(const char* uuid,
uint32_t properties = NIMBLE_PROPERTY::READ | uint32_t properties =
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE); NIMBLE_PROPERTY::WRITE);
NimBLECharacteristic* createCharacteristic(const NimBLEUUID &uuid, NimBLECharacteristic* createCharacteristic(const NimBLEUUID &uuid,
uint32_t properties = NIMBLE_PROPERTY::READ | uint32_t properties =
NIMBLE_PROPERTY::WRITE); NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE);
void dump(); void dump();
NimBLECharacteristic* getCharacteristic(const char* uuid); NimBLECharacteristic* getCharacteristic(const char* uuid);
NimBLECharacteristic* getCharacteristic(const NimBLEUUID &uuid); NimBLECharacteristic* getCharacteristic(const NimBLEUUID &uuid);
NimBLEUUID getUUID(); NimBLEUUID getUUID();
NimBLEServer* getServer(); NimBLEServer* getServer();
bool start(); bool start();
// void stop(); std::string toString();
std::string toString(); uint16_t getHandle();
uint16_t getHandle();
uint8_t m_instId = 0;
private: private:
NimBLEService(const char* uuid, uint16_t numHandles, NimBLEServer* pServer); NimBLEService(const char* uuid, uint16_t numHandles, NimBLEServer* pServer);
NimBLEService(const NimBLEUUID &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); friend class NimBLEServer;
friend class NimBLEDevice;
NimBLECharacteristicMap m_characteristicMap; uint16_t m_handle;
uint16_t m_handle; NimBLEServer* m_pServer;
NimBLEServer* m_pServer = nullptr; NimBLEUUID m_uuid;
NimBLEUUID m_uuid; uint16_t m_numHandles;
uint16_t m_numHandles; std::vector<NimBLECharacteristic*> m_chrVec;
void setHandle(uint16_t handle);
}; // BLEService }; // NimBLEService
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) #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_ */