diff --git a/README.md b/README.md index 514416e2..6b7ff2f4 100644 --- a/README.md +++ b/README.md @@ -15,11 +15,29 @@ A fork of the NimBLE stack restructured for compilation in the Ardruino IDE with Why? Because the Bluedroid library is too bulky. -Initial client code testing has resulted in code size reduction of ~115k and reduced ram consumption of ~37k. - -Server code testing results from @beegee-toyo [from the project here](https://github.com/beegee-tokyo/ESP32WiFiBLE-NimBLE): +Initial testing has resulted in code size reduction of ~50% and reduced ram consumption of ~100k. +## BLE_client example comparison (Debug): +#### Arduino BLE Library +Sketch uses 1214909 bytes (57%) of program storage space. +Memory after connection: Free Heap: 171308 +#### NimBLE-Arduino library +Sketch uses 622076 bytes (29%) of program storage space. +Memory after connection: Free Heap: 270408 + +## BLE_notify (server) example comparison (Debug): +#### Arduino BLE Library +Sketch uses 1208409 bytes (57%) of program storage space. +Memory after connection: Free Heap: 173300 + +#### NimBLE-Arduino library +Sketch uses 609044 bytes (29%) of program storage space. +Memory after connection: Free Heap: 269448 + + +## Server code testing results from @beegee-toyo [from the project here](https://github.com/beegee-tokyo/ESP32WiFiBLE-NimBLE): + ### Memory usage (compilation output) #### Arduino BLE library ```log @@ -35,9 +53,8 @@ Flash: [======= ] 69.5% (used 911378 bytes from 1310720 bytes) #### Arduino BLE library **`Internal Total heap 259104, internal Free Heap 91660`** #### NimBLE-Arduino library -**`Internal Total heap 290288, internal Free Heap 182344`** - - +**`Internal Total heap 290288, internal Free Heap 182344`** + # Installation: Download as .zip and extract to Arduino/libraries folder, or in Arduino IDE from Sketch menu -> Include library -> Add .Zip library. diff --git a/src/NimBLEAdvertising.h b/src/NimBLEAdvertising.h index f30b1581..f57fe228 100644 --- a/src/NimBLEAdvertising.h +++ b/src/NimBLEAdvertising.h @@ -27,7 +27,6 @@ /**************************/ #include "NimBLEUUID.h" -#include "FreeRTOS.h" #include diff --git a/src/NimBLECharacteristic.cpp b/src/NimBLECharacteristic.cpp index dd1be3bf..3b2758f3 100644 --- a/src/NimBLECharacteristic.cpp +++ b/src/NimBLECharacteristic.cpp @@ -391,7 +391,7 @@ void NimBLECharacteristic::notify(bool is_notification) { om = ble_hs_mbuf_from_flat(data, length); if(!is_notification) { - m_semaphoreConfEvt.take("indicate"); + m_semaphoreConfEvt.take(); rc = ble_gattc_indicate_custom((*it).first, m_handle, om); if(rc != 0){ m_semaphoreConfEvt.give(); diff --git a/src/NimBLECharacteristic.h b/src/NimBLECharacteristic.h index 25e1ec7f..4d648ef5 100644 --- a/src/NimBLECharacteristic.h +++ b/src/NimBLECharacteristic.h @@ -44,7 +44,7 @@ typedef enum { #include "NimBLEDescriptor.h" #include "NimBLEUUID.h" #include "NimBLEValue.h" -#include "FreeRTOS.h" +#include "NimBLESemaphore.h" #include #include @@ -136,7 +136,6 @@ class NimBLECharacteristic { ////////////////////////////////////////////////////// private: - friend class NimBLEServer; friend class NimBLEService; // friend class NimBLEDescriptor; @@ -155,6 +154,7 @@ class NimBLECharacteristic { NimBLECharacteristicCallbacks* m_pCallbacks; NimBLEService* m_pService; NimBLEValue m_value; + NimBLESemaphore m_semaphoreConfEvt = NimBLESemaphore("ConfEvt"); // uint16_t m_permissions; void addDescriptor(NimBLEDescriptor* pDescriptor); @@ -163,8 +163,6 @@ class NimBLECharacteristic { void setSubscribe(struct ble_gap_event *event); static int handleGapEvent(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg); - - FreeRTOS::Semaphore m_semaphoreConfEvt = FreeRTOS::Semaphore("ConfEvt"); }; // NimBLECharacteristic diff --git a/src/NimBLEClient.cpp b/src/NimBLEClient.cpp index a72b4e37..7ebe8a7f 100644 --- a/src/NimBLEClient.cpp +++ b/src/NimBLEClient.cpp @@ -55,6 +55,9 @@ NimBLEClient::NimBLEClient() m_conn_id = BLE_HS_CONN_HANDLE_NONE; m_haveServices = false; m_isConnected = false; + m_waitingToConnect = false; + m_deleteCallbacks = true; + m_pSemaphore = nullptr; m_connectTimeout = 30000; m_pConnParams.scan_itvl = 16; // Scan interval in 0.625ms units (NimBLE Default) @@ -144,7 +147,7 @@ bool NimBLEClient::connect(const NimBLEAddress &address, uint8_t type, bool refr memcpy(&peerAddrt.val, address.getNative(),6); peerAddrt.type = type; - m_semaphoreOpenEvt.take("connect"); + NIMBLE_SEMAPHORE_TAKE(m_pSemaphore, "Connect") /** Try to connect the the advertiser. Allow 30 seconds (30000 ms) for * timeout (default value of m_connectTimeout). @@ -165,15 +168,15 @@ bool NimBLEClient::connect(const NimBLEAddress &address, uint8_t type, bool refr m_peerAddress.toString().c_str(), rc, NimBLEUtils::returnCodeToString(rc)); - m_semaphoreOpenEvt.give(); + NIMBLE_SEMAPHORE_DELETE(m_pSemaphore) m_waitingToConnect = false; return false; } m_waitingToConnect = true; - rc = m_semaphoreOpenEvt.wait("connect"); // Wait for the connection to complete. - + rc = NIMBLE_SEMAPHORE_WAIT(m_pSemaphore) //m_pSemaphore->wait(); // Wait for the connection to complete. + NIMBLE_SEMAPHORE_DELETE(m_pSemaphore) if(rc != 0){ return false; } @@ -208,16 +211,16 @@ bool NimBLEClient::connect(const NimBLEAddress &address, uint8_t type, bool refr * @return True on success. */ bool NimBLEClient::secureConnection() { - - m_semeaphoreSecEvt.take("secureConnection"); + NIMBLE_SEMAPHORE_TAKE(m_pSemaphore, "Secure connection") int rc = NimBLEDevice::startSecurity(m_conn_id); if(rc != 0){ - m_semeaphoreSecEvt.give(); + NIMBLE_SEMAPHORE_DELETE(m_pSemaphore) return false; } - rc = m_semeaphoreSecEvt.wait("secureConnection"); + rc = NIMBLE_SEMAPHORE_WAIT(m_pSemaphore) + NIMBLE_SEMAPHORE_DELETE(m_pSemaphore) if(rc != 0){ return false; } @@ -412,21 +415,24 @@ bool NimBLEClient::retrieveServices() { return false; } - m_semaphoreSearchCmplEvt.take("retrieveServices"); + NIMBLE_SEMAPHORE_TAKE(m_pSemaphore, "Retrieve Services") int rc = ble_gattc_disc_all_svcs(m_conn_id, NimBLEClient::serviceDiscoveredCB, this); if (rc != 0) { NIMBLE_LOGE(LOG_TAG, "ble_gattc_disc_all_svcs: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); m_haveServices = false; - m_semaphoreSearchCmplEvt.give(); + NIMBLE_SEMAPHORE_DELETE(m_pSemaphore) return false; } // wait until we have all the services // If sucessful, remember that we now have services. - m_haveServices = (m_semaphoreSearchCmplEvt.wait("retrieveServices") == 0); - if(m_haveServices){ + rc = NIMBLE_SEMAPHORE_WAIT(m_pSemaphore); + NIMBLE_SEMAPHORE_DELETE(m_pSemaphore) + + if(rc == 0) { + m_haveServices = true; for (auto &it: m_servicesVector) { if(!m_isConnected || !it->retrieveCharacteristics()) { NIMBLE_LOGE(LOG_TAG, "Disconnected, could not retrieve characteristics -aborting"); @@ -472,9 +478,7 @@ int NimBLEClient::serviceDiscoveredCB( } case BLE_HS_EDONE:{ // All services discovered; start discovering characteristics. - - //NIMBLE_LOGD(LOG_TAG,"Giving search semaphore - completed"); - peer->m_semaphoreSearchCmplEvt.give(0); + NIMBLE_SEMAPHORE_GIVE(peer->m_pSemaphore, 0) rc = 0; break; } @@ -486,7 +490,7 @@ int NimBLEClient::serviceDiscoveredCB( if (rc != 0) { // pass non-zero to semaphore on error to indicate an error finding services - peer->m_semaphoreSearchCmplEvt.give(1); + NIMBLE_SEMAPHORE_GIVE(peer->m_pSemaphore, 1) } NIMBLE_LOGD(LOG_TAG,"<< Service Discovered. status: %d", rc); return rc; @@ -603,9 +607,7 @@ uint16_t NimBLEClient::getMTU() { //client->m_conn_id = BLE_HS_CONN_HANDLE_NONE; // Indicate a non-success return value to any semaphores waiting - client->m_semaphoreOpenEvt.give(1); - client->m_semaphoreSearchCmplEvt.give(1); - client->m_semeaphoreSecEvt.give(1); + NIMBLE_SEMAPHORE_GIVE(client->m_pSemaphore, 1) client->m_pClientCallbacks->onDisconnect(client); @@ -644,15 +646,16 @@ uint16_t NimBLEClient::getMTU() { NIMBLE_LOGE(LOG_TAG, "ble_gattc_exchange_mtu: rc=%d %s",rc, NimBLEUtils::returnCodeToString(rc)); // if error getting mtu indicate a connection error. - client->m_semaphoreOpenEvt.give(rc); + NIMBLE_SEMAPHORE_GIVE(client->m_pSemaphore, rc) } } else { // Connection attempt failed NIMBLE_LOGE(LOG_TAG, "Error: Connection failed; status=%d %s", event->connect.status, NimBLEUtils::returnCodeToString(event->connect.status)); + NIMBLE_SEMAPHORE_GIVE(client->m_pSemaphore, event->connect.status) } - client->m_semaphoreOpenEvt.give(event->connect.status); + return 0; } // BLE_GAP_EVENT_CONNECT @@ -748,19 +751,19 @@ uint16_t NimBLEClient::getMTU() { } } - client->m_semeaphoreSecEvt.give(event->enc_change.status); + NIMBLE_SEMAPHORE_GIVE(client->m_pSemaphore, event->enc_change.status) return 0; } //BLE_GAP_EVENT_ENC_CHANGE case BLE_GAP_EVENT_MTU: { if(client->m_conn_id != event->mtu.conn_handle){ - return 0; //BLE_HS_ENOTCONN BLE_ATT_ERR_INVALID_HANDLE + return 0; } NIMBLE_LOGI(LOG_TAG, "mtu update event; conn_handle=%d mtu=%d", event->mtu.conn_handle, event->mtu.value); - client->m_semaphoreOpenEvt.give(0); - //client->m_mtu = event->mtu.value; + + NIMBLE_SEMAPHORE_GIVE(client->m_pSemaphore, 0) return 0; } // BLE_GAP_EVENT_MTU diff --git a/src/NimBLEClient.h b/src/NimBLEClient.h index e2cea9e1..82fb93af 100644 --- a/src/NimBLEClient.h +++ b/src/NimBLEClient.h @@ -60,12 +60,13 @@ class NimBLEClient { void updateConnParams(uint16_t minInterval, uint16_t maxInterval, uint16_t latency, uint16_t timeout); - private: NimBLEClient(); ~NimBLEClient(); friend class NimBLEDevice; friend class NimBLERemoteService; + friend class NimBLERemoteCharacteristic; + friend class NimBLERemoteDescriptor; static int handleGapEvent(struct ble_gap_event *event, void *arg); static int serviceDiscoveredCB(uint16_t conn_handle, const struct ble_gatt_error *error, const struct ble_gatt_svc *service, void *arg); @@ -73,20 +74,15 @@ class NimBLEClient { bool retrieveServices(); //Retrieve services from the server // void onHostReset(); - NimBLEAddress m_peerAddress = NimBLEAddress(""); // The BD address of the remote server. - uint16_t m_conn_id; - bool m_haveServices = false; // Have we previously obtain the set of services from the remote server. - bool m_isConnected = false; // Are we currently connected. - bool m_waitingToConnect =false; - bool m_deleteCallbacks = true; - int32_t m_connectTimeout; - //uint16_t m_mtu = 23; - - NimBLEClientCallbacks* m_pClientCallbacks = nullptr; - - FreeRTOS::Semaphore m_semaphoreOpenEvt = FreeRTOS::Semaphore("OpenEvt"); - FreeRTOS::Semaphore m_semaphoreSearchCmplEvt = FreeRTOS::Semaphore("SearchCmplEvt"); - FreeRTOS::Semaphore m_semeaphoreSecEvt = FreeRTOS::Semaphore("Security"); + NimBLEAddress m_peerAddress = NimBLEAddress("\0\0\0\0\0\0"); // The BD address of the remote server. + uint16_t m_conn_id; + bool m_haveServices; // Have we previously obtain the set of services from the remote server. + bool m_isConnected; // Are we currently connected. + bool m_waitingToConnect; + bool m_deleteCallbacks; + int32_t m_connectTimeout; + NimBLESemaphore* m_pSemaphore; + NimBLEClientCallbacks* m_pClientCallbacks; std::vector m_servicesVector; diff --git a/src/NimBLEDescriptor.h b/src/NimBLEDescriptor.h index 8af3560d..464f2ef3 100644 --- a/src/NimBLEDescriptor.h +++ b/src/NimBLEDescriptor.h @@ -22,7 +22,6 @@ #include "NimBLECharacteristic.h" #include "NimBLEUUID.h" -#include "FreeRTOS.h" #include diff --git a/src/NimBLERemoteCharacteristic.cpp b/src/NimBLERemoteCharacteristic.cpp index 52b98f25..52ede8bc 100644 --- a/src/NimBLERemoteCharacteristic.cpp +++ b/src/NimBLERemoteCharacteristic.cpp @@ -58,6 +58,7 @@ static const char* LOG_TAG = "NimBLERemoteCharacteristic"; m_notifyCallback = nullptr; m_rawData = nullptr; m_dataLen = 0; + m_pSemaphore = nullptr; } // NimBLERemoteCharacteristic @@ -164,7 +165,7 @@ int NimBLERemoteCharacteristic::descriptorDiscCB(uint16_t conn_handle, } case BLE_HS_EDONE:{ /* All descriptors in this characteristic discovered; */ - characteristic->m_semaphoreGetDescEvt.give(0); + NIMBLE_SEMAPHORE_GIVE(characteristic->m_pSemaphore, 0) rc = 0; break; } @@ -175,7 +176,7 @@ int NimBLERemoteCharacteristic::descriptorDiscCB(uint16_t conn_handle, if (rc != 0) { /* Error; abort discovery. */ // pass non-zero to semaphore on error to indicate an error finding descriptors - characteristic->m_semaphoreGetDescEvt.give(1); + NIMBLE_SEMAPHORE_GIVE(characteristic->m_pSemaphore, 1) } NIMBLE_LOGD(LOG_TAG,"<< Descriptor Discovered. status: %d", rc); return rc; @@ -189,9 +190,8 @@ bool NimBLERemoteCharacteristic::retrieveDescriptors(uint16_t endHdl) { NIMBLE_LOGD(LOG_TAG, ">> retrieveDescriptors() for characteristic: %s", getUUID().toString().c_str()); int rc = 0; - //removeDescriptors(); // Remove any existing descriptors. - m_semaphoreGetDescEvt.take("retrieveDescriptors"); + NIMBLE_SEMAPHORE_TAKE(m_pSemaphore, "Retrieve Descs") rc = ble_gattc_disc_all_dscs(getRemoteService()->getClient()->getConnId(), m_handle, @@ -200,13 +200,14 @@ bool NimBLERemoteCharacteristic::retrieveDescriptors(uint16_t endHdl) { this); if (rc != 0) { NIMBLE_LOGE(LOG_TAG, "ble_gattc_disc_all_chrs: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); - m_semaphoreGetDescEvt.give(); + NIMBLE_SEMAPHORE_DELETE(m_pSemaphore) return false; } - if(m_semaphoreGetDescEvt.wait("retrieveCharacteristics") != 0) { - // if there was an error release the resources - //removeDescriptors(); + rc = NIMBLE_SEMAPHORE_WAIT(m_pSemaphore) + NIMBLE_SEMAPHORE_DELETE(m_pSemaphore) + + if(rc != 0) { return false; } @@ -217,7 +218,7 @@ bool NimBLERemoteCharacteristic::retrieveDescriptors(uint16_t endHdl) { /** * @brief Retrieve the vector of descriptors. - */ + */ std::vector* NimBLERemoteCharacteristic::getDescriptors() { return &m_descriptorVector; } // getDescriptors @@ -337,7 +338,7 @@ std::string NimBLERemoteCharacteristic::readValue() { } do { - m_semaphoreReadCharEvt.take("readValue"); + NIMBLE_SEMAPHORE_TAKE(m_pSemaphore, "Read value") rc = ble_gattc_read_long(pClient->getConnId(), m_handle, 0, NimBLERemoteCharacteristic::onReadCB, @@ -345,11 +346,11 @@ std::string NimBLERemoteCharacteristic::readValue() { if (rc != 0) { NIMBLE_LOGE(LOG_TAG, "Error: Failed to read characteristic; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc)); - m_semaphoreReadCharEvt.give(0); + NIMBLE_SEMAPHORE_DELETE(m_pSemaphore) return ""; } - rc = m_semaphoreReadCharEvt.wait("readValue"); + rc = NIMBLE_SEMAPHORE_WAIT(m_pSemaphore) switch(rc){ case 0: case BLE_HS_EDONE: @@ -367,10 +368,12 @@ std::string NimBLERemoteCharacteristic::readValue() { break; /* Else falls through. */ default: + NIMBLE_SEMAPHORE_DELETE(m_pSemaphore) return ""; } } while(rc != 0 && retryCount--); + NIMBLE_SEMAPHORE_DELETE(m_pSemaphore) NIMBLE_LOGD(LOG_TAG, "<< readValue(): length: %d", m_value.length()); return m_value; } // readValue @@ -402,7 +405,7 @@ int NimBLERemoteCharacteristic::onReadCB(uint16_t conn_handle, } } // Read complete release semaphore and let the app can continue. - characteristic->m_semaphoreReadCharEvt.give(error->status); + NIMBLE_SEMAPHORE_GIVE(characteristic->m_pSemaphore, error->status) return 0; } @@ -540,7 +543,7 @@ bool NimBLERemoteCharacteristic::writeValue(const uint8_t* data, size_t length, } do { - m_semaphoreWriteCharEvt.take("writeValue"); + NIMBLE_SEMAPHORE_TAKE(m_pSemaphore, "Write Value") if(length > mtu) { NIMBLE_LOGI(LOG_TAG,"long write %d bytes", length); @@ -556,11 +559,11 @@ bool NimBLERemoteCharacteristic::writeValue(const uint8_t* data, size_t length, } if (rc != 0) { NIMBLE_LOGE(LOG_TAG, "Error: Failed to write characteristic; rc=%d", rc); - m_semaphoreWriteCharEvt.give(); + NIMBLE_SEMAPHORE_DELETE(m_pSemaphore) return false; } - rc = m_semaphoreWriteCharEvt.wait("writeValue"); + rc = NIMBLE_SEMAPHORE_WAIT(m_pSemaphore) switch(rc){ case 0: @@ -580,10 +583,12 @@ bool NimBLERemoteCharacteristic::writeValue(const uint8_t* data, size_t length, break; /* Else falls through. */ default: + NIMBLE_SEMAPHORE_DELETE(m_pSemaphore) return false; } } while(rc != 0 && retryCount--); + NIMBLE_SEMAPHORE_DELETE(m_pSemaphore) NIMBLE_LOGD(LOG_TAG, "<< writeValue, rc: %d",rc); return (rc == 0); } // writeValue @@ -605,8 +610,7 @@ int NimBLERemoteCharacteristic::onWriteCB(uint16_t conn_handle, } NIMBLE_LOGI(LOG_TAG, "Write complete; status=%d conn_handle=%d", error->status, conn_handle); - - characteristic->m_semaphoreWriteCharEvt.give(error->status); + NIMBLE_SEMAPHORE_GIVE(characteristic->m_pSemaphore, error->status) return 0; } @@ -642,14 +646,5 @@ size_t NimBLERemoteCharacteristic::getDataLength() { } -void NimBLERemoteCharacteristic::releaseSemaphores() { - for (auto &it: m_descriptorVector) { - it->releaseSemaphores(); - } - m_semaphoreWriteCharEvt.give(1); - m_semaphoreGetDescEvt.give(1); - m_semaphoreReadCharEvt.give(1); -} - #endif // #if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL) #endif /* CONFIG_BT_ENABLED */ diff --git a/src/NimBLERemoteCharacteristic.h b/src/NimBLERemoteCharacteristic.h index b4748fe0..43a402eb 100644 --- a/src/NimBLERemoteCharacteristic.h +++ b/src/NimBLERemoteCharacteristic.h @@ -20,8 +20,6 @@ #include "nimconfig.h" #if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL) -//#include "NimBLEUUID.h" -//#include "FreeRTOS.h" #include "NimBLERemoteService.h" #include "NimBLERemoteDescriptor.h" @@ -79,10 +77,8 @@ class NimBLERemoteCharacteristic { bool retrieveDescriptors(uint16_t endHdl); static int onReadCB(uint16_t conn_handle, const struct ble_gatt_error *error, struct ble_gatt_attr *attr, void *arg); static int onWriteCB(uint16_t conn_handle, const struct ble_gatt_error *error, struct ble_gatt_attr *attr, void *arg); - void releaseSemaphores(); static int descriptorDiscCB(uint16_t conn_handle, const struct ble_gatt_error *error, - uint16_t chr_val_handle, const struct ble_gatt_dsc *dsc, - void *arg); + uint16_t chr_val_handle, const struct ble_gatt_dsc *dsc, void *arg); // Private properties NimBLEUUID m_uuid; @@ -90,13 +86,11 @@ class NimBLERemoteCharacteristic { uint16_t m_handle; uint16_t m_defHandle; NimBLERemoteService* m_pRemoteService; - FreeRTOS::Semaphore m_semaphoreGetDescEvt = FreeRTOS::Semaphore("GetDescEvt"); - FreeRTOS::Semaphore m_semaphoreReadCharEvt = FreeRTOS::Semaphore("ReadCharEvt"); - FreeRTOS::Semaphore m_semaphoreWriteCharEvt = FreeRTOS::Semaphore("WriteCharEvt"); std::string m_value; uint8_t* m_rawData; size_t m_dataLen; notify_callback m_notifyCallback; + NimBLESemaphore* m_pSemaphore; // We maintain a vector of descriptors owned by this characteristic. std::vector m_descriptorVector; diff --git a/src/NimBLERemoteDescriptor.cpp b/src/NimBLERemoteDescriptor.cpp index 52743bbe..22923ee2 100644 --- a/src/NimBLERemoteDescriptor.cpp +++ b/src/NimBLERemoteDescriptor.cpp @@ -47,7 +47,7 @@ NimBLERemoteDescriptor::NimBLERemoteDescriptor(NimBLERemoteCharacteristic* pRemo } m_handle = dsc->handle; m_pRemoteCharacteristic = pRemoteCharacteristic; - + m_pSemaphore = nullptr; } @@ -106,7 +106,7 @@ int NimBLERemoteDescriptor::onReadCB(uint16_t conn_handle, } // Read complete release semaphore and let the app can continue. - desc->m_semaphoreReadDescrEvt.give(error->status); + NIMBLE_SEMAPHORE_GIVE(desc->m_pSemaphore, error->status) return 0; } @@ -128,7 +128,7 @@ std::string NimBLERemoteDescriptor::readValue() { } do { - m_semaphoreReadDescrEvt.take("ReadDescriptor"); + NIMBLE_SEMAPHORE_TAKE(m_pSemaphore, "Descriptor Read") rc = ble_gattc_read_long(pClient->getConnId(), m_handle, 0, NimBLERemoteDescriptor::onReadCB, @@ -136,11 +136,11 @@ std::string NimBLERemoteDescriptor::readValue() { if (rc != 0) { NIMBLE_LOGE(LOG_TAG, "Error: Failed to read descriptor; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc)); - m_semaphoreReadDescrEvt.give(0); + NIMBLE_SEMAPHORE_DELETE(m_pSemaphore) return ""; } - rc = m_semaphoreReadDescrEvt.wait("ReadDescriptor"); + rc = NIMBLE_SEMAPHORE_WAIT(m_pSemaphore) switch(rc){ case 0: @@ -159,10 +159,12 @@ std::string NimBLERemoteDescriptor::readValue() { break; /* Else falls through. */ default: + NIMBLE_SEMAPHORE_DELETE(m_pSemaphore) return ""; } } while(rc != 0 && retryCount--); + NIMBLE_SEMAPHORE_DELETE(m_pSemaphore) NIMBLE_LOGD(LOG_TAG, "<< Descriptor readValue(): length: %d", m_value.length()); return m_value; } // readValue @@ -227,7 +229,7 @@ int NimBLERemoteDescriptor::onWriteCB(uint16_t conn_handle, NIMBLE_LOGD(LOG_TAG, "Write complete; status=%d conn_handle=%d", error->status, conn_handle); - descriptor->m_semaphoreDescWrite.give(error->status); + NIMBLE_SEMAPHORE_GIVE(descriptor->m_pSemaphore, error->status) return 0; } @@ -265,7 +267,7 @@ bool NimBLERemoteDescriptor::writeValue(const uint8_t* data, size_t length, bool } do { - m_semaphoreDescWrite.take("WriteDescriptor"); + NIMBLE_SEMAPHORE_TAKE(m_pSemaphore, "Descriptor Write") if(length > mtu) { NIMBLE_LOGI(LOG_TAG,"long write %d bytes", length); @@ -282,12 +284,11 @@ bool NimBLERemoteDescriptor::writeValue(const uint8_t* data, size_t length, bool if (rc != 0) { NIMBLE_LOGE(LOG_TAG, "Error: Failed to write descriptor; rc=%d", rc); - m_semaphoreDescWrite.give(); + NIMBLE_SEMAPHORE_DELETE(m_pSemaphore) return false; } - rc = m_semaphoreDescWrite.wait("WriteDescriptor"); - + rc = NIMBLE_SEMAPHORE_WAIT(m_pSemaphore) switch(rc){ case 0: case BLE_HS_EDONE: @@ -306,10 +307,12 @@ bool NimBLERemoteDescriptor::writeValue(const uint8_t* data, size_t length, bool break; /* Else falls through. */ default: + NIMBLE_SEMAPHORE_DELETE(m_pSemaphore) return false; } } while(rc != 0 && retryCount--); + NIMBLE_SEMAPHORE_DELETE(m_pSemaphore) NIMBLE_LOGD(LOG_TAG, "<< Descriptor writeValue, rc: %d",rc); return (rc == 0); } // writeValue @@ -335,13 +338,5 @@ bool NimBLERemoteDescriptor::writeValue(uint8_t newValue, bool response) { } // writeValue -/** - * @brief In the event of an error this is called to make sure we don't block. - */ -void NimBLERemoteDescriptor::releaseSemaphores() { - m_semaphoreDescWrite.give(1); - m_semaphoreReadDescrEvt.give(1); -} - #endif // #if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL) #endif /* CONFIG_BT_ENABLED */ diff --git a/src/NimBLERemoteDescriptor.h b/src/NimBLERemoteDescriptor.h index 47632926..c0dad03c 100644 --- a/src/NimBLERemoteDescriptor.h +++ b/src/NimBLERemoteDescriptor.h @@ -52,10 +52,7 @@ class NimBLERemoteDescriptor { NimBLEUUID m_uuid; // UUID of this descriptor. std::string m_value; // Last received value of the descriptor. NimBLERemoteCharacteristic* m_pRemoteCharacteristic; // Reference to the Remote characteristic of which this descriptor is associated. - FreeRTOS::Semaphore m_semaphoreReadDescrEvt = FreeRTOS::Semaphore("ReadDescrEvt"); - FreeRTOS::Semaphore m_semaphoreDescWrite = FreeRTOS::Semaphore("WriteDescEvt"); - - + NimBLESemaphore* m_pSemaphore; }; #endif // #if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL) diff --git a/src/NimBLERemoteService.cpp b/src/NimBLERemoteService.cpp index 624de63e..0ab7e55b 100644 --- a/src/NimBLERemoteService.cpp +++ b/src/NimBLERemoteService.cpp @@ -31,7 +31,7 @@ static const char* LOG_TAG = "NimBLERemoteService"; */ NimBLERemoteService::NimBLERemoteService(NimBLEClient* pClient, const struct ble_gatt_svc* service) { - NIMBLE_LOGD(LOG_TAG, ">> BLERemoteService()"); + NIMBLE_LOGD(LOG_TAG, ">> NimBLERemoteService()"); m_pClient = pClient; switch (service->uuid.u.type) { case BLE_UUID_TYPE_16: @@ -47,11 +47,12 @@ NimBLERemoteService::NimBLERemoteService(NimBLEClient* pClient, const struct ble m_uuid = nullptr; break; } - m_startHandle = service->start_handle; - m_endHandle = service->end_handle; + m_startHandle = service->start_handle; + m_endHandle = service->end_handle; m_haveCharacteristics = false; + m_pSemaphore = nullptr; - NIMBLE_LOGD(LOG_TAG, "<< BLERemoteService()"); + NIMBLE_LOGD(LOG_TAG, "<< NimBLERemoteService()"); } @@ -120,7 +121,7 @@ int NimBLERemoteService::characteristicDiscCB(uint16_t conn_handle, /** All characteristics in this service discovered; start discovering * characteristics in the next service. */ - service->m_semaphoreGetCharEvt.give(0); + NIMBLE_SEMAPHORE_GIVE(service->m_pSemaphore, 0) rc = 0; break; } @@ -131,10 +132,8 @@ int NimBLERemoteService::characteristicDiscCB(uint16_t conn_handle, if (rc != 0) { /* Error; abort discovery. */ // pass non-zero to semaphore on error to indicate an error finding characteristics - // release memory from any characteristics we created - //service->removeCharacteristics(); --this will now be done when we clear services on returning with error NIMBLE_LOGE(LOG_TAG, "characteristicDiscCB() rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); - service->m_semaphoreGetCharEvt.give(1); + NIMBLE_SEMAPHORE_GIVE(service->m_pSemaphore, 1) } NIMBLE_LOGD(LOG_TAG,"<< Characteristic Discovered. status: %d", rc); return rc; @@ -150,9 +149,8 @@ bool NimBLERemoteService::retrieveCharacteristics() { NIMBLE_LOGD(LOG_TAG, ">> retrieveCharacteristics() for service: %s", getUUID().toString().c_str()); int rc = 0; - //removeCharacteristics(); // Forget any previous characteristics. - m_semaphoreGetCharEvt.take("retrieveCharacteristics"); + NIMBLE_SEMAPHORE_TAKE(m_pSemaphore, "Retrieve Chars") rc = ble_gattc_disc_all_chrs(m_pClient->getConnId(), m_startHandle, @@ -162,12 +160,15 @@ bool NimBLERemoteService::retrieveCharacteristics() { if (rc != 0) { NIMBLE_LOGE(LOG_TAG, "ble_gattc_disc_all_chrs: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); m_haveCharacteristics = false; - m_semaphoreGetCharEvt.give(); + NIMBLE_SEMAPHORE_DELETE(m_pSemaphore) return false; } - m_haveCharacteristics = (m_semaphoreGetCharEvt.wait("retrieveCharacteristics") == 0); - if(m_haveCharacteristics){ + rc = NIMBLE_SEMAPHORE_WAIT(m_pSemaphore) + NIMBLE_SEMAPHORE_DELETE(m_pSemaphore) + + if(rc == 0){ + m_haveCharacteristics = true; uint16_t endHdl = 0xFFFF; NIMBLE_LOGD(LOG_TAG, "Found %d Characteristics", m_characteristicVector.size()); @@ -333,16 +334,5 @@ std::string NimBLERemoteService::toString() { } // toString -/** - * @brief called when an error occurrs and we need to release the semaphores to resume operations. - * Will release all characteristic and subsequently all descriptor semaphores for this service. - */ -void NimBLERemoteService::releaseSemaphores() { - for(auto &it: m_characteristicVector) { - it->releaseSemaphores(); - } - m_semaphoreGetCharEvt.give(1); -} - #endif // #if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL) #endif /* CONFIG_BT_ENABLED */ diff --git a/src/NimBLERemoteService.h b/src/NimBLERemoteService.h index c13dfb2c..209c9401 100644 --- a/src/NimBLERemoteService.h +++ b/src/NimBLERemoteService.h @@ -22,7 +22,7 @@ #include "NimBLEClient.h" #include "NimBLEUUID.h" -#include "FreeRTOS.h" +#include "NimBLESemaphore.h" #include "NimBLERemoteCharacteristic.h" #include @@ -39,11 +39,9 @@ class NimBLERemoteService { virtual ~NimBLERemoteService(); // Public methods - NimBLERemoteCharacteristic* getCharacteristic(const char* uuid); // Get the specified characteristic reference. - NimBLERemoteCharacteristic* getCharacteristic(const NimBLEUUID &uuid); // Get the specified characteristic reference. -// BLERemoteCharacteristic* getCharacteristic(uint16_t uuid); // Get the specified characteristic reference. - std::vector* getCharacteristics(); -// void getCharacteristics(std::map* pCharacteristicMap); + NimBLERemoteCharacteristic* getCharacteristic(const char* uuid); // Get the specified characteristic reference. + NimBLERemoteCharacteristic* getCharacteristic(const NimBLEUUID &uuid); // Get the specified characteristic reference. + std::vector* getCharacteristics(); NimBLEClient* getClient(void); // Get a reference to the client associated with this service. uint16_t getHandle(); // Get the handle of this service. @@ -68,7 +66,6 @@ class NimBLERemoteService { uint16_t getStartHandle(); // Get the start handle for this service. uint16_t getEndHandle(); // Get the end handle for this service. - void releaseSemaphores(); void removeCharacteristics(); // Properties @@ -78,10 +75,10 @@ class NimBLERemoteService { bool m_haveCharacteristics; // Have we previously obtained the characteristics. NimBLEClient* m_pClient; - FreeRTOS::Semaphore m_semaphoreGetCharEvt = FreeRTOS::Semaphore("GetCharEvt"); NimBLEUUID m_uuid; // The UUID of this service. uint16_t m_startHandle; // The starting handle of this service. uint16_t m_endHandle; // The ending handle of this service. + NimBLESemaphore* m_pSemaphore; }; // BLERemoteService #endif // #if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL) diff --git a/src/NimBLEScan.cpp b/src/NimBLEScan.cpp index 74c59102..88005d0d 100644 --- a/src/NimBLEScan.cpp +++ b/src/NimBLEScan.cpp @@ -248,7 +248,8 @@ bool NimBLEScan::start(uint32_t duration, void (*scanCompleteCB)(NimBLEScanResul } m_stopped = false; - m_semaphoreScanEnd.take("start"); + + m_semaphoreScanEnd.take(); // Save the callback to be invoked when the scan completes. m_scanCompleteCB = scanCompleteCB; @@ -298,7 +299,7 @@ bool NimBLEScan::start(uint32_t duration, void (*scanCompleteCB)(NimBLEScanResul */ NimBLEScanResults NimBLEScan::start(uint32_t duration, bool is_continue) { if(start(duration, nullptr, is_continue)) { - m_semaphoreScanEnd.wait("start"); // Wait for the semaphore to release. + m_semaphoreScanEnd.wait(); // Wait for the semaphore to release. } return m_scanResults; } // start diff --git a/src/NimBLEScan.h b/src/NimBLEScan.h index a1884e4a..25742054 100644 --- a/src/NimBLEScan.h +++ b/src/NimBLEScan.h @@ -20,7 +20,7 @@ #if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER) #include "NimBLEAdvertisedDevice.h" -#include "FreeRTOS.h" +#include "NimBLESemaphore.h" #include "host/ble_gap.h" @@ -83,7 +83,7 @@ class NimBLEScan { bool m_stopped; bool m_wantDuplicates; NimBLEScanResults m_scanResults; - FreeRTOS::Semaphore m_semaphoreScanEnd = FreeRTOS::Semaphore("ScanEnd"); + NimBLESemaphore m_semaphoreScanEnd = NimBLESemaphore("ScanEnd"); uint32_t m_duration; }; diff --git a/src/NimBLESemaphore.cpp b/src/NimBLESemaphore.cpp new file mode 100644 index 00000000..329f669e --- /dev/null +++ b/src/NimBLESemaphore.cpp @@ -0,0 +1,174 @@ +/* + * NimBLESemaphore.cpp + * + * Created: on May 7 2020 + * Author H2zero + * + * Originally: + * FreeRTOS.cpp + * + * Created on: Feb 24, 2017 + * Author: kolban + */ +#include "sdkconfig.h" +#include "NimBLESemaphore.h" +#include "NimBLELog.h" + +static const char* LOG_TAG = "NimBLESemaphore"; + + +/** + * @brief Wait for a semaphore to be released by trying to take it and + * then releasing it again. + * @param [in] owner A debug tag. + * @return The value associated with the semaphore. + */ +uint32_t NimBLESemaphore::wait() { + m_refCount++; + NIMBLE_LOGD(LOG_TAG, ">> wait: Semaphore waiting: %s", toString().c_str()); + xSemaphoreTake(m_semaphore, portMAX_DELAY); + + m_refCount--; + xSemaphoreGive(m_semaphore); + + NIMBLE_LOGD(LOG_TAG, "<< wait: Semaphore released: %s", toString().c_str()); + return m_value; +} // wait + + +/** + * @brief Wait for a semaphore to be released in a given period of time by trying to take it and + * then releasing it again. The value associated with the semaphore can be taken by value() call after return + * @param [in] owner A debug tag. + * @param [in] timeoutMs timeout to wait in ms. + * @return True if we took the semaphore within timeframe. + */ +bool NimBLESemaphore::timedWait(uint32_t timeoutMs) { + NIMBLE_LOGD(LOG_TAG, ">> wait: Semaphore waiting: %s", toString().c_str()); + + auto ret = xSemaphoreTake(m_semaphore, timeoutMs); + + xSemaphoreGive(m_semaphore); + + NIMBLE_LOGD(LOG_TAG, "<< wait: Semaphore %s released: %d", toString().c_str(), ret); + return ret; +} // timedWait + + +NimBLESemaphore::NimBLESemaphore(const char *name) { + m_semaphore = xSemaphoreCreateBinary(); + xSemaphoreGive(m_semaphore); + m_name = name; + m_owner = ""; + m_value = 0; + m_refCount = 0; +} + +NimBLESemaphore::NimBLESemaphore() { + m_semaphore = xSemaphoreCreateBinary(); + xSemaphoreGive(m_semaphore); + m_owner = ""; + m_value = 0; + m_refCount = 0; +} + + +NimBLESemaphore::~NimBLESemaphore() { + //NIMBLE_LOGD(LOG_TAG, "Semaphore delete: %s", toString().c_str()); + vSemaphoreDelete(m_semaphore); +} + + +/** + * @brief Give a semaphore. + * The Semaphore is given. + */ +void NimBLESemaphore::give() { + m_refCount--; + NIMBLE_LOGD(LOG_TAG, "Semaphore giving: %s", toString().c_str()); + xSemaphoreGive(m_semaphore); +} // Semaphore::give + + +/** + * @brief Give a semaphore. + * The Semaphore is given with an associated value. + * @param [in] value The value to associate with the semaphore. + */ +void NimBLESemaphore::give(uint32_t value) { + m_value = value; + give(); +} // give + + +/** + * @brief Give a semaphore from an ISR. + */ +void NimBLESemaphore::giveFromISR() { + BaseType_t higherPriorityTaskWoken; + xSemaphoreGiveFromISR(m_semaphore, &higherPriorityTaskWoken); +} // giveFromISR + + +bool NimBLESemaphore::take() { + return take(""); +} +/** + * @brief Take a semaphore. + * Take a semaphore and wait indefinitely. + * @param [in] owner The new owner (for debugging) + * @return True if we took the semaphore. + */ +bool NimBLESemaphore::take(const char* owner) { + m_refCount++; + NIMBLE_LOGD(LOG_TAG, "Semaphore taking: %s", toString().c_str()); + bool rc = xSemaphoreTake(m_semaphore, portMAX_DELAY) == pdTRUE; + if (rc) { + m_owner = owner; + NIMBLE_LOGD(LOG_TAG, "Semaphore taken: %s", toString().c_str()); + } else { + NIMBLE_LOGE(LOG_TAG, "Semaphore NOT taken: %s", toString().c_str()); + } + return rc; +} // Semaphore::take + + +/** + * @brief Take a semaphore. + * Take a semaphore but return if we haven't obtained it in the given period of milliseconds. + * @param [in] timeoutMs Timeout in milliseconds. + * @param [in] owner The new owner (for debugging) + * @return True if we took the semaphore. + */ +bool NimBLESemaphore::take(uint32_t timeoutMs) { + NIMBLE_LOGD(LOG_TAG, "Semaphore taking: %s", toString().c_str()); + bool rc = xSemaphoreTake(m_semaphore, timeoutMs / portTICK_PERIOD_MS) == pdTRUE; + if (rc) { + NIMBLE_LOGD(LOG_TAG, "Semaphore taken: %s", toString().c_str()); + } else { + NIMBLE_LOGE(LOG_TAG, "Semaphore NOT taken: %s", toString().c_str()); + } + return rc; +} // Semaphore::take + + +size_t NimBLESemaphore::getRefCount() { + return m_refCount; +} + +/** + * @brief Create a string representation of the semaphore. + * @return A string representation of the semaphore. + */ +std::string NimBLESemaphore::toString() { + char hex[14]; + std::string res = "owner: "; + res += m_owner; + snprintf(hex, sizeof(hex), " (0x%08x)", (uint32_t)m_semaphore); + res += hex; + snprintf(hex, sizeof(hex), " RefCnt: %d", m_refCount); + res += hex; + + return res; +} // toString + diff --git a/src/NimBLESemaphore.h b/src/NimBLESemaphore.h new file mode 100644 index 00000000..365e3c18 --- /dev/null +++ b/src/NimBLESemaphore.h @@ -0,0 +1,76 @@ +/* + * NimBLESemaphore.h + * + * Created: on May 7 2020 + * Author H2zero + * + * Originally: + * FreeRTOS.h + * + * Created on: Feb 24, 2017 + * Author: kolban + */ + +#ifndef MAIN_NIMBLE_SEMAPHORE_H_ +#define MAIN_NIMBLE_SEMAPHORE_H_ + +#include // Include the base FreeRTOS definitions. +#include // Include the semaphore definitions. + +#include + +#define NIMBLE_SEMAPHORE_TAKE(pSemaphore, owner) \ + if(pSemaphore == nullptr) { \ + pSemaphore = new NimBLESemaphore(); \ + } \ + pSemaphore->take(owner); \ + +#define NIMBLE_SEMAPHORE_GIVE(pSemaphore, value) \ + if(pSemaphore != nullptr) { \ + pSemaphore->give(value); \ + } + +#define NIMBLE_SEMAPHORE_WAIT(pSemaphore) \ + pSemaphore->wait(); + +/* A little hack here for thread safety, we save the semaphore in nimble_temp + * then set the semapore pointer to NULL so that any thread calling take at + * nearly the same time will create a new object, because (pSemaphore == nullptr), + * before this is deleted. If you know a better way, please let me know! + */ +#define NIMBLE_SEMAPHORE_DELETE(pSemaphore) \ + if(pSemaphore != nullptr && !pSemaphore->getRefCount()) { \ + NimBLESemaphore* nimble_temp = pSemaphore; \ + pSemaphore = nullptr; \ + delete(nimble_temp); \ + } + +/** + * @brief Interface to %FreeRTOS functions. + */ +class NimBLESemaphore { +public: + NimBLESemaphore(const char *name); + NimBLESemaphore(); + ~NimBLESemaphore(); + void give(); + void give(uint32_t value); + void giveFromISR(); + bool take(); + bool take(const char* owner); + bool take(uint32_t timeoutMs); + std::string toString(); + bool timedWait(uint32_t timeoutMs = portMAX_DELAY); + uint32_t wait(); + uint32_t value(){ return m_value; }; + size_t getRefCount(); + +private: + SemaphoreHandle_t m_semaphore; + const char *m_name; + const char *m_owner; + uint32_t m_value; + size_t m_refCount; +}; + +#endif /* MAIN_NIMBLE_SEMAPHORE_H_ */ diff --git a/src/NimBLEServer.h b/src/NimBLEServer.h index 903eb239..7afedca8 100644 --- a/src/NimBLEServer.h +++ b/src/NimBLEServer.h @@ -25,7 +25,6 @@ #include "NimBLEAdvertising.h" #include "NimBLEService.h" #include "NimBLESecurity.h" -#include "FreeRTOS.h" #include diff --git a/src/NimBLEService.h b/src/NimBLEService.h index 1cb0f615..8f63be87 100644 --- a/src/NimBLEService.h +++ b/src/NimBLEService.h @@ -23,8 +23,6 @@ #include "NimBLECharacteristic.h" #include "NimBLEServer.h" #include "NimBLEUUID.h" -#include "FreeRTOS.h" - class NimBLEServer; class NimBLECharacteristic;